loxxy 0.0.20 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'entry'
4
+ require_relative '../datatype/all_datatypes'
5
+
6
+ module Loxxy
7
+ module BackEnd
8
+ # Representation of a Lox variable.
9
+ # It is a named slot that can be associated with a value at the time.
10
+ class Variable
11
+ include Entry # Add expected behaviour for symbol table entries
12
+
13
+ # @return [Datatype::BuiltinDatatype] the value assigned to the variable
14
+ attr_reader :value
15
+
16
+ # Create a variable with given name and initial value
17
+ # @param aName [String] The name of the variable
18
+ # @param aValue [Datatype::BuiltinDatatype] the initial assigned value
19
+ def initialize(aName, aValue = Datatype::Nil.instance)
20
+ init_name(aName)
21
+ assign(aValue)
22
+ end
23
+
24
+ # @param aValue [Datatype::BuiltinDatatype] the assigned value
25
+ def assign(aValue)
26
+ @value = aValue
27
+ end
28
+ end # class
29
+ end # module
30
+ end # module
@@ -62,6 +62,12 @@ module Loxxy
62
62
  value.to_s # Default implementation...
63
63
  end
64
64
 
65
+ # Part of the 'visitee' role in Visitor design pattern.
66
+ # @param visitor [Ast::ASTVisitor] the visitor
67
+ def accept(visitor)
68
+ visitor.visit_builtin(self)
69
+ end
70
+
65
71
  protected
66
72
 
67
73
  def validated_value(aValue)
@@ -30,8 +30,8 @@ module Loxxy
30
30
  rule('program' => 'declaration_plus EOF').as 'lox_program'
31
31
 
32
32
  # Declarations: bind an identifier to something
33
- rule('declaration_plus' => 'declaration_plus declaration')
34
- rule('declaration_plus' => 'declaration')
33
+ rule('declaration_plus' => 'declaration_plus declaration').as 'declaration_plus_more'
34
+ rule('declaration_plus' => 'declaration').as 'declaration_plus_end'
35
35
  rule('declaration' => 'classDecl')
36
36
  rule('declaration' => 'funDecl')
37
37
  rule('declaration' => 'varDecl')
@@ -46,8 +46,8 @@ module Loxxy
46
46
 
47
47
  rule('funDecl' => 'FUN function')
48
48
 
49
- rule('varDecl' => 'VAR IDENTIFIER SEMICOLON')
50
- rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON')
49
+ rule('varDecl' => 'VAR IDENTIFIER SEMICOLON').as 'var_declaration'
50
+ rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON').as 'var_initialization'
51
51
 
52
52
  # Statements: produce side effects, but don't introduce bindings
53
53
  rule('statement' => 'exprStmt')
@@ -76,14 +76,14 @@ module Loxxy
76
76
  rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
77
77
  rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
78
78
  rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement')
79
- rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
79
+ rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE').as 'block_stmt'
80
80
  rule('block' => 'LEFT_BRACE RIGHT_BRACE')
81
81
 
82
82
  # Expressions: produce values
83
83
  rule('expression_opt' => 'expression')
84
84
  rule('expression_opt' => [])
85
85
  rule('expression' => 'assignment')
86
- rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
86
+ rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment').as 'assign_expr'
87
87
  rule('assignment' => 'logic_or')
88
88
  rule('owner_opt' => 'call DOT')
89
89
  rule('owner_opt' => [])
@@ -137,7 +137,7 @@ module Loxxy
137
137
  rule('primary' => 'THIS')
138
138
  rule('primary' => 'NUMBER').as 'literal_expr'
139
139
  rule('primary' => 'STRING').as 'literal_expr'
140
- rule('primary' => 'IDENTIFIER')
140
+ rule('primary' => 'IDENTIFIER').as 'variable_expr'
141
141
  rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN').as 'grouping_expr'
142
142
  rule('primary' => 'SUPER DOT IDENTIFIER')
143
143
 
@@ -31,7 +31,7 @@ module Loxxy
31
31
  ast_tree = parser.parse(lox_input)
32
32
  visitor = Ast::ASTVisitor.new(ast_tree)
33
33
 
34
- # Back-end launches the tree walking & reponds to visit events
34
+ # Back-end launches the tree walking & responds to visit events
35
35
  # by executing the code determined by the visited AST node.
