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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 293bf5f6fb0eb5a1daf2723484a2867909c03cc99b20270e562d288d334f0bdc
4
- data.tar.gz: 9e26854c6d4334f9869d0cd420ebd4365c0f978666e7ede8310d25e600cf5834
3
+ metadata.gz: f171f0046ff95784ed21b369751ca65d25e0ff757d1b2dccc534087a4b9c4a8d
4
+ data.tar.gz: 579badf6a30436962cb18e63dfee1ed361cfd152e9fecf54f2bfd25395db086b
5
5
  SHA512:
6
- metadata.gz: 2ac692f16a41b162001fee869d426abf634f1a2bb82bf17eac26d8c35939a8ca63234246a6ff88cc04db3833057b445e2650773652ce102a0dcc4f3aa90182e8
7
- data.tar.gz: 2764cb3d1703fdc948180cb2060f93a0ef72148f2877f5b711ecacd17d2ba52a9d3b9f7736eb53130850635cb0d127f2a5161a8361cb4a2268fc6275d15a6b1c
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
- theChildren[1].callee = theChildren[0]
264
- theChildren[1]
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[0]
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, aVarStmt.subnodes[0])
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.pop
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, stack)
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 enclosing (parent) environment.
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 variable with given name and initial value
20
+ # Create a function with given name
20
21
  # @param aName [String] The name of the variable
21
- # @param aValue [Datatype::BuiltinDatatype] the initial assigned value
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 = aStack
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 = current_env
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.defns.each_pair do |nm, _item|
53
- environments = name2envs[nm]
54
- if environments.size == 1
55
- name2envs.delete(nm)
56
- else
57
- environments.pop
58
- name2envs[nm] = environments
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
- @current_env = current_env.enclosing
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') # .as 'refinement_plus_more'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.1.06'
4
+ VERSION = '0.1.07'
5
5
  end
@@ -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)
@@ -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
- var foo = "bar";
273
- print foo; // => bar
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
- it 'should print the hello world message' do
442
- expect { subject.evaluate(hello_world) }.not_to raise_error
443
- expect(sample_cfg[:ostream].string).to eq('Hello, world!')
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
- end # context
452
+ # rubocop: enable Style/StringConcatenation
446
453
 
447
- context 'Test suite:' do
448
- it "should complain if one argument isn't a number" do
449
- source = '1 + nil;'
450
- err = Loxxy::RuntimeError
451
- err_msg = 'Operands must be two numbers or two strings.'
452
- expect { subject.evaluate(source) }.to raise_error(err, err_msg)
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.06
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-06 00:00:00.000000000 Z
11
+ date: 2021-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley