loxxy 0.0.24 → 0.1.0

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: 6085bf18afb919e5ef4d327162470c9d5ca95dde67e00dce78bb26abd6563dec
4
- data.tar.gz: f1c7732a34b6df2721c20d82404c6ce83bdc8a75b695e4bbe13c51f62d9ac371
3
+ metadata.gz: 42a88690625576399255ae7fadd3ac6aee022fc8d56a8e10c1206b55eca207ab
4
+ data.tar.gz: 4042d268f199763405a264480765bbd5fe7aeaa998eb5e0a4353ae702c3bef2a
5
5
  SHA512:
6
- metadata.gz: 7546a14b935700e5050411a7805c2b2d00ccfc99160e3241b512289a6cc1762df2f39b7c9845c6ffe7fb95f1412865ef373f5308c9cf333fd72e2025a4a17ed8
7
- data.tar.gz: 3675956929831e52963759862e8f9d40e5ebd3df471dee87d82784c2d1230ce6057882368042c907741be1038d300b7c83c27ed4bb8d979f5efa2ac79eaa3b1a
6
+ metadata.gz: 21a6a82509ead9c0d9f38c6798147dda38c436d87b8800a33ca7027553631866ce4215f2eb90c482f3c5a9b0bb42e99b7868fca888b78fcf7eca67afcd7db756
7
+ data.tar.gz: 71b53af0df630894c0d45639a84f427f34c7b92a5092e07cea16bc3786af666be13b0035a13c07b2910c374c76caa009c3acdbd675f430c8f6e4b7951c2b68a7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,77 @@
1
+ ## [0.1.00] - 2021-02-20
2
+ - Version number bumped, `Loxxy` supports function definitions
3
+
4
+ ### Added
5
+ - Class `Ast::LoxFunStmt` a node that represents a function declaration
6
+ - Method `Ast::ASTBuilder#reduce_fun_decl`
7
+ - Method `Ast::ASTBuilder#reduce_function` creates a `LoxFunStmt` instance
8
+ - Method `Ast::ASTBuilder#reduce_parameters_plus_more` for dealing with function parameters
9
+ - Method `Ast::ASTBuilder#reduce_parameters_plus_end`
10
+ - Method `Ast::ASTVisitor#visit_fun_stmt` for visiting an `Ast::LoxFunStmt` node
11
+ - Method `Ast::LoxBlockStmt#empty?` returns true if the code block is empty
12
+ - Method `BackEnd::Engine#after_fun_stmt`
13
+ - Method `Backend::NativeFunction#call`
14
+ - Method `Backend::NativeFunction#to_str`
15
+ - Method `Backend::Function` implementation of a function object.
16
+
17
+ ### Changed
18
+ - Method `BackEnd::Engine#after_call_expr`
19
+
20
+ ### Fixed
21
+ - Fixed inconsistencies in documentation comments.
22
+
23
+ ## [0.0.28] - 2021-02-15
24
+ - The interpreter implements function calls (to a native function).
25
+
26
+ ### Added
27
+ - Class `Ast::LoxCallExpr` a node that represents a function call expression
28
+ - Method `Ast::ASTBuilder#reduce_call_expr`
29
+ - Method `Ast::ASTBuilder#reduce_refinement_plus_end`
30
+ - Method `Ast::ASTBuilder#reduce_call_arglist` creates a `LoxCallExpr` node
31
+ - Method `Ast::ASTBuilder#reduce_arguments_plus_more` builds the function argument array
32
+ - Method `Ast::ASTVisitor#visit_call_expr` for visiting an `Ast::LoxCallExpr` node
33
+ - Method `BackEnd::Engine#after_call_expr`implements the evaluation of a function call.
34
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
35
+
36
+ ## [0.0.27] - 2021-01-24
37
+ - The interpreter implements `while` loops.
38
+
39
+ ### Added
40
+ - Class `Ast::LoxForStmt` a node that represents a `for` statement
41
+ - Method `Ast::ASTBuilder#reduce_for_stmt`
42
+ - Method `Ast::ASTBuilder#reduce_for_control` creates an `Ast::LoxForStmt` node
43
+ - Method `Ast::ASTVisitor#visit_for_stmt` for visiting an `Ast::LoxWhileStmt` node
44
+ - Method `BackEnd::Engine#before_for_stmt` builds a new environment for the loop variable
45
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
46
+
47
+ ### Changed
48
+ - File `README.md` updated.
49
+
50
+ ## [0.0.26] - 2021-01-22
51
+ - The interpreter implements `while` loops.
52
+
53
+ ### Added
54
+ - Class `Ast::LoxWhileStmt` a node that represents a `while` statement
55
+ - Method `Ast::ASTBuilder#reduce_while_stmt` creates an `Ast::LoxWhileStmt` node
56
+ - Method `Ast::ASTVisitor#visit_while_stmt` for visiting an `Ast::LoxWhileStmt` node
57
+ - Method `BackEnd::Engine#after_while_stmt` implements the while looping structure
58
+
59
+ ### Changed
60
+ - File `README.md` updated.
61
+
62
+ ## [0.0.25] - 2021-01-21
63
+ - The interpreter implements blocks of code.
64
+
65
+ ### Added
66
+ - Class `Ast::LoxBlockStmt` a node that represents a block of code
67
+ - Method `Ast::ASTBuilder#reduce_block_stmt` creates an `Ast::LoxBlockStmt` node
68
+ - Method `Ast::ASTVisitor#visit_block_stmt` for visiting an `Ast::LoxBlockStmt` node
69
+ - Method `BackEnd::Engine#before_block_stmt` creates an new enclosed Environment
70
+ - Method `BackEnd::Engine#after_block_stmt` close enclosed Environment and make parent Environment the current one
71
+
72
+ ### Changed
73
+ - File `README.md` updated.
74
+
1
75
  ## [0.0.24] - 2021-01-20
2
76
  - The interpreter implements the assignment of variables.
3
77
 
data/README.md CHANGED
@@ -14,9 +14,13 @@ 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 statement except:
18
+ - Functions and closures,
19
+ - Classes and objects.
20
+
21
+ These will be implemented soon.
22
+
18
23
 
19
- But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
24
 
21
25
  ## What's the fuss about Lox?
22
26
  ... Nothing...
@@ -131,7 +135,7 @@ program
131
135
  On one hand, the parser covers the complete Lox grammar and should therefore, in principle,
132
136
  parse any valid Lox program.
133
137
 
134
- On the other hand, the interpreter is under development and currently it can evaluate only a tiny subset of __Lox__.
138
+ On the other hand, the interpreter is under development and currently it can evaluate only a subset of __Lox__.
135
139
  But the situation is changing almost daily, stay tuned...
136
140
 
137
141
  Here are the language features currently supported by the interpreter:
@@ -141,9 +145,12 @@ Here are the language features currently supported by the interpreter:
141
145
  - [Datatypes](#datatypes)
142
146
  - [Statements](#statements)
143
147
  -[Expressions](#expressions)
144
- - [Variable declarations](#var-statement)
148
+ - [Variable declarations](#var-statement)
149
+ - [For statement](#for-statement)
145
150
  - [If Statement](#if-statement)
146
151
  - [Print Statement](#print-statement)
152
+ - [While Statement](#while-statement)
153
+ - [Block Statement](#block-statement)
147
154
 
148
155
  ### Comments
149
156
 
@@ -180,7 +187,9 @@ Loxxy supports the following statements:
180
187
 
181
188
  -[Variable declarations](#var-statement)
182
189
  -[If Statement](#if-statement)
183
- -[Print Statement](#print-statement)
190
+ -[Print Statement](#print-statement)
191
+ -[While Statement](#while-statement)
192
+ -[Block Statement](#block-statement)
184
193
 
185
194
  #### Expressions
186
195
 
@@ -268,8 +277,6 @@ foo = "baz"; // Variable assignment
268
277
  print foo; // Output: baz
269
278
  ```
270
279
 
271
-
272
-
273
280
  #### Variable declarations
274
281
  ``` javascript
275
282
  var iAmAVariable = "my-initial-value";
@@ -277,6 +284,15 @@ var iAmNil; // __Lox__ initializes variables to nil by default;
277
284
  print iAmNil; // output: nil
278
285
  ```
279
286
 
287
+ #### For statement
288
+
289
+ Similar to the `for` statement in `C` language
290
+ ``` javascript
291
+ for (var a = 1; a < 10; a = a + 1) {
292
+ print a; // Output: 123456789
293
+ }
294
+ ```
295
+
280
296
  #### If statement
281
297
 
282
298
  Based on a given condition, an if statement executes one of two statements:
@@ -297,20 +313,21 @@ The problem in a nutshell: in a nested if ... else ... statement like this:
297
313
  ``` javascript
298
314
  'if (true) if (false) print "bad"; else print "good";
299
315
  ```
300
- ... there is an ambiguity. Indeed, according to the __Lox__ grammar, the `else` could be bound
316
+ ... there is an ambiguity.
317
+ Indeed, according to the __Lox__ grammar, the `else` could be bound
301
318
  either to the first `if` or to the second one.
302
319
  This ambiguity is usually lifted by applying an ad-hoc rule: an `else` is aways bound to the most
303
320
  recent (rightmost) `if`.
304
- Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
321
+ Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
305
322
  As a consequence,it complains about the found ambiguity and stops the parsing...
306
323
  Although `Rley` can cope with ambiguities, this requires the use of an advanced data structure
307
324
  called `Shared Packed Parse Forest (SPPF)`.
308
- SPPF are much more complex to handle than the `common` parse trees present in most compiler or interpreter books.
325
+ SPPF are much more complex to handle than the "common" parse trees present in most compiler or interpreter books.
309
326
  Therefore, a future version of `Rley` will incorporate the capability to define disambuiguation rules.
310
327
 
311
328
  In the meantime, the `Loxxy` will progress on other __Lox__ features like:
312
- - Variables,
313
329
  - Block structures...
330
+ - Iteration structures (`for` and `while` loops)
314
331
 
315
332
 
316
333
  #### Print Statement
@@ -322,6 +339,29 @@ print "Hello, world!"; // Output: Hello, world!
322
339
 
323
340
  ```
324
341
 
342
+ #### While Statement
343
+
344
+ ``` javascript
345
+ var a = 1;
346
+ while (a < 10) {
347
+ print a;
348
+ a = a + 1;
349
+ } // Output: 123456789
350
+ ```
351
+
352
+ #### Block Statement
353
+ __Lox__ has code blocks.
354
+ ``` javascript
355
+ var a = "outer";
356
+
357
+ {
358
+ var a = "inner";
359
+ print a; // output: inner
360
+ }
361
+
362
+ print a; // output: outer
363
+ ```
364
+
325
365
  ## Installation
326
366
 
327
367
  Add this line to your application's Gemfile:
@@ -344,7 +384,7 @@ TODO: Write usage instructions here
344
384
 
345
385
  ## Other Lox implementations in Ruby
346
386
 
347
- For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
387
+ For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
348
388
  There are other Ruby-based projects as well:
349
389
  - [SlowLox](https://github.com/ArminKleinert/SlowLox), described as a "1-to-1 conversion of JLox to Ruby"
350
390
  - [rulox](https://github.com/LevitatingBusinessMan/rulox)
@@ -1,14 +1,19 @@
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'
9
11
  require_relative 'lox_logical_expr'
10
12
  require_relative 'lox_assign_expr'
13
+ require_relative 'lox_block_stmt'
14
+ require_relative 'lox_while_stmt'
11
15
  require_relative 'lox_print_stmt'
12
16
  require_relative 'lox_if_stmt'
17
+ require_relative 'lox_for_stmt'
13
18
  require_relative 'lox_var_stmt'
14
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]
@@ -193,105 +221,57 @@ module Loxxy
193
221
  Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
194
222
  end
195
223
 
196
- # rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
197
- def reduce_assign_expr(_production, _range, tokens, theChildren)
198
- var_name = theChildren[1].token.lexeme.dup
199
- Ast::LoxAssignExpr.new(tokens[1].position, var_name, theChildren[3])
200
- end
201
-
202
- # rule('logic_or' => 'logic_and disjunct_plus')
203
- def reduce_logic_or_plus(production, range, tokens, theChildren)
204
- reduce_logical_expr(production, range, tokens, theChildren)
205
- end
206
-
207
- # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
208
- def reduce_logic_or_plus_more(production, range, tokens, theChildren)
209
- reduce_binary_plus_more(production, range, tokens, theChildren)
210
- end
211
-
212
- # rule('disjunct_plus' => 'OR logic_and')
213
- def reduce_logic_or_plus_end(production, range, tokens, theChildren)
214
- reduce_binary_plus_end(production, range, tokens, theChildren)
215
- end
216
-
217
- # rule('logic_and' => 'equality conjunct_plus')
218
- def reduce_logic_and_plus(production, range, tokens, theChildren)
219
- reduce_logical_expr(production, range, tokens, theChildren)
220
- end
221
-
222
- # rule('conjunct_plus' => 'conjunct_plus AND equality')
223
- def reduce_logic_and_plus_more(production, range, tokens, theChildren)
224
- reduce_binary_plus_more(production, range, tokens, theChildren)
225
- end
226
-
227
- # rule('conjunct_plus' => 'AND equality')
228
- def reduce_logic_and_plus_end(production, range, tokens, theChildren)
229
- reduce_binary_plus_end(production, range, tokens, theChildren)
230
- end
231
-
232
- # rule('equality' => 'comparison equalityTest_plus')
233
- def reduce_equality_plus(production, range, tokens, theChildren)
234
- reduce_binary_operator(production, range, tokens, theChildren)
224
+ # rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as ''
225
+ def reduce_while_stmt(_production, _range, tokens, theChildren)
226
+ Ast::LoxWhileStmt.new(tokens[1].position, theChildren[2], theChildren[4])
235
227
  end
236
228
 
237
- # rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
238
- def reduce_equality_t_plus_more(production, range, tokens, theChildren)
239
- reduce_binary_plus_more(production, range, tokens, theChildren)
229
+ # rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
230
+ def reduce_block_stmt(_production, _range, tokens, theChildren)
231
+ decls = LoxSeqDecl.new(tokens[1].position, theChildren[1])
232
+ Ast::LoxBlockStmt.new(tokens[1].position, decls)
240
233
  end
241
234
 
242
- # rule('equalityTest_star' => 'equalityTest comparison')
243
- def reduce_equality_t_plus_end(production, range, tokens, theChildren)
244
- reduce_binary_plus_end(production, range, tokens, theChildren)
235
+ # rule('block' => 'LEFT_BRACE RIGHT_BRACE').as 'block_empty'
236
+ def reduce_block_empty(_production, _range, tokens, theChildren)
237
+ Ast::LoxBlockStmt.new(tokens[0].position, nil)
245
238
  end
246
239
 
247
- # rule('comparison' => 'term comparisonTest_plus')
248
- def reduce_comparison_plus(production, range, tokens, theChildren)
249
- reduce_binary_operator(production, range, tokens, theChildren)
240
+ # rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
241
+ def reduce_assign_expr(_production, _range, tokens, theChildren)
242
+ var_name = theChildren[1].token.lexeme.dup
243
+ Ast::LoxAssignExpr.new(tokens[1].position, var_name, theChildren[3])
250
244
  end
251
245
 
252
246
  # rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
253
247
  # TODO: is it meaningful to implement this rule?
254
248
 
255
- # rule('comparisonTest_plus' => 'comparisonTest term')
256
- def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
257
- reduce_binary_plus_end(production, range, tokens, theChildren)
258
- end
259
-
260
- # rule('term' => 'factor additive_plus')
261
- def reduce_term_additive(production, range, tokens, theChildren)
262
- reduce_binary_operator(production, range, tokens, theChildren)
263
- end
264
-
265
- # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
266
- def reduce_additive_plus_more(production, range, tokens, theChildren)
267
- reduce_binary_plus_more(production, range, tokens, theChildren)
268
- end
269
-
270
- # rule('additive_plus' => 'additionOp factor')
271
- def reduce_additive_plus_end(production, range, tokens, theChildren)
272
- 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)
273
254
  end
274
255
 
275
- # rule('factor' => 'multiplicative_plus')
276
- def reduce_factor_multiplicative(production, range, tokens, theChildren)
277
- 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]
278
260
  end
279
261
 
280
- # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
281
- def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
282
- 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]
283
265
  end
284
266
 
285
- # rule('multiplicative_plus' => 'multOp unary')
286
- def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
287
- reduce_binary_plus_end(production, range, tokens, theChildren)
288
- 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
289
273
 
290
- # rule('unary' => 'unaryOp unary')
291
- def reduce_unary_expr(_production, _range, tokens, theChildren)
292
- operator = Name2unary[theChildren[0].symbol.name].to_sym
293
- operand = theChildren[1]
294
- LoxUnaryExpr.new(tokens[0].position, operator, operand)
274
+ LoxCallExpr.new(tokens[0].position, args)
295
275
  end
296
276
 
297
277
  # rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
@@ -313,6 +293,33 @@ module Loxxy
313
293
  var_name = theChildren[0].token.lexeme
314
294
  LoxVariableExpr.new(tokens[0].position, var_name)
315
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
+ fun_stmt = 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
316
323
  end # class
317
324
  end # module
318
325
  end # module