36
36
  engine = BackEnd::Engine.new(config)
37
37
  engine.execute(visitor)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.20'
4
+ VERSION = '0.0.25'
5
5
  end
@@ -31,8 +31,17 @@ module Loxxy
31
31
  context 'Listening to visitor events:' do
32
32
  let(:greeting) { Datatype::LXString.new('Hello, world') }
33
33
  let(:sample_pos) { double('fake-position') }
34
+ let(:var_decl) { Ast::LoxVarStmt.new(sample_pos, 'greeting', greeting) }
34
35
  let(:lit_expr) { Ast::LoxLiteralExpr.new(sample_pos, greeting) }
35
36
 
37
+
38
+ it "should react to 'after_var_stmt' event" do
39
+ expect { subject.after_var_stmt(var_decl) }.not_to raise_error
40
+ current_env = subject.symbol_table.current_env
41
+ expect(current_env.defns['greeting']).to be_kind_of(Variable)
42
+ expect(current_env.defns['greeting'].value).to eq(greeting)
43
+ end
44
+
36
45
  it "should react to 'before_literal_expr' event" do
37
46
  expect { subject.before_literal_expr(lit_expr) }.not_to raise_error
38
47
  expect(subject.stack.pop).to eq(greeting)
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+
5
+ # Load the class under test
6
+ require_relative '../../lib/loxxy/back_end/environment'
7
+
8
+ module Loxxy
9
+ module BackEnd
10
+ describe Environment do
11
+ let(:foo) { Datatype::LXString.new('foo') }
12
+ let(:bar) { Datatype::LXString.new('bar') }
13
+ let(:mother) { Environment.new }
14
+ subject { Environment.new(mother) }
15
+
16
+ # Shortand factory method.
17
+ def var(aName, aValue)
18
+ Variable.new(aName, aValue)
19
+ end
20
+
21
+ context 'Initialization:' do
22
+ it 'could be initialized without argument' do
23
+ expect { Environment.new }.not_to raise_error
24
+ end
25
+
26
+ it 'could be initialized with a parent environment' do
27
+ expect { Environment.new(mother) }.not_to raise_error
28
+ end
29
+
30
+ it "shouldn't have definitions by default" do
31
+ expect(subject.defns).to be_empty
32
+ end
33
+
34
+ it 'should know its parent (if any)' do
35
+ expect(subject.enclosing).to eq(mother)
36
+ end
37
+ end # context
38
+
39
+ context 'Provided services:' do
40
+ it 'should accept the addition of a variable' do
41
+ subject.insert(var('a', foo))
42
+ expect(subject.defns).not_to be_empty
43
+ var_a = subject.defns['a']
44
+ expect(var_a).to be_kind_of(Variable)
45
+ expect(var_a.name).to eq('a')
46
+ end
47
+
48
+ it 'should accept the addition of multiple variables' do
49
+ subject.insert(var('a', foo))
50
+ expect(subject.defns).not_to be_empty
51
+
52
+ subject.insert(var('b', bar))
53
+ var_b = subject.defns['b']
54
+ expect(var_b).to be_kind_of(Variable)
55
+ expect(var_b.name).to eq('b')
56
+ end
57
+
58
+ # it 'should set the suffix of just created variable' do
59
+ # subject.insert(var('a'))
60
+ # var_a = subject.defns['a']
61
+ # expect(var_a.suffix).to eq("_#{subject.object_id.to_s(16)}")
62
+ # end
63
+
64
+ # it 'should complain when variable names collide' do
65
+ # subject.insert(var('c'))
66
+ # expect(subject.defns['c']).to be_kind_of(Datatype::Variable)
67
+ # err = StandardError
68
+ # err_msg = "Variable with name 'c' already exists."
69
+ # expect { subject.insert(var('c')) }.to raise_error(err, err_msg)
70
+ # end
71
+ end # context
72
+ end # describe
73
+ end # module
74
+ end # module
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/loxxy/back_end/variable'
5
+
6
+ # Load the class under test
7
+ require_relative '../../lib/loxxy/back_end/symbol_table'
8
+
9
+ module Loxxy
10
+ module BackEnd
11
+ describe SymbolTable do
12
+ subject { SymbolTable.new }
13
+
14
+ context 'Initialization:' do
15
+ it 'should be initialized without argument' do
16
+ expect { SymbolTable.new }.not_to raise_error
17
+ end
18
+
19
+ it 'should have a root BackEnd' do
20
+ expect(subject.root).not_to be_nil
21
+ expect(subject.current_env).to eq(subject.root)
22
+ expect(subject.root).to be_kind_of(BackEnd::Environment)
23
+ end
24
+
25
+ it "shouldn't have names at initialization" do
26
+ expect(subject.name2envs).to be_empty
27
+ end
28
+
29
+ it 'should be empty at initialization' do
30
+ expect(subject).to be_empty
31
+ end
32
+ end # context
33
+
34
+ context 'Provided services:' do
35
+ def var(aName)
36
+ Variable.new(aName)
37
+ end
38
+
39
+ it 'should allow the addition of a variable' do
40
+ expect { subject.insert(var('q')) }.not_to raise_error
41
+ expect(subject).not_to be_empty
42
+ expect(subject.name2envs['q']).to be_kind_of(Array)
43
+ expect(subject.name2envs['q'].size).to eq(1)
44
+ expect(subject.name2envs['q'].first).to eq(subject.current_env)
45
+ expect(subject.current_env.defns['q']).to be_kind_of(BackEnd::Variable)
46
+ end
47
+
48
+ it 'should allow the addition of several labels for same env' do
49
+ subject.insert(var('q'))
50
+ # expect(i_name).to match(/^q_[0-9a-z]*$/)
51
+
52
+ expect { subject.insert(var('x')) }.not_to raise_error
53
+ expect(subject.name2envs['x']).to be_kind_of(Array)
54
+ expect(subject.name2envs['x'].first).to eq(subject.current_env)
55
+ expect(subject.current_env.defns['x']).to be_kind_of(BackEnd::Variable)
56
+ end
57
+
58
+ it 'should allow the entry into a new scope' do
59
+ subject.insert(var('q'))
60
+ new_env = BackEnd::Environment.new
61
+ expect { subject.enter_environment(new_env) }.not_to raise_error
62
+ expect(subject.current_env).to eq(new_env)
63
+ expect(subject.current_env.enclosing).to eq(subject.root)
64
+ expect(subject.name2envs['q']).to eq([subject.root])
65
+ end
66
+
67
+ it 'should allow the addition of same name in different scopes' do
68
+ subject.insert(var('q'))
69
+ subject.enter_environment(BackEnd::Environment.new)
70
+ subject.insert(var('q'))
71
+ expect(subject.name2envs['q']).to be_kind_of(Array)
72
+ expect(subject.name2envs['q'].size).to eq(2)
73
+ expect(subject.name2envs['q'].first).to eq(subject.root)
74
+ expect(subject.name2envs['q'].last).to eq(subject.current_env)
75
+ expect(subject.current_env.defns['q']).to be_kind_of(BackEnd::Variable)
76
+ end
77
+
78
+ it 'should allow the removal of a scope' do
79
+ subject.insert(var('q'))
80
+ new_env = BackEnd::Environment.new
81
+ subject.enter_environment(new_env)
82
+ subject.insert(var('q'))
83
+ expect(subject.name2envs['q'].size).to eq(2)
84
+
85
+ expect { subject.leave_environment }.not_to raise_error
86
+ expect(subject.current_env).to eq(subject.root)
87
+ expect(subject.name2envs['q'].size).to eq(1)
88
+ expect(subject.name2envs['q']).to eq([subject.root])
89
+ end
90
+
91
+ it 'should allow the search of an entry based on its name' do
92
+ subject.insert(var('q'))
93
+ subject.insert(var('x'))
94
+ subject.enter_environment(BackEnd::Environment.new)
95
+ subject.insert(var('q'))
96
+ subject.insert(var('y'))
97
+
98
+ # Search for unknown name
99
+ expect(subject.lookup('z')).to be_nil
100
+
101
+ # Search for existing unique names
102
+ expect(subject.lookup('y')).to eq(subject.current_env.defns['y'])
103
+ expect(subject.lookup('x')).to eq(subject.root.defns['x'])
104
+
105
+ # Search for redefined name
106
+ expect(subject.lookup('q')).to eq(subject.current_env.defns['q'])
107
+ end
108
+
109
+ # it 'should allow the search of an entry based on its i_name' do
110
+ # subject.insert(var('q'))
111
+ # i_name_x = subject.insert(var('x'))
112
+ # subject.enter_environment(BackEnd::Environment.new)
113
+ # i_name_q2 = subject.insert(var('q'))
114
+ # i_name_y = subject.insert(var('y'))
115
+
116
+ # # Search for unknown i_name
117
+ # expect(subject.lookup_i_name('dummy')).to be_nil
118
+
119
+ # curr_scope = subject.current_env
120
+ # # # Search for existing unique names
121
+ # expect(subject.lookup_i_name(i_name_y)).to eq(curr_scope.defns['y'])
122
+ # expect(subject.lookup_i_name(i_name_x)).to eq(subject.root.defns['x'])
123
+
124
+ # # Search for redefined name
125
+ # expect(subject.lookup_i_name(i_name_q2)).to eq(curr_scope.defns['q'])
126
+ # end
127
+
128
+ it 'should list all the variables defined in all the szcope chain' do
129
+ subject.insert(var('q'))
130
+ subject.enter_environment(BackEnd::Environment.new)
131
+ subject.insert(var('x'))
132
+ subject.enter_environment(BackEnd::Environment.new)
133
+ subject.insert(var('y'))
134
+ subject.insert(var('x'))
135
+
136
+ vars = subject.all_variables
137
+ expect(vars.map(&:name)).to eq(%w[q x y x])
138
+ end
139
+ end # context
140
+ end # describe
141
+ end # module
142
+ end # module
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+
5
+ # Load the class under test
6
+ require_relative '../../lib/loxxy/back_end/variable'
7
+
8
+
9
+ module Loxxy
10
+ module BackEnd
11
+ describe Variable do
12
+ let(:sample_name) { 'iAmAVariable' }
13
+ let(:sample_value) { 'here is my value' }
14
+ subject { Variable.new(sample_name, sample_value) }
15
+
16
+ context 'Initialization:' do
17
+ it 'should be initialized with a name and a value, or...' do
18
+ expect { Variable.new(sample_name, sample_value) }.not_to raise_error
19
+ end
20
+
21
+ it 'should be initialized with just a name' do
22
+ expect { Variable.new(sample_name) }.not_to raise_error
23
+ end
24
+
25
+ it 'should know its name' do
26
+ expect(subject.name).to eq(sample_name)
27
+ end
28
+
29
+ it 'should have a frozen name' do
30
+ expect(subject.name).to be_frozen
31
+ end
32
+
33
+ it 'should know its value (if provided)' do
34
+ expect(subject.value).to eq(sample_value)
35
+ end
36
+
37
+ it 'should have a nil value otherwise' do
38
+ instance = Variable.new(sample_name)
39
+ expect(instance.value).to eq(Datatype::Nil.instance)
40
+ end
41
+
42
+ # it 'should know its default internal name' do
43
+ # # By default: internal name == label
44
+ # expect(subject.i_name).to eq(subject.label)
45
+ # end
46
+
47
+ # it 'should have a nil suffix' do
48
+ # expect(subject.suffix).to be_nil
49
+ # end
50
+ end # context
51
+
52
+ context 'Provided service:' do
53
+ let(:sample_suffix) { 'sample-suffix' }
54
+ it 'should have a label equal to its user-defined name' do
55
+ # expect(subject.label).to eq(subject.name)
56
+ end
57
+
58
+ it 'should accept a suffix' do
59
+ # expect { subject.suffix = sample_suffix }.not_to raise_error
60
+ # expect(subject.suffix).to eq(sample_suffix)
61
+ end
62
+
63
+ it 'should calculate its internal name' do
64
+ # # Rule: empty suffix => internal name == label
65
+ # subject.suffix = ''
66
+ # expect(subject.i_name).to eq(subject.label)
67
+
68
+ # # Rule: suffix starting underscore: internal name = label + suffix
69
+ # subject.suffix = '_10'
70
+ # expect(subject.i_name).to eq(subject.label + subject.suffix)
71
+
72
+ # # Rule: ... otherwise: internal name == suffix
73
+ # subject.suffix = sample_suffix
74
+ # expect(subject.i_name).to eq(subject.suffix)
75
+ end
76
+ end # context
77
+ end # describe
78
+ end # module
79
+ end # module
@@ -64,7 +64,8 @@ module Loxxy
64
64
  it 'should parse a false literal' do
