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 +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +22 -8
- data/lib/loxxy/ast/all_lox_nodes.rb +1 -0
- data/lib/loxxy/ast/ast_builder.rb +6 -0
- data/lib/loxxy/ast/ast_visitor.rb +8 -0
- data/lib/loxxy/ast/lox_block_stmt.rb +23 -0
- data/lib/loxxy/back_end/engine.rb +10 -0
- data/lib/loxxy/back_end/environment.rb +3 -3
- data/lib/loxxy/back_end/symbol_table.rb +3 -3
- data/lib/loxxy/front_end/grammar.rb +1 -1
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/environment_spec.rb +1 -1
- data/spec/back_end/symbol_table_spec.rb +1 -1
- data/spec/interpreter_spec.rb +32 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15da87c78ce0d90162679c2b76478cfaff17d9f88f78d4109190b83952146956
|
4
|
+
data.tar.gz: 0c9b1834f17cc51da96ec568d0804131b65dd6b82125ace0e4c56d6062250d09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c31994ac27f67cc461fc10453468a42a17691f1b5c3171e23d26dcf9ecd6b5f5de8ad97184037990de6268039e57f8b0d5e80e4411f9c0a1a02b5edba766e91
|
7
|
+
data.tar.gz: 15ceead67dc859d2e4d3ca3e97aaa67372b581317fdbac8a66241ffb3322acec3115a5dab619a11d0a740676734fe5e517b9b1ca3aff218800ac07d693927263
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
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
|
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
|
12
|
+
# The enclosing (parent) environment.
|
13
13
|
# @return [Environment, NilClass]
|
14
|
-
attr_accessor :
|
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
|
-
@
|
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.
|
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.
|
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.
|
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
|
data/lib/loxxy/version.rb
CHANGED
@@ -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.
|
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
|
|
data/spec/interpreter_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|