loxxy 0.0.26 → 0.1.02

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: 1258e225c3c22ba2c8a1df645f2ee39fc6fc2330a62b288a15f59e1ca40596b2
4
- data.tar.gz: 102ac797634d4abf2e72902f568c5b0cc5ec161433e5e4c4e0888e7142508d65
3
+ metadata.gz: ddfc01e822c7e68c87d515649f6ef6e2c800c926ca289dfe9f65edeff24e7015
4
+ data.tar.gz: db5a6a8052c0c15f00920c6d76fcd8c6f9cfe380905c20b53f8e74cc7c6d4f84
5
5
  SHA512:
6
- metadata.gz: a1715b6c93b1861fadafac38327ea2fdee592f2b0b46320b04acd7e64db166911dc614d59412dae9d0bbccea9ca95f9299aac276eba9ee2a87a58963a2ec7d13
7
- data.tar.gz: 62915b07a3066c8068f41fd622d9d9be4d8baf29b6c3ef0849b3e410be5d52343fa8c00b8bbff1665deefc838cd29a423852f9666af3e410aee77b538f4030f0
6
+ metadata.gz: f5a85f8a0a4a762f43a9dd51ab972f924a95ed15ed9e864cef0c60f4815902cbfb905fef624cc100bae9283985f8f6c7f960fbd72bd8d87e9ee3ab211eb7dc24
7
+ data.tar.gz: 10522ab655b99e31007dccaa479b4ec59a9340160f6768c084c3b96825f4e89f9f1c46c1655117763d66f28004f5d8e2c193315c80a65dd49b733587bd25823a
data/CHANGELOG.md CHANGED
@@ -1,8 +1,72 @@
1
+ ## [0.1.02] - 2021-02-21
2
+ - Function definition and call documented in `README.md`
3
+
4
+ ### Changed
5
+ - File `README.md` updated todescribe function definition and function call.
6
+
7
+ ### Fixed
8
+ - Method `BackEnd::Engine#after_print_stmt` now handles of empty stack or nil data.
9
+ - Method `BackEnd::Engine#after_call_expr` was pushing one spurious item onto data stack.
10
+
11
+ ## [0.1.01] - 2021-02-20
12
+ ### Fixed
13
+ - Fixed most offences for Rubocop.
14
+
15
+
16
+ ## [0.1.00] - 2021-02-20
17
+ - Version number bumped, `Loxxy` supports function definitions
18
+
19
+ ### Added
20
+ - Class `Ast::LoxFunStmt` a node that represents a function declaration
21
+ - Method `Ast::ASTBuilder#reduce_fun_decl`
22
+ - Method `Ast::ASTBuilder#reduce_function` creates a `LoxFunStmt` instance
23
+ - Method `Ast::ASTBuilder#reduce_parameters_plus_more` for dealing with function parameters
24
+ - Method `Ast::ASTBuilder#reduce_parameters_plus_end`
25
+ - Method `Ast::ASTVisitor#visit_fun_stmt` for visiting an `Ast::LoxFunStmt` node
26
+ - Method `Ast::LoxBlockStmt#empty?` returns true if the code block is empty
27
+ - Method `BackEnd::Engine#after_fun_stmt`
28
+ - Method `Backend::NativeFunction#call`
29
+ - Method `Backend::NativeFunction#to_str`
30
+ - Method `Backend::Function` implementation of a function object.
31
+
32
+ ### Changed
33
+ - Method `BackEnd::Engine#after_call_expr`
34
+
35
+ ### Fixed
36
+ - Fixed inconsistencies in documentation comments.
37
+
38
+ ## [0.0.28] - 2021-02-15
39
+ - The interpreter implements function calls (to a native function).
40
+
41
+ ### Added
42
+ - Class `Ast::LoxCallExpr` a node that represents a function call expression
43
+ - Method `Ast::ASTBuilder#reduce_call_expr`
44
+ - Method `Ast::ASTBuilder#reduce_refinement_plus_end`
45
+ - Method `Ast::ASTBuilder#reduce_call_arglist` creates a `LoxCallExpr` node
46
+ - Method `Ast::ASTBuilder#reduce_arguments_plus_more` builds the function argument array
47
+ - Method `Ast::ASTVisitor#visit_call_expr` for visiting an `Ast::LoxCallExpr` node
48
+ - Method `BackEnd::Engine#after_call_expr`implements the evaluation of a function call.
49
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
50
+
51
+ ## [0.0.27] - 2021-01-24
52
+ - The interpreter implements `while` loops.
53
+
54
+ ### Added
55
+ - Class `Ast::LoxForStmt` a node that represents a `for` statement
56
+ - Method `Ast::ASTBuilder#reduce_for_stmt`
57
+ - Method `Ast::ASTBuilder#reduce_for_control` creates an `Ast::LoxForStmt` node
58
+ - Method `Ast::ASTVisitor#visit_for_stmt` for visiting an `Ast::LoxWhileStmt` node
59
+ - Method `BackEnd::Engine#before_for_stmt` builds a new environment for the loop variable
60
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
61
+
62
+ ### Changed
63
+ - File `README.md` updated.
64
+
1
65
  ## [0.0.26] - 2021-01-22
2
66
  - The interpreter implements `while` loops.
3
67
 
4
68
  ### Added
5
- - Class `Ast::LoxWhileStmt` a node that represents a while statement
69
+ - Class `Ast::LoxWhileStmt` a node that represents a `while` statement
6
70
  - Method `Ast::ASTBuilder#reduce_while_stmt` creates an `Ast::LoxWhileStmt` node
7
71
  - Method `Ast::ASTVisitor#visit_while_stmt` for visiting an `Ast::LoxWhileStmt` node
8
72
  - Method `BackEnd::Engine#after_while_stmt` implements the while looping structure
data/README.md CHANGED
@@ -14,9 +14,12 @@ 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 all allowed __Lox__ expressions and statements except:
18
+ - Closures,
19
+ - Classes and objects.
20
+
21
+ These will be implemented soon.
18
22
 
19
- But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
23
 
21
24
  ## What's the fuss about Lox?
22
25
  ... Nothing...
@@ -32,11 +35,11 @@ Although __Lox__ is fairly simple, it is far from a toy language:
32
35
  - Functions and closures
33
36
  - Object-orientation (classes, methods, inheritance).
34
37
 