65
65
  input = 'false;'
66
66
  ptree = subject.parse(input)
67
- leaf = ptree.root
67
+ expect(ptree.root).to be_kind_of(Ast::LoxSeqDecl)
68
+ leaf = ptree.root.subnodes[0]
68
69
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
69
70
  expect(leaf.literal).to be_equal(Datatype::False.instance)
70
71
  end
@@ -72,7 +73,8 @@ module Loxxy
72
73
  it 'should parse a true literal' do
73
74
  input = 'true;'
74
75
  ptree = subject.parse(input)
75
- leaf = ptree.root
76
+ expect(ptree.root).to be_kind_of(Ast::LoxSeqDecl)
77
+ leaf = ptree.root.subnodes[0]
76
78
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
77
79
  expect(leaf.literal).to be_equal(Datatype::True.instance)
78
80
  end
@@ -81,7 +83,7 @@ module Loxxy
81
83
  inputs = %w[1234; 12.34;]
82
84
  inputs.each do |source|
83
85
  ptree = subject.parse(source)
84
- leaf = ptree.root
86
+ leaf = ptree.root.subnodes[0]
85
87
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
86
88
  expect(leaf.literal).to be_kind_of(Datatype::Number)
87
89
  expect(leaf.literal.value).to eq(source.to_f)
