loxxy 0.4.00 → 0.4.04
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/.rubocop.yml +24 -0
- data/CHANGELOG.md +42 -12
- data/lib/loxxy/ast/ast_builder.rb +52 -89
- data/lib/loxxy/ast/lox_node.rb +1 -1
- data/lib/loxxy/front_end/grammar.rb +19 -45
- data/lib/loxxy/version.rb +1 -1
- data/loxxy.gemspec +2 -2
- data/spec/front_end/raw_parser_spec.rb +10 -6
- data/spec/spec_helper.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e321e9f44f39776e2684a1a2c2f2d032460847ff46cdea45791cf8724696d038
|
|
4
|
+
data.tar.gz: 1c455133b79cd26939998ebd791c5b39872de1ccf59fa6b8c8d4c7d819114021
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '031899513309e2950fd9dcf9fc782a14299bed0337e6ca2383351b277c69e1e5afc865d82a9bfce030d4780b9c738de18f18db970d862b344ac98ad00f22a2a7'
|
|
7
|
+
data.tar.gz: 60938437329c31b76307ea181e5596191b5261d55aeca369365fd8076f08879e2adfb112ec8cbaf1fd9d365e9e13816b644e1b897dd4f7840e37d9d20cc89ac8
|
data/.rubocop.yml
CHANGED
|
@@ -50,6 +50,9 @@ Layout/IndentationConsistency:
|
|
|
50
50
|
Layout/HeredocIndentation:
|
|
51
51
|
Enabled: false
|
|
52
52
|
|
|
53
|
+
Layout/LineEndStringConcatenationIndentation:
|
|
54
|
+
Enabled: true
|
|
55
|
+
|
|
53
56
|
Layout/MultilineHashBraceLayout:
|
|
54
57
|
Enabled: true
|
|
55
58
|
|
|
@@ -81,6 +84,9 @@ Layout/TrailingWhitespace:
|
|
|
81
84
|
Lint/AmbiguousAssignment:
|
|
82
85
|
Enabled: true
|
|
83
86
|
|
|
87
|
+
Lint/AmbiguousRange:
|
|
88
|
+
Enabled: true
|
|
89
|
+
|
|
84
90
|
Lint/DeprecatedConstants:
|
|
85
91
|
Enabled: true
|
|
86
92
|
|
|
@@ -96,6 +102,9 @@ Lint/EmptyBlock:
|
|
|
96
102
|
Lint/EmptyClass:
|
|
97
103
|
Enabled: false
|
|
98
104
|
|
|
105
|
+
Lint/EmptyInPattern:
|
|
106
|
+
Enabled: true
|
|
107
|
+
|
|
99
108
|
Lint/LambdaWithoutLiteralBlock:
|
|
100
109
|
Enabled: true
|
|
101
110
|
|
|
@@ -192,6 +201,9 @@ Naming/ClassAndModuleCamelCase:
|
|
|
192
201
|
Naming/BlockParameterName:
|
|
193
202
|
Enabled: true
|
|
194
203
|
|
|
204
|
+
Naming/InclusiveLanguage:
|
|
205
|
+
Enabled: true
|
|
206
|
+
|
|
195
207
|
Naming/MethodParameterName:
|
|
196
208
|
Enabled: false
|
|
197
209
|
|
|
@@ -273,11 +285,17 @@ Style/HashTransformValues:
|
|
|
273
285
|
Style/IfUnlessModifier:
|
|
274
286
|
Enabled: false
|
|
275
287
|
|
|
288
|
+
Style/InPatternThen:
|
|
289
|
+
Enabled: true
|
|
290
|
+
|
|
276
291
|
Style/InverseMethods:
|
|
277
292
|
Enabled: false
|
|
278
293
|
|
|
279
294
|
Style/MissingRespondToMissing:
|
|
280
295
|
Enabled: false
|
|
296
|
+
|
|
297
|
+
Style/MultilineInPatternThen:
|
|
298
|
+
Enabled: true
|
|
281
299
|
|
|
282
300
|
Style/NegatedIfElseCondition:
|
|
283
301
|
Enabled: true
|
|
@@ -290,6 +308,9 @@ Style/NilLambda:
|
|
|
290
308
|
|
|
291
309
|
Style/NumericLiterals:
|
|
292
310
|
Enabled: false
|
|
311
|
+
|
|
312
|
+
Style/QuotedSymbols:
|
|
313
|
+
Enabled: true
|
|
293
314
|
|
|
294
315
|
Style/RaiseArgs:
|
|
295
316
|
Enabled: true
|
|
@@ -302,6 +323,9 @@ Style/RedundantReturn:
|
|
|
302
323
|
|
|
303
324
|
Style/RedundantSelf:
|
|
304
325
|
Enabled: true
|
|
326
|
+
|
|
327
|
+
Style/RedundantSelfAssignmentBranch:
|
|
328
|
+
Enabled: true
|
|
305
329
|
|
|
306
330
|
Style/RegexpLiteral:
|
|
307
331
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
## [0.4.04] - 2021-09-11
|
|
2
|
+
- Grammar optimization (3 rules less, use of match_closest feature from `Rley`)
|
|
3
|
+
|
|
4
|
+
### Changed
|
|
5
|
+
- Method `Ast::AstBuilder#reduce_arguments` code re-styling: use shorthand &:
|
|
6
|
+
- File `grammar.rb` use `match_closest` constraint from Rley to cope with dangling else statement
|
|
7
|
+
|
|
8
|
+
## [0.4.02] - 2021-09-10
|
|
9
|
+
- Fixes in`AST::AstBuilder` class to cope with changes in Rley 0.8.03
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- File `loxxy.gemspec` forced dependency to Rley 0.8.03
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Method `Ast::AstBuilder#reduce_class_naming` fixed access to `IDENTIFER` in (LESS IDENTIFER)?
|
|
16
|
+
- Method `Ast::AstBuilder#reduce_var_declaration` fixed access to `expression` in (EQUAL expression)?
|
|
17
|
+
- Method `Ast::AstBuilder#reduce_assign_expr`fixed access to `call` in (call DOT)?
|
|
18
|
+
- Method `Ast::AstBuilder#reduce_parameters`fixed access to `IDENTIFIER` in (COMMA IDENTIFIER)*
|
|
19
|
+
- Method `Ast::AstBuilder#reduce_arguments`fixed access to `expression` in (COMMA expression)*
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## [0.4.01] - 2021-08-22
|
|
24
|
+
- Grammar and AST::AstBuilder adapted to take profit of extended grammar notiation in Rley 0.8.01
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Class `Ast::ASTBuilder` removal of methods made redundant with new Rley version
|
|
28
|
+
- File `.rubocop.yml` added config for new cops
|
|
29
|
+
- File `grammar.rb` changed rules to use new extended rule syntax in Rley
|
|
30
|
+
|
|
1
31
|
## [0.4.00] - 2021-05-24
|
|
2
32
|
- Version bump. `Loxxy` is capable to run the LoxLox interpreter, an interpreter written in `Lox`.
|
|
3
33
|
|
|
@@ -45,14 +75,14 @@
|
|
|
45
75
|
- Milestone: `Loxxy` passes all reference test suite.
|
|
46
76
|
|
|
47
77
|
### Fixed
|
|
48
|
-
- Method `BackEnd::Resolver#before_variable_expr`: Standard `Lox` allows re-declaration of a variable at top-level scope
|
|
78
|
+
- Method `BackEnd::Resolver#before_variable_expr`: Standard `Lox` allows re-declaration of a variable at top-level scope
|
|
79
|
+
|
|
49
80
|
|
|
50
|
-
|
|
51
81
|
## [0.2.06] - 2021-05-04
|
|
52
82
|
- Nearly passing the 'official' test suite, fixing non-compliant behavior, specialized exceptions for errors
|
|
53
83
|
|
|
54
84
|
### New
|
|
55
|
-
- Module `LoxFileTester` module that hosts methods that simplify the tests of `Lox` source file.
|
|
85
|
+
- Module `LoxFileTester` module that hosts methods that simplify the tests of `Lox` source file.
|
|
56
86
|
|
|
57
87
|
### Changed
|
|
58
88
|
- Folder `test_suite` vastly reorganized. Sub-folder `baseline` contains spec files testing the `Lox` files from official implementation
|
|
@@ -62,7 +92,7 @@
|
|
|
62
92
|
|
|
63
93
|
### Fixed
|
|
64
94
|
- `0/0` expression results in a ZeroDivisionError exception, in Lox this result to a NaN (Not a Number). Now, `Loxxy` is aligned to standard `Lox`
|
|
65
|
-
- `FrontEnd::Scanner` now always treats expression like `-123` as the unary or binary minus operator applied to a positive number.
|
|
95
|
+
- `FrontEnd::Scanner` now always treats expression like `-123` as the unary or binary minus operator applied to a positive number.
|
|
66
96
|
|
|
67
97
|
## [0.2.05] - 2021-04-26
|
|
68
98
|
- `Loxxy` now transforms for loops into while loops (desugaring), fix in Scanner class
|
|
@@ -89,7 +119,7 @@
|
|
|
89
119
|
- Method `BackEnd::Resolver#after_for_stmt now accepts nil test expression
|
|
90
120
|
|
|
91
121
|
## [0.2.03] - 2021-04-24
|
|
92
|
-
- Fixes for the set (field) expressions, `accept` methods for AST nodes are meta-programmed
|
|
122
|
+
- Fixes for the set (field) expressions, `accept` methods for AST nodes are meta-programmed
|
|
93
123
|
|
|
94
124
|
### New
|
|
95
125
|
- Module `Ast::Visitee` provides the `define_accept` method that generate `accept` method with meta-programming
|
|
@@ -264,7 +294,7 @@
|
|
|
264
294
|
- Fix and test suite for return statements
|
|
265
295
|
|
|
266
296
|
### CHANGED
|
|
267
|
-
- `Loxxy` reports an error when a return statement occurs in top-level scope
|
|
297
|
+
- `Loxxy` reports an error when a return statement occurs in top-level scope
|
|
268
298
|
|
|
269
299
|
### Fixed
|
|
270
300
|
- A return without explicit value genrated an exception in some cases.
|
|
@@ -288,7 +318,7 @@
|
|
|
288
318
|
- Class `BackEnd::Environment`: added the attributes `predecessor` and `embedding` to support closures.
|
|
289
319
|
- Class `BackeEnd::LoxFunction`: added the attribute `closure` that is equal to the environment where the function is declared.
|
|
290
320
|
- Constructor `BackEnd::LoxFunction#new` now takes a `BackEnd::Engine`as its fourth parameter
|
|
291
|
-
- Methods `BackEnd::SymbolTable#enter_environment`, `BackEnd::SymbolTable#leave_environment` take into account closures.
|
|
321
|
+
- Methods `BackEnd::SymbolTable#enter_environment`, `BackEnd::SymbolTable#leave_environment` take into account closures.
|
|
292
322
|
|
|
293
323
|
### Fixed
|
|
294
324
|
- Method `Ast::AstBuilder#after_var_stmt` now takes into account the value from the top of stack
|
|
@@ -334,7 +364,7 @@
|
|
|
334
364
|
|
|
335
365
|
### Changed
|
|
336
366
|
- File `console` renamed to `loxxy`. Very basic command-line interface.
|
|
337
|
-
- Custom exception classes
|
|
367
|
+
- Custom exception classes
|
|
338
368
|
- File `README.md` updated list of supported `Lox` keywords.
|
|
339
369
|
|
|
340
370
|
|
|
@@ -365,8 +395,8 @@
|
|
|
365
395
|
- Method `Ast::ASTVisitor#visit_fun_stmt` for visiting an `Ast::LoxFunStmt` node
|
|
366
396
|
- Method `Ast::LoxBlockStmt#empty?` returns true if the code block is empty
|
|
367
397
|
- Method `BackEnd::Engine#after_fun_stmt`
|
|
368
|
-
- Method `BackEnd::NativeFunction#call`
|
|
369
|
-
- Method `BackEnd::NativeFunction#to_str`
|
|
398
|
+
- Method `BackEnd::NativeFunction#call`
|
|
399
|
+
- Method `BackEnd::NativeFunction#to_str`
|
|
370
400
|
- Method `BackEnd::LoxFunction` runtime representation of a Lox function.
|
|
371
401
|
|
|
372
402
|
### Changed
|
|
@@ -435,7 +465,7 @@
|
|
|
435
465
|
- Method `Ast::ASTBuilder#reduce_assign_expr` creates an `Ast::LoxAssignExpr` node
|
|
436
466
|
- Method `Ast::ASTVisitor#visit_assign_expr` for visiting an `Ast::LoxAssignExpr` node
|
|
437
467
|
- Method `BackEnd::Engine#after_assign_expr` implementation of the assignment
|
|
438
|
-
- Method `BackEnd::Variable#assign` to assign a value to a variable
|
|
468
|
+
- Method `BackEnd::Variable#assign` to assign a value to a variable
|
|
439
469
|
|
|
440
470
|
## [0.0.23] - 2021-01-20
|
|
441
471
|
- Fix for variables without explicit initialization.
|
|
@@ -705,4 +735,4 @@
|
|
|
705
735
|
|
|
706
736
|
## [0.0.1] - 2020-12-27
|
|
707
737
|
### Added
|
|
708
|
-
- Initial Github commit
|
|
738
|
+
- Initial Github commit
|
|
@@ -143,63 +143,36 @@ module Loxxy
|
|
|
143
143
|
# SEMANTIC ACTIONS
|
|
144
144
|
#####################################
|
|
145
145
|
|
|
146
|
-
# rule('program' => 'EOF').as '
|
|
147
|
-
def reduce_null_program(_production, _range, _tokens, _theChildren)
|
|
148
|
-
Ast::LoxNoopExpr.new(tokens[0].position)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# rule('program' => 'declaration_plus EOF').as ''
|
|
146
|
+
# rule('program' => 'declaration+ EOF').as ''
|
|
152
147
|
def reduce_lox_program(_production, _range, tokens, theChildren)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
theChildren[0] << theChildren[1]
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# rule('declaration_plus' => 'declaration')
|
|
162
|
-
def reduce_declaration_plus_end(_production, _range, _tokens, theChildren)
|
|
163
|
-
[theChildren[0]]
|
|
148
|
+
if theChildren[0].empty?
|
|
149
|
+
Ast::LoxNoopExpr.new(tokens[0].position)
|
|
150
|
+
else
|
|
151
|
+
LoxSeqDecl.new(tokens[0].position, theChildren[0])
|
|
152
|
+
end
|
|
164
153
|
end
|
|
165
154
|
|
|
166
155
|
# rule('classDecl' => 'CLASS classNaming class_body')
|
|
167
156
|
def reduce_class_decl(_production, _range, _tokens, theChildren)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
parent = theChildren[1].last
|
|
171
|
-
else
|
|
172
|
-
name = theChildren[1]
|
|
173
|
-
parent = nil
|
|
174
|
-
end
|
|
157
|
+
name = theChildren[1].first
|
|
158
|
+
parent = theChildren[1].last
|
|
175
159
|
Ast::LoxClassStmt.new(tokens[1].position, name, parent, theChildren[2])
|
|
176
160
|
end
|
|
177
161
|
|
|
178
|
-
# rule('classNaming' => 'IDENTIFIER')
|
|
179
|
-
def
|
|
180
|
-
theChildren[
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
super_var = LoxVariableExpr.new(super_token.position, super_token.lexeme)
|
|
162
|
+
# rule('classNaming' => 'IDENTIFIER (LESS IDENTIFIER)?')
|
|
163
|
+
def reduce_class_naming(_production, _range, _tokens, theChildren)
|
|
164
|
+
if theChildren[1].nil?
|
|
165
|
+
super_var = nil
|
|
166
|
+
else
|
|
167
|
+
super_token = theChildren[1].last.token
|
|
168
|
+
super_var = LoxVariableExpr.new(super_token.position, super_token.lexeme)
|
|
169
|
+
end
|
|
187
170
|
[theChildren[0].token.lexeme, super_var]
|
|
188
171
|
end
|
|
189
172
|
|
|
190
|
-
# rule('class_body' => 'LEFT_BRACE
|
|
173
|
+
# rule('class_body' => 'LEFT_BRACE function* RIGHT_BRACE')
|
|
191
174
|
def reduce_class_body(_production, _range, _tokens, theChildren)
|
|
192
|
-
theChildren[1]
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# rule('method_plus' => 'method_plus function')
|
|
196
|
-
def reduce_method_plus_more(_production, _range, _tokens, theChildren)
|
|
197
|
-
theChildren[0] << theChildren[1]
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# rule('method_plus' => 'function')
|
|
201
|
-
def reduce_method_plus_end(_production, _range, _tokens, theChildren)
|
|
202
|
-
theChildren
|
|
175
|
+
theChildren[1]
|
|
203
176
|
end
|
|
204
177
|
|
|
205
178
|
# rule('funDecl' => 'FUN function')
|
|
@@ -212,16 +185,11 @@ module Loxxy
|
|
|
212
185
|
return_first_child(range, tokens, theChildren) # Discard the semicolon
|
|
213
186
|
end
|
|
214
187
|
|
|
215
|
-
# rule('varDecl' => 'VAR IDENTIFIER SEMICOLON')
|
|
188
|
+
# rule('varDecl' => 'VAR IDENTIFIER (EQUAL expression)? SEMICOLON')
|
|
216
189
|
def reduce_var_declaration(_production, _range, tokens, theChildren)
|
|
217
190
|
var_name = theChildren[1].token.lexeme.dup
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
# rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON')
|
|
222
|
-
def reduce_var_initialization(_production, _range, tokens, theChildren)
|
|
223
|
-
var_name = theChildren[1].token.lexeme.dup
|
|
224
|
-
Ast::LoxVarStmt.new(tokens[1].position, var_name, theChildren[3])
|
|
191
|
+
init_val = theChildren[2] ? theChildren[2].last : nil
|
|
192
|
+
Ast::LoxVarStmt.new(tokens[1].position, var_name, init_val)
|
|
225
193
|
end
|
|
226
194
|
|
|
227
195
|
# rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
|
|
@@ -248,7 +216,7 @@ module Loxxy
|
|
|
248
216
|
for_stmt
|
|
249
217
|
end
|
|
250
218
|
|
|
251
|
-
# rule('forControl' => 'forInitialization forTest
|
|
219
|
+
# rule('forControl' => 'forInitialization forTest expression?')
|
|
252
220
|
def reduce_for_control(_production, _range, tokens, theChildren)
|
|
253
221
|
(init, test, update) = theChildren
|
|
254
222
|
if test.nil? && update
|
|
@@ -265,7 +233,7 @@ module Loxxy
|
|
|
265
233
|
nil
|
|
266
234
|
end
|
|
267
235
|
|
|
268
|
-
# rule('forTest' => '
|
|
236
|
+
# rule('forTest' => 'expression? SEMICOLON')
|
|
269
237
|
def reduce_for_test(_production, range, tokens, theChildren)
|
|
270
238
|
return_first_child(range, tokens, theChildren)
|
|
271
239
|
end
|
|
@@ -292,7 +260,7 @@ module Loxxy
|
|
|
292
260
|
Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
|
|
293
261
|
end
|
|
294
262
|
|
|
295
|
-
# rule('returnStmt' => 'RETURN
|
|
263
|
+
# rule('returnStmt' => 'RETURN expression? SEMICOLON')
|
|
296
264
|
def reduce_return_stmt(_production, _range, tokens, theChildren)
|
|
297
265
|
Ast::LoxReturnStmt.new(tokens[1].position, theChildren[1])
|
|
298
266
|
end
|
|
@@ -302,34 +270,31 @@ module Loxxy
|
|
|
302
270
|
Ast::LoxWhileStmt.new(tokens[1].position, theChildren[2], theChildren[4])
|
|
303
271
|
end
|
|
304
272
|
|
|
305
|
-
# rule('block' => 'LEFT_BRACE
|
|
273
|
+
# rule('block' => 'LEFT_BRACE declaration* RIGHT_BRACE')
|
|
306
274
|
def reduce_block_stmt(_production, _range, tokens, theChildren)
|
|
307
|
-
decls =
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
275
|
+
decls = nil
|
|
276
|
+
if theChildren[1]
|
|
277
|
+
pos = tokens[1].position
|
|
278
|
+
decls = LoxSeqDecl.new(tokens[1].position, theChildren[1])
|
|
279
|
+
else
|
|
280
|
+
pos = tokens[0].position
|
|
281
|
+
end
|
|
282
|
+
Ast::LoxBlockStmt.new(pos, decls)
|
|
314
283
|
end
|
|
315
284
|
|
|
316
|
-
# rule('assignment' => '
|
|
285
|
+
# rule('assignment' => '(call DOT)? IDENTIFIER EQUAL assignment')
|
|
317
286
|
def reduce_assign_expr(_production, _range, tokens, theChildren)
|
|
318
287
|
name_assignee = theChildren[1].token.lexeme.dup
|
|
319
|
-
if theChildren[0]
|
|
320
|
-
theChildren[0].
|
|
321
|
-
|
|
322
|
-
theChildren[
|
|
288
|
+
if theChildren[0]
|
|
289
|
+
set_expr = Ast::LoxSetExpr.new(tokens[1].position, theChildren[0].first)
|
|
290
|
+
set_expr.property = name_assignee
|
|
291
|
+
set_expr.value = theChildren[3]
|
|
292
|
+
set_expr
|
|
323
293
|
else
|
|
324
294
|
Ast::LoxAssignExpr.new(tokens[1].position, name_assignee, theChildren[3])
|
|
325
295
|
end
|
|
326
296
|
end
|
|
327
297
|
|
|
328
|
-
# rule('owner_opt' => 'call DOT')
|
|
329
|
-
def reduce_set_expr(_production, _range, tokens, theChildren)
|
|
330
|
-
Ast::LoxSetExpr.new(tokens[1].position, theChildren[0])
|
|
331
|
-
end
|
|
332
|
-
|
|
333
298
|
|
|
334
299
|
# rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
|
|
335
300
|
# TODO: is it meaningful to implement this rule?
|
|
@@ -343,6 +308,7 @@ module Loxxy
|
|
|
343
308
|
|
|
344
309
|
# rule('call' => 'primary refinement_plus').as 'call_expr'
|
|
345
310
|
def reduce_call_expr(_production, _range, _tokens, theChildren)
|
|
311
|
+
# return theChildren[0] unless theChildren[1]
|
|
346
312
|
members = theChildren.flatten
|
|
347
313
|
call_expr = nil
|
|
348
314
|
loop do
|
|
@@ -365,7 +331,7 @@ module Loxxy
|
|
|
365
331
|
theChildren
|
|
366
332
|
end
|
|
367
333
|
|
|
368
|
-
# rule('refinement' => 'LEFT_PAREN
|
|
334
|
+
# rule('refinement' => 'LEFT_PAREN arguments? RIGHT_PAREN')
|
|
369
335
|
def reduce_call_arglist(_production, _range, tokens, theChildren)
|
|
370
336
|
args = theChildren[1] || []
|
|
371
337
|
if args.size > 255
|
|
@@ -422,24 +388,21 @@ module Loxxy
|
|
|
422
388
|
LoxFunStmt.new(pos, first_child.token.lexeme, theChildren[2], theChildren[4])
|
|
423
389
|
end
|
|
424
390
|
|
|
425
|
-
# rule('parameters' => '
|
|
426
|
-
def
|
|
427
|
-
|
|
428
|
-
|
|
391
|
+
# rule('parameters' => 'IDENTIFIER (COMMA IDENTIFIER)*').as 'parameters'
|
|
392
|
+
def reduce_parameters(_production, _range, _tokens, theChildren)
|
|
393
|
+
first_lexeme = theChildren[0].token.lexeme
|
|
394
|
+
return [first_lexeme] unless theChildren[1]
|
|
429
395
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
[theChildren[0].token.lexeme]
|
|
396
|
+
successors = theChildren[1].map { |seq_node| seq_node.last.token.lexeme }
|
|
397
|
+
successors.unshift(first_lexeme)
|
|
433
398
|
end
|
|
434
399
|
|
|
435
|
-
# rule('arguments' => '
|
|
436
|
-
def
|
|
437
|
-
theChildren[0]
|
|
438
|
-
end
|
|
400
|
+
# rule('arguments' => 'expression (COMMA expression)*')
|
|
401
|
+
def reduce_arguments(_production, _range, _tokens, theChildren)
|
|
402
|
+
return [theChildren[0]] unless theChildren[1]
|
|
439
403
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
theChildren
|
|
404
|
+
successors = theChildren[1].map(&:last)
|
|
405
|
+
successors.unshift(theChildren[0])
|
|
443
406
|
end
|
|
444
407
|
end # class
|
|
445
408
|
end # module
|
data/lib/loxxy/ast/lox_node.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Loxxy
|
|
|
11
11
|
# Let nodes take `visitee` role as defined in the Visitor design pattern
|
|
12
12
|
extend ASTVisitee
|
|
13
13
|
|
|
14
|
-
# return [Rley::Lexical::Position] Position of the entry in the input stream.
|
|
14
|
+
# @return [Rley::Lexical::Position] Position of the entry in the input stream.
|
|
15
15
|
attr_reader :position
|
|
16
16
|
|
|
17
17
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
|
@@ -7,9 +7,9 @@ module Loxxy
|
|
|
7
7
|
module FrontEnd
|
|
8
8
|
########################################
|
|
9
9
|
# Grammar for Lox language
|
|
10
|
-
#
|
|
10
|
+
# Authoritative grammar at:
|
|
11
11
|
# https://craftinginterpreters.com/appendix-i.html
|
|
12
|
-
builder = Rley::
|
|
12
|
+
builder = Rley::grammar_builder do
|
|
13
13
|
# Punctuators, separators...
|
|
14
14
|
add_terminals('LEFT_PAREN', 'RIGHT_PAREN', 'LEFT_BRACE', 'RIGHT_BRACE')
|
|
15
15
|
add_terminals('COMMA', 'DOT', 'MINUS', 'PLUS')
|
|
@@ -26,34 +26,23 @@ module Loxxy
|
|
|
26
26
|
add_terminals('EOF')
|
|
27
27
|
|
|
28
28
|
# Top-level rule that matches an entire Lox program
|
|
29
|
-
rule('program' => 'EOF').as '
|
|
30
|
-
rule('program' => 'declaration_plus EOF').as 'lox_program'
|
|
29
|
+
rule('program' => 'declaration* EOF').as 'lox_program'
|
|
31
30
|
|
|
32
31
|
# Declarations: bind an identifier to something
|
|
33
|
-
rule('declaration_plus' => 'declaration_plus declaration').as 'declaration_plus_more'
|
|
34
|
-
rule('declaration_plus' => 'declaration').as 'declaration_plus_end'
|
|
35
32
|
rule('declaration' => 'classDecl')
|
|
36
33
|
rule('declaration' => 'funDecl')
|
|
37
34
|
rule('declaration' => 'varDecl')
|
|
38
|
-
rule('declaration' => '
|
|
35
|
+
rule('declaration' => 'statement')
|
|
39
36
|
|
|
40
37
|
rule('classDecl' => 'CLASS classNaming class_body').as 'class_decl'
|
|
41
|
-
rule('classNaming' => 'IDENTIFIER LESS IDENTIFIER').as '
|
|
42
|
-
rule('
|
|
43
|
-
rule('class_body' => 'LEFT_BRACE methods_opt RIGHT_BRACE').as 'class_body'
|
|
44
|
-
rule('methods_opt' => 'method_plus')
|
|
45
|
-
rule('methods_opt' => [])
|
|
46
|
-
rule('method_plus' => 'method_plus function').as 'method_plus_more'
|
|
47
|
-
rule('method_plus' => 'function').as 'method_plus_end'
|
|
38
|
+
rule('classNaming' => 'IDENTIFIER (LESS IDENTIFIER)?').as 'class_naming'
|
|
39
|
+
rule('class_body' => 'LEFT_BRACE function* RIGHT_BRACE').as 'class_body'
|
|
48
40
|
|
|
49
41
|
rule('funDecl' => 'FUN function').as 'fun_decl'
|
|
50
42
|
|
|
51
|
-
rule('varDecl' => 'VAR IDENTIFIER SEMICOLON').as 'var_declaration'
|
|
52
|
-
rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON').as 'var_initialization'
|
|
43
|
+
rule('varDecl' => 'VAR IDENTIFIER (EQUAL expression)? SEMICOLON').as 'var_declaration'
|
|
53
44
|
|
|
54
45
|
# Statements: produce side effects, but don't introduce bindings
|
|
55
|
-
rule('stmt' => 'statement')
|
|
56
|
-
rule('stmt' => 'unbalancedStmt') # Tweak to cope with "dangling else" problem
|
|
57
46
|
rule('statement' => 'exprStmt')
|
|
58
47
|
rule('statement' => 'forStmt')
|
|
59
48
|
rule('statement' => 'ifStmt')
|
|
@@ -65,32 +54,25 @@ module Loxxy
|
|
|
65
54
|
rule('exprStmt' => 'expression SEMICOLON').as 'exprStmt'
|
|
66
55
|
|
|
67
56
|
rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement').as 'for_stmt'
|
|
68
|
-
rule('forControl' => 'forInitialization forTest
|
|
57
|
+
rule('forControl' => 'forInitialization forTest expression?').as 'for_control'
|
|
69
58
|
rule('forInitialization' => 'varDecl')
|
|
70
59
|
rule('forInitialization' => 'exprStmt')
|
|
71
60
|
rule('forInitialization' => 'SEMICOLON').as 'empty_for_initialization'
|
|
72
|
-
rule('forTest' => '
|
|
73
|
-
rule('forUpdate' => 'expression_opt')
|
|
61
|
+
rule('forTest' => 'expression? SEMICOLON').as 'for_test'
|
|
74
62
|
|
|
75
|
-
rule('ifStmt' => 'IF ifCondition statement ELSE statement').as 'if_else_stmt'
|
|
76
|
-
rule('
|
|
77
|
-
rule('unbalancedStmt' => 'IF ifCondition statement ELSE unbalancedStmt').as 'if_else_stmt'
|
|
63
|
+
rule('ifStmt' => 'IF ifCondition statement ELSE {match_closest: "IF"} statement').as 'if_else_stmt'
|
|
64
|
+
rule('ifStmt' => 'IF ifCondition statement').as 'if_stmt'
|
|
78
65
|
rule('ifCondition' => 'LEFT_PAREN expression RIGHT_PAREN').as 'keep_symbol2'
|
|
79
66
|
|
|
80
67
|
rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
|
|
81
|
-
rule('returnStmt' => 'RETURN
|
|
68
|
+
rule('returnStmt' => 'RETURN expression? SEMICOLON').as 'return_stmt'
|
|
82
69
|
rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as 'while_stmt'
|
|
83
|
-
rule('block' => 'LEFT_BRACE
|
|
84
|
-
rule('block' => 'LEFT_BRACE RIGHT_BRACE').as 'block_empty'
|
|
70
|
+
rule('block' => 'LEFT_BRACE declaration* RIGHT_BRACE').as 'block_stmt'
|
|
85
71
|
|
|
86
72
|
# Expressions: produce values
|
|
87
|
-
rule('expression_opt' => 'expression')
|
|
88
|
-
rule('expression_opt' => [])
|
|
89
73
|
rule('expression' => 'assignment')
|
|
90
|
-
rule('assignment' => '
|
|
74
|
+
rule('assignment' => '(call DOT)? IDENTIFIER EQUAL assignment').as 'assign_expr'
|
|
91
75
|
rule('assignment' => 'logic_or')
|
|
92
|
-
rule('owner_opt' => 'call DOT').as 'set_expr'
|
|
93
|
-
rule('owner_opt' => [])
|
|
94
76
|
rule('logic_or' => 'logic_and')
|
|
95
77
|
rule('logic_or' => 'logic_and disjunct_plus').as 'logical_expr'
|
|
96
78
|
rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'binary_plus_more'
|
|
@@ -130,10 +112,8 @@ module Loxxy
|
|
|
130
112
|
rule('unaryOp' => 'BANG')
|
|
131
113
|
rule('unaryOp' => 'MINUS')
|
|
132
114
|
rule('call' => 'primary')
|
|
133
|
-
rule('call' => 'primary
|
|
134
|
-
rule('
|
|
135
|
-
rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
|
|
136
|
-
rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
|
|
115
|
+
rule('call' => 'primary refinement+').as 'call_expr'
|
|
116
|
+
rule('refinement' => 'LEFT_PAREN arguments? RIGHT_PAREN').as 'call_arglist'
|
|
137
117
|
rule('refinement' => 'DOT IDENTIFIER').as 'get_expr'
|
|
138
118
|
rule('primary' => 'TRUE').as 'literal_expr'
|
|
139
119
|
rule('primary' => 'FALSE').as 'literal_expr'
|
|
@@ -146,15 +126,9 @@ module Loxxy
|
|
|
146
126
|
rule('primary' => 'SUPER DOT IDENTIFIER').as 'super_expr'
|
|
147
127
|
|
|
148
128
|
# Utility rules
|
|
149
|
-
rule('function' => 'IDENTIFIER LEFT_PAREN
|
|
150
|
-
rule('
|
|
151
|
-
rule('
|
|
152
|
-
rule('parameters' => 'parameters COMMA IDENTIFIER').as 'parameters_plus_more'
|
|
153
|
-
rule('parameters' => 'IDENTIFIER').as 'parameters_plus_end'
|
|
154
|
-
rule('arguments_opt' => 'arguments')
|
|
155
|
-
rule('arguments_opt' => [])
|
|
156
|
-
rule('arguments' => 'arguments COMMA expression').as 'arguments_plus_more'
|
|
157
|
-
rule('arguments' => 'expression').as 'arguments_plus_end'
|
|
129
|
+
rule('function' => 'IDENTIFIER LEFT_PAREN parameters? RIGHT_PAREN block').as 'function'
|
|
130
|
+
rule('parameters' => 'IDENTIFIER (COMMA IDENTIFIER)*').as 'parameters'
|
|
131
|
+
rule('arguments' => 'expression (COMMA expression)*').as 'arguments'
|
|
158
132
|
end
|
|
159
133
|
|
|
160
134
|
unless defined?(Grammar)
|
data/lib/loxxy/version.rb
CHANGED
data/loxxy.gemspec
CHANGED
|
@@ -48,7 +48,7 @@ Gem::Specification.new do |spec|
|
|
|
48
48
|
DESCR_END
|
|
49
49
|
spec.homepage = 'https://github.com/famished-tiger/loxxy'
|
|
50
50
|
spec.license = 'MIT'
|
|
51
|
-
spec.required_ruby_version = '~> 2.
|
|
51
|
+
spec.required_ruby_version = '~> 2.5'
|
|
52
52
|
|
|
53
53
|
spec.bindir = 'bin'
|
|
54
54
|
spec.executables = ['loxxy']
|
|
@@ -58,7 +58,7 @@ Gem::Specification.new do |spec|
|
|
|
58
58
|
PkgExtending.pkg_documentation(spec)
|
|
59
59
|
|
|
60
60
|
# Runtime dependencies
|
|
61
|
-
spec.add_dependency 'rley', '~> 0.
|
|
61
|
+
spec.add_dependency 'rley', '~> 0.8.03'
|
|
62
62
|
|
|
63
63
|
# Development dependencies
|
|
64
64
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
@@ -26,7 +26,10 @@ module Loxxy
|
|
|
26
26
|
# program => declaration_star EOF
|
|
27
27
|
# where the declaration_star MUST be empty
|
|
28
28
|
expect(aParseTree.root.symbol.name).to eq('program')
|
|
29
|
-
eof = aParseTree.root.subnodes
|
|
29
|
+
(decls, eof) = aParseTree.root.subnodes
|
|
30
|
+
expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
|
|
31
|
+
expect(decls.symbol.name).to eq('declaration_star')
|
|
32
|
+
expect(decls.subnodes).to be_empty
|
|
30
33
|
expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
|
|
31
34
|
expect(eof.symbol.name).to eq('EOF')
|
|
32
35
|
end
|
|
@@ -73,11 +76,12 @@ LOX_END
|
|
|
73
76
|
expect(root.symbol.name).to eq('program')
|
|
74
77
|
(decls, eof) = root.subnodes
|
|
75
78
|
expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
|
|
76
|
-
expect(decls.symbol.name).to eq('
|
|
77
|
-
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
80
|
-
|
|
79
|
+
expect(decls.symbol.name).to eq('declaration_star')
|
|
80
|
+
expect(decls.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
|
81
|
+
expect(decls.subnodes[0].symbol.name).to eq('declaration_star')
|
|
82
|
+
expect(decls.subnodes[1]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
|
83
|
+
expect(decls.subnodes[1].symbol.name).to eq('declaration')
|
|
84
|
+
statement = decls.subnodes[1].subnodes[0]
|
|
81
85
|
expect(statement).to be_kind_of(Rley::PTree::NonTerminalNode)
|
|
82
86
|
expect(statement.symbol.name).to eq('statement')
|
|
83
87
|
prnt_stmt = statement.subnodes[0]
|
data/spec/spec_helper.rb
CHANGED
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.4.
|
|
4
|
+
version: 0.4.04
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dimitri Geshef
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-09-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rley
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.8.03
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.8.03
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -173,7 +173,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
173
173
|
requirements:
|
|
174
174
|
- - "~>"
|
|
175
175
|
- !ruby/object:Gem::Version
|
|
176
|
-
version: '2.
|
|
176
|
+
version: '2.5'
|
|
177
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
178
|
requirements:
|
|
179
179
|
- - ">="
|