loxxy 0.0.26 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1258e225c3c22ba2c8a1df645f2ee39fc6fc2330a62b288a15f59e1ca40596b2
4
- data.tar.gz: 102ac797634d4abf2e72902f568c5b0cc5ec161433e5e4c4e0888e7142508d65
3
+ metadata.gz: 47ae4c0af1698de7187c530ec5feec9390a664ff681aae6c47a36e351bda229d
4
+ data.tar.gz: ff309645b8e71608e6a3801d4a4be558b8e86d59d658af4ec2eb796989e88455
5
5
  SHA512:
6
- metadata.gz: a1715b6c93b1861fadafac38327ea2fdee592f2b0b46320b04acd7e64db166911dc614d59412dae9d0bbccea9ca95f9299aac276eba9ee2a87a58963a2ec7d13
7
- data.tar.gz: 62915b07a3066c8068f41fd622d9d9be4d8baf29b6c3ef0849b3e410be5d52343fa8c00b8bbff1665deefc838cd29a423852f9666af3e410aee77b538f4030f0
6
+ metadata.gz: 73bea958f39a8cb97ed2e59c049ab58006845519a642d20e72cbdd554be60cb3b553f84dcdd64a36410796f83e87bb3239eee9933148b821b074916f4198a810
7
+ data.tar.gz: a3235fd86a5c99c0ecf581be106e53d59a0643b3ac6299fbd0eccd0f5eacc5a15b63df3a22fb4edef8b30314fa0360432b017d9a2417668e365e2b9dd3f8b2b8
@@ -1,8 +1,22 @@
1
+ ## [0.0.27] - 2021-01-24
2
+ - The interpreter implements `while` loops.
3
+
4
+ ### Added
5
+ - Class `Ast::LoxForStmt` a node that represents a `for` statement
6
+ - Method `Ast::ASTBuilder#reduce_for_stmt`
7
+ - Method `Ast::ASTBuilder#reduce_for_control` creates an `Ast::LoxForStmt` node
8
+ - Method `Ast::ASTVisitor#visit_for_stmt` for visiting an `Ast::LoxWhileStmt` node
9
+ - Method `BackEnd::Engine#before_for_stmt` builds a new environment for the loop variable
10
+ - Method `BackEnd::Engine#after_for_stmt` implements most of the `for` control flow
11
+
12
+ ### Changed
13
+ - File `README.md` updated.
14
+
1
15
  ## [0.0.26] - 2021-01-22
2
16
  - The interpreter implements `while` loops.
3
17
 
4
18
  ### Added
5
- - Class `Ast::LoxWhileStmt` a node that represents a while statement
19
+ - Class `Ast::LoxWhileStmt` a node that represents a `while` statement
6
20
  - Method `Ast::ASTBuilder#reduce_while_stmt` creates an `Ast::LoxWhileStmt` node
7
21
  - Method `Ast::ASTVisitor#visit_while_stmt` for visiting an `Ast::LoxWhileStmt` node
8
22
  - Method `BackEnd::Engine#after_while_stmt` implements the while looping structure
data/README.md CHANGED
@@ -14,7 +14,7 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
14
14
 
15
15
  ### Current status
16
16
  The project is still in inception and the interpreter is being implemented...
17
- Currently it can execute a tiny subset of __Lox__ language.
17
+ Currently it can execute a subset of __Lox__ language.
18
18
 
19
19
  But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
20
 
@@ -141,7 +141,8 @@ Here are the language features currently supported by the interpreter:
141
141
  - [Datatypes](#datatypes)
142
142
  - [Statements](#statements)
143
143
  -[Expressions](#expressions)
144
- - [Variable declarations](#var-statement)
144
+ - [Variable declarations](#var-statement)
145
+ - [For statement](#for-statement)
145
146
  - [If Statement](#if-statement)
146
147
  - [Print Statement](#print-statement)
147
148
  - [While Statement](#while-statement)
@@ -279,6 +280,15 @@ var iAmNil; // __Lox__ initializes variables to nil by default;
279
280
  print iAmNil; // output: nil
280
281
  ```
281
282
 
283
+ #### For statement
284
+
285
+ Similar to the `for` statement in `C` language
286
+ ``` javascript
287
+ for (var a = 1; a < 10; a = a + 1) {
288
+ print a; // Output: 123456789
289
+ }
290
+ ```
291
+
282
292
  #### If statement
283
293
 
284
294
  Based on a given condition, an if statement executes one of two statements:
@@ -12,5 +12,6 @@ require_relative 'lox_block_stmt'
12
12
  require_relative 'lox_while_stmt'
13
13
  require_relative 'lox_print_stmt'
14
14
  require_relative 'lox_if_stmt'
15
+ require_relative 'lox_for_stmt'
15
16
  require_relative 'lox_var_stmt'
16
17
  require_relative 'lox_seq_decl'
@@ -180,6 +180,29 @@ module Loxxy
180
180
  Ast::LoxVarStmt.new(tokens[1].position, var_name, theChildren[3])
181
181
  end
182
182
 
183
+ # rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
184
+ def reduce_for_stmt(_production, _range, _tokens, theChildren)
185
+ for_stmt = theChildren[2]
186
+ for_stmt.body_stmt = theChildren[4]
187
+ for_stmt
188
+ end
189
+
190
+ # rule('forControl' => 'forInitialization forTest forUpdate')
191
+ def reduce_for_control(_production, _range, tokens, theChildren)
192
+ (init, test, update) = theChildren
193
+ Ast::LoxForStmt.new(tokens[0].position, init, test, update)
194
+ end
195
+
196
+ # rule('forInitialization' => 'SEMICOLON')
197
+ def reduce_empty_for_initialization(_production, _range, _tokens, _theChildren)
198
+ nil
199
+ end
200
+
201
+ # rule('forTest' => 'expression_opt SEMICOLON')
202
+ def reduce_for_test(_production, range, tokens, theChildren)
203
+ return_first_child(range, tokens, theChildren)
204
+ end
205
+
183
206
  # rule('ifStmt' => 'IF ifCondition statement elsePart_opt')
184
207
  def reduce_if_stmt(_production, _range, tokens, theChildren)
185
208
  condition = theChildren[1]
@@ -67,6 +67,14 @@ module Loxxy
67
67
  broadcast(:after_var_stmt, aVarStmt)
68
68
  end
69
69
 
70
+ # Visit event. The visitor is about to visit a for statement.
71
+ # @param aForStmt [AST::LOXForStmt] the for statement node to visit
72
+ def visit_for_stmt(aForStmt)
73
+ broadcast(:before_for_stmt, aForStmt)
74
+ traverse_subnodes(aForStmt) # The condition is visited/evaluated here...
75
+ broadcast(:after_for_stmt, aForStmt, self)
76
+ end
77
+
70
78
  # Visit event. The visitor is about to visit a if statement.
71
79
  # @param anIfStmt [AST::LOXIfStmt] the if statement node to visit
72
80
  def visit_if_stmt(anIfStmt)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxForStmt < LoxCompoundExpr
8
+ # @return [LoxNode] test expression
9
+ attr_reader :test_expr
10
+
11
+ # @return [LoxNode] update expression
12
+ attr_reader :update_expr
13
+
14
+ # @return [LoxNode] body statement
15
+ attr_accessor :body_stmt
16
+
17
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
18
+ # @param initialization [Loxxy::Ast::LoxNode]
19
+ # @param testExpr [Loxxy::Ast::LoxNode]
20
+ # @param updateExpr [Loxxy::Ast::LoxNode]
21
+ # @param body [Loxxy::Ast::LoxNode]
22
+ def initialize(aPosition, initialization, testExpr, updateExpr)
23
+ child = initialization ? [initialization] : []
24
+ super(aPosition, child)
25
+ @test_expr = testExpr
26
+ @update_expr = updateExpr
27
+ end
28
+
29
+ # Part of the 'visitee' role in Visitor design pattern.
30
+ # @param visitor [Ast::ASTVisitor] the visitor
31
+ def accept(visitor)
32
+ visitor.visit_for_stmt(self)
33
+ end
34
+
35
+ # Accessor to the condition expression
36
+ # @return [LoxNode]
37
+ def condition
38
+ subnodes[0]
39
+ end
40
+ end # class
41
+ end # module
42
+ end # module
@@ -51,6 +51,23 @@ module Loxxy
51
51
  symbol_table.insert(new_var)
52
52
  end
53
53
 
54
+ def before_for_stmt(aForStmt)
55
+ before_block_stmt(aForStmt)
56
+ end
57
+
58
+ def after_for_stmt(aForStmt, aVisitor)
59
+ loop do
60
+ aForStmt.test_expr.accept(aVisitor)
61
+ condition = stack.pop
62
+ break unless condition.truthy?
63
+
64
+ aForStmt.body_stmt.accept(aVisitor)
65
+ aForStmt.update_expr&.accept(aVisitor)
66
+ stack.pop
67
+ end
68
+ after_block_stmt(aForStmt)
69
+ end
70
+
54
71
  def after_if_stmt(anIfStmt, aVisitor)
55
72
  # Retrieve the result of the condition evaluation
56
73
  condition = stack.pop
@@ -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'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.26'
4
+ VERSION = '0.0.27'
5
5
  end
@@ -358,6 +358,44 @@ LOX_END
358
358
  expect(sample_cfg[:ostream].string).to eq('012')
359
359
  end
360
360
 
361
+ it 'should implement single statement for loops' do
362
+ program = <<-LOX_END
363
+ // Single-expression body.
364
+ for (var c = 0; c < 3;) print c = c + 1;
365
+ // output: 1
366
+ // output: 2
367
+ // output: 3
368
+ LOX_END
369
+ expect { subject.evaluate(program) }.not_to raise_error
370
+ expect(sample_cfg[:ostream].string).to eq('123')
371
+ end
372
+
373
+ it 'should implement for loops with block body' do
374
+ program = <<-LOX_END
375
+ // Block body.
376
+ for (var a = 0; a < 3; a = a + 1) {
377
+ print a;
378
+ }
379
+ // output: 0
380
+ // output: 1
381
+ // output: 2
382
+ LOX_END
383
+ expect { subject.evaluate(program) }.not_to raise_error
384
+ expect(sample_cfg[:ostream].string).to eq('012')
385
+ end
386
+
387
+ it 'should implement for loops without initialization' do
388
+ program = <<-LOX_END
389
+ var i = 0;
390
+ // No variable in initialization.
391
+ for (; i < 2; i = i + 1) print i;
392
+ // output: 0
393
+ // output: 1
394
+ LOX_END
395
+ expect { subject.evaluate(program) }.not_to raise_error
396
+ expect(sample_cfg[:ostream].string).to eq('01')
397
+ end
398
+
361
399
  it 'should print the hello world message' do
362
400
  expect { subject.evaluate(hello_world) }.not_to raise_error
363
401
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.26
4
+ version: 0.0.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-22 00:00:00.000000000 Z
11
+ date: 2021-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -91,6 +91,7 @@ files:
91
91
  - lib/loxxy/ast/lox_binary_expr.rb
92
92
  - lib/loxxy/ast/lox_block_stmt.rb
93
93
  - lib/loxxy/ast/lox_compound_expr.rb
94
+ - lib/loxxy/ast/lox_for_stmt.rb
94
95
  - lib/loxxy/ast/lox_grouping_expr.rb
95
96
  - lib/loxxy/ast/lox_if_stmt.rb
96
97
  - lib/loxxy/ast/lox_literal_expr.rb