@@ -96,7 +98,7 @@ module Loxxy
96
98
  ]
97
99
  inputs.each do |source|
98
100
  ptree = subject.parse(source)
99
- leaf = ptree.root
101
+ leaf = ptree.root.subnodes[0]
100
102
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
101
103
  expect(leaf.literal).to be_kind_of(Datatype::LXString)
102
104
  expect(leaf.literal.value).to eq(source.gsub(/(^")|(";$)/, ''))
@@ -106,7 +108,7 @@ module Loxxy
106
108
  it 'should parse a nil literal' do
107
109
  input = 'nil;'
108
110
  ptree = subject.parse(input)
109
- leaf = ptree.root
111
+ leaf = ptree.root.subnodes[0]
110
112
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
111
113
  expect(leaf.literal).to be_equal(Datatype::Nil.instance)
112
114
  end
@@ -119,7 +121,7 @@ module Loxxy
119
121
  print "Hello, world!";
120
122
  LOX_END
121
123
  ptree = subject.parse(program)
122
- prnt_stmt = ptree.root
124
+ prnt_stmt = ptree.root.subnodes[0]
123
125
  expect(prnt_stmt).to be_kind_of(Ast::LoxPrintStmt)
124
126
  expect(prnt_stmt.subnodes[0]).to be_kind_of(Ast::LoxLiteralExpr)