35
- In other words, __Lox__ contains interesting features expected from most general-purpose
38
+ In other words, __Lox__ contains interesting features found in most general-purpose
36
39
  languages.
37
40
 
38
41
  ### What's missing in Lox?
39
- __Lox__ was constrained by design and therefore was not aimed to be a language used in real-world applications.
42
+ __Lox__ was constrained by design and was therefore not aimed to be a language used in real-world applications.
40
43
  Here are some missing parts to make it a _practical_ language:
41
44
  - Collections (arrays, maps, ...)
42
45
  - Modules (importing stuff from other packages/files)
@@ -66,6 +69,23 @@ lox = Loxxy::Interpreter.new
66
69
  lox.evaluate(lox_program) # Output: Hello, world!
67
70
  ```
68
71
 
72
+ ## A function definition example
73
+ ```ruby
74
+ require 'loxxy'
75
+
76
+ lox_program = <<LOX_END
77
+ fun add4(n) {
78
+ n + 4;
79
+ }
80
+
81
+ print add4(6); // Output: 10
82
+ LOX_END
83
+
84
+ lox = Loxxy::Interpreter.new
85
+ lox.evaluate(lox_program) # Output 10
86
+ ```
87
+
88
+
69
89
  ## Retrieving the result from a Lox program
70
90
  The __Loxxy__ interpreter returns the value of the last evaluated expression.
71
91
 
@@ -141,11 +161,13 @@ Here are the language features currently supported by the interpreter:
141
161
  - [Datatypes](#datatypes)
142
162
  - [Statements](#statements)
143
163
  -[Expressions](#expressions)
144
- - [Variable declarations](#var-statement)
164
+ - [Variable declarations](#var-statement)
165
+ - [For statement](#for-statement)
145
166
  - [If Statement](#if-statement)
146
167
  - [Print Statement](#print-statement)
147
168
  - [While Statement](#while-statement)
148
169
  - [Block Statement](#block-statement)
170
+ - [Function declaration](#func-statement)
149
171
 
150
172
  ### Comments
151
173
 
@@ -172,19 +194,21 @@ loxxy supports all the standard __Lox__ datatypes:
172
194
  ### Statements
173
195
 
174
196
  Loxxy supports the following statements:
175
- - [Expressions](#expressions)
197
+ - [Expressions](#expressions)
176
198
  -[Arithmetic expressions](#arithmetic-expressions)
177
199
  -[String concatenation](#string-concatenation)
178
200
  -[Comparison expressions](#comparison-expressions)
179
201
  -[Logical expressions](#logical-expressions)
180
202
  -[Grouping expressions](#grouping-expressions)
181
- -[Variable expressions and assignments](#variable-expressions)
182
-
203
+ -[Variable expressions and assignments](#variable-expressions)
204
+ -[Function call](#function-call)
205
+
183
206
  -[Variable declarations](#var-statement)
184
207
  -[If Statement](#if-statement)
185
208
  -[Print Statement](#print-statement)
186
- -[While Statement](#while-statement)
187
- -[Block Statement](#block-statement)
209
+ -[While Statement](#while-statement)
210
+ -[Block Statement](#block-statement)
211
+ -[Function Declaration](#function-declaration)
188
212
 
189
213
  #### Expressions
190
214
 
@@ -279,6 +303,24 @@ var iAmNil; // __Lox__ initializes variables to nil by default;
279
303
  print iAmNil; // output: nil
280
304
  ```
281
305
 
306
+ #### Function call
307
+ ``` javascript
308
+ // Calling a function without argument
309
+ print clock();
310
+
311
+ // Assumption: there exists a function `add` that takes two arguments
312
+ print add(2, 3);
313
+ ```
314
+
315
+ #### For statement
316
+
317
+ Similar to the `for` statement in `C` language
318
+ ``` javascript
319
+ for (var a = 1; a < 10; a = a + 1) {
320
+ print a; // Output: 123456789
321
+ }
322
+ ```
323
+
282
324
  #### If statement
283
325
 
284
326
  Based on a given condition, an if statement executes one of two statements:
@@ -348,6 +390,19 @@ var a = "outer";
348
390
  print a; // output: outer
