Browse Source

Fix closure environment binding

master
Dylan Baker 5 years ago
parent
commit
f64680858b

+ 1
- 0
lib/ahem/ast/function_call.rb View File

@@ -21,6 +21,7 @@ class AST::FunctionCall
21 21
     end
22 22
 
23 23
     if function.is_a?(AST::FunctionDefinition)
24
+      env = function.env unless function.env.nil?
24 25
       if @arguments.size != function.parameters.size
25 26
         raise "Arity mismatch in call to function #{function.name}"
26 27
       end

+ 5
- 4
lib/ahem/ast/function_definition.rb View File

@@ -1,10 +1,11 @@
1 1
 class AST::FunctionDefinition
2
-  attr_reader :name, :parameters, :body
2
+  attr_reader :name, :parameters, :body, :env
3 3
 
4
-  def initialize(name, parameters, body)
4
+  def initialize(name, parameters, body, env = nil)
5 5
     @name = name
6 6
     @parameters = parameters
7 7
     @body = body
8
+    @env = env
8 9
   end
9 10
 
10 11
   def ==(other)
@@ -14,7 +15,7 @@ class AST::FunctionDefinition
14 15
   end
15 16
 
16 17
   def execute(env)
17
-    env.set(@name.name, self) unless @name.nil?
18
-    self
18
+    return AST::FunctionDefinition.new(@name, @parameters, @body, env) if @name.nil?
19
+    env.set(@name.name, self)
19 20
   end
20 21
 end

+ 45
- 0
spec/ast/function_call_spec.rb View File

@@ -200,4 +200,49 @@ RSpec.describe AST::FunctionCall do
200 200
     ).execute(Environment.new)
201 201
     expect(result).to eq(5.0)
202 202
   end
203
+
204
+  # function outer() {
205
+  #   let x = "hello world";
206
+  #   function () {
207
+  #     x;
208
+  #   };
209
+  # }
210
+  # let y = outer();
211
+  # y();
212
+  it 'keeps a reference to a closure\'s environment' do
213
+    env = Environment.new
214
+    AST::FunctionDefinition.new(
215
+      AST::Identifier.new('outer'),
216
+      [],
217
+      AST::Block.new(
218
+        [
219
+          AST::VariableDeclaration.new(
220
+            AST::Identifier.new('x'),
221
+            AST::String.new('hello world')
222
+          ),
223
+          AST::FunctionDefinition.new(
224
+            nil,
225
+            [],
226
+            AST::Block.new(
227
+              [
228
+                AST::Identifier.new('x'),
229
+              ]
230
+            )
231
+          )
232
+        ]
233
+      )
234
+    ).execute(env)
235
+    AST::VariableDeclaration.new(
236
+      AST::Identifier.new('y'),
237
+      AST::FunctionCall.new(
238
+        AST::Identifier.new('outer'),
239
+        []
240
+      )
241
+    ).execute(env)
242
+    result = AST::FunctionCall.new(
243
+      AST::Identifier.new('y'),
244
+      []
245
+    ).execute(env)
246
+    expect(result).to eq("hello world")
247
+  end
203 248
 end

Loading…
Cancel
Save