loxxy 0.0.24 → 0.0.25

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: 15da87c78ce0d90162679c2b76478cfaff17d9f88f78d4109190b83952146956
4
+ data.tar.gz: 0c9b1834f17cc51da96ec568d0804131b65dd6b82125ace0e4c56d6062250d09
5
5
  SHA512:
6
- metadata.gz: 7546a14b935700e5050411a7805c2b2d00ccfc99160e3241b512289a6cc1762df2f39b7c9845c6ffe7fb95f1412865ef373f5308c9cf333fd72e2025a4a17ed8
7
- data.tar.gz: 3675956929831e52963759862e8f9d40e5ebd3df471dee87d82784c2d1230ce6057882368042c907741be1038d300b7c83c27ed4bb8d979f5efa2ac79eaa3b1a
6
+ metadata.gz: 6c31994ac27f67cc461fc10453468a42a17691f1b5c3171e23d26dcf9ecd6b5f5de8ad97184037990de6268039e57f8b0d5e80e4411f9c0a1a02b5edba766e91
7
+ data.tar.gz: 15ceead67dc859d2e4d3ca3e97aaa67372b581317fdbac8a66241ffb3322acec3115a5dab619a11d0a740676734fe5e517b9b1ca3aff218800ac07d693927263
@@ -1,3 +1,16 @@
1
+ ## [0.0.25] - 2021-01-21
2
+ - The interpreter implements block of code.
3
+
4
+ ### Added
5
+ - Class `Ast::LoxBlockStmt` a node that represents a block of code
6
+ - Method `Ast::ASTBuilder#reduce_block_stmt` creates an `Ast::LoxBlockStmt` node
7
+ - Method `Ast::ASTVisitor#visit_block_stmt` for visiting an `Ast::LoxBlockStmt` node
8
+ - Method `BackEnd::Engine#before_block_stmt` creates an new enclosed Environment
9
+ - Method `BackEnd::Engine#after_block_stmt` close enclosed Environment and make parent Environment the current one
10
+
11
+ ### Changed
12
+ - File `README.md` updated.
13
+
1
14
  ## [0.0.24] - 2021-01-20
2
15
  - The interpreter implements the assignment of variables.
3
16
 
