loxxy 0.0.15 → 0.0.20

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.
@@ -19,6 +19,14 @@ module Loxxy
19
19
  def true?
20
20
  true
21
21
  end
22
+
23
+ # Check for equality of a Lox True with another Lox object / Ruby true
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
22
30
  end # class
23
31
 
24
32
  True.instance.freeze # Make the sole instance immutable
@@ -68,9 +68,9 @@ module Loxxy
68
68
  rule('forTest' => 'expression_opt SEMICOLON')
69
69
  rule('forUpdate' => 'expression_opt')
70
70
 
71
- rule('ifStmt' => 'IF ifCondition statement elsePart_opt')
72
- rule('ifCondition' => 'LEFT_PAREN expression RIGHT_PAREN')
73
- rule('elsePart_opt' => 'ELSE statement')
71
+ rule('ifStmt' => 'IF ifCondition statement elsePart_opt').as 'if_stmt'
72
+ rule('ifCondition' => 'LEFT_PAREN expression RIGHT_PAREN').as 'keep_symbol2'
73
+ rule('elsePart_opt' => 'ELSE statement').as 'keep_symbol2'
74
74
  rule('elsePart_opt' => [])
75
75
 
76
76
  rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
@@ -121,7 +121,7 @@ module Loxxy
121
121
  rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
122
122
  rule('multOp' => 'SLASH')
123
123
  rule('multOp' => 'STAR')
124
- rule('unary' => 'unaryOp unary')
124
+ rule('unary' => 'unaryOp unary').as 'unary_expr'
125
125
  rule('unary' => 'call')
126
126
  rule('unaryOp' => 'BANG')
127
127
  rule('unaryOp' => 'MINUS')
@@ -138,7 +138,7 @@ module Loxxy
138
138
  rule('primary' => 'NUMBER').as 'literal_expr'
139
139
  rule('primary' => 'STRING').as 'literal_expr'
140
140
  rule('primary' => 'IDENTIFIER')
141
- rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
141
+ rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN').as 'grouping_expr'
142
142
  rule('primary' => 'SUPER DOT IDENTIFIER')
143
143
 
144
144
  # Utility rules
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.15'
4
+ VERSION = '0.0.20'
5
5
  end
@@ -262,12 +262,12 @@ LOX_END
262
262
  end # context
263
263
 
264
264
  context 'Parsing logical expressions' do
265
- it 'should parse the logical operations betweentwo sub-expression' do
265
+ it 'should parse the logical operations between two sub-expression' do
266
266
  %w[or and].each do |connector|
267
267
  input = "5 > 2 #{connector} 3 <= 4;"
268
268
  ptree = subject.parse(input)
269
269
  expr = ptree.root
270
- expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
270
+ expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
271
271
  expect(expr.operator).to eq(connector.to_sym)
272
272
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
273
273
  expect(expr.operands[0].operator).to eq(:>)
@@ -284,9 +284,9 @@ LOX_END
284
284
  input = '4 > 3 and 1 < 2 or 4 >= 5;'
285
285
  ptree = subject.parse(input)
286
286
  expr = ptree.root
287
- expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
287
+ expect(expr).to be_kind_of(Ast::LoxLogicalExpr)
288
288
  expect(expr.operator).to eq(:or) # or has lower precedence than and
289
- expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
289
+ expect(expr.operands[0]).to be_kind_of(Ast::LoxLogicalExpr)
290
290
  expect(expr.operands[0].operator).to eq(:and)
291
291
  conjuncts = expr.operands[0].operands
292
292
  expect(conjuncts[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -25,32 +25,231 @@ module Loxxy
25
25
  end
26
26
  end # context
27
27
 
28
- context 'Evaluating Lox code:' do
29
- let(:hello_world) { 'print "Hello, world!";' }
30
-
31
- it 'should evaluate core data types' do
32
- result = subject.evaluate('true; // Not false')
33
- expect(result).to be_kind_of(Loxxy::Datatype::True)
34
- end
35
-
28
+ context 'Evaluating arithmetic operations code:' do
36
29
  it 'should evaluate an addition of two numbers' do
37
30
  result = subject.evaluate('123 + 456; // => 579')
38
31
  expect(result).to be_kind_of(Loxxy::Datatype::Number)
39
32
  expect(result == 579).to be_true
40
33
  end
41
34
 
42
- it 'should evaluate a substraction of two numbers' do
35
+ it 'should evaluate a subtraction of two numbers' do
43
36
  result = subject.evaluate('4 - 3; // => 1')
44
37
  expect(result).to be_kind_of(Loxxy::Datatype::Number)
45
38
  expect(result == 1).to be_true
46
39
  end
47
40
 
41
+ it 'should evaluate a multiplication of two numbers' do
42
+ result = subject.evaluate('5 * 3; // => 15')
43
+ expect(result).to be_kind_of(Loxxy::Datatype::Number)
44
+ expect(result == 15).to be_true
45
+ end
46
+
47
+ it 'should evaluate a division of two numbers' do
48
+ result = subject.evaluate('8 / 2; // => 4')
49
+ expect(result).to be_kind_of(Loxxy::Datatype::Number)
50
+ expect(result == 4).to be_true
51
+ end
52
+ end # context
53
+
54
+ context 'Evaluating Lox code:' do
55
+ let(:hello_world) { 'print "Hello, world!";' }
56
+
57
+ it 'should evaluate core data types' do
58
+ result = subject.evaluate('true; // Not false')
59
+ expect(result).to be_kind_of(Loxxy::Datatype::True)
60
+ end
61
+
48
62
  it 'should evaluate string concatenation' do
49
63
  result = subject.evaluate('"str" + "ing"; // => "string"')
50
64
  expect(result).to be_kind_of(Loxxy::Datatype::LXString)
51
65
  expect(result == 'string').to be_true
52
66
  end
53
67
 
68
+ it 'should perform the equality tests of two values' do
69
+ [
70
+ ['nil == nil;', true],
71
+ ['true == true;', true],
72
+ ['true == false;', false],
73
+ ['1 == 1;', true],
74
+ ['1 == 2;', false],
75
+ ['0 == 0;', true],
76
+ ['"str" == "str";', true],
77
+ ['"str" == "ing";', false],
78
+ ['"" == "";', true],
79
+ ['nil == false;', false],
80
+ ['false == 0;', false],
81
+ ['0 == "0";', false]
82
+ ].each do |(source, predicted)|
83
+ lox = Loxxy::Interpreter.new
84
+ result = lox.evaluate(source)
85
+ expect(result.value == predicted).to be_truthy
86
+ end
87
+ end
88
+
89
+ it 'should perform the inequality test of two values' do
90
+ [
91
+ ['nil != nil;', false],
92
+ ['true != true;', false],
93
+ ['true != false;', true],
94
+ ['1 != 1;', false],
95
+ ['1 != 2;', true],
96
+ ['0 != 0;', false],
97
+ ['"str" != "str";', false],
98
+ ['"str" != "ing";', true],
99
+ ['"" != "";', false],
100
+ ['nil != false;', true],
101
+ ['false != 0;', true],
102
+ ['0 != "0";', true]
103
+ ].each do |(source, predicted)|
104
+ lox = Loxxy::Interpreter.new
105
+ result = lox.evaluate(source)
106
+ expect(result.value == predicted).to be_truthy
107
+ end
108
+ end
109
+
110
+ it 'should evaluate a comparison of two numbers' do
111
+ [
112
+ ['1 < 2;', true],
113
+ ['2 < 2;', false],
114
+ ['2 < 1;', false],
115
+ ['1 <= 2;', true],
116
+ ['2 <= 2;', true],
117
+ ['2 <= 1;', false],
118
+ ['1 > 2;', false],
119
+ ['2 > 2;', false],
120
+ ['2 > 1;', true],
121
+ ['1 >= 2;', false],
122
+ ['2 >= 2;', true],
123
+ ['2 >= 1;', true],
124
+ ['0 < -0;', false],
125
+ ['-0 < 0;', false],
126
+ ['0 > -0;', false],
127
+ ['-0 > 0;', false],
128
+ ['0 <= -0;', true],
129
+ ['-0 <= 0;', true],
130
+ ['0 >= -0;', true],
131
+ ['-0 >= 0;', true]
132
+ ].each do |(source, predicted)|
133
+ lox = Loxxy::Interpreter.new
134
+ result = lox.evaluate(source)
135
+ expect(result.value == predicted).to be_truthy
136
+ end
137
+ end
138
+
139
+ it 'should evaluate the change sign of a number' do
140
+ [
141
+ ['- 3;', -3],
142
+ ['- - 3;', 3],
143
+ ['- - - 3;', -3]
144
+ ].each do |(source, predicted)|
145
+ lox = Loxxy::Interpreter.new
146
+ result = lox.evaluate(source)
147
+ expect(result.value == predicted).to be_truthy
148
+ end
149
+ end
150
+
151
+ it 'should evaluate the negation of an object' do
152
+ [
153
+ ['!true;', false],
154
+ ['!false;', true],
155
+ ['!!true;', true],
156
+ ['!123;', false],
157
+ ['!0;', false],
158
+ ['!nil;', true],
159
+ ['!"";', false]
160
+ ].each do |(source, predicted)|
161
+ lox = Loxxy::Interpreter.new
162
+ result = lox.evaluate(source)
163
+ expect(result.value == predicted).to be_truthy
164
+ end
165
+ end
166
+
167
+ it 'should evaluate the "conjunction" of two values' do
168
+ [
169
+ # Return the first falsey argument
170
+ ['false and 1;', false],
171
+ ['nil and 1;', nil],
172
+ ['true and 1;', 1],
173
+ ['1 and 2 and false;', false],
174
+ ['1 and 2 and nil;', nil],
175
+
176
+ # Return the last argument if all are truthy
177
+ ['1 and true;', true],
178
+ ['0 and true;', true],
179
+ ['"false" and 0;', 0],
180
+ ['1 and 2 and 3;', 3]
181
+
182
+ # TODO test short-circuit at first false argument
183
+ ].each do |(source, predicted)|
184
+ lox = Loxxy::Interpreter.new
185
+ result = lox.evaluate(source)
186
+ expect(result.value == predicted).to be_truthy
187
+ end
188
+ end
189
+
190
+ it 'should evaluate the "disjunction" of two values' do
191
+ [
192
+ # Return the first truthy argument
193
+ ['1 or true;', 1],
194
+ ['false or 1;', 1],
195
+ ['nil or 1;', 1],
196
+ ['false or false or true;', true],
197
+ ['1 and 2 and nil;', nil],
198
+
199
+ # Return the last argument if all are falsey
200
+ ['false or false;', false],
201
+ ['nil or false;', false],
202
+ ['false or nil;', nil],
203
+ ['false or false or false;', false],
204
+ ['false or false or nil;', nil]
205
+
206
+ # TODO test short-circuit at first false argument
207
+ ].each do |(source, predicted)|
208
+ lox = Loxxy::Interpreter.new
209
+ result = lox.evaluate(source)
210
+ expect(result.value == predicted).to be_truthy
211
+ end
212
+ end
213
+
214
+ it 'should supprt expressions between parentheses' do
215
+ [
216
+ ['3 + 4 * 5;', 23],
217
+ ['(3 + 4) * 5;', 35],
218
+ ['(5 - (3 - 1)) + -(1);', 2]
219
+ ].each do |(source, predicted)|
220
+ lox = Loxxy::Interpreter.new
221
+ result = lox.evaluate(source)
222
+ expect(result.value == predicted).to be_truthy
223
+ end
224
+ end
225
+
226
+ it 'should evaluate an if statement' do
227
+ [
228
+ # Evaluate the 'then' expression if the condition is true.
229
+ ['if (true) print "then-branch";', 'then-branch'],
230
+ ['if (false) print "ignored";', ''],
231
+ # TODO: test with then block body
232
+ # TODO: test with assignment in if condition
233
+
234
+ # Evaluate the 'else' expression if the condition is false.
235
+ ['if (true) print "then-branch"; else print "else-branch";', 'then-branch'],
236
+ ['if (false) print "then-branch"; else print "else-branch";', 'else-branch'],
237
+ ['if (0) print "then-branch"; else print "else-branch";', 'then-branch'],
238
+ ['if (nil) print "then-branch"; else print "else-branch";', 'else-branch'],
239
+ # TODO: test with else block body
240
+
241
+ # TODO: A dangling else binds to the right-most if.
242
+ # ['if (true) if (false) print "bad"; else print "good";', 'good'],
243
+ # ['if (false) if (true) print "bad"; else print "worse";', 'bad']
244
+ ].each do |(source, predicted)|
245
+ io = StringIO.new
246
+ cfg = { ostream: io }
247
+ lox = Loxxy::Interpreter.new(cfg)
248
+ result = lox.evaluate(source)
249
+ expect(io.string).to eq(predicted)
250
+ end
251
+ end
252
+
54
253
  it 'should print the hello world message' do
55
254
  expect { subject.evaluate(hello_world) }.not_to raise_error
56
255
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-11 00:00:00.000000000 Z
11
+ date: 2021-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -89,10 +89,14 @@ files:
89
89
  - lib/loxxy/ast/ast_visitor.rb
90
90
  - lib/loxxy/ast/lox_binary_expr.rb
91
91
  - lib/loxxy/ast/lox_compound_expr.rb
92
+ - lib/loxxy/ast/lox_grouping_expr.rb
93
+ - lib/loxxy/ast/lox_if_stmt.rb
92
94
  - lib/loxxy/ast/lox_literal_expr.rb
95
+ - lib/loxxy/ast/lox_logical_expr.rb
93
96
  - lib/loxxy/ast/lox_node.rb
94
97
  - lib/loxxy/ast/lox_noop_expr.rb
95
98
  - lib/loxxy/ast/lox_print_stmt.rb
99
+ - lib/loxxy/ast/lox_unary_expr.rb
96
100
  - lib/loxxy/back_end/engine.rb
97
101
  - lib/loxxy/datatype/all_datatypes.rb
98
102
  - lib/loxxy/datatype/boolean.rb