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 +4 -4
- data/CHANGELOG.md +93 -34
- data/README.md +51 -14
- data/lib/loxxy/ast/all_lox_nodes.rb +4 -0
- data/lib/loxxy/ast/ast_builder.rb +44 -6
- data/lib/loxxy/ast/ast_visitor.rb +40 -1
- data/lib/loxxy/ast/lox_assign_expr.rb +27 -0
- data/lib/loxxy/ast/lox_block_stmt.rb +23 -0
- data/lib/loxxy/ast/lox_for_stmt.rb +42 -0
- data/lib/loxxy/ast/lox_if_stmt.rb +1 -1
- data/lib/loxxy/ast/lox_seq_decl.rb +0 -6
- data/lib/loxxy/ast/lox_var_stmt.rb +1 -1
- data/lib/loxxy/ast/lox_while_stmt.rb +32 -0
- data/lib/loxxy/back_end/engine.rb +53 -1
- data/lib/loxxy/back_end/entry.rb +2 -2
- data/lib/loxxy/back_end/environment.rb +3 -3
- data/lib/loxxy/back_end/symbol_table.rb +3 -3
- data/lib/loxxy/back_end/variable.rb +7 -2
- data/lib/loxxy/datatype/builtin_datatype.rb +6 -0
- data/lib/loxxy/front_end/grammar.rb +7 -7
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/environment_spec.rb +2 -2
- data/spec/back_end/symbol_table_spec.rb +3 -3
- data/spec/back_end/variable_spec.rb +4 -4
- data/spec/interpreter_spec.rb +127 -4
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47ae4c0af1698de7187c530ec5feec9390a664ff681aae6c47a36e351bda229d
|
4
|
+
data.tar.gz: ff309645b8e71608e6a3801d4a4be558b8e86d59d658af4ec2eb796989e88455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73bea958f39a8cb97ed2e59c049ab58006845519a642d20e72cbdd554be60cb3b553f84dcdd64a36410796f83e87bb3239eee9933148b821b074916f4198a810
|
7
|
+
data.tar.gz: a3235fd86a5c99c0ecf581be106e53d59a0643b3ac6299fbd0eccd0f5eacc5a15b63df3a22fb4edef8b30314fa0360432b017d9a2417668e365e2b9dd3f8b2b8
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
159
|
+
### Added
|
101
160
|
- Method `Datatype::Number#-` implmenting the subtraction operation
|
102
161
|
|
103
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
271
|
+
### Changed
|
213
272
|
- File `spec_helper.rb`: removed Bundler dependency
|
214
273
|
- File `README.md` Added example with AST visualization.
|
215
274
|
|
216
|
-
|
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
|
-
|
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
|
-
|
288
|
+
### Added
|
230
289
|
- Method `LXString::==` equality operator.
|
231
290
|
|
232
|
-
|
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
|
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
|
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; //
|
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
|
-
|
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.
|
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
|
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,
|
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,
|
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,
|
163
|
-
[
|
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
|
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
|
@@ -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
|
-
|
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
|
data/lib/loxxy/back_end/entry.rb
CHANGED
@@ -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
|
12
|
+
# The enclosing (parent) environment.
|
13
13
|
# @return [Environment, NilClass]
|
14
|
-
attr_accessor :
|
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
|
-
@
|
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.
|
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.
|
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.
|
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
|
-
|
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' => [])
|
data/lib/loxxy/version.rb
CHANGED
@@ -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.
|
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
|
-
|
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.
|
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)
|
data/spec/interpreter_spec.rb
CHANGED
@@ -53,10 +53,12 @@ module Loxxy
|
|
53
53
|
|
54
54
|
context 'Evaluating Lox code:' do
|
55
55
|
let(:hello_world) do
|
56
|
-
lox
|
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
|
-
|
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.
|
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-
|
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
|