loxxy 0.0.22 → 0.0.27

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acd7b65477866aee4f022731a70f67cad63f482eae6ae5e3f5a35cd56c43f9b0
4
- data.tar.gz: 4980c1dafbd83c1ed0a41cf5da09d0decba43e912722c5052fc3e4f63626d73a
3
+ metadata.gz: 47ae4c0af1698de7187c530ec5feec9390a664ff681aae6c47a36e351bda229d
4
+ data.tar.gz: ff309645b8e71608e6a3801d4a4be558b8e86d59d658af4ec2eb796989e88455
5
5
  SHA512:
6
- metadata.gz: e8853388df750173e1a9e818e3aac92f33f98ff75cf5c575d0b583758c59938810a593823a73a6914a28413ba697d53147d7317b744484d43b4026c76a182bc4
7
- data.tar.gz: 3426ce17792625f908f2a31c879b0a312cbefbe18c82848f769d78bcac550d3700349fc1408bbeddb7738b47735ed583fd89355636c8c377a7cde3baf2f5071f
6
+ metadata.gz: 73bea958f39a8cb97ed2e59c049ab58006845519a642d20e72cbdd554be60cb3b553f84dcdd64a36410796f83e87bb3239eee9933148b821b074916f4198a810
7
+ data.tar.gz: a3235fd86a5c99c0ecf581be106e53d59a0643b3ac6299fbd0eccd0f5eacc5a15b63df3a22fb4edef8b30314fa0360432b017d9a2417668e365e2b9dd3f8b2b8
@@ -1,7 +1,66 @@
1
+ ## [0.0.27] - 2021-01-24
2
+ - The interpreter implements `while` loops.
3
+
4
+ ### Added
5
+ - Class `Ast::LoxForStmt` a node that represents a `for` statement
6
+ - Method `Ast::ASTBuilder#reduce_for_stmt`
7
+ - Method `Ast::ASTBuilder#reduce_for_control` creates an `Ast::LoxForStmt` node
8
+ - Method `Ast::ASTVisitor#visit_for_stmt` for visiting an `Ast::LoxWhileStmt` node
9
+ - Method `BackEnd::Engine#before_for_stmt` builds a new environment for the loop variable
10
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
11
+
12
+ ### Changed
13
+ - File `README.md` updated.
14
+
15
+ ## [0.0.26] - 2021-01-22
16
+ - The interpreter implements `while` loops.
17
+
18
+ ### Added
19
+ - Class `Ast::LoxWhileStmt` a node that represents a `while` statement
20
+ - Method `Ast::ASTBuilder#reduce_while_stmt` creates an `Ast::LoxWhileStmt` node
21
+ - Method `Ast::ASTVisitor#visit_while_stmt` for visiting an `Ast::LoxWhileStmt` node
22
+ - Method `BackEnd::Engine#after_while_stmt` implements the while looping structure
23
+
24
+ ### Changed
25
+ - File `README.md` updated.
26
+
27
+ ## [0.0.25] - 2021-01-21
28
+ - The interpreter implements blocks of code.
29
+
30
+ ### Added
31
+ - Class `Ast::LoxBlockStmt` a node that represents a block of code
32
+ - Method `Ast::ASTBuilder#reduce_block_stmt` creates an `Ast::LoxBlockStmt` node
33
+ - Method `Ast::ASTVisitor#visit_block_stmt` for visiting an `Ast::LoxBlockStmt` node
34
+ - Method `BackEnd::Engine#before_block_stmt` creates an new enclosed Environment
35
+ - Method `BackEnd::Engine#after_block_stmt` close enclosed Environment and make parent Environment the current one
36
+
37
+ ### Changed
38
+ - File `README.md` updated.
39
+
40
+ ## [0.0.24] - 2021-01-20
41
+ - The interpreter implements the assignment of variables.
42
+
43
+ ### Added
44
+ - Class `Ast::LoxAssignExpr` a node that represents the assignment of a value to a variable
45
+ - Method `Ast::ASTBuilder#reduce_assign_expr` creates an `Ast::LoxAssignExpr` node
46
+ - Method `Ast::ASTVisitor#visit_assign_expr` for visiting an `Ast::LoxAssignExpr` node
47
+ - Method `BackEnd::Engine#after_assign_expr` implementation of the assignment
48
+ - Method `BackEnd::Variable#assign` to assign a value to a variable
49
+
50
+ ## [0.0.23] - 2021-01-20
51
+ - Fix for variables without explicit initialization.
52
+
53
+ ### Added
54
+ - Method `Ast::ASTVisitor#visit_builtin` for visiting `Datatype::BuiltinDatatype` value.
55
+ - Method `BackEnd::Engine#before_visit_builtin` push the data value onto the stack.
56
+
57
+ ### Fixed
58
+ - Method `Ast::LoxVarStmt#initialize`: in case no explicit value provided then use `Datatype::Nil.instance`instead of Ruby `nil`
59
+
1
60
  ## [0.0.22] - 2021-01-17
2
61
  - The interpreter can retrieve the value of a variable.
3
62
 
4
- ## Added
63
+ ### Added
5
64
  - Method `Ast::ASTBuilder#declaration_plus_more` and `Ast::ASTBuilder#declaration_plus_end` to allow multiple expressions/statements
6
65
  - Method `Ast::ASTBuilder#reduce_var_expression` creates an `Ast::LoxVariableExpr` node
7
66
  - Method `Ast::ASTVisitor#visit_var_expr` for visiting `Ast::LoxVariableExpr` nodes
@@ -9,14 +68,14 @@
9
68
  - Class `Ast::LoxVarExpr` a node that represents a variable occurrence in an expression
10
69
  - Method `Engine::after_variable_expr`: retrieve the value of variable with given name
11
70
 
12
- ## Changed
71
+ ### Changed
13
72
  - Method `Ast::ASTBuilder#reduce_lox_program` to support multiple statements/declarations
14
73
  - File `README.md` updated.
15
74
 
16
75
  ## [0.0.21] - 2021-01-16
17
76
  - The interpreter supports the declaration global variables.
18
77
 
19
- ## Added
78
+ ### Added
20
79
  - Class `BackEnd::Entry`, mixin module for objects put in the symbol table
21
80
  - Class `BackEnd::Environment` that keeps track of variables in a given context.
22
81
  - Class `BackEnd::SymbolTable` that keeps track of environments.
@@ -29,7 +88,7 @@
29
88
  ## [0.0.20] - 2021-01-15
30
89
  - The interpreter supports the `if` ... `else` statement.
31
90
 
32
- ## Added
91
+ ### Added
33
92
  - Class `Ast::LoxItStmt`, AST node specific for `if` `else` statements
34
93
  - Method `Ast::ASTBuilder#reduce_if_stmt` as semantic action for if ... else
35
94
  - Method `Ast::ASTVisitor#visit_if_stmt` for visiting `LoxIfStmt` nodes
@@ -38,20 +97,20 @@
38
97
  ## [0.0.19] - 2021-01-14
39
98
  - The interpreter supports expressions between parentheses (grouping).
40
99
 
41
- ## Added
100
+ ### Added
42
101
  - Class `Ast::LoxLogicalExpr`
43
102
  - Method `Ast::ASTBuilder#reduce_grouping_expr` as semantic action for grouping expression
44
103
  - Method `Ast::ASTVisitor#visit_grouping_expr` for visiting grouping expressions
45
104
  - Method `Engine::after_grouping_expr`for the evaluation of grouping expressions
46
105
 
47
- ## Changed
106
+ ### Changed
48
107
  - File `grammar.rb` rules for if ... else were given names in order to activate semantic actions.
49
108
  - File `README.md` updated with little `if ... else` documentation.
50
109
 
51
110
  ## [0.0.18] - 2021-01-13
52
111
  - The interpreter can evaluate `and`, `or`expressions.
53
112
 
54
- ## Added
113
+ ### Added
55
114
  - Class `Ast::LoxLogicalExpr`
56
115
  - Method `Ast::ASTBuilder#reduce_logical_expr` for the semantic action require for `and`, `or`
57
116
  - Method `Ast::ASTVisitor#visit_logical_expr` for visiting logical expressions
@@ -61,7 +120,7 @@
61
120
  - The interpreter can evaluate all arithmetic and comparison operations.
62
121
  - It implements `==`, `!=` and the unary operations `!`, `-`
63
122
 
64
- ## Added
123
+ ### Added
65
124
  - Class `Ast::LoxUnaryExpr`
66
125
  - Method `Ast::ASTBuilder#reduce_unary_expr` to support the evaluation of `!` and ``-@`
67
126
  - Method `Ast::ASTVisitor#visit_unnary_expr` for visiting unary expressions
@@ -69,14 +128,14 @@
69
128
  - In class `Datatype::BuiltinDatatype` the methods `falsey?`, `truthy?`, `!`, `!=`
70
129
  - In class `Datatype::Number`the methods `<`, `<=`, ´>´, `>=` and `-@`
71
130
 
72
- ## Changed
131
+ ### Changed
73
132
  - File `README.md` updated.
74
133
 
75
134
  ## [0.0.16] - 2021-01-11
76
135
  - The interpreter can evaluate product and division of two numbers.
77
136
  - It also implements equality `==` and inequality `!=` operators
78
137
 
79
- ## Added
138
+ ### Added
80
139
  - Method `Datatype::False#==` for equality testing
81
140
  - Method `Datatype::False#!=` for inequality testing
82
141
  - Method `Datatype::LXString#==` for equality testing
@@ -90,17 +149,17 @@
90
149
  - Method `Datatype::True#==` for equality testing
91
150
  - Method `Datatype::True#!=` for inequality testing
92
151
 
93
- ## Changed
152
+ ### Changed
94
153
  - Method `BackEnd::Engine#after_binary_expr` to allow `*`, `/`, `==`, `!=` operators
95
154
  - File `README.md` updated for the newly implemented operators
96
155
 
97
156
  ## [0.0.15] - 2021-01-11
98
157
  - The interpreter can evaluate substraction between two numbers.
99
158
 
100
- ## Added
159
+ ### Added
101
160
  - Method `Datatype::Number#-` implmenting the subtraction operation
102
161
 
103
- ## Changed
162
+ ### Changed
104
163
  - File `README.md` minor editorial changes.
105
164
  - File `lx_string_spec.rb` Added test for string concatentation
106
165
  - File `number_spec.rb` Added tests for addition and subtraction operations
@@ -109,7 +168,7 @@
109
168
  ## [0.0.14] - 2021-01-10
110
169
  - The interpreter can evaluate addition of numbers and string concatenation
111
170
 
112
- ## Added
171
+ ### Added
113
172
  - Method `Ast::ASTVisitor#visit_binary_expr` for visiting binary expressions
114
173
  - Method `Ast::LoxBinaryExpr#accept` for visitor pattern
115
174
  - Method `BackEnd::Engine#after_binary_expr` to trigger execution of binary operator
@@ -117,79 +176,79 @@
117
176
  - Method `Datatype::LXString#+` implementation of the string concatenation
118
177
  - Method `Datatype::Number#+` implementation of the addition of numbers
119
178
 
120
- ## Changed
179
+ ### Changed
121
180
  - File `interpreter_spec.rb` Added tests for addition operation and string concatenation
122
181
 
123
182
 
124
183
  ## [0.0.13] - 2021-01-10
125
184
  - The interpreter can evaluate directly simple literals.
126
185
 
127
- ## Changed
186
+ ### Changed
128
187
  - Class `AST::ASTBuilder` added `reduce_exprStmt` to support the evaluation of literals.
129
188
  - File `README.md` added one more example.
130
189
  - File `parser_spec.rb` Updated the tests to reflect the change in the AST.
131
190
  - File `interpreter_spec.rb` Added a test for literal expression.
132
191
 
133
- ## Fixed
192
+ ### Fixed
134
193
  - File `loxxy.rb`: shorthand method `lox_true` referenced the ... false object (oops).
135
194
 
136
195
  ## [0.0.12] - 2021-01-09
137
196
  - Initial interpreter capable of evaluating a tiny subset of Lox language.
138
197
 
139
- ## Added
198
+ ### Added
140
199
  - Class `AST::LoxNoopExpr`
141
200
  - Class `AST::LoxPrintStmt`
142
201
  - Class `BackEnd::Engine` implementation of the print statement logic
143
202
  - Class `Interpreter`
144
203
 
145
- ## Changed
204
+ ### Changed
146
205
  - Class `Ast::ASTVisitor` Added visit method
147
206
  - File `README.md` added Hello world example.
148
207
 
149
208
  ## [0.0.11] - 2021-01-08
150
209
  - AST node generation for logical expression (and, or).
151
210
 
152
- ## Changed
211
+ ### Changed
153
212
  - Class `AST::ASTBuilder` added `reduce_` methods for logical operations.
154
213
  - File `grammar.rb`added name to logical expression rules
155
214
  - File `README.md` added gem version and license badges, expanded roadmap section.
156
215
 
157
- ## Fixed
216
+ ### Fixed
158
217
  - File `grammar.rb`: a rule had incomplete non-terminal name `conjunct_` in its lhs.
159
218
 
160
219
 
161
220
  ## [0.0.10] - 2021-01-08
162
221
  - AST node generation for equality expression.
163
222
 
164
- ## Changed
223
+ ### Changed
165
224
  - Class `AST::ASTBuilder` refactoring and added `reduce_` methods for equality operations.
166
225
  - File `grammar.rb`added name to equality rules
167
226
  - File `README.md` added gem version and license badges, expanded roadmap section.
168
227
 
169
- ## Fixed
228
+ ### Fixed
170
229
  - File `grammar.rb`: a rule had still the discarded non-terminal `equalityTest_star` in its lhs.
171
230
 
172
231
  ## [0.0.9] - 2021-01-07
173
232
  - AST node generation for comparison expression.
174
233
 
175
- ## Changed
234
+ ### Changed
176
235
  - Class `AST::ASTBuilder` added `reduce_` methods for comparison operations.
177
236
  - File `grammar.rb`added name to comparison rules
178
237
 
179
238
  ## [0.0.8] - 2021-01-07
180
239
  - AST node generation for arithmetic operations of number literals.
181
240
 
182
- ## Changed
241
+ ### Changed
183
242
  - Class `AST::ASTBuilder` added `reduce_` methods for arithmetic operations.
184
243
  - File `grammar.rb`added name to arithmetic rules
185
244
 
186
- ## Fixed
245
+ ### Fixed
187
246
  - File `grammar.rb`: second rule for `factor` had a missing member in rhs.
188
247
 
189
248
  ## [0.0.7] - 2021-01-06
190
249
  - Lox grammar reworked, initial AST classes created.
191
250
 
192
- ## Added
251
+ ### Added
193
252
  - Class `Parser` this one generates AST's (Abstract Syntax Tree)
194
253
  - Class `AST::ASTVisitor` draft initial implementation.
195
254
  - Class `AST::BinaryExpr` draft initial implementation.
@@ -197,7 +256,7 @@
197
256
  - Class `AST::LiteralExpr` draft initial implementation.
198
257
  - Class `AST::LoxNode` draft initial implementation.
199
258
 
200
- ## Changed
259
+ ### Changed
201
260
  - File `spec_helper.rb`: removed Bundler dependency
202
261
  - Class `AST::ASTBuilder` added initial `reduce_` methods.
203
262
  - File `README.md` Removed example with AST generation since this is in flux.
@@ -205,31 +264,31 @@
205
264
  ## [0.0.6] - 2021-01-03
206
265
  - First iteration of a parser with AST generation.
207
266
 
208
- ## Added
267
+ ### Added
209
268
  - Class `Parser` this one generates AST's (Abstract Syntax Tree)
210
269
  - Class `AST::ASTBuilder` default code to generate an AST.
211
270
 
212
- ## Changed
271
+ ### Changed
213
272
  - File `spec_helper.rb`: removed Bundler dependency
214
273
  - File `README.md` Added example with AST visualization.
215
274
 
216
- ## Fixed
275
+ ### Fixed
217
276
  - File `grammar.rb` ensure that the constant `Grammar` is created once only.
218
277
 
219
278
  ## [0.0.5] - 2021-01-02
220
279
  - Improved example in `README.md`, code re-styling to please `Rubocop` 1.7
221
280
 
222
- ## Changed
281
+ ### Changed
223
282
  - Code re-styling to please `Rubocop` 1.7
224
283
  - File `README.md` Improved example with better parse tree visualization.
225
284
 
226
285
  ## [0.0.4] - 2021-01-01
227
286
  - A first parser implementation able to parse Lox source code.
228
287
 
229
- ## Added
288
+ ### Added
230
289
  - Method `LXString::==` equality operator.
231
290
 
232
- ## Changed
291
+ ### Changed
233
292
  - class `Parser` renamed to `RawParser`
234
293
  - File `README.md` Added an example showing the use of `RawParser` class.
235
294
 
data/README.md CHANGED
@@ -14,7 +14,7 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
14
14
 
15
15
  ### Current status
16
16
  The project is still in inception and the interpreter is being implemented...
17
- Currently it can execute a tiny subset of __Lox__ language.
17
+ Currently it can execute a subset of __Lox__ language.
18
18
 
19
19
  But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
20
 
@@ -131,7 +131,7 @@ program
131
131
  On one hand, the parser covers the complete Lox grammar and should therefore, in principle,
132
132
  parse any valid Lox program.
133
133
 
134
- On the other hand, the interpreter is under development and currently it can evaluate only a tiny subset of __Lox__.
134
+ On the other hand, the interpreter is under development and currently it can evaluate only a subset of __Lox__.
135
135
  But the situation is changing almost daily, stay tuned...
136
136
 
137
137
  Here are the language features currently supported by the interpreter:
@@ -141,9 +141,12 @@ Here are the language features currently supported by the interpreter:
141
141
  - [Datatypes](#datatypes)
142
142
  - [Statements](#statements)
143
143
  -[Expressions](#expressions)
144
- - [Variable declarations](#var-statement)
144
+ - [Variable declarations](#var-statement)
145
+ - [For statement](#for-statement)
145
146
  - [If Statement](#if-statement)
146
147
  - [Print Statement](#print-statement)
148
+ - [While Statement](#while-statement)
149
+ - [Block Statement](#block-statement)
147
150
 
148
151
  ### Comments
149
152
 
@@ -176,11 +179,13 @@ Loxxy supports the following statements:
176
179
  -[Comparison expressions](#comparison-expressions)
177
180
  -[Logical expressions](#logical-expressions)
178
181
  -[Grouping expressions](#grouping-expressions)
179
- -[Variable expressions](#variable-expressions)
182
+ -[Variable expressions and assignments](#variable-expressions)
180
183
 
181
184
  -[Variable declarations](#var-statement)
182
185
  -[If Statement](#if-statement)
183
- -[Print Statement](#print-statement)
186
+ -[Print Statement](#print-statement)
187
+ -[While Statement](#while-statement)
188
+ -[Block Statement](#block-statement)
184
189
 
185
190
  #### Expressions
186
191
 
@@ -259,22 +264,30 @@ print 3 + 4 * 5; // => 23
259
264
  print (3 + 4) * 5; // => 35
260
265
  ```
261
266
 
262
- #### Variable expressions
267
+ #### Variable expressions and assignments
263
268
  In __Lox__, a variable expression is nothing than retrieving the value of a variable.
264
269
  ``` javascript
265
270
  var foo = "bar;" // Variable declaration
266
- foo; // Varible expression (= retrieving its value)
271
+ print foo; // Variable expression (= use its value)
272
+ foo = "baz"; // Variable assignment
273
+ print foo; // Output: baz
267
274
  ```
268
275
 
269
276
  #### Variable declarations
270
277
  ``` javascript
271
278
  var iAmAVariable = "my-initial-value";
272
279
  var iAmNil; // __Lox__ initializes variables to nil by default;
280
+ print iAmNil; // output: nil
273
281
  ```
274
282
 
275
- Warning: current version cannot assign a value to an existing variable.
276
- Expect this capability to be implemented in the coming days.
283
+ #### For statement
277
284
 
285
+ Similar to the `for` statement in `C` language
286
+ ``` javascript
287
+ for (var a = 1; a < 10; a = a + 1) {
288
+ print a; // Output: 123456789
289
+ }
290
+ ```
278
291
 
279
292
  #### If statement
280
293
 
@@ -296,20 +309,21 @@ The problem in a nutshell: in a nested if ... else ... statement like this:
296
309
  ``` javascript
297
310
  'if (true) if (false) print "bad"; else print "good";
298
311
  ```
299
- ... there is an ambiguity. Indeed, according to the __Lox__ grammar, the `else` could be bound
312
+ ... there is an ambiguity.
313
+ Indeed, according to the __Lox__ grammar, the `else` could be bound
300
314
  either to the first `if` or to the second one.
301
315
  This ambiguity is usually lifted by applying an ad-hoc rule: an `else` is aways bound to the most
302
316
  recent (rightmost) `if`.
303
- Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
317
+ Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
304
318
  As a consequence,it complains about the found ambiguity and stops the parsing...
305
319
  Although `Rley` can cope with ambiguities, this requires the use of an advanced data structure
306
320
  called `Shared Packed Parse Forest (SPPF)`.
307
- SPPF are much more complex to handle than the `common` parse trees present in most compiler or interpreter books.
321
+ SPPF are much more complex to handle than the "common" parse trees present in most compiler or interpreter books.
308
322
  Therefore, a future version of `Rley` will incorporate the capability to define disambuiguation rules.
309
323
 
310
324
  In the meantime, the `Loxxy` will progress on other __Lox__ features like:
311
- - Variables,
312
325
  - Block structures...
326
+ - Iteration structures (`for` and `while` loops)
313
327
 
314
328
 
315
329
  #### Print Statement
@@ -321,6 +335,29 @@ print "Hello, world!"; // Output: Hello, world!
321
335
 
322
336
  ```
323
337
 
338
+ #### While Statement
339
+
340
+ ``` javascript
341
+ var a = 1;
342
+ while (a < 10) {
343
+ print a;
344
+ a = a + 1;
345
+ } // Output: 123456789
346
+ ```
347
+
348
+ #### Block Statement
349
+ __Lox__ has code blocks.
350
+ ``` javascript
351
+ var a = "outer";
352
+
353
+ {
354
+ var a = "inner";
355
+ print a; // output: inner
356
+ }
357
+
358
+ print a; // output: outer
359
+ ```
360
+
324
361
  ## Installation
325
362
 
326
363
  Add this line to your application's Gemfile:
@@ -343,7 +380,7 @@ TODO: Write usage instructions here
343
380
 
344
381
  ## Other Lox implementations in Ruby
345
382
 
346
- For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
383
+ For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
347
384
  There are other Ruby-based projects as well:
348
385
  - [SlowLox](https://github.com/ArminKleinert/SlowLox), described as a "1-to-1 conversion of JLox to Ruby"
349
386
  - [rulox](https://github.com/LevitatingBusinessMan/rulox)
@@ -7,7 +7,11 @@ require_relative 'lox_grouping_expr'
7
7
  require_relative 'lox_unary_expr'
8
8
  require_relative 'lox_binary_expr'
9
9
  require_relative 'lox_logical_expr'
10
+ require_relative 'lox_assign_expr'
11
+ require_relative 'lox_block_stmt'
12
+ require_relative 'lox_while_stmt'
10
13
  require_relative 'lox_print_stmt'
11
14
  require_relative 'lox_if_stmt'
15
+ require_relative 'lox_for_stmt'
12
16
  require_relative 'lox_var_stmt'
13
17
  require_relative 'lox_seq_decl'
@@ -149,18 +149,18 @@ module Loxxy
149
149
  end
150
150
 
151
151
  # rule('program' => 'declaration_plus EOF').as ''
152
- def reduce_lox_program(_production, range, tokens, theChildren)
152
+ def reduce_lox_program(_production, _range, tokens, theChildren)
153
153
  LoxSeqDecl.new(tokens[0].position, theChildren[0])
154
154
  end
155
155
 
156
156
  # rule('declaration_plus' => 'declaration_plus declaration').as ''
157
- def reduce_declaration_plus_more(_production, range, tokens, theChildren)
157
+ def reduce_declaration_plus_more(_production, _range, _tokens, theChildren)
158
158
  theChildren[0] << theChildren[1]
159
159
  end
160
160
 
161
161
  # rule('declaration_plus' => 'declaration')
162
- def reduce_declaration_plus_end(_production, range, tokens, theChildren)
163
- [ theChildren[0] ]
162
+ def reduce_declaration_plus_end(_production, _range, _tokens, theChildren)
163
+ [theChildren[0]]
164
164
  end
165
165
 
166
166
  # rule('exprStmt' => 'expression SEMICOLON')
@@ -180,6 +180,28 @@ module Loxxy
180
180
  Ast::LoxVarStmt.new(tokens[1].position, var_name, theChildren[3])
181
181
  end
182
182
 
183
+ # rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
184
+ def reduce_for_stmt(_production, _range, _tokens, theChildren)
185
+ for_stmt = theChildren[2]
186
+ for_stmt.body_stmt = theChildren[4]
187
+ for_stmt
188
+ end
189
+
190
+ # rule('forControl' => 'forInitialization forTest forUpdate')
191
+ def reduce_for_control(_production, _range, tokens, theChildren)
192
+ (init, test, update) = theChildren
193
+ Ast::LoxForStmt.new(tokens[0].position, init, test, update)
194
+ end
195
+
196
+ # rule('forInitialization' => 'SEMICOLON')
197
+ def reduce_empty_for_initialization(_production, _range, _tokens, _theChildren)
198
+ nil
199
+ end
200
+
201
+ # rule('forTest' => 'expression_opt SEMICOLON')
202
+ def reduce_for_test(_production, range, tokens, theChildren)
203
+ return_first_child(range, tokens, theChildren)
204
+ end
183
205
 
184
206
  # rule('ifStmt' => 'IF ifCondition statement elsePart_opt')
185
207
  def reduce_if_stmt(_production, _range, tokens, theChildren)
@@ -194,6 +216,23 @@ module Loxxy
194
216
  Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
195
217
  end
196
218
 
219
+ # rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as ''
220
+ def reduce_while_stmt(_production, _range, tokens, theChildren)
221
+ Ast::LoxWhileStmt.new(tokens[1].position, theChildren[2], theChildren[4])
222
+ end
223
+
224
+ # rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
225
+ def reduce_block_stmt(_production, _range, tokens, theChildren)
226
+ decls = LoxSeqDecl.new(tokens[1].position, theChildren[1])
227
+ Ast::LoxBlockStmt.new(tokens[1].position, decls)
228
+ end
229
+
230
+ # rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
231
+ def reduce_assign_expr(_production, _range, tokens, theChildren)
232
+ var_name = theChildren[1].token.lexeme.dup
233
+ Ast::LoxAssignExpr.new(tokens[1].position, var_name, theChildren[3])
234
+ end
235
+
197
236
  # rule('logic_or' => 'logic_and disjunct_plus')
198
237
  def reduce_logic_or_plus(production, range, tokens, theChildren)
199
238
  reduce_logical_expr(production, range, tokens, theChildren)
@@ -307,8 +346,7 @@ module Loxxy
307
346
  def reduce_variable_expr(_production, _range, tokens, theChildren)
308
347
  var_name = theChildren[0].token.lexeme
309
348
  LoxVariableExpr.new(tokens[0].position, var_name)
310
- end#
311
-
349
+ end
312
350
  end # class
313
351
  end # module
314
352
  end # module
@@ -67,6 +67,14 @@ module Loxxy
67
67
  broadcast(:after_var_stmt, aVarStmt)
68
68
  end
69
69
 
70
+ # Visit event. The visitor is about to visit a for statement.
71
+ # @param aForStmt [AST::LOXForStmt] the for statement node to visit
72
+ def visit_for_stmt(aForStmt)
73
+ broadcast(:before_for_stmt, aForStmt)
74
+ traverse_subnodes(aForStmt) # The condition is visited/evaluated here...
75
+ broadcast(:after_for_stmt, aForStmt, self)
76
+ end
77
+
70
78
  # Visit event. The visitor is about to visit a if statement.
71
79
  # @param anIfStmt [AST::LOXIfStmt] the if statement node to visit
72
80
  def visit_if_stmt(anIfStmt)
@@ -83,6 +91,30 @@ module Loxxy
83
91
  broadcast(:after_print_stmt, aPrintStmt)
84
92
  end
85
93
 
94
+ # Visit event. The visitor is about to visit a while statement node.
95
+ # @param aWhileStmt [AST::LOXWhileStmt] the while statement node to visit
96
+ def visit_while_stmt(aWhileStmt)
97
+ broadcast(:before_while_stmt, aWhileStmt)
98
+ traverse_subnodes(aWhileStmt) # The condition is visited/evaluated here...
99
+ broadcast(:after_while_stmt, aWhileStmt, self)
100
+ end
101
+
102
+ # Visit event. The visitor is about to visit a block statement.
103
+ # @param aBlockStmt [AST::LOXBlockStmt] the print statement node to visit
104
+ def visit_block_stmt(aBlockStmt)
105
+ broadcast(:before_block_stmt, aBlockStmt)
106
+ traverse_subnodes(aBlockStmt)
107
+ broadcast(:after_block_stmt, aBlockStmt)
108
+ end
109
+
110
+ # Visit event. The visitor is visiting an assignment node
111
+ # @param aLiteralExpr [AST::LoxAssignExpr] the variable assignment node to visit.
112
+ def visit_assign_expr(anAssignExpr)
113
+ broadcast(:before_assign_expr, anAssignExpr)
114
+ traverse_subnodes(anAssignExpr)
115
+ broadcast(:after_assign_expr, anAssignExpr)
116
+ end
117
+
86
118
  # Visit event. The visitor is about to visit a logical expression.
87
119
  # Since logical expressions may take shorcuts by not evaluating all their
88
120
  # sub-expressiosns, they are responsible for visiting or not their children.
@@ -129,13 +161,20 @@ module Loxxy
129
161
  broadcast(:after_literal_expr, aLiteralExpr)
130
162
  end
131
163
 
132
- # Visit event. The visitor is visiting a variable reference node
164
+ # Visit event. The visitor is visiting a variable usage node
133
165
  # @param aLiteralExpr [AST::LoxVariableExpr] the variable reference node to visit.
134
166
  def visit_variable_expr(aVariableExpr)
135
167
  broadcast(:before_variable_expr, aVariableExpr)
136
168
  broadcast(:after_variable_expr, aVariableExpr, self)
137
169
  end
138
170
 
171
+ # Visit event. The visitor is about to visit the given terminal datatype value.
172
+ # @param aNonTerminalNode [Ast::BuiltinDattype] the built-in datatype value
173
+ def visit_builtin(aValue)
174
+ broadcast(:before_visit_builtin, aValue)
175
+ broadcast(:after_visit_builtin, aValue)
176
+ end
177
+
139
178
  # Visit event. The visitor is about to visit the given non terminal node.
140
179
  # @param aNonTerminalNode [Rley::PTree::NonTerminalNode] the node to visit.
141
180
  def visit_nonterminal(_non_terminal_node)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ # This AST node represents the assignment of a value to a variable
8
+ class LoxAssignExpr < LoxCompoundExpr
9
+ # @return [String] variable name
10
+ attr_reader :name
11
+
12
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
13
+ # @param aName [String] name of the variable
14
+ # @param aValue [Loxxy::Ast::LoxNode, NilClass] value to assign
15
+ def initialize(aPosition, aName, aValue)
16
+ super(aPosition, [aValue])
17
+ @name = aName
18
+ end
19
+
20
+ # Part of the 'visitee' role in Visitor design pattern.
21
+ # @param visitor [Ast::ASTVisitor] the visitor
22
+ def accept(visitor)
23
+ visitor.visit_assign_expr(self)
24
+ end
25
+ end # class
26
+ end # module
27
+ end # module
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxBlockStmt < LoxCompoundExpr
8
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
+ # @param operand [Loxxy::Ast::LoxSeqDecl]
10
+ def initialize(aPosition, decls)
11
+ super(aPosition, [decls])
12
+ end
13
+
14
+ # Part of the 'visitee' role in Visitor design pattern.
15
+ # @param visitor [Ast::ASTVisitor] the visitor
16
+ def accept(visitor)
17
+ visitor.visit_block_stmt(self)
18
+ end
19
+
20
+ alias operands subnodes
21
+ end # class
22
+ end # module
23
+ end # module
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxForStmt < LoxCompoundExpr
8
+ # @return [LoxNode] test expression
9
+ attr_reader :test_expr
10
+
11
+ # @return [LoxNode] update expression
12
+ attr_reader :update_expr
13
+
14
+ # @return [LoxNode] body statement
15
+ attr_accessor :body_stmt
16
+
17
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
18
+ # @param initialization [Loxxy::Ast::LoxNode]
19
+ # @param testExpr [Loxxy::Ast::LoxNode]
20
+ # @param updateExpr [Loxxy::Ast::LoxNode]
21
+ # @param body [Loxxy::Ast::LoxNode]
22
+ def initialize(aPosition, initialization, testExpr, updateExpr)
23
+ child = initialization ? [initialization] : []
24
+ super(aPosition, child)
25
+ @test_expr = testExpr
26
+ @update_expr = updateExpr
27
+ end
28
+
29
+ # Part of the 'visitee' role in Visitor design pattern.
30
+ # @param visitor [Ast::ASTVisitor] the visitor
31
+ def accept(visitor)
32
+ visitor.visit_for_stmt(self)
33
+ end
34
+
35
+ # Accessor to the condition expression
36
+ # @return [LoxNode]
37
+ def condition
38
+ subnodes[0]
39
+ end
40
+ end # class
41
+ end # module
42
+ end # module
@@ -32,4 +32,4 @@ module Loxxy
32
32
  end
33
33
  end # class
34
34
  end # module
35
- end # module
35
+ end # module
@@ -5,12 +5,6 @@ require_relative 'lox_compound_expr'
5
5
  module Loxxy
6
6
  module Ast
7
7
  class LoxSeqDecl < LoxCompoundExpr
8
- # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
- # @param declarations [Arrya<Loxxy::Ast::LoxNode>]
10
- def initialize(aPosition, declarations)
11
- super(aPosition, declarations)
12
- end
13
-
14
8
  # Part of the 'visitee' role in Visitor design pattern.
15
9
  # @param visitor [Ast::ASTVisitor] the visitor
16
10
  def accept(visitor)
@@ -13,7 +13,7 @@ module Loxxy
13
13
  # @param aName [String] name of the variable
14
14
  # @param aValue [Loxxy::Ast::LoxNode, NilClass] initial value for the variable
15
15
  def initialize(aPosition, aName, aValue)
16
- initial_value = aValue ? [aValue] : []
16
+ initial_value = aValue ? [aValue] : [Datatype::Nil.instance]
17
17
  super(aPosition, initial_value)
18
18
  @name = aName
19
19
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxWhileStmt < LoxCompoundExpr
8
+ # @return [LoxNode] body of the while loop (as a statement)
9
+ attr_reader :body
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param condExpr [Loxxy::Ast::LoxNode] iteration condition
13
+ # @param theBody [Loxxy::Ast::LoxNode]
14
+ def initialize(aPosition, condExpr, theBody)
15
+ super(aPosition, [condExpr])
16
+ @body = theBody
17
+ end
18
+
19
+ # Part of the 'visitee' role in Visitor design pattern.
20
+ # @param visitor [Ast::ASTVisitor] the visitor
21
+ def accept(visitor)
22
+ visitor.visit_while_stmt(self)
23
+ end
24
+
25
+ # Accessor to the condition expression
26
+ # @return [LoxNode]
27
+ def condition
28
+ subnodes[0]
29
+ end
30
+ end # class
31
+ end # module
32
+ end # module
@@ -51,11 +51,28 @@ module Loxxy
51
51
  symbol_table.insert(new_var)
52
52
  end
53
53
 
54
+ def before_for_stmt(aForStmt)
55
+ before_block_stmt(aForStmt)
56
+ end
57
+
58
+ def after_for_stmt(aForStmt, aVisitor)
59
+ loop do
60
+ aForStmt.test_expr.accept(aVisitor)
61
+ condition = stack.pop
62
+ break unless condition.truthy?
63
+
64
+ aForStmt.body_stmt.accept(aVisitor)
65
+ aForStmt.update_expr&.accept(aVisitor)
66
+ stack.pop
67
+ end
68
+ after_block_stmt(aForStmt)
69
+ end
70
+
54
71
  def after_if_stmt(anIfStmt, aVisitor)
55
72
  # Retrieve the result of the condition evaluation
56
73
  condition = stack.pop
57
74
  if condition.truthy?
58
- result = anIfStmt.then_stmt.accept(aVisitor)
75
+ anIfStmt.then_stmt.accept(aVisitor)
59
76
  elsif anIfStmt.else_stmt
60
77
  anIfStmt.else_stmt.accept(aVisitor)
61
78
  end
@@ -66,6 +83,35 @@ module Loxxy
66
83
  @ostream.print tos.to_str
67
84
  end
68
85
 
86
+ def after_while_stmt(aWhileStmt, aVisitor)
87
+ loop do
88
+ condition = stack.pop
89
+ break unless condition.truthy?
90
+
91
+ aWhileStmt.body.accept(aVisitor)
92
+ aWhileStmt.condition.accept(aVisitor)
93
+ end
94
+ end
95
+
96
+ def before_block_stmt(_aBlockStmt)
97
+ new_env = Environment.new
98
+ symbol_table.enter_environment(new_env)
99
+ end
100
+
101
+ def after_block_stmt(_aBlockStmt)
102
+ symbol_table.leave_environment
103
+ end
104
+
105
+ def after_assign_expr(anAssignExpr)
106
+ var_name = anAssignExpr.name
107
+ variable = symbol_table.lookup(var_name)
108
+ raise StandardError, "Unknown variable #{var_name}" unless variable
109
+
110
+ value = stack.pop
111
+ variable.assign(value)
112
+ stack.push value # An expression produces a value
113
+ end
114
+
69
115
  def after_logical_expr(aLogicalExpr, visitor)
70
116
  op = aLogicalExpr.operator
71
117
  operand1 = stack.pop # only first operand was evaluated
@@ -129,6 +175,7 @@ module Loxxy
129
175
  var_name = aVarExpr.name
130
176
  var = symbol_table.lookup(var_name)
131
177
  raise StandardError, "Unknown variable #{var_name}" unless var
178
+
132
179
  var.value.accept(aVisitor) # Evaluate the variable value
133
180
  end
134
181
 
@@ -136,6 +183,11 @@ module Loxxy
136
183
  def before_literal_expr(literalExpr)
137
184
  stack.push(literalExpr.literal)
138
185
  end
186
+
187
+ # @param aNonTerminalNode [Ast::BuiltinDattype] the built-in datatype value
188
+ def before_visit_builtin(aValue)
189
+ stack.push(aValue)
190
+ end
139
191
  end # class
140
192
  end # module
141
193
  end # module
@@ -11,7 +11,7 @@ module Loxxy
11
11
  =begin
12
12
  # @return [String] Suffix for building the internal name of the entry.
13
13
  attr_accessor :suffix
14
- =end
14
+ =end
15
15
 
16
16
  # Initialize the entry with given name
17
17
  # @param aName [String] The name of the entry
@@ -35,7 +35,7 @@ module Loxxy
35
35
  (suffix.nil? || suffix.empty?) ? label : suffix
36
36
  end
37
37
  end
38
- =end
38
+ =end
39
39
  end # module
40
40
  end # module
41
41
  end # module
@@ -9,9 +9,9 @@ module Loxxy
9
9
  # of a relation or a relation definition.
10
10
  # It contains a map of names to the objects they name (e.g. logical var)
11
11
  class Environment
12
- # The parent (enclosing) environment.
12
+ # The enclosing (parent) environment.
13
13
  # @return [Environment, NilClass]
14
- attr_accessor :parent
14
+ attr_accessor :enclosing
15
15
 
16
16
  # Mapping from user-defined name to related definition
17
17
  # (say, a variable object)
@@ -21,7 +21,7 @@ module Loxxy
21
21
  # Construct a environment instance.
22
22
  # @param aParent [Environment, NilClass] Parent environment to this one.
23
23
  def initialize(aParent = nil)
24
- @parent = aParent
24
+ @enclosing = aParent
25
25
  @defns = {}
26
26
  end
27
27
 
@@ -44,7 +44,7 @@ module Loxxy
44
44
  # to be a child of current environment and to be itself the new current environment.
45
45
  # @param anEnv [BackEnd::Environment] the Environment that
46
46
  def enter_environment(anEnv)
47
- anEnv.parent = current_env
47
+ anEnv.enclosing = current_env
48
48
  @current_env = anEnv
49
49
  end
50
50
 
@@ -60,7 +60,7 @@ module Loxxy
60
60
  end
61
61
  raise StandardError, 'Cannot remove root environment.' if current_env == root
62
62
 
63
- @current_env = current_env.parent
63
+ @current_env = current_env.enclosing
64
64
  end
65
65
 
66
66
  # Add an entry with given name to current environment.
@@ -114,7 +114,7 @@ module Loxxy
114
114
  while skope
115
115
  vars_of_environment = skope.defns.select { |_, item| item.kind_of?(Variable) }
116
116
  vars = vars_of_environment.values.concat(vars)
117
- skope = skope.parent
117
+ skope = skope.enclosing
118
118
  end
119
119
 
120
120
  vars
@@ -9,15 +9,20 @@ module Loxxy
9
9
  # It is a named slot that can be associated with a value at the time.
10
10
  class Variable
11
11
  include Entry # Add expected behaviour for symbol table entries
12
-
12
+
13
13
  # @return [Datatype::BuiltinDatatype] the value assigned to the variable
14
- attr_accessor :value
14
+ attr_reader :value
15
15
 
16
16
  # Create a variable with given name and initial value
17
17
  # @param aName [String] The name of the variable
18
18
  # @param aValue [Datatype::BuiltinDatatype] the initial assigned value
19
19
  def initialize(aName, aValue = Datatype::Nil.instance)
20
20
  init_name(aName)
21
+ assign(aValue)
22
+ end
23
+
24
+ # @param aValue [Datatype::BuiltinDatatype] the assigned value
25
+ def assign(aValue)
21
26
  @value = aValue
22
27
  end
23
28
  end # class
@@ -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)
@@ -60,12 +60,12 @@ module Loxxy
60
60
 
61
61
  rule('exprStmt' => 'expression SEMICOLON').as 'exprStmt'
62
62
 
63
- rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
64
- rule('forControl' => 'forInitialization forTest forUpdate')
63
+ rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement').as 'for_stmt'
64
+ rule('forControl' => 'forInitialization forTest forUpdate').as 'for_control'
65
65
  rule('forInitialization' => 'varDecl')
66
66
  rule('forInitialization' => 'exprStmt')
67
- rule('forInitialization' => 'SEMICOLON')
68
- rule('forTest' => 'expression_opt SEMICOLON')
67
+ rule('forInitialization' => 'SEMICOLON').as 'empty_for_initialization'
68
+ rule('forTest' => 'expression_opt SEMICOLON').as 'for_test'
69
69
  rule('forUpdate' => 'expression_opt')
70
70
 
71
71
  rule('ifStmt' => 'IF ifCondition statement elsePart_opt').as 'if_stmt'
@@ -75,15 +75,15 @@ module Loxxy
75
75
 
76
76
  rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
77
77
  rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
78
- rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement')
79
- rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
78
+ rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as 'while_stmt'
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' => [])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.22'
4
+ VERSION = '0.0.27'
5
5
  end
@@ -9,7 +9,7 @@ module Loxxy
9
9
  module BackEnd
10
10
  describe Environment do
11
11
  let(:foo) { Datatype::LXString.new('foo') }
12
- let(:bar) { Datatype::LXString.new('bar') }
12
+ let(:bar) { Datatype::LXString.new('bar') }
13
13
  let(:mother) { Environment.new }
14
14
  subject { Environment.new(mother) }
15
15
 
@@ -32,7 +32,7 @@ module Loxxy
32
32
  end
33
33
 
34
34
  it 'should know its parent (if any)' do
35
- expect(subject.parent).to eq(mother)
35
+ expect(subject.enclosing).to eq(mother)
36
36
  end
37
37
  end # context
38
38
 
@@ -46,8 +46,8 @@ module Loxxy
46
46
  end
47
47
 
48
48
  it 'should allow the addition of several labels for same env' do
49
- i_name = subject.insert(var('q'))
50
- #expect(i_name).to match(/^q_[0-9a-z]*$/)
49
+ subject.insert(var('q'))
50
+ # expect(i_name).to match(/^q_[0-9a-z]*$/)
51
51
 
52
52
  expect { subject.insert(var('x')) }.not_to raise_error
53
53
  expect(subject.name2envs['x']).to be_kind_of(Array)
@@ -60,7 +60,7 @@ module Loxxy
60
60
  new_env = BackEnd::Environment.new
61
61
  expect { subject.enter_environment(new_env) }.not_to raise_error
62
62
  expect(subject.current_env).to eq(new_env)
63
- expect(subject.current_env.parent).to eq(subject.root)
63
+ expect(subject.current_env.enclosing).to eq(subject.root)
64
64
  expect(subject.name2envs['q']).to eq([subject.root])
65
65
  end
66
66
 
@@ -17,10 +17,10 @@ module Loxxy
17
17
  it 'should be initialized with a name and a value, or...' do
18
18
  expect { Variable.new(sample_name, sample_value) }.not_to raise_error
19
19
  end
20
-
20
+
21
21
  it 'should be initialized with just a name' do
22
22
  expect { Variable.new(sample_name) }.not_to raise_error
23
- end
23
+ end
24
24
 
25
25
  it 'should know its name' do
26
26
  expect(subject.name).to eq(sample_name)
@@ -29,11 +29,11 @@ module Loxxy
29
29
  it 'should have a frozen name' do
30
30
  expect(subject.name).to be_frozen
31
31
  end
32
-
32
+
33
33
  it 'should know its value (if provided)' do
34
34
  expect(subject.value).to eq(sample_value)
35
35
  end
36
-
36
+
37
37
  it 'should have a nil value otherwise' do
38
38
  instance = Variable.new(sample_name)
39
39
  expect(instance.value).to eq(Datatype::Nil.instance)
@@ -53,10 +53,12 @@ module Loxxy
53
53
 
54
54
  context 'Evaluating Lox code:' do
55
55
  let(:hello_world) do
56
- lox =<<-LOX_END
56
+ lox = <<-LOX_END
57
57
  var greeting = "Hello"; // Declaring a variable
58
58
  print greeting + ", " + "world!"; // ... Playing with concatenation
59
59
  LOX_END
60
+
61
+ lox
60
62
  end
61
63
 
62
64
  it 'should evaluate core data types' do
@@ -240,7 +242,7 @@ LOX_END
240
242
  ['if (true) print "then-branch"; else print "else-branch";', 'then-branch'],
241
243
  ['if (false) print "then-branch"; else print "else-branch";', 'else-branch'],
242
244
  ['if (0) print "then-branch"; else print "else-branch";', 'then-branch'],
243
- ['if (nil) print "then-branch"; else print "else-branch";', 'else-branch'],
245
+ ['if (nil) print "then-branch"; else print "else-branch";', 'else-branch']
244
246
  # TODO: test with else block body
245
247
 
246
248
  # TODO: A dangling else binds to the right-most if.
@@ -250,7 +252,7 @@ LOX_END
250
252
  io = StringIO.new
251
253
  cfg = { ostream: io }
252
254
  lox = Loxxy::Interpreter.new(cfg)
253
- result = lox.evaluate(source)
255
+ lox.evaluate(source)
254
256
  expect(io.string).to eq(predicted)
255
257
  end
256
258
  end
@@ -267,12 +269,133 @@ LOX_END
267
269
  it 'should accept variable mention' do
268
270
  program = <<-LOX_END
269
271
  var foo = "bar";
270
- print foo;
272
+ print foo; // => bar
271
273
  LOX_END
272
274
  expect { subject.evaluate(program) }.not_to raise_error
273
275
  expect(sample_cfg[:ostream].string).to eq('bar')
274
276
  end
275
277
 
278
+ it 'should set uninitialized variables to nil' do
279
+ program = <<-LOX_END
280
+ var a;
281
+ print a; // => nil
282
+ LOX_END
283
+ expect { subject.evaluate(program) }.not_to raise_error
284
+ expect(sample_cfg[:ostream].string).to eq('nil')
285
+ end
286
+
287
+ it 'should accept assignments to a global variable' do
288
+ program = <<-LOX_END
289
+ var a = "before";
290
+ print a; // output: before
291
+
292
+ a = "after";
293
+ print a; // output: after
294
+
295
+ print a = "arg"; // output: arg
296
+ print a; // output: arg
297
+ LOX_END
298
+ expect { subject.evaluate(program) }.not_to raise_error
299
+ expect(sample_cfg[:ostream].string).to eq('beforeafterargarg')
300
+ end
301
+
302
+ it 'should support variables local to a block' do
303
+ program = <<-LOX_END
304
+ {
305
+ var a = "first";
306
+ print a;
307
+ }
308
+ {
309
+ var a = "second";
310
+ print a;
311
+ }
312
+ LOX_END
313
+ expect { subject.evaluate(program) }.not_to raise_error
314
+ expect(sample_cfg[:ostream].string).to eq('firstsecond')
315
+ end
316
+
317
+ it 'should support the shadowing of variables in a block' do
318
+ program = <<-LOX_END
319
+ var a = "outer";
320
+
321
+ {
322
+ var a = "inner";
323
+ print a; // output: inner
324
+ }
325
+
326
+ print a; // output: outer
327
+ LOX_END
328
+ expect { subject.evaluate(program) }.not_to raise_error
329
+ expect(sample_cfg[:ostream].string).to eq('innerouter')
330
+ end
331
+
332
+ it 'should implement single statement while loops' do
333
+ program = <<-LOX_END
334
+ // Single-expression body.
335
+ var c = 0;
336
+ while (c < 3) print c = c + 1;
337
+ // output: 1
338
+ // output: 2
339
+ // output: 3
340
+ LOX_END
341
+ expect { subject.evaluate(program) }.not_to raise_error
342
+ expect(sample_cfg[:ostream].string).to eq('123')
343
+ end
344
+
345
+ it 'should implement block body while loops' do
346
+ program = <<-LOX_END
347
+ // Block body.
348
+ var a = 0;
349
+ while (a < 3) {
350
+ print a;
351
+ a = a + 1;
352
+ }
353
+ // output: 0
354
+ // output: 1
355
+ // output: 2
356
+ LOX_END
357
+ expect { subject.evaluate(program) }.not_to raise_error
358
+ expect(sample_cfg[:ostream].string).to eq('012')
359
+ end
360
+
361
+ it 'should implement single statement for loops' do
362
+ program = <<-LOX_END
363
+ // Single-expression body.
364
+ for (var c = 0; c < 3;) print c = c + 1;
365
+ // output: 1
366
+ // output: 2
367
+ // output: 3
368
+ LOX_END
369
+ expect { subject.evaluate(program) }.not_to raise_error
370
+ expect(sample_cfg[:ostream].string).to eq('123')
371
+ end
372
+
373
+ it 'should implement for loops with block body' do
374
+ program = <<-LOX_END
375
+ // Block body.
376
+ for (var a = 0; a < 3; a = a + 1) {
377
+ print a;
378
+ }
379
+ // output: 0
380
+ // output: 1
381
+ // output: 2
382
+ LOX_END
383
+ expect { subject.evaluate(program) }.not_to raise_error
384
+ expect(sample_cfg[:ostream].string).to eq('012')
385
+ end
386
+
387
+ it 'should implement for loops without initialization' do
388
+ program = <<-LOX_END
389
+ var i = 0;
390
+ // No variable in initialization.
391
+ for (; i < 2; i = i + 1) print i;
392
+ // output: 0
393
+ // output: 1
394
+ LOX_END
395
+ expect { subject.evaluate(program) }.not_to raise_error
396
+ expect(sample_cfg[:ostream].string).to eq('01')
397
+ end
398
+
276
399
  it 'should print the hello world message' do
277
400
  expect { subject.evaluate(hello_world) }.not_to raise_error
278
401
  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.22
4
+ version: 0.0.27
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-17 00:00:00.000000000 Z
11
+ date: 2021-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -87,8 +87,11 @@ files:
87
87
  - lib/loxxy/ast/all_lox_nodes.rb
88
88
  - lib/loxxy/ast/ast_builder.rb
89
89
  - lib/loxxy/ast/ast_visitor.rb
90
+ - lib/loxxy/ast/lox_assign_expr.rb
90
91
  - lib/loxxy/ast/lox_binary_expr.rb
92
+ - lib/loxxy/ast/lox_block_stmt.rb
91
93
  - lib/loxxy/ast/lox_compound_expr.rb
94
+ - lib/loxxy/ast/lox_for_stmt.rb
92
95
  - lib/loxxy/ast/lox_grouping_expr.rb
93
96
  - lib/loxxy/ast/lox_if_stmt.rb
94
97
  - lib/loxxy/ast/lox_literal_expr.rb
@@ -100,6 +103,7 @@ files:
100
103
  - lib/loxxy/ast/lox_unary_expr.rb
101
104
  - lib/loxxy/ast/lox_var_stmt.rb
102
105
  - lib/loxxy/ast/lox_variable_expr.rb
106
+ - lib/loxxy/ast/lox_while_stmt.rb
103
107
  - lib/loxxy/back_end/engine.rb
104
108
  - lib/loxxy/back_end/entry.rb
105
109
  - lib/loxxy/back_end/environment.rb