loxxy 0.1.06 → 0.1.07
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 +16 -0
- data/README.md +0 -1
- data/lib/loxxy/ast/ast_builder.rb +16 -3
- data/lib/loxxy/back_end/engine.rb +3 -4
- data/lib/loxxy/back_end/environment.rb +8 -1
- data/lib/loxxy/back_end/lox_function.rb +9 -6
- data/lib/loxxy/back_end/symbol_table.rb +19 -9
- data/lib/loxxy/front_end/grammar.rb +1 -1
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/engine_spec.rb +2 -0
- data/spec/interpreter_spec.rb +30 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f171f0046ff95784ed21b369751ca65d25e0ff757d1b2dccc534087a4b9c4a8d
|
4
|
+
data.tar.gz: 579badf6a30436962cb18e63dfee1ed361cfd152e9fecf54f2bfd25395db086b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0470f022b121ceaff279f75024e07b78a218c3baa4d44e6f9bf54f4ebddf18341b27f697fe68c727eb2d61add2c55e7c102fbf73bfd29ef4fb3965024247f747
|
7
|
+
data.tar.gz: 320dbb075351069f624103f1d93aadf903ec6a5b7bb685871f53227b496760ac67ae9fa1dae85491efd9b94cfeb90eac8f66e104b4f70eaac9b0da081dfde567
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## [0.1.07] - 2021-03-14
|
2
|
+
- `Loxxy` now supports nested functions and closures
|
3
|
+
|
4
|
+
### Changed
|
5
|
+
- Method `Ast::AstBuilder#reduce_call_expr` now supports nested call expressions (e.g. `getCallback()();` )
|
6
|
+
- Class `BackEnd::Environment`: added the attributes `predecessor` and `embedding` to support closures.
|
7
|
+
- Class `BackeEnd::LoxFunction`: added the attribute `closure` that is equal to the environment where the function is declared.
|
8
|
+
- Constructor `BackEnd::LoxFunction#new` now takes a `BackEnd::Engine`as its fourth parameter
|
9
|
+
- Methods `BackEnd::SymbolTable#enter_environment`, `BackEnd::SymbolTable#leave_environment` take into account closures.
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
- Method `Ast::AstBuilder#after_var_stmt` now takes into account the value from the top of stack
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
1
17
|
## [0.1.06] - 2021-03-06
|
2
18
|
- Parameters/arguments checks in function declaration and call
|
3
19
|
|
data/README.md
CHANGED
@@ -15,7 +15,6 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
|
|
15
15
|
### Current status
|
16
16
|
The project is still in inception and the interpreter is being implemented...
|
17
17
|
Currently it can execute all allowed __Lox__ expressions and statements except:
|
18
|
-
- Closures,
|
19
18
|
- Classes and objects.
|
20
19
|
|
21
20
|
These will be implemented soon.
|
@@ -260,13 +260,26 @@ module Loxxy
|
|
260
260
|
|
261
261
|
# rule('call' => 'primary refinement_plus').as 'call_expr'
|
262
262
|
def reduce_call_expr(_production, _range, _tokens, theChildren)
|
263
|
-
|
264
|
-
|
263
|
+
members = theChildren.flatten
|
264
|
+
call_expr = nil
|
265
|
+
loop do
|
266
|
+
(callee, call_expr) = members.shift(2)
|
267
|
+
call_expr.callee = callee
|
268
|
+
members.unshift(call_expr)
|
269
|
+
break if members.size == 1
|
270
|
+
end
|
271
|
+
|
272
|
+
call_expr
|
273
|
+
end
|
274
|
+
|
275
|
+
# rule('refinement_plus' => 'refinement_plus refinement')
|
276
|
+
def reduce_refinement_plus_more(_production, _range, _tokens, theChildren)
|
277
|
+
theChildren[0] << theChildren[1]
|
265
278
|
end
|
266
279
|
|
267
280
|
# rule('refinement_plus' => 'refinement').
|
268
281
|
def reduce_refinement_plus_end(_production, _range, _tokens, theChildren)
|
269
|
-
theChildren
|
282
|
+
theChildren
|
270
283
|
end
|
271
284
|
|
272
285
|
# rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
|
@@ -62,7 +62,7 @@ module Loxxy
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def after_var_stmt(aVarStmt)
|
65
|
-
new_var = Variable.new(aVarStmt.name,
|
65
|
+
new_var = Variable.new(aVarStmt.name, stack.pop)
|
66
66
|
symbol_table.insert(new_var)
|
67
67
|
end
|
68
68
|
|
@@ -126,9 +126,8 @@ module Loxxy
|
|
126
126
|
variable = symbol_table.lookup(var_name)
|
127
127
|
raise StandardError, "Unknown variable #{var_name}" unless variable
|
128
128
|
|
129
|
-
value = stack.
|
129
|
+
value = stack.last # ToS remains since an assignment produces a value
|
130
130
|
variable.assign(value)
|
131
|
-
stack.push value # An expression produces a value
|
132
131
|
end
|
133
132
|
|
134
133
|
def after_logical_expr(aLogicalExpr, visitor)
|
@@ -234,7 +233,7 @@ module Loxxy
|
|
234
233
|
end
|
235
234
|
|
236
235
|
def after_fun_stmt(aFunStmt, _visitor)
|
237
|
-
function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body,
|
236
|
+
function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, self)
|
238
237
|
new_var = Variable.new(aFunStmt.name, function)
|
239
238
|
symbol_table.insert(new_var)
|
240
239
|
end
|
@@ -9,10 +9,17 @@ 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
|
12
|
+
# The enclosing (parent) environment.
|
13
13
|
# @return [Environment, NilClass]
|
14
14
|
attr_accessor :enclosing
|
15
15
|
|
16
|
+
# The previous environment in the environment chain.
|
17
|
+
# @return [Environment, NilClass]
|
18
|
+
attr_accessor :predecessor
|
19
|
+
|
20
|
+
# @return [Boolean] true if this environment is part of a closure (contains an embedded function)
|
21
|
+
attr_accessor :embedding
|
22
|
+
|
16
23
|
# Mapping from user-defined name to related definition
|
17
24
|
# (say, a variable object)
|
18
25
|
# @return [Hash{String => Variable}] Pairs of the kind
|
@@ -8,22 +8,24 @@ module Loxxy
|
|
8
8
|
# Representation of a Lox function.
|
9
9
|
# It is a named slot that can be associated with a value at the time.
|
10
10
|
class LoxFunction
|
11
|
-
# @return [String]
|
11
|
+
# @return [String] The name of the function (if any)
|
12
12
|
attr_reader :name
|
13
13
|
|
14
14
|
# @return [Array<>] the parameters
|
15
15
|
attr_reader :parameters
|
16
16
|
attr_reader :body
|
17
17
|
attr_reader :stack
|
18
|
+
attr_reader :closure
|
18
19
|
|
19
|
-
# Create a
|
20
|
+
# Create a function with given name
|
20
21
|
# @param aName [String] The name of the variable
|
21
|
-
|
22
|
-
def initialize(aName, parameterList, aBody, aStack)
|
22
|
+
def initialize(aName, parameterList, aBody, anEngine)
|
23
23
|
@name = aName.dup
|
24
24
|
@parameters = parameterList
|
25
25
|
@body = aBody.kind_of?(Ast::LoxNoopExpr) ? aBody : aBody.subnodes[0]
|
26
|
-
@stack =
|
26
|
+
@stack = anEngine.stack
|
27
|
+
@closure = anEngine.symbol_table.current_env
|
28
|
+
anEngine.symbol_table.current_env.embedding = true
|
27
29
|
end
|
28
30
|
|
29
31
|
def arity
|
@@ -35,7 +37,8 @@ module Loxxy
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def call(engine, aVisitor)
|
38
|
-
new_env = Environment.new(engine.symbol_table.current_env)
|
40
|
+
# new_env = Environment.new(engine.symbol_table.current_env)
|
41
|
+
new_env = Environment.new(closure)
|
39
42
|
engine.symbol_table.enter_environment(new_env)
|
40
43
|
|
41
44
|
parameters&.each do |param_name|
|
@@ -44,23 +44,33 @@ 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.enclosing
|
47
|
+
if anEnv.enclosing && (anEnv.enclosing != current_env)
|
48
|
+
anEnv.predecessor = current_env
|
49
|
+
else
|
50
|
+
anEnv.enclosing = current_env
|
51
|
+
end
|
48
52
|
@current_env = anEnv
|
49
53
|
end
|
50
54
|
|
51
55
|
def leave_environment
|
52
|
-
current_env.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
unless current_env.embedding
|
57
|
+
current_env.defns.each_pair do |nm, _item|
|
58
|
+
environments = name2envs[nm]
|
59
|
+
if environments.size == 1
|
60
|
+
name2envs.delete(nm)
|
61
|
+
else
|
62
|
+
environments.pop
|
63
|
+
name2envs[nm] = environments
|
64
|
+
end
|
59
65
|
end
|
60
66
|
end
|
61
67
|
raise StandardError, 'Cannot remove root environment.' if current_env == root
|
62
68
|
|
63
|
-
|
69
|
+
if current_env.predecessor
|
70
|
+
@current_env = current_env.predecessor
|
71
|
+
else
|
72
|
+
@current_env = current_env.enclosing
|
73
|
+
end
|
64
74
|
end
|
65
75
|
|
66
76
|
# Add an entry with given name to current environment.
|
@@ -127,7 +127,7 @@ module Loxxy
|
|
127
127
|
rule('unaryOp' => 'MINUS')
|
128
128
|
rule('call' => 'primary')
|
129
129
|
rule('call' => 'primary refinement_plus').as 'call_expr'
|
130
|
-
rule('refinement_plus' => 'refinement_plus refinement')
|
130
|
+
rule('refinement_plus' => 'refinement_plus refinement').as 'refinement_plus_more'
|
131
131
|
rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
|
132
132
|
rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
|
133
133
|
rule('refinement' => 'DOT IDENTIFIER')
|
data/lib/loxxy/version.rb
CHANGED
@@ -36,6 +36,8 @@ module Loxxy
|
|
36
36
|
|
37
37
|
|
38
38
|
it "should react to 'after_var_stmt' event" do
|
39
|
+
# Precondition: value to assign is on top of stack
|
40
|
+
subject.stack.push(greeting)
|
39
41
|
expect { subject.after_var_stmt(var_decl) }.not_to raise_error
|
40
42
|
current_env = subject.symbol_table.current_env
|
41
43
|
expect(current_env.defns['greeting']).to be_kind_of(Variable)
|
data/spec/interpreter_spec.rb
CHANGED
@@ -53,15 +53,6 @@ module Loxxy
|
|
53
53
|
end # context
|
54
54
|
|
55
55
|
context 'Evaluating Lox code:' do
|
56
|
-
let(:hello_world) do
|
57
|
-
lox = <<-LOX_END
|
58
|
-
var greeting = "Hello"; // Declaring a variable
|
59
|
-
print greeting + ", " + "world!"; // ... Playing with concatenation
|
60
|
-
LOX_END
|
61
|
-
|
62
|
-
lox
|
63
|
-
end
|
64
|
-
|
65
56
|
it 'should evaluate core data types' do
|
66
57
|
result = subject.evaluate('true; // Not false')
|
67
58
|
expect(result).to be_kind_of(Loxxy::Datatype::True)
|
@@ -269,9 +260,9 @@ LOX_END
|
|
269
260
|
|
270
261
|
it 'should accept variable mention' do
|
271
262
|
program = <<-LOX_END
|
272
|
-
|
273
|
-
|
274
|
-
LOX_END
|
263
|
+
var foo = "bar";
|
264
|
+
print foo; // => bar
|
265
|
+
LOX_END
|
275
266
|
expect { subject.evaluate(program) }.not_to raise_error
|
276
267
|
expect(sample_cfg[:ostream].string).to eq('bar')
|
277
268
|
end
|
@@ -438,18 +429,35 @@ LOX_END
|
|
438
429
|
expect(result).to eq(3)
|
439
430
|
end
|
440
431
|
|
441
|
-
|
442
|
-
|
443
|
-
|
432
|
+
# rubocop: disable Style/StringConcatenation
|
433
|
+
it 'should support local functions and closures' do
|
434
|
+
program = <<-LOX_END
|
435
|
+
fun makeCounter() {
|
436
|
+
var i = 0;
|
437
|
+
fun count() {
|
438
|
+
i = i + 1;
|
439
|
+
print i;
|
440
|
+
}
|
441
|
+
|
442
|
+
return count;
|
443
|
+
}
|
444
|
+
|
445
|
+
var counter = makeCounter();
|
446
|
+
counter(); // "1".
|
447
|
+
counter(); // "2".
|
448
|
+
LOX_END
|
449
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
450
|
+
expect(sample_cfg[:ostream].string).to eq('1' + '2')
|
444
451
|
end
|
445
|
-
|
452
|
+
# rubocop: enable Style/StringConcatenation
|
446
453
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
expect { subject.evaluate(
|
454
|
+
it 'should print the hello world message' do
|
455
|
+
program = <<-LOX_END
|
456
|
+
var greeting = "Hello"; // Declaring a variable
|
457
|
+
print greeting + ", " + "world!"; // ... Playing with concatenation
|
458
|
+
LOX_END
|
459
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
460
|
+
expect(sample_cfg[:ostream].string).to eq('Hello, world!')
|
453
461
|
end
|
454
462
|
end # context
|
455
463
|
end # describe
|
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.1.
|
4
|
+
version: 0.1.07
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-03-
|
11
|
+
date: 2021-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|