data/README.md CHANGED
@@ -144,6 +144,7 @@ Here are the language features currently supported by the interpreter:
144
144
  - [Variable declarations](#var-statement)
145
145
  - [If Statement](#if-statement)
146
146
  - [Print Statement](#print-statement)
147
+ - [Block Statement](#block-statement)
147
148
 
148
149
  ### Comments
149
150
 
@@ -180,7 +181,8 @@ Loxxy supports the following statements:
180
181
 
181
182
  -[Variable declarations](#var-statement)
182
183
  -[If Statement](#if-statement)
183
- -[Print Statement](#print-statement)
184
+ -[Print Statement](#print-statement)
185
+ -[Block Statement](#block-statement)
184
186
 
185
187
  #### Expressions
186
188
 
@@ -268,8 +270,6 @@ foo = "baz"; // Variable assignment
268
270
  print foo; // Output: baz
269
271
  ```
270
272
 
271
-
272
-
273
273
  #### Variable declarations
274
274
  ``` javascript
275
275
  var iAmAVariable = "my-initial-value";
@@ -297,20 +297,21 @@ The problem in a nutshell: in a nested if ... else ... statement like this:
297
297
  ``` javascript
298
298
  'if (true) if (false) print "bad"; else print "good";
299
299
  ```
300
- ... there is an ambiguity. Indeed, according to the __Lox__ grammar, the `else` could be bound
300
+ ... there is an ambiguity.
301
+ Indeed, according to the __Lox__ grammar, the `else` could be bound
301
302
  either to the first `if` or to the second one.
302
303
  This ambiguity is usually lifted by applying an ad-hoc rule: an `else` is aways bound to the most
303
304
  recent (rightmost) `if`.
304
- Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
305
+ Being a generic parsing library, `Rley` doesn't apply any of these supplemental rules.
305
306
  As a consequence,it complains about the found ambiguity and stops the parsing...
306
307
  Although `Rley` can cope with ambiguities, this requires the use of an advanced data structure
307
308
  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.
309
+ SPPF are much more complex to handle than the "common" parse trees present in most compiler or interpreter books.
309
310
  Therefore, a future version of `Rley` will incorporate the capability to define disambuiguation rules.
310
311
 
311
312
  In the meantime, the `Loxxy` will progress on other __Lox__ features like:
312
- - Variables,
313
313
  - Block structures...
314
+ - Iteration structures (`for` and `while` loops)
314
315
 
315
316
 
316
317
  #### Print Statement
@@ -322,6 +323,19 @@ print "Hello, world!"; // Output: Hello, world!
322
323
 
323
324
  ```
324
325
 
326
+ #### Block Statement
327
+ __Lox__ has code blocks.
328
+ ``` javascript
329
+ var a = "outer";
330
+
331
+ {
332
+ var a = "inner";
333
+ print a; // output: inner
334
+ }
335
+
336
+ print a; // output: outer
337
+ ```
338
+
325
339
  ## Installation
326
340
 
327
341
  Add this line to your application's Gemfile:
@@ -344,7 +358,7 @@ TODO: Write usage instructions here
344
358
 
345
359
  ## Other Lox implementations in Ruby
346
360
 
347
- For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
361
+ For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
348
362
  There are other Ruby-based projects as well:
349
363
  - [SlowLox](https://github.com/ArminKleinert/SlowLox), described as a "1-to-1 conversion of JLox to Ruby"
350
364
  - [rulox](https://github.com/LevitatingBusinessMan/rulox)
@@ -8,6 +8,7 @@ require_relative 'lox_unary_expr'
8
8
  require_relative 'lox_binary_expr'
9
9
  require_relative 'lox_logical_expr'
10
10
  require_relative 'lox_assign_expr'
11
+ require_relative 'lox_block_stmt'
11
12
  require_relative 'lox_print_stmt'
12
13
  require_relative 'lox_if_stmt'
13
14
  require_relative 'lox_var_stmt'
@@ -193,6 +193,12 @@ module Loxxy
193
193
  Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
194
194
  end
195
195
 
196
+ # rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
197
+ def reduce_block_stmt(_production, _range, tokens, theChildren)
198
+ decls = LoxSeqDecl.new(tokens[1].position, theChildren[1])
199
+ Ast::LoxBlockStmt.new(tokens[1].position, decls)
200
+ end
201
+
196
202
  # rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
197
203
  def reduce_assign_expr(_production, _range, tokens, theChildren)
198
204
  var_name = theChildren[1].token.lexeme.dup
@@ -83,6 +83,14 @@ module Loxxy
83
83
  broadcast(:after_print_stmt, aPrintStmt)
84
84
  end
85
85
 
86
+ # Visit event. The visitor is about to visit a block statement.
87
+ # @param aBlockStmt [AST::LOXBlockStmt] the print statement node to visit
88
+ def visit_block_stmt(aBlockStmt)
89
+ broadcast(:before_block_stmt, aBlockStmt)
90
+ traverse_subnodes(aBlockStmt)
91
+ broadcast(:after_block_stmt, aBlockStmt)
92
+ end
93
+
86
94
  # Visit event. The visitor is visiting an assignment node
87
95
  # @param aLiteralExpr [AST::LoxAssignExpr] the variable assignment node to visit.
88
96
  def visit_assign_expr(anAssignExpr)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxBlockStmt < LoxCompoundExpr
8
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
+ # @param operand [Loxxy::Ast::LoxSeqDecl]
10
+ def initialize(aPosition, decls)
11
+ super(aPosition, [decls])
12
+ end
13
+
14
+ # Part of the 'visitee' role in Visitor design pattern.
15
+ # @param visitor [Ast::ASTVisitor] the visitor
16
+ def accept(visitor)
17
+ visitor.visit_block_stmt(self)
18
+ end
19
+
20
+ alias operands subnodes
21
+ end # class
22
+ end # module
23
+ end # module
@@ -66,10 +66,20 @@ module Loxxy
66
66
  @ostream.print tos.to_str
67
67
  end
68
68
 
69
+ def before_block_stmt(_aBlockStmt)
70
+ new_env = Environment.new
71
+ symbol_table.enter_environment(new_env)
72
+ end
73
+
74
+ def after_block_stmt(_aBlockStmt)
75
+ symbol_table.leave_environment
76
+ end
77
+
69
78
  def after_assign_expr(anAssignExpr)
70
79
  var_name = anAssignExpr.name
71
80
  variable = symbol_table.lookup(var_name)
72
81
  raise StandardError, "Unknown variable #{var_name}" unless variable
82
+
73
83
  value = stack.pop
74
84
  variable.assign(value)
75
85
  stack.push value # An expression produces a value
@@ -9,9 +9,9 @@ module Loxxy
9
9
  # of a relation or a relation definition.
10
10
  # It contains a map of names to the objects they name (e.g. logical var)
11
11
  class Environment
12
- # The parent (enclosing) environment.
12
+ # The enclosing (parent) environment.
13
13
  # @return [Environment, NilClass]
14
- attr_accessor :parent
14
+ attr_accessor :enclosing
15
15
 
16
16
  # Mapping from user-defined name to related definition
17
17
  # (say, a variable object)
@@ -21,7 +21,7 @@ module Loxxy
21
21
  # Construct a environment instance.
22
22
  # @param aParent [Environment, NilClass] Parent environment to this one.
23
23
  def initialize(aParent = nil)
24
- @parent = aParent
24
+ @enclosing = aParent
25
25
  @defns = {}
26
26
  end
27
27
 
@@ -44,7 +44,7 @@ module Loxxy
44
44
  # to be a child of current environment and to be itself the new current environment.
45
45
  # @param anEnv [BackEnd::Environment] the Environment that
46
46
  def enter_environment(anEnv)
47
- anEnv.parent = current_env
47
+ anEnv.enclosing = current_env
48
48
  @current_env = anEnv
49
49
  end
50
50
 
@@ -60,7 +60,7 @@ module Loxxy
60
60
  end
61
61
  raise StandardError, 'Cannot remove root environment.' if current_env == root
62
62
 
63
- @current_env = current_env.parent
63
+ @current_env = current_env.enclosing
64
64
  end
65
65
 
66
66
  # Add an entry with given name to current environment.
@@ -114,7 +114,7 @@ module Loxxy
114
114
  while skope
115
115
  vars_of_environment = skope.defns.select { |_, item| item.kind_of?(Variable) }
116
116
  vars = vars_of_environment.values.concat(vars)
117
- skope = skope.parent
117
+ skope = skope.enclosing
118
118
  end
119
119
 
120
120
  vars
@@ -76,7 +76,7 @@ module Loxxy
76
76
  rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
77
77
  rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
78
78
  rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement')
79
- rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
79
+ rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE').as 'block_stmt'
80
80
  rule('block' => 'LEFT_BRACE RIGHT_BRACE')
81
81
 
82
82
  # Expressions: produce values
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.24'
4
+ VERSION = '0.0.25'
5
5
  end
@@ -32,7 +32,7 @@ module Loxxy
32
32
  end
33
33
 
34
34
  it 'should know its parent (if any)' do
35
- expect(subject.parent).to eq(mother)
35
+ expect(subject.enclosing).to eq(mother)
36
36
  end
37
37
  end # context
38
38
 
@@ -60,7 +60,7 @@ module Loxxy
60
60
  new_env = BackEnd::Environment.new
61
61
  expect { subject.enter_environment(new_env) }.not_to raise_error
62
62
  expect(subject.current_env).to eq(new_env)
63
- expect(subject.current_env.parent).to eq(subject.root)
63
+ expect(subject.current_env.enclosing).to eq(subject.root)
64
64
  expect(subject.name2envs['q']).to eq([subject.root])
65
65
  end
66
66
 
@@ -288,10 +288,10 @@ LOX_END
288
288
  program = <<-LOX_END
289
289
  var a = "before";
290
290
  print a; // output: before
291
-
291
+
292
292
  a = "after";
293
293
  print a; // output: after
294
-
294
+
295
295
  print a = "arg"; // output: arg
296
296
  print a; // output: arg
297
297
  LOX_END
@@ -299,6 +299,36 @@ LOX_END
299
299
  expect(sample_cfg[:ostream].string).to eq('beforeafterargarg')
300
300
  end
301
301
 
302
+ it 'should support variables local to a block' do
303
+ program = <<-LOX_END
304
+ {
305
+ var a = "first";
306
+ print a;
307
+ }
308
+ {
309
+ var a = "second";
310
+ print a;
311
+ }
312
+ LOX_END
313
+ expect { subject.evaluate(program) }.not_to raise_error
314
+ expect(sample_cfg[:ostream].string).to eq('firstsecond')
315
+ end
316
+
317
+ it 'should support the shadowing of variables in a block' do
318
+ program = <<-LOX_END
319
+ var a = "outer";
320
+
321
+ {
322
+ var a = "inner";
323
+ print a; // output: inner
324
+ }
325
+
326
+ print a; // output: outer
327
+ LOX_END
328
+ expect { subject.evaluate(program) }.not_to raise_error
329
+ expect(sample_cfg[:ostream].string).to eq('innerouter')
330
+ end
331
+
302
332
  it 'should print the hello world message' do
303
333
  expect { subject.evaluate(hello_world) }.not_to raise_error
304
334
  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.24
4
+ version: 0.0.25
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-20 00:00:00.000000000 Z
11
+ date: 2021-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -89,6 +89,7 @@ files:
89
89
  - lib/loxxy/ast/ast_visitor.rb
90
90
  - lib/loxxy/ast/lox_assign_expr.rb
91
91
  - lib/loxxy/ast/lox_binary_expr.rb
92
+ - lib/loxxy/ast/lox_block_stmt.rb
92
93
  - lib/loxxy/ast/lox_compound_expr.rb
93
94
  - lib/loxxy/ast/lox_grouping_expr.rb
94
95
  - lib/loxxy/ast/lox_if_stmt.rb