125
127
  expect(prnt_stmt.subnodes[0].literal).to be_kind_of(Loxxy::Datatype::LXString)
@@ -131,7 +133,7 @@ LOX_END
131
133
  it 'should parse the addition of two number literals' do
132
134
  input = '123 + 456;'
133
135
  ptree = subject.parse(input)
134
- expr = ptree.root
136
+ expr = ptree.root.subnodes[0]
135
137
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
136
138
  expect(expr.operator).to eq(:+)
137
139
  expect(expr.operands[0].literal.value).to eq(123)
@@ -141,7 +143,7 @@ LOX_END
141
143
  it 'should parse the subtraction of two number literals' do
142
144
  input = '4 - 3;'
143
145
  ptree = subject.parse(input)
144
- expr = ptree.root
146
+ expr = ptree.root.subnodes[0]
145
147
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
146
148
  expect(expr.operator).to eq(:-)
147
149
  expect(expr.operands[0].literal.value).to eq(4)
@@ -151,7 +153,7 @@ LOX_END
151
153
  it 'should parse multiple additive operations' do
152
154
  input = '5 + 2 - 3;'
153
155
  ptree = subject.parse(input)
154
- expr = ptree.root
156
+ expr = ptree.root.subnodes[0]
155
157
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
156
158
  expect(expr.operator).to eq(:-)
157
159
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -164,7 +166,7 @@ LOX_END
164
166
  it 'should parse the division of two number literals' do
165
167
  input = '8 / 2;'
166
168
  ptree = subject.parse(input)
167
- expr = ptree.root
169
+ expr = ptree.root.subnodes[0]
168
170
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
169
171
  expect(expr.operator).to eq(:/)
170
172
  expect(expr.operands[0].literal.value).to eq(8)
@@ -174,7 +176,7 @@ LOX_END
174
176
  it 'should parse the product of two number literals' do
175
177
  input = '12.34 * 0.3;'
176
178
  ptree = subject.parse(input)
177
- expr = ptree.root
179
+ expr = ptree.root.subnodes[0]
178
180
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
179
181
  expect(expr.operator).to eq(:*)
180
182
  expect(expr.operands[0].literal.value).to eq(12.34)
@@ -184,7 +186,7 @@ LOX_END
184
186
  it 'should parse multiple multiplicative operations' do
185
187
  input = '5 * 2 / 3;'
186
188
  ptree = subject.parse(input)
187
- expr = ptree.root
189
+ expr = ptree.root.subnodes[0]
188
190
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
189
191
  expect(expr.operator).to eq(:/)
190
192
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -197,7 +199,7 @@ LOX_END
197
199
  it 'should parse combination of terms and factors' do
198
200
  input = '5 + 2 / 3;'
199
201
  ptree = subject.parse(input)
200
- expr = ptree.root
202
+ expr = ptree.root.subnodes[0]
201
203
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
202
204
  expect(expr.operator).to eq(:+)
203
205
  expect(expr.operands[0].literal.value).to eq(5)
@@ -212,7 +214,7 @@ LOX_END
212
214
  it 'should parse the concatenation of two string literals' do
213
215
  input = '"Lo" + "ve";'
214
216
  ptree = subject.parse(input)
215
- expr = ptree.root
217
+ expr = ptree.root.subnodes[0]
216
218
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
217
219
  expect(expr.operator).to eq(:+)
218
220
  expect(expr.operands[0].literal.value).to eq('Lo')
@@ -225,7 +227,7 @@ LOX_END
225
227
  %w[> >= < <=].each do |predicate|
226
228
  input = "3 #{predicate} 2;"
227
229
  ptree = subject.parse(input)
228
- expr = ptree.root
230
+ expr = ptree.root.subnodes[0]
229
231
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
230
232
  expect(expr.operator).to eq(predicate.to_sym)
231
233
  expect(expr.operands[0].literal.value).to eq(3)
@@ -239,7 +241,7 @@ LOX_END
239
241
  %w[!= ==].each do |predicate|
240
242
  input = "3 #{predicate} 2;"
241
243
  ptree = subject.parse(input)
242
- expr = ptree.root
244
+ expr = ptree.root.subnodes[0]
243
245
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
244
246
  expect(expr.operator).to eq(predicate.to_sym)
245
247
  expect(expr.operands[0].literal.value).to eq(3)
@@ -250,7 +252,7 @@ LOX_END
250
252
  it 'should parse combination of equality expressions' do
251
253
  input = '5 != 2 == false; // A bit contrived example'
252
254
  ptree = subject.parse(input)
253
- expr = ptree.root
255
+ expr = ptree.root.subnodes[0]
254
256
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
255
257
  expect(expr.operator).to eq(:==)
256
258
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -266,7 +268,7 @@ LOX_END
266
268
  %w[or and].each do |connector|
267
269
  input = "5 > 2 #{connector} 3 <= 4;"
268
270
  ptree = subject.parse(input)
269
- expr = ptree.root
271
+ expr = ptree.root.subnodes[0]
270
272
  expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
271
273
  expect(expr.operator).to eq(connector.to_sym)
272
274
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -283,7 +285,7 @@ LOX_END
283
285
  it 'should parse a combinations of logical expressions' do
284
286
  input = '4 > 3 and 1 < 2 or 4 >= 5;'
285
287
  ptree = subject.parse(input)
286
- expr = ptree.root
288
+ expr = ptree.root.subnodes[0]
287
289
  expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
288
290
  expect(expr.operator).to eq(:or) # or has lower precedence than and
289
291
  expect(expr.operands[0]).to be_kind_of(Ast::LoxLogicalExpr)