349
391
  ```
350
392
 
393
+ #### Function Declaration
394
+ The keyword `fun` is used to begin a function declaration.
395
+ In __Lox__ a function has a name and a body (which may be empty).
396
+
397
+ ``` javascript
398
+ fun add4(n) // `add4` will be the name of the function
399
+ {
400
+ n + 4;
401
+ }
402
+
403
+ print add4(6); // output: 10
404
+ ```
405
+
351
406
  ## Installation
352
407
 
353
408
  Add this line to your application's Gemfile:
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'lox_fun_stmt'
3
4
  require_relative 'lox_variable_expr'
4
5
  require_relative 'lox_literal_expr'
5
6
  require_relative 'lox_noop_expr'
7
+ require_relative 'lox_call_expr'
6
8
  require_relative 'lox_grouping_expr'
7
9
  require_relative 'lox_unary_expr'
8
10
  require_relative 'lox_binary_expr'
@@ -12,5 +14,6 @@ require_relative 'lox_block_stmt'
12
14
  require_relative 'lox_while_stmt'
13
15
  require_relative 'lox_print_stmt'
14
16
  require_relative 'lox_if_stmt'
17
+ require_relative 'lox_for_stmt'
15
18
  require_relative 'lox_var_stmt'
16
19
  require_relative 'lox_seq_decl'
@@ -163,6 +163,11 @@ module Loxxy
163
163
  [theChildren[0]]
164
164
  end
165
165
 
166
+ # rule('funDecl' => 'FUN function')
167
+ def reduce_fun_decl(_production, _range, _tokens, theChildren)
168
+ theChildren[1]
169
+ end
170
+
166
171
  # rule('exprStmt' => 'expression SEMICOLON')
167
172
  def reduce_exprStmt(_production, range, tokens, theChildren)
168
173
  return_first_child(range, tokens, theChildren) # Discard the semicolon
@@ -180,6 +185,29 @@ module Loxxy
180
185
  Ast::LoxVarStmt.new(tokens[1].position, var_name, theChildren[3])
181
186
  end
182
187
 
188
+ # rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
189
+ def reduce_for_stmt(_production, _range, _tokens, theChildren)
190
+ for_stmt = theChildren[2]
191
+ for_stmt.body_stmt = theChildren[4]
192
+ for_stmt
193
+ end
194
+
195
+ # rule('forControl' => 'forInitialization forTest forUpdate')
196
+ def reduce_for_control(_production, _range, tokens, theChildren)
197
+ (init, test, update) = theChildren
198
+ Ast::LoxForStmt.new(tokens[0].position, init, test, update)
199
+ end
200
+
201
+ # rule('forInitialization' => 'SEMICOLON')
202
+ def reduce_empty_for_initialization(_production, _range, _tokens, _theChildren)
203
+ nil
204
+ end
205
+
206
+ # rule('forTest' => 'expression_opt SEMICOLON')
207
+ def reduce_for_test(_production, range, tokens, theChildren)
208
+ return_first_child(range, tokens, theChildren)
209
+ end
210
+
183
211
  # rule('ifStmt' => 'IF ifCondition statement elsePart_opt')
184
212
  def reduce_if_stmt(_production, _range, tokens, theChildren)
185
213
  condition = theChildren[1]
@@ -204,105 +232,46 @@ module Loxxy
204
232
  Ast::LoxBlockStmt.new(tokens[1].position, decls)
205
233
  end
206
234
 
235
+ # rule('block' => 'LEFT_BRACE RIGHT_BRACE').as 'block_empty'
236
+ def reduce_block_empty(_production, _range, tokens, _children)
237
+ Ast::LoxBlockStmt.new(tokens[0].position, nil)
238
+ end
239
+
207
240
  # rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
208
241
  def reduce_assign_expr(_production, _range, tokens, theChildren)
209
242
  var_name = theChildren[1].token.lexeme.dup
210
243
  Ast::LoxAssignExpr.new(tokens[1].position, var_name, theChildren[3])
211
244
  end
212
245
 
213
- # rule('logic_or' => 'logic_and disjunct_plus')
214
- def reduce_logic_or_plus(production, range, tokens, theChildren)
215
- reduce_logical_expr(production, range, tokens, theChildren)
216
- end
217
-
218
- # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
219
- def reduce_logic_or_plus_more(production, range, tokens, theChildren)
220
- reduce_binary_plus_more(production, range, tokens, theChildren)
221
- end
222
-
223
- # rule('disjunct_plus' => 'OR logic_and')
224
- def reduce_logic_or_plus_end(production, range, tokens, theChildren)
225
- reduce_binary_plus_end(production, range, tokens, theChildren)
226
- end
227
-
228
- # rule('logic_and' => 'equality conjunct_plus')
229
- def reduce_logic_and_plus(production, range, tokens, theChildren)
230
- reduce_logical_expr(production, range, tokens, theChildren)
231
- end
232
-
233
- # rule('conjunct_plus' => 'conjunct_plus AND equality')
234
- def reduce_logic_and_plus_more(production, range, tokens, theChildren)
235
- reduce_binary_plus_more(production, range, tokens, theChildren)
236
- end
237
-
238
- # rule('conjunct_plus' => 'AND equality')
239
- def reduce_logic_and_plus_end(production, range, tokens, theChildren)
240
- reduce_binary_plus_end(production, range, tokens, theChildren)
241
- end
242
-
243
- # rule('equality' => 'comparison equalityTest_plus')
244
- def reduce_equality_plus(production, range, tokens, theChildren)
245
- reduce_binary_operator(production, range, tokens, theChildren)
246
- end
247
-
248
- # rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
249
- def reduce_equality_t_plus_more(production, range, tokens, theChildren)
250
- reduce_binary_plus_more(production, range, tokens, theChildren)
251
- end
252
-
253
- # rule('equalityTest_star' => 'equalityTest comparison')
254
- def reduce_equality_t_plus_end(production, range, tokens, theChildren)
255
- reduce_binary_plus_end(production, range, tokens, theChildren)
256
- end
257
-
258
- # rule('comparison' => 'term comparisonTest_plus')
259
- def reduce_comparison_plus(production, range, tokens, theChildren)
260
- reduce_binary_operator(production, range, tokens, theChildren)
261
- end
262
-
263
246
  # rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
264
247
  # TODO: is it meaningful to implement this rule?
265
248
 
266
- # rule('comparisonTest_plus' => 'comparisonTest term')
267
- def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
268
- reduce_binary_plus_end(production, range, tokens, theChildren)
269
- end
270
-
271
- # rule('term' => 'factor additive_plus')
272
- def reduce_term_additive(production, range, tokens, theChildren)
273
- reduce_binary_operator(production, range, tokens, theChildren)
274
- end
275
-
276
- # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
277
- def reduce_additive_plus_more(production, range, tokens, theChildren)
278
- reduce_binary_plus_more(production, range, tokens, theChildren)
279
- end
280
-
281
- # rule('additive_plus' => 'additionOp factor')
282
- def reduce_additive_plus_end(production, range, tokens, theChildren)
283
- reduce_binary_plus_end(production, range, tokens, theChildren)
249
+ # rule('unary' => 'unaryOp unary')
250
+ def reduce_unary_expr(_production, _range, tokens, theChildren)
251
+ operator = Name2unary[theChildren[0].symbol.name].to_sym
252
+ operand = theChildren[1]
253
+ LoxUnaryExpr.new(tokens[0].position, operator, operand)
284
254
  end
285
255
 
286
- # rule('factor' => 'multiplicative_plus')
287
- def reduce_factor_multiplicative(production, range, tokens, theChildren)
288
- reduce_binary_operator(production, range, tokens, theChildren)
256
+ # rule('call' => 'primary refinement_plus').as 'call_expr'
257
+ def reduce_call_expr(_production, _range, _tokens, theChildren)
258
+ theChildren[1].callee = theChildren[0]
259
+ theChildren[1]
289
260
  end
290
261
 
291
- # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
292
- def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
293
- reduce_binary_plus_more(production, range, tokens, theChildren)
262
+ # rule('refinement_plus' => 'refinement').
263
+ def reduce_refinement_plus_end(_production, _range, _tokens, theChildren)
264
+ theChildren[0]
294
265
  end
295
266
 
296
- # rule('multiplicative_plus' => 'multOp unary')
297
- def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
298
- reduce_binary_plus_end(production, range, tokens, theChildren)
299
- end
267
+ # rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
268
+ def reduce_call_arglist(_production, _range, tokens, theChildren)
269
+ args = theChildren[1] || []
270
+ if args.size > 255
271
+ raise StandardError, "Can't have more than 255 arguments."
272
+ end
300
273
 
301
- # rule('unary' => 'unaryOp unary')
302
- def reduce_unary_expr(_production, _range, tokens, theChildren)
303
- operator = Name2unary[theChildren[0].symbol.name].to_sym
304
- operand = theChildren[1]
305
- LoxUnaryExpr.new(tokens[0].position, operator, operand)
274
+ LoxCallExpr.new(tokens[0].position, args)
306
275
  end
307
276
 
308
277
  # rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
@@ -324,6 +293,33 @@ module Loxxy
324
293
  var_name = theChildren[0].token.lexeme
325
294
  LoxVariableExpr.new(tokens[0].position, var_name)
326
295
  end
296
+
297
+ # rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block').as 'function'
298
+ def reduce_function(_production, _range, _tokens, theChildren)
299
+ first_child = theChildren.first
300
+ pos = first_child.token.position
301
+ LoxFunStmt.new(pos, first_child.token.lexeme, theChildren[2], theChildren[4])
302
+ end
303
+
304
+ # rule('parameters' => 'parameters COMMA IDENTIFIER')
305
+ def reduce_parameters_plus_more(_production, _range, _tokens, theChildren)
306
+ theChildren[0] << theChildren[2].token.lexeme
307
+ end
308
+
309
+ # rule('parameters' => 'IDENTIFIER')
310
+ def reduce_parameters_plus_end(_production, _range, _tokens, theChildren)
311
+ [theChildren[0].token.lexeme]
312
+ end
313
+
314
+ # rule('arguments' => 'arguments COMMA expression')
315
+ def reduce_arguments_plus_more(_production, _range, _tokens, theChildren)
316
+ theChildren[0] << theChildren[2]
317
+ end
318
+
319
+ # rule('arguments' => 'expression')
320
+ def reduce_arguments_plus_end(_production, _range, _tokens, theChildren)
321
+ theChildren
322
+ end
327
323
  end # class
328
324
  end # module
329
325
  end # module
@@ -12,7 +12,7 @@ module Loxxy
12
12
  # attr_reader(:runtime)
13
13
 
14
14
  # Build a visitor for the given top.
15
- # @param aRoot [AST::LoxNode] the parse tree to visit.
15
+ # @param aTop [AST::LoxNode] the parse tree to visit.
16
16
  def initialize(aTop)
17
17
  raise StandardError if aTop.nil?
18
18
 
@@ -52,7 +52,7 @@ module Loxxy
52
52
  end
53
53
 
54
54
  # Visit event. The visitor is about to visit a variable declaration statement.
55
- # @param aPrintStmt [AST::LOXVarStmt] the variable declaration node to visit
55
+ # @param aSeqDecls [AST::LOXSeqDecl] the variable declaration node to visit
56
56
  def visit_seq_decl(aSeqDecls)
57
57
  broadcast(:before_seq_decl, aSeqDecls)
58
58
  traverse_subnodes(aSeqDecls)
@@ -60,13 +60,21 @@ module Loxxy
60
60
  end
61
61
 
62
62
  # Visit event. The visitor is about to visit a variable declaration statement.
63
- # @param aPrintStmt [AST::LOXVarStmt] the variable declaration node to visit
63
+ # @param aVarStmt [AST::LOXVarStmt] the variable declaration node to visit
64
64
  def visit_var_stmt(aVarStmt)
65
65
  broadcast(:before_var_stmt, aVarStmt)
66
66
  traverse_subnodes(aVarStmt)
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)
@@ -95,12 +103,12 @@ module Loxxy
95
103
  # @param aBlockStmt [AST::LOXBlockStmt] the print statement node to visit
96
104
  def visit_block_stmt(aBlockStmt)
97
105
  broadcast(:before_block_stmt, aBlockStmt)
98
- traverse_subnodes(aBlockStmt)
106
+ traverse_subnodes(aBlockStmt) unless aBlockStmt.empty?
99
107
  broadcast(:after_block_stmt, aBlockStmt)
100
108
  end
101
109
 
102
110
  # Visit event. The visitor is visiting an assignment node
103
- # @param aLiteralExpr [AST::LoxAssignExpr] the variable assignment node to visit.
111
+ # @param anAssignExpr [AST::LoxAssignExpr] the variable assignment node to visit.
104
112
  def visit_assign_expr(anAssignExpr)
105
113
  broadcast(:before_assign_expr, anAssignExpr)
106
114
  traverse_subnodes(anAssignExpr)
@@ -110,7 +118,7 @@ module Loxxy
110
118
  # Visit event. The visitor is about to visit a logical expression.
111
119
  # Since logical expressions may take shorcuts by not evaluating all their
112
120
  # sub-expressiosns, they are responsible for visiting or not their children.
113
- # @param aBinaryExpr [AST::LOXBinaryExpr] the logical expression node to visit
121
+ # @param aLogicalExpr [AST::LOXLogicalExpr] the logical expression node to visit
114
122
  def visit_logical_expr(aLogicalExpr)
115
123
  broadcast(:before_logical_expr, aLogicalExpr)
116
124
 
@@ -137,6 +145,14 @@ module Loxxy
137
145
  broadcast(:after_unary_expr, anUnaryExpr)
138
146
  end
139
147
 
148
+ # Visit event. The visitor is about to visit a call expression.
149
+ # @param aCallExpr [AST::LoxCallExpr] call expression to visit
150
+ def visit_call_expr(aCallExpr)
151
+ broadcast(:before_call_expr, aCallExpr)
152
+ traverse_subnodes(aCallExpr)
153
+ broadcast(:after_call_expr, aCallExpr, self)
154
+ end
155
+
140
156
  # Visit event. The visitor is about to visit a grouping expression.
141
157
  # @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
142
158
  def visit_grouping_expr(aGroupingExpr)
@@ -154,25 +170,35 @@ module Loxxy
154
170
  end
155
171
 
156
172
  # Visit event. The visitor is visiting a variable usage node
157
- # @param aLiteralExpr [AST::LoxVariableExpr] the variable reference node to visit.
173
+ # @param aVariableExpr [AST::LoxVariableExpr] the variable reference node to visit.
158
174
  def visit_variable_expr(aVariableExpr)
159
175
  broadcast(:before_variable_expr, aVariableExpr)
160
176
  broadcast(:after_variable_expr, aVariableExpr, self)
161
177
  end
162
178
 
163
179
  # Visit event. The visitor is about to visit the given terminal datatype value.
164
- # @param aNonTerminalNode [Ast::BuiltinDattype] the built-in datatype value
180
+ # @param aValue [Ast::BuiltinDattype] the built-in datatype value
165
181
  def visit_builtin(aValue)
166
182
  broadcast(:before_visit_builtin, aValue)
167
183
  broadcast(:after_visit_builtin, aValue)
168
184
  end
169
185
 
186
+ # Visit event. The visitor is about to visit a function statement node.
187
+ # @param aFunStmt [AST::LoxFunStmt] function declaration to visit
188
+ def visit_fun_stmt(aFunStmt)
189
+ broadcast(:before_fun_stmt, aFunStmt)
190
+ traverse_subnodes(aFunStmt)
191
+ broadcast(:after_fun_stmt, aFunStmt, self)
192
+ end
193
+
170
194
  # Visit event. The visitor is about to visit the given non terminal node.
171
195
  # @param aNonTerminalNode [Rley::PTree::NonTerminalNode] the node to visit.
172
- def visit_nonterminal(_non_terminal_node)
196
+ def visit_nonterminal(aNonTerminalNode)
173
197
  # Loxxy interpreter encountered a CST node (Concrete Syntax Tree)
174
198
  # that it cannot handle.
175
- raise NotImplementedError, 'Loxxy cannot execute this code yet.'
199
+ symb = aNonTerminalNode.symbol.name
200
+ msg = "Loxxy cannot execute this code yet for non-terminal symbol '#{symb}'."
201
+ raise NotImplementedError, msg
176
202
  end
177
203
 
178
204
  private
@@ -6,11 +6,15 @@ module Loxxy
6
6
  module Ast
7
7
  class LoxBlockStmt < LoxCompoundExpr
8
8
  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
- # @param operand [Loxxy::Ast::LoxSeqDecl]
9
+ # @param decls [Loxxy::Ast::LoxSeqDecl]
10
10
  def initialize(aPosition, decls)
11
11
  super(aPosition, [decls])
12
12
  end
13
13
 
14
+ def empty?
15
+ subnodes.size == 1 && subnodes[0].nil?
16
+ end
17
+
14
18
  # Part of the 'visitee' role in Visitor design pattern.
15
19
  # @param visitor [Ast::ASTVisitor] the visitor
16
20
  def accept(visitor)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxCallExpr < LoxCompoundExpr
8
+ attr_accessor :callee
9
+ attr_reader :arguments
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param argList [Array<Loxxy::Ast::LoxNode>]
13
+ def initialize(aPosition, argList)
14
+ super(aPosition, [])
15
+ @arguments = argList
16
+ end
17
+
18
+ # Part of the 'visitee' role in Visitor design pattern.
19
+ # @param visitor [Ast::ASTVisitor] the visitor
20
+ def accept(visitor)
21
+ visitor.visit_call_expr(self)
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
@@ -0,0 +1,41 @@
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
+ def initialize(aPosition, initialization, testExpr, updateExpr)
22
+ child = initialization ? [initialization] : []
23
+ super(aPosition, child)
24
+ @test_expr = testExpr
25
+ @update_expr = updateExpr
26
+ end
27
+
28
+ # Part of the 'visitee' role in Visitor design pattern.
29
+ # @param visitor [Ast::ASTVisitor] the visitor
30
+ def accept(visitor)
31
+ visitor.visit_for_stmt(self)
32
+ end
33
+
34
+ # Accessor to the condition expression
35
+ # @return [LoxNode]
36
+ def condition
37
+ subnodes[0]
38
+ end
39
+ end # class
40
+ end # module
41
+ end # module
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ # rubocop: disable Style/AccessorGrouping
8
+ class LoxFunStmt < LoxCompoundExpr
9
+ attr_reader :name
10
+ attr_reader :params
11
+ attr_reader :body
12
+
13
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
14
+ # @param aName [String]
15
+ # @param arguments [Array<String>]
16
+ # @param body [Ast::LoxBlockStmt]
17
+ def initialize(aPosition, aName, paramList, aBody)
18
+ super(aPosition, [])
19
+ @name = aName
20
+ @params = paramList
21
+ @body = aBody
22
+ end
23
+
24
+ # Part of the 'visitee' role in Visitor design pattern.
25
+ # @param visitor [Ast::ASTVisitor] the visitor
26
+ def accept(visitor)
27
+ visitor.visit_fun_stmt(self)
28
+ end
29
+
30
+ alias operands subnodes
31
+ end # class
32
+ # rubocop: enable Style/AccessorGrouping
33
+ end # module
34
+ end # module
@@ -12,7 +12,9 @@ module Loxxy
12
12
  attr_reader :else_stmt
13
13
 
14
14
  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
15
- # @param subExpr [Loxxy::Ast::LoxNode]
15
+ # @param condExpr [Loxxy::Ast::LoxNode]
16
+ # @param thenStmt [Loxxy::Ast::LoxNode]
17
+ # @param elseStmt [Loxxy::Ast::LoxNode]
16
18
  def initialize(aPosition, condExpr, thenStmt, elseStmt)
17
19
  super(aPosition, [condExpr])
18
20
  @then_stmt = thenStmt
@@ -2,6 +2,7 @@
2
2
 
3
3
  # Load all the classes implementing AST nodes
4
4
  require_relative '../ast/all_lox_nodes'
5
+ require_relative 'function'
5
6
  require_relative 'symbol_table'
6
7
 
7
8
  module Loxxy
@@ -25,6 +26,8 @@ module Loxxy
25
26
  @ostream = config.include?(:ostream) ? config[:ostream] : $stdout
26
27
  @symbol_table = SymbolTable.new
27
28
  @stack = []
29
+
30
+ init_globals
28
31
  end
29
32
 
30
33
  # Given an abstract syntax parse tree visitor, launch the visit
@@ -51,6 +54,23 @@ module Loxxy
51
54
  symbol_table.insert(new_var)
52
55
  end
53
56
 
57
+ def before_for_stmt(aForStmt)
58
+ before_block_stmt(aForStmt)
59
+ end
60
+
61
+ def after_for_stmt(aForStmt, aVisitor)
62
+ loop do
63
+ aForStmt.test_expr.accept(aVisitor)
64
+ condition = stack.pop
65
+ break unless condition.truthy?
66
+
67
+ aForStmt.body_stmt.accept(aVisitor)
68
+ aForStmt.update_expr&.accept(aVisitor)
69
+ stack.pop
70
+ end
71
+ after_block_stmt(aForStmt)
72
+ end
73
+
54
74
  def after_if_stmt(anIfStmt, aVisitor)
55
75
  # Retrieve the result of the condition evaluation
56
76
  condition = stack.pop
@@ -63,7 +83,7 @@ module Loxxy
63
83
 
64
84
  def after_print_stmt(_printStmt)
65
85
  tos = stack.pop
66
- @ostream.print tos.to_str
86
+ @ostream.print tos ? tos.to_str : 'nil'
67
87
  end
68
88
 
69
89
  def after_while_stmt(aWhileStmt, aVisitor)
@@ -150,6 +170,27 @@ module Loxxy
150
170
  end
151
171
  end
152
172
 
173
+ def after_call_expr(aCallExpr, aVisitor)
174
+ # Evaluate callee part
175
+ aCallExpr.callee.accept(aVisitor)
176
+ callee = stack.pop
177
+ aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) }
178
+
179
+ if callee.kind_of?(NativeFunction)
180
+ stack.push callee.call # Pass arguments
181
+ else
182
+ new_env = Environment.new(symbol_table.current_env)
183
+ symbol_table.enter_environment(new_env)
184
+ callee.parameters&.each do |param_name|
185
+ local = Variable.new(param_name, stack.pop)
186
+ symbol_table.insert(local)
187
+ end
188
+ callee.call(aVisitor)
189
+
190
+ symbol_table.leave_environment
191
+ end
192
+ end
193
+
153
194
  def after_grouping_expr(_groupingExpr)
154
195
  # Do nothing: work was already done by visiting /evaluating the subexpression
155
196
  end
@@ -167,10 +208,50 @@ module Loxxy
167
208
  stack.push(literalExpr.literal)
168
209
  end
169
210
 
170
- # @param aNonTerminalNode [Ast::BuiltinDattype] the built-in datatype value
211
+ # @param aValue [Ast::BuiltinDattype] the built-in datatype value
171
212
  def before_visit_builtin(aValue)
172
213
  stack.push(aValue)
173
214
  end
215
+
216
+ def after_fun_stmt(aFunStmt, _visitor)
217
+ function = Function.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, stack)
218
+ new_var = Variable.new(aFunStmt.name, function)
219
+ symbol_table.insert(new_var)
220
+ end
221
+
222
+ private
223
+
224
+ NativeFunction = Struct.new(:callable, :interp) do
225
+ def accept(_visitor)
226
+ interp.stack.push self
227
+ end
228
+
229
+ def call
230
+ callable.call
231
+ end
232
+
233
+ def to_str
234
+ '<native fn>'
235
+ end
236
+ end
237
+
238
+ def init_globals
239
+ add_native_fun('clock', native_clock)
240
+ end
241
+
242
+ def add_native_fun(aName, aProc)
243
+ native_fun = Variable.new(aName, NativeFunction.new(aProc, self))
244
+ symbol_table.insert(native_fun)
245
+ end
246
+
247
+ # Ruby-native function that returns (as float) the number of seconds since
248
+ # a given time reference.
249
+ def native_clock
250
+ proc do
251
+ now = Time.now.to_f
252
+ Datatype::Number.new(now)
253
+ end
254
+ end
174
255
  end # class
175
256
  end # module
176
257
  end # module
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../datatype/all_datatypes'
4
+
5
+ module Loxxy
6
+ module BackEnd
7
+ # rubocop: disable Style/AccessorGrouping
8
+ # Representation of a Lox function.
9
+ # It is a named slot that can be associated with a value at the time.
10
+ class Function
11
+ # @return [String]
12
+ attr_reader :name
13
+
14
+ # @return [Array<>] the parameters
15
+ attr_reader :parameters
16
+ attr_reader :body
17
+ attr_reader :stack
18
+
19
+ # Create a variable with given name and initial value
20
+ # @param aName [String] The name of the variable
21
+ # @param aValue [Datatype::BuiltinDatatype] the initial assigned value
22
+ def initialize(aName, parameterList, aBody, aStack)
23
+ @name = aName.dup
24
+ @parameters = parameterList
25
+ @body = aBody
26
+ @stack = aStack
27
+ end
28
+
29
+ def accept(_visitor)
30
+ stack.push self
31
+ end
32
+
33
+ def call(aVisitor)
34
+ body.empty? ? Datatype::Nil.instance : body.accept(aVisitor)
35
+ end
36
+
37
+ # Text representation of a Lox function
38
+ def to_str
39
+ "<fn #{name}>"
40
+ end
41
+ end # class
42
+ # rubocop: enable Style/AccessorGrouping
43
+ end # module
44
+ end # module
@@ -44,7 +44,7 @@ module Loxxy
44
44
  rule('function_star' => 'function_star function')
45
45
  rule('function_star' => [])
46
46
 
47
- rule('funDecl' => 'FUN function')
47
+ rule('funDecl' => 'FUN function').as 'fun_decl'
48
48
 
49
49
  rule('varDecl' => 'VAR IDENTIFIER SEMICOLON').as 'var_declaration'
50
50
  rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON').as 'var_initialization'
@@ -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'
@@ -77,7 +77,7 @@ module Loxxy
77
77
  rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
78
78
  rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as 'while_stmt'
79
79
  rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE').as 'block_stmt'
80
- rule('block' => 'LEFT_BRACE RIGHT_BRACE')
80
+ rule('block' => 'LEFT_BRACE RIGHT_BRACE').as 'block_empty'
81
81
 
82
82
  # Expressions: produce values
83
83
  rule('expression_opt' => 'expression')
@@ -88,37 +88,37 @@ module Loxxy
88
88
  rule('owner_opt' => 'call DOT')
89
89
  rule('owner_opt' => [])
90
90
  rule('logic_or' => 'logic_and')
91
- rule('logic_or' => 'logic_and disjunct_plus').as 'logic_or_plus'
92
- rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'logic_or_plus_more'
93
- rule('disjunct_plus' => 'OR logic_and').as 'logic_or_plus_end'
91
+ rule('logic_or' => 'logic_and disjunct_plus').as 'logical_expr'
92
+ rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'binary_plus_more'
93
+ rule('disjunct_plus' => 'OR logic_and').as 'binary_plus_end'
94
94
  rule('logic_and' => 'equality')
95
- rule('logic_and' => 'equality conjunct_plus').as 'logic_and_plus'
96
- rule('conjunct_plus' => 'conjunct_plus AND equality').as 'logic_and_plus_more'
97
- rule('conjunct_plus' => 'AND equality').as 'logic_and_plus_end'
95
+ rule('logic_and' => 'equality conjunct_plus').as 'logical_expr'
96
+ rule('conjunct_plus' => 'conjunct_plus AND equality').as 'binary_plus_more'
97
+ rule('conjunct_plus' => 'AND equality').as 'binary_plus_end'
98
98
  rule('equality' => 'comparison')
99
- rule('equality' => 'comparison equalityTest_plus').as 'equality_plus'
100
- rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as 'equality_t_plus_more'
101
- rule('equalityTest_plus' => 'equalityTest comparison').as 'equality_t_plus_end'
99
+ rule('equality' => 'comparison equalityTest_plus').as 'binary_operator'
100
+ rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as 'binary_plus_more'
101
+ rule('equalityTest_plus' => 'equalityTest comparison').as 'binary_plus_end'
102
102
  rule('equalityTest' => 'BANG_EQUAL')
103
103
  rule('equalityTest' => 'EQUAL_EQUAL')
104
104
  rule('comparison' => 'term')
105
- rule('comparison' => 'term comparisonTest_plus').as 'comparison_plus'
105
+ rule('comparison' => 'term comparisonTest_plus').as 'binary_operator'
106
106
  rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
107
- rule('comparisonTest_plus' => 'comparisonTest term').as 'comparison_t_plus_end'
107
+ rule('comparisonTest_plus' => 'comparisonTest term').as 'binary_plus_end'
108
108
  rule('comparisonTest' => 'GREATER')
109
109
  rule('comparisonTest' => 'GREATER_EQUAL')
110
110
  rule('comparisonTest' => 'LESS')
111
111
  rule('comparisonTest' => 'LESS_EQUAL')
112
112
  rule('term' => 'factor')
113
- rule('term' => 'factor additive_plus').as 'term_additive'
114
- rule('additive_plus' => 'additive_plus additionOp factor').as 'additive_plus_more'
115
- rule('additive_plus' => 'additionOp factor').as 'additive_plus_end'
113
+ rule('term' => 'factor additive_plus').as 'binary_operator'
114
+ rule('additive_plus' => 'additive_plus additionOp factor').as 'binary_plus_more'
115
+ rule('additive_plus' => 'additionOp factor').as 'binary_plus_end'
116
116
  rule('additionOp' => 'MINUS')
117
117
  rule('additionOp' => 'PLUS')
118
118
  rule('factor' => 'unary')
119
- rule('factor' => 'unary multiplicative_plus').as 'factor_multiplicative'
120
- rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'multiplicative_plus_more'
121
- rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
119
+ rule('factor' => 'unary multiplicative_plus').as 'binary_operator'
120
+ rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'binary_plus_more'
121
+ rule('multiplicative_plus' => 'multOp unary').as 'binary_plus_end'
122
122
  rule('multOp' => 'SLASH')
123
123
  rule('multOp' => 'STAR')
124
124
  rule('unary' => 'unaryOp unary').as 'unary_expr'
@@ -126,10 +126,10 @@ module Loxxy
126
126
  rule('unaryOp' => 'BANG')
127
127
  rule('unaryOp' => 'MINUS')
128
128
  rule('call' => 'primary')
129
- rule('call' => 'primary refinement_plus')
130
- rule('refinement_plus' => 'refinement_plus refinement')
131
- rule('refinement_plus' => 'refinement')
132
- rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
129
+ rule('call' => 'primary refinement_plus').as 'call_expr'
130
+ rule('refinement_plus' => 'refinement_plus refinement') # .as 'refinement_plus_more'
131
+ rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
132
+ rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
133
133
  rule('refinement' => 'DOT IDENTIFIER')
134
134
  rule('primary' => 'TRUE').as 'literal_expr'
135
135
  rule('primary' => 'FALSE').as 'literal_expr'
@@ -142,15 +142,15 @@ module Loxxy
142
142
  rule('primary' => 'SUPER DOT IDENTIFIER')
143
143
 
144
144
  # Utility rules
145
- rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block')
145
+ rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block').as 'function'
146
146
  rule('params_opt' => 'parameters')
147
147
  rule('params_opt' => [])
148
- rule('parameters' => 'parameters COMMA IDENTIFIER')
149
- rule('parameters' => 'IDENTIFIER')
148
+ rule('parameters' => 'parameters COMMA IDENTIFIER').as 'parameters_plus_more'
149
+ rule('parameters' => 'IDENTIFIER').as 'parameters_plus_end'
150
150
  rule('arguments_opt' => 'arguments')
151
151
  rule('arguments_opt' => [])
152
- rule('arguments' => 'arguments COMMA expression')
153
- rule('arguments' => 'expression')
152
+ rule('arguments' => 'arguments COMMA expression').as 'arguments_plus_more'
153
+ rule('arguments' => 'expression').as 'arguments_plus_end'
154
154
  end
155
155
 
156
156
  unless defined?(Grammar)
@@ -11,7 +11,7 @@ module Loxxy
11
11
 
12
12
  # Constructor.
13
13
  # @param aValue [Datatype::BuiltinDatatype] the Lox data value
14
- # @param theLexeme [String] the lexeme (= piece of text from input)
14
+ # @param aLexeme [String] the lexeme (= piece of text from input)
15
15
  # @param aTerminal [Rley::Syntax::Terminal, String]
16
16
  # The terminal symbol corresponding to the lexeme.
17
17
  # @param aPosition [Rley::Lexical::Position] The position of lexeme
data/lib/loxxy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.26'
4
+ VERSION = '0.1.02'
5
5
  end
@@ -26,6 +26,7 @@ module Loxxy
26
26
  expect(subject.to_str).to eq(sample_text)
27
27
  end
28
28
 
29
+ # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
29
30
  it 'compares with another Lox string' do
30
31
  result = subject == LXString.new(sample_text.dup)
31
32
  expect(result).to be_true
@@ -40,6 +41,7 @@ module Loxxy
40
41
  result = LXString.new('') == LXString.new('')
41
42
  expect(result).to be_true
42
43
  end
44
+ # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
43
45
 
44
46
  it 'compares with a Ruby string' do
45
47
  result = subject == sample_text.dup
@@ -21,6 +21,7 @@ module Loxxy
21
21
  end
22
22
  end
23
23
 
24
+ # rubocop: disable Lint/FloatComparison
24
25
  context 'Provided services:' do
25
26
  it 'should compare with other Lox numbers' do
26
27
  result = subject == Number.new(sample_value)
@@ -51,7 +52,8 @@ module Loxxy
51
52
  subtraction = subject - Number.new(10)
52
53
  expect(subtraction == -22.34).to be_true
53
54
  end
54
- end
55
+ end # context
56
+ # rubocop: enable Lint/FloatComparison
55
57
  end # describe
56
58
  end # module
57
59
  end # module
@@ -208,6 +208,7 @@ LOX_END
208
208
  expect(eof_token.terminal).to eq('EOF')
209
209
  end
210
210
 
211
+ # rubocop: disable Lint/PercentStringArray
211
212
  it 'should skip end of line comments' do
212
213
  input = <<-LOX_END
213
214
  // first comment
@@ -223,6 +224,7 @@ LOX_END
223
224
  ]
224
225
  match_expectations(subject, expectations)
225
226
  end
227
+ # rubocop: enable Lint/PercentStringArray
226
228
 
227
229
  it 'should cope with single slash (divide) expression' do
228
230
  subject.start_with('8 / 2')
@@ -9,6 +9,7 @@ require_relative '../lib/loxxy/interpreter'
9
9
  module Loxxy
10
10
  # This spec contains the bare bones test for the Interpreter class.
11
11
  # The execution of Lox code is tested elsewhere.
12
+ # rubocop: disable Metrics/BlockLength
12
13
  describe Interpreter do
13
14
  let(:sample_cfg) do
14
15
  { ostream: StringIO.new }
@@ -358,10 +359,76 @@ LOX_END
358
359
  expect(sample_cfg[:ostream].string).to eq('012')
359
360
  end
360
361
 
362
+ it 'should implement single statement for loops' do
363
+ program = <<-LOX_END
364
+ // Single-expression body.
365
+ for (var c = 0; c < 3;) print c = c + 1;
366
+ // output: 1
367
+ // output: 2
368
+ // output: 3
369
+ LOX_END
370
+ expect { subject.evaluate(program) }.not_to raise_error
371
+ expect(sample_cfg[:ostream].string).to eq('123')
372
+ end
373
+
374
+ it 'should implement for loops with block body' do
375
+ program = <<-LOX_END
376
+ // Block body.
377
+ for (var a = 0; a < 3; a = a + 1) {
378
+ print a;
379
+ }
380
+ // output: 0
381
+ // output: 1
382
+ // output: 2
383
+ LOX_END
384
+ expect { subject.evaluate(program) }.not_to raise_error
385
+ expect(sample_cfg[:ostream].string).to eq('012')
386
+ end
387
+
388
+ it 'should implement nullary function calls' do
389
+ program = <<-LOX_END
390
+ print clock(); // Lox expects the 'clock' predefined native function
391
+ LOX_END
392
+ expect { subject.evaluate(program) }.not_to raise_error
393
+ tick = sample_cfg[:ostream].string
394
+ expect(Time.now.to_f - tick.to_f).to be < 0.1
395
+ end
396
+
397
+ it 'should implement function definition' do
398
+ program = <<-LOX_END
399
+ fun printSum(a, b) {
400
+ print a + b;
401
+ }
402
+ printSum(1, 2);
403
+ LOX_END
404
+ expect { subject.evaluate(program) }.not_to raise_error
405
+ expect(sample_cfg[:ostream].string).to eq('3')
406
+ end
407
+
408
+ it 'should support functions with empty body' do
409
+ program = <<-LOX_END
410
+ fun f() {}
411
+ print f();
412
+ LOX_END
413
+ expect { subject.evaluate(program) }.not_to raise_error
414
+ expect(sample_cfg[:ostream].string).to eq('nil')
415
+ end
416
+
417
+ it 'should provide print representation of functions' do
418
+ program = <<-LOX_END
419
+ fun foo() {}
420
+ print foo; // output: <fn foo>
421
+ print clock; // output: <native fn>
422
+ LOX_END
423
+ expect { subject.evaluate(program) }.not_to raise_error
424
+ expect(sample_cfg[:ostream].string).to eq('<fn foo><native fn>')
425
+ end
426
+
361
427
  it 'should print the hello world message' do
362
428
  expect { subject.evaluate(hello_world) }.not_to raise_error
363
429
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
364
430
  end
365
431
  end # context
366
432
  end # describe
433
+ # rubocop: enable Metrics/BlockLength
367
434
  end # module
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.26
4
+ version: 0.1.02
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-22 00:00:00.000000000 Z
11
+ date: 2021-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -90,7 +90,10 @@ files:
90
90
  - lib/loxxy/ast/lox_assign_expr.rb
91
91
  - lib/loxxy/ast/lox_binary_expr.rb
92
92
  - lib/loxxy/ast/lox_block_stmt.rb
93
+ - lib/loxxy/ast/lox_call_expr.rb
93
94
  - lib/loxxy/ast/lox_compound_expr.rb
95
+ - lib/loxxy/ast/lox_for_stmt.rb
96
+ - lib/loxxy/ast/lox_fun_stmt.rb
94
97
  - lib/loxxy/ast/lox_grouping_expr.rb
95
98
  - lib/loxxy/ast/lox_if_stmt.rb
96
99
  - lib/loxxy/ast/lox_literal_expr.rb
@@ -106,6 +109,7 @@ files:
106
109
  - lib/loxxy/back_end/engine.rb
107
110
  - lib/loxxy/back_end/entry.rb
108
111
  - lib/loxxy/back_end/environment.rb
112
+ - lib/loxxy/back_end/function.rb
109
113
  - lib/loxxy/back_end/symbol_table.rb
110
114
  - lib/loxxy/back_end/variable.rb
111
115
  - lib/loxxy/datatype/all_datatypes.rb