loxxy 0.0.11 → 0.0.16
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +74 -0
- data/README.md +155 -55
- data/lib/loxxy.rb +23 -2
- data/lib/loxxy/ast/all_lox_nodes.rb +5 -0
- data/lib/loxxy/ast/ast_builder.rb +53 -27
- data/lib/loxxy/ast/ast_visitor.rb +43 -55
- data/lib/loxxy/ast/lox_binary_expr.rb +6 -0
- data/lib/loxxy/ast/lox_compound_expr.rb +1 -1
- data/lib/loxxy/ast/lox_node.rb +5 -0
- data/lib/loxxy/ast/lox_noop_expr.rb +16 -0
- data/lib/loxxy/ast/lox_print_stmt.rb +21 -0
- data/lib/loxxy/back_end/engine.rb +62 -0
- data/lib/loxxy/datatype/boolean.rb +11 -0
- data/lib/loxxy/datatype/builtin_datatype.rb +6 -0
- data/lib/loxxy/datatype/false.rb +22 -0
- data/lib/loxxy/datatype/lx_string.rb +46 -10
- data/lib/loxxy/datatype/nil.rb +22 -0
- data/lib/loxxy/datatype/number.rb +94 -1
- data/lib/loxxy/datatype/true.rb +22 -0
- data/lib/loxxy/front_end/grammar.rb +4 -4
- data/lib/loxxy/interpreter.rb +40 -0
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/engine_spec.rb +43 -0
- data/spec/datatype/boolean_spec.rb +31 -0
- data/spec/datatype/lx_string_spec.rb +32 -5
- data/spec/datatype/nil_spec.rb +26 -0
- data/spec/datatype/number_spec.rb +57 -0
- data/spec/front_end/parser_spec.rb +24 -73
- data/spec/interpreter_spec.rb +116 -0
- metadata +17 -2
data/lib/loxxy/datatype/true.rb
CHANGED
@@ -13,6 +13,28 @@ module Loxxy
|
|
13
13
|
def initialize
|
14
14
|
super(true)
|
15
15
|
end
|
16
|
+
|
17
|
+
# Is this object representing the true value in Lox?
|
18
|
+
# @return [TrueClass]
|
19
|
+
def true?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check for equality of a Lox True with another Lox object
|
24
|
+
# @param other [Datatype::True, TrueClass, Object]
|
25
|
+
# @return [Datatype::Boolean]
|
26
|
+
def ==(other)
|
27
|
+
thruthy = other.kind_of?(True) || other.kind_of?(TrueClass)
|
28
|
+
thruthy ? True.instance : False.instance
|
29
|
+
end
|
30
|
+
|
31
|
+
# Check for inequality of a Lox True with another Lox object
|
32
|
+
# @param other [Datatype::BuiltinDatatype, TrueClass, Object]
|
33
|
+
# @return [Datatype::Boolean]
|
34
|
+
def !=(other)
|
35
|
+
thruthy = other.kind_of?(True) || other.kind_of?(TrueClass)
|
36
|
+
thruthy ? False.instance : True.instance
|
37
|
+
end
|
16
38
|
end # class
|
17
39
|
|
18
40
|
True.instance.freeze # Make the sole instance immutable
|
@@ -26,8 +26,8 @@ module Loxxy
|
|
26
26
|
add_terminals('EOF')
|
27
27
|
|
28
28
|
# Top-level rule that matches an entire Lox program
|
29
|
-
rule('program' => 'EOF')
|
30
|
-
rule('program' => 'declaration_plus EOF')
|
29
|
+
rule('program' => 'EOF').as 'null_program'
|
30
|
+
rule('program' => 'declaration_plus EOF').as 'lox_program'
|
31
31
|
|
32
32
|
# Declarations: bind an identifier to something
|
33
33
|
rule('declaration_plus' => 'declaration_plus declaration')
|
@@ -58,7 +58,7 @@ module Loxxy
|
|
58
58
|
rule('statement' => 'whileStmt')
|
59
59
|
rule('statement' => 'block')
|
60
60
|
|
61
|
-
rule('exprStmt' => 'expression SEMICOLON')
|
61
|
+
rule('exprStmt' => 'expression SEMICOLON').as 'exprStmt'
|
62
62
|
|
63
63
|
rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
|
64
64
|
rule('forControl' => 'forInitialization forTest forUpdate')
|
@@ -73,7 +73,7 @@ module Loxxy
|
|
73
73
|
rule('elsePart_opt' => 'ELSE statement')
|
74
74
|
rule('elsePart_opt' => [])
|
75
75
|
|
76
|
-
rule('printStmt' => 'PRINT expression SEMICOLON')
|
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
79
|
rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './front_end/parser'
|
4
|
+
require_relative './ast/ast_visitor'
|
5
|
+
require_relative './back_end/engine'
|
6
|
+
|
7
|
+
module Loxxy
|
8
|
+
# A Lox tree-walking interpreter.
|
9
|
+
# It acts as a facade object that:
|
10
|
+
# - hides the internal plumbings of the front-end and back-end parts.
|
11
|
+
# - delegates all the core work to its subordinate objects.
|
12
|
+
# @note WIP: very crude implementation.
|
13
|
+
class Interpreter
|
14
|
+
# return [Hash]
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
# @param theOptions [Hash]
|
18
|
+
def initialize(theOptions = {})
|
19
|
+
@config = theOptions
|
20
|
+
end
|
21
|
+
|
22
|
+
# Evaluate the given Lox program.
|
23
|
+
# Return the result of the last executed expression (if any)
|
24
|
+
# @param lox_input [String] Lox program to evaluate
|
25
|
+
# @return [Loxxy::Datatype::BuiltinDatatype]
|
26
|
+
def evaluate(lox_input)
|
27
|
+
# Front-end scans, parses the input and blurps an AST...
|
28
|
+
parser = FrontEnd::Parser.new
|
29
|
+
|
30
|
+
# The AST is the data object passed to the back-end
|
31
|
+
ast_tree = parser.parse(lox_input)
|
32
|
+
visitor = Ast::ASTVisitor.new(ast_tree)
|
33
|
+
|
34
|
+
# Back-end launches the tree walking & reponds to visit events
|
35
|
+
# by executing the code determined by the visited AST node.
|
36
|
+
engine = BackEnd::Engine.new(config)
|
37
|
+
engine.execute(visitor)
|
38
|
+
end
|
39
|
+
end # class
|
40
|
+
end # module
|
data/lib/loxxy/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../lib/loxxy/back_end/engine'
|
8
|
+
|
9
|
+
module Loxxy
|
10
|
+
module BackEnd
|
11
|
+
describe Engine do
|
12
|
+
let(:sample_options) do
|
13
|
+
{ ostream: StringIO.new }
|
14
|
+
end
|
15
|
+
subject { Engine.new(sample_options) }
|
16
|
+
|
17
|
+
context 'Initialization:' do
|
18
|
+
it 'should accept a option Hash at initialization' do
|
19
|
+
expect { Engine.new(sample_options) }.not_to raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should know its config options' do
|
23
|
+
expect(subject.config).to eq(sample_options)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should have an empty stack' do
|
27
|
+
expect(subject.stack).to be_empty
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'Listening to visitor events:' do
|
32
|
+
let(:greeting) { Datatype::LXString.new('Hello, world') }
|
33
|
+
let(:sample_pos) { double('fake-position') }
|
34
|
+
let(:lit_expr) { Ast::LoxLiteralExpr.new(sample_pos, greeting) }
|
35
|
+
|
36
|
+
it "should react to 'before_literal_expr' event" do
|
37
|
+
expect { subject.before_literal_expr(lit_expr) }.not_to raise_error
|
38
|
+
expect(subject.stack.pop).to eq(greeting)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end # describe
|
42
|
+
end # module
|
43
|
+
end # module
|
@@ -0,0 +1,31 @@
|
|
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/datatype/boolean'
|
7
|
+
|
8
|
+
module Loxxy
|
9
|
+
module Datatype
|
10
|
+
describe Boolean do
|
11
|
+
let(:thruth_value) { false }
|
12
|
+
subject { Boolean.new(thruth_value) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'should accept a boolean value at initialization' do
|
16
|
+
expect { Boolean.new(thruth_value) }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should know its value' do
|
20
|
+
expect(subject.value).to eq(thruth_value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'Provided services:' do
|
25
|
+
it 'should give its display representation' do
|
26
|
+
expect(subject.to_str).to eq('false')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end # describe
|
30
|
+
end # module
|
31
|
+
end # module
|
@@ -22,12 +22,39 @@ module Loxxy
|
|
22
22
|
end
|
23
23
|
|
24
24
|
context 'Provided services:' do
|
25
|
-
it '
|
26
|
-
expect(subject).to eq(sample_text)
|
27
|
-
|
25
|
+
it 'should give its display representation' do
|
26
|
+
expect(subject.to_str).to eq(sample_text)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'compares with another Lox string' do
|
30
|
+
result = subject == LXString.new(sample_text.dup)
|
31
|
+
expect(result).to be_true
|
32
|
+
|
33
|
+
result = subject == LXString.new('other-text')
|
34
|
+
expect(result).to be_false
|
35
|
+
|
36
|
+
result = subject == LXString.new('')
|
37
|
+
expect(result).to be_false
|
38
|
+
|
39
|
+
# Two empty strings are equal
|
40
|
+
result = LXString.new('') == LXString.new('')
|
41
|
+
expect(result).to be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'compares with a Ruby string' do
|
45
|
+
result = subject == sample_text.dup
|
46
|
+
expect(result).to be_true
|
47
|
+
|
48
|
+
result = subject == 'other-text'
|
49
|
+
expect(result).to be_false
|
50
|
+
|
51
|
+
result = subject == ''
|
52
|
+
expect(result).to be_false
|
53
|
+
end
|
28
54
|
|
29
|
-
|
30
|
-
|
55
|
+
it 'performs the concatenation with another string' do
|
56
|
+
concatenation = LXString.new('str') + LXString.new('ing')
|
57
|
+
expect(concatenation == 'string').to be_true
|
31
58
|
end
|
32
59
|
end
|
33
60
|
end # describe
|
@@ -0,0 +1,26 @@
|
|
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/datatype/boolean'
|
7
|
+
|
8
|
+
module Loxxy
|
9
|
+
module Datatype
|
10
|
+
describe Nil do
|
11
|
+
subject { Nil.instance }
|
12
|
+
|
13
|
+
context 'Initialization:' do
|
14
|
+
it 'should know its value' do
|
15
|
+
expect(subject.value).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'Provided services:' do
|
20
|
+
it 'should give its display representation' do
|
21
|
+
expect(subject.to_str).to eq('nil')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end # describe
|
25
|
+
end # module
|
26
|
+
end # module
|
@@ -0,0 +1,57 @@
|
|
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/datatype/number'
|
7
|
+
|
8
|
+
module Loxxy
|
9
|
+
module Datatype
|
10
|
+
describe Number do
|
11
|
+
let(:sample_value) { -12.34 }
|
12
|
+
subject { Number.new(sample_value) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'should accept a Numeric value at initialization' do
|
16
|
+
expect { Number.new(sample_value) }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should know its value' do
|
20
|
+
expect(subject.value).to eq(sample_value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'Provided services:' do
|
25
|
+
it 'should compare with other Lox numbers' do
|
26
|
+
result = subject == Number.new(sample_value)
|
27
|
+
expect(result).to be_true
|
28
|
+
|
29
|
+
result = subject == Number.new(5)
|
30
|
+
expect(result).to be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should compare with Ruby numbers' do
|
34
|
+
result = subject == sample_value
|
35
|
+
expect(result).to be_true
|
36
|
+
|
37
|
+
result = subject == 5
|
38
|
+
expect(result).to be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should give its display representation' do
|
42
|
+
expect(subject.to_str).to eq(sample_value.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should compute the addition with another number' do
|
46
|
+
addition = subject + Number.new(10)
|
47
|
+
expect(addition == -2.34).to be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should compute the subtraction with another number' do
|
51
|
+
subtraction = subject - Number.new(10)
|
52
|
+
expect(subtraction == -22.34).to be_true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end # describe
|
56
|
+
end # module
|
57
|
+
end # module
|
@@ -37,7 +37,7 @@ module Loxxy
|
|
37
37
|
# Parse results MUST to comply to grammar rule:
|
38
38
|
# program => declaration_star EOF
|
39
39
|
# where the declaration_star MUST be empty
|
40
|
-
expect(aParseTree.root
|
40
|
+
expect(aParseTree.root).to be_kind_of(Ast::LoxNoopExpr)
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should cope with an empty input' do
|
@@ -64,7 +64,7 @@ module Loxxy
|
|
64
64
|
it 'should parse a false literal' do
|
65
65
|
input = 'false;'
|
66
66
|
ptree = subject.parse(input)
|
67
|
-
leaf =
|
67
|
+
leaf = ptree.root
|
68
68
|
expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
|
69
69
|
expect(leaf.literal).to be_equal(Datatype::False.instance)
|
70
70
|
end
|
@@ -72,7 +72,7 @@ module Loxxy
|
|
72
72
|
it 'should parse a true literal' do
|
73
73
|
input = 'true;'
|
74
74
|
ptree = subject.parse(input)
|
75
|
-
leaf =
|
75
|
+
leaf = ptree.root
|
76
76
|
expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
|
77
77
|
expect(leaf.literal).to be_equal(Datatype::True.instance)
|
78
78
|
end
|
@@ -81,7 +81,7 @@ module Loxxy
|
|
81
81
|
inputs = %w[1234; 12.34;]
|
82
82
|
inputs.each do |source|
|
83
83
|
ptree = subject.parse(source)
|
84
|
-
leaf =
|
84
|
+
leaf = ptree.root
|
85
85
|
expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
|
86
86
|
expect(leaf.literal).to be_kind_of(Datatype::Number)
|
87
87
|
expect(leaf.literal.value).to eq(source.to_f)
|
@@ -96,7 +96,7 @@ module Loxxy
|
|
96
96
|
]
|
97
97
|
inputs.each do |source|
|
98
98
|
ptree = subject.parse(source)
|
99
|
-
leaf =
|
99
|
+
leaf = ptree.root
|
100
100
|
expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
|
101
101
|
expect(leaf.literal).to be_kind_of(Datatype::LXString)
|
102
102
|
expect(leaf.literal.value).to eq(source.gsub(/(^")|(";$)/, ''))
|
@@ -106,7 +106,7 @@ module Loxxy
|
|
106
106
|
it 'should parse a nil literal' do
|
107
107
|
input = 'nil;'
|
108
108
|
ptree = subject.parse(input)
|
109
|
-
leaf =
|
109
|
+
leaf = ptree.root
|
110
110
|
expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
|
111
111
|
expect(leaf.literal).to be_equal(Datatype::Nil.instance)
|
112
112
|
end
|
@@ -119,21 +119,11 @@ module Loxxy
|
|
119
119
|
print "Hello, world!";
|
120
120
|
LOX_END
|
121
121
|
ptree = subject.parse(program)
|
122
|
-
|
123
|
-
expect(
|
124
|
-
(prnt_stmt
|
125
|
-
expect(prnt_stmt).to be_kind_of(
|
126
|
-
expect(prnt_stmt.
|
127
|
-
expect(prnt_stmt.subnodes.size).to eq(3)
|
128
|
-
expect(prnt_stmt.subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
|
129
|
-
expect(prnt_stmt.subnodes[0].symbol.name).to eq('PRINT')
|
130
|
-
expect(prnt_stmt.subnodes[1]).to be_kind_of(Loxxy::Ast::LoxLiteralExpr)
|
131
|
-
expect(prnt_stmt.subnodes[1].literal).to be_kind_of(Loxxy::Datatype::LXString)
|
132
|
-
expect(prnt_stmt.subnodes[1].literal.value).to eq('Hello, world!')
|
133
|
-
expect(prnt_stmt.subnodes[2]).to be_kind_of(Rley::PTree::TerminalNode)
|
134
|
-
expect(prnt_stmt.subnodes[2].symbol.name).to eq('SEMICOLON')
|
135
|
-
expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
|
136
|
-
expect(eof.symbol.name).to eq('EOF')
|
122
|
+
prnt_stmt = ptree.root
|
123
|
+
expect(prnt_stmt).to be_kind_of(Ast::LoxPrintStmt)
|
124
|
+
expect(prnt_stmt.subnodes[0]).to be_kind_of(Ast::LoxLiteralExpr)
|
125
|
+
expect(prnt_stmt.subnodes[0].literal).to be_kind_of(Loxxy::Datatype::LXString)
|
126
|
+
expect(prnt_stmt.subnodes[0].literal.value).to eq('Hello, world!')
|
137
127
|
end
|
138
128
|
end # context
|
139
129
|
|
@@ -141,10 +131,7 @@ LOX_END
|
|
141
131
|
it 'should parse the addition of two number literals' do
|
142
132
|
input = '123 + 456;'
|
143
133
|
ptree = subject.parse(input)
|
144
|
-
|
145
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
146
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
147
|
-
expr = parent.subnodes[0]
|
134
|
+
expr = ptree.root
|
148
135
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
149
136
|
expect(expr.operator).to eq(:+)
|
150
137
|
expect(expr.operands[0].literal.value).to eq(123)
|
@@ -154,10 +141,7 @@ LOX_END
|
|
154
141
|
it 'should parse the subtraction of two number literals' do
|
155
142
|
input = '4 - 3;'
|
156
143
|
ptree = subject.parse(input)
|
157
|
-
|
158
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
159
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
160
|
-
expr = parent.subnodes[0]
|
144
|
+
expr = ptree.root
|
161
145
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
162
146
|
expect(expr.operator).to eq(:-)
|
163
147
|
expect(expr.operands[0].literal.value).to eq(4)
|
@@ -167,10 +151,7 @@ LOX_END
|
|
167
151
|
it 'should parse multiple additive operations' do
|
168
152
|
input = '5 + 2 - 3;'
|
169
153
|
ptree = subject.parse(input)
|
170
|
-
|
171
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
172
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
173
|
-
expr = parent.subnodes[0]
|
154
|
+
expr = ptree.root
|
174
155
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
175
156
|
expect(expr.operator).to eq(:-)
|
176
157
|
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|
@@ -183,10 +164,7 @@ LOX_END
|
|
183
164
|
it 'should parse the division of two number literals' do
|
184
165
|
input = '8 / 2;'
|
185
166
|
ptree = subject.parse(input)
|
186
|
-
|
187
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
188
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
189
|
-
expr = parent.subnodes[0]
|
167
|
+
expr = ptree.root
|
190
168
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
191
169
|
expect(expr.operator).to eq(:/)
|
192
170
|
expect(expr.operands[0].literal.value).to eq(8)
|
@@ -196,10 +174,7 @@ LOX_END
|
|
196
174
|
it 'should parse the product of two number literals' do
|
197
175
|
input = '12.34 * 0.3;'
|
198
176
|
ptree = subject.parse(input)
|
199
|
-
|
200
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
201
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
202
|
-
expr = parent.subnodes[0]
|
177
|
+
expr = ptree.root
|
203
178
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
204
179
|
expect(expr.operator).to eq(:*)
|
205
180
|
expect(expr.operands[0].literal.value).to eq(12.34)
|
@@ -209,10 +184,7 @@ LOX_END
|
|
209
184
|
it 'should parse multiple multiplicative operations' do
|
210
185
|
input = '5 * 2 / 3;'
|
211
186
|
ptree = subject.parse(input)
|
212
|
-
|
213
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
214
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
215
|
-
expr = parent.subnodes[0]
|
187
|
+
expr = ptree.root
|
216
188
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
217
189
|
expect(expr.operator).to eq(:/)
|
218
190
|
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|
@@ -225,10 +197,7 @@ LOX_END
|
|
225
197
|
it 'should parse combination of terms and factors' do
|
226
198
|
input = '5 + 2 / 3;'
|
227
199
|
ptree = subject.parse(input)
|
228
|
-
|
229
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
230
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
231
|
-
expr = parent.subnodes[0]
|
200
|
+
expr = ptree.root
|
232
201
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
233
202
|
expect(expr.operator).to eq(:+)
|
234
203
|
expect(expr.operands[0].literal.value).to eq(5)
|
@@ -243,10 +212,7 @@ LOX_END
|
|
243
212
|
it 'should parse the concatenation of two string literals' do
|
244
213
|
input = '"Lo" + "ve";'
|
245
214
|
ptree = subject.parse(input)
|
246
|
-
|
247
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
248
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
249
|
-
expr = parent.subnodes[0]
|
215
|
+
expr = ptree.root
|
250
216
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
251
217
|
expect(expr.operator).to eq(:+)
|
252
218
|
expect(expr.operands[0].literal.value).to eq('Lo')
|
@@ -259,10 +225,7 @@ LOX_END
|
|
259
225
|
%w[> >= < <=].each do |predicate|
|
260
226
|
input = "3 #{predicate} 2;"
|
261
227
|
ptree = subject.parse(input)
|
262
|
-
|
263
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
264
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
265
|
-
expr = parent.subnodes[0]
|
228
|
+
expr = ptree.root
|
266
229
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
267
230
|
expect(expr.operator).to eq(predicate.to_sym)
|
268
231
|
expect(expr.operands[0].literal.value).to eq(3)
|
@@ -276,10 +239,7 @@ LOX_END
|
|
276
239
|
%w[!= ==].each do |predicate|
|
277
240
|
input = "3 #{predicate} 2;"
|
278
241
|
ptree = subject.parse(input)
|
279
|
-
|
280
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
281
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
282
|
-
expr = parent.subnodes[0]
|
242
|
+
expr = ptree.root
|
283
243
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
284
244
|
expect(expr.operator).to eq(predicate.to_sym)
|
285
245
|
expect(expr.operands[0].literal.value).to eq(3)
|
@@ -290,10 +250,7 @@ LOX_END
|
|
290
250
|
it 'should parse combination of equality expressions' do
|
291
251
|
input = '5 != 2 == false; // A bit contrived example'
|
292
252
|
ptree = subject.parse(input)
|
293
|
-
|
294
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
295
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
296
|
-
expr = parent.subnodes[0]
|
253
|
+
expr = ptree.root
|
297
254
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
298
255
|
expect(expr.operator).to eq(:==)
|
299
256
|
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|
@@ -309,10 +266,7 @@ LOX_END
|
|
309
266
|
%w[or and].each do |connector|
|
310
267
|
input = "5 > 2 #{connector} 3 <= 4;"
|
311
268
|
ptree = subject.parse(input)
|
312
|
-
|
313
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
314
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
315
|
-
expr = parent.subnodes[0]
|
269
|
+
expr = ptree.root
|
316
270
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
317
271
|
expect(expr.operator).to eq(connector.to_sym)
|
318
272
|
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|
@@ -329,10 +283,7 @@ LOX_END
|
|
329
283
|
it 'should parse a combinations of logical expressions' do
|
330
284
|
input = '4 > 3 and 1 < 2 or 4 >= 5;'
|
331
285
|
ptree = subject.parse(input)
|
332
|
-
|
333
|
-
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
334
|
-
expect(parent.symbol.name).to eq('exprStmt')
|
335
|
-
expr = parent.subnodes[0]
|
286
|
+
expr = ptree.root
|
336
287
|
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
337
288
|
expect(expr.operator).to eq(:or) # or has lower precedence than and
|
338
289
|
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|