Browse Source

For loop/environment fixes

There was a problem where the body of a for loop was treated like a
function and got an entirely new environment. This meant that you could
not, for instance, modify a variable in a loop and keep the changed
value after the loop finished executing.

Also, for consistency, switch to storing only fully evaluated values in
the environment.
master
Dylan Baker 5 years ago
parent
commit
d3ea85d84d

+ 1
- 1
lib/ahem/ast/assignment.rb View File

@@ -12,7 +12,7 @@ class AST::Assignment
12 12
 
13 13
   def execute(env)
14 14
     if env.get(@name.name)
15
-      env.set(@name.name, @value)
15
+      env.set(@name.name, @value.execute(env))
16 16
     else
17 17
       raise "Invalid assignment to undeclared variable #{@name.name}"
18 18
     end

+ 2
- 3
lib/ahem/ast/for_loop.rb View File

@@ -15,9 +15,8 @@ class AST::ForLoop
15 15
 
16 16
   def execute(env)
17 17
     @iterable.execute(env).each do |i|
18
-      _env = Environment.new
19
-      _env.set(@iterator.name, i)
20
-      @block.statements.each { |statement| statement.execute(_env) }
18
+      env.set(@iterator.name, i)
19
+      @block.statements.each { |statement| statement.execute(env) }
21 20
     end
22 21
   end
23 22
 end

+ 1
- 1
lib/ahem/ast/variable_declaration.rb View File

@@ -13,7 +13,7 @@ class AST::VariableDeclaration
13 13
 
14 14
   def execute(env)
15 15
     if env.data[@name.name].nil?
16
-      env.set(@name.name, @value)
16
+      env.set(@name.name, @value.execute(env))
17 17
     else
18 18
       raise "Invalid declaration of previously declared variable #{@name.name}"
19 19
     end

+ 1
- 1
spec/ast/assignment_spec.rb View File

@@ -6,7 +6,7 @@ RSpec.describe AST::Assignment do
6 6
     AST::Assignment.new(AST::Identifier.new('x'), AST::Number.new(6.0)).execute(
7 7
       env
8 8
     )
9
-    expect(env.get('x').execute(env)).to eq(6.0)
9
+    expect(env.get('x')).to eq(6.0)
10 10
   end
11 11
 
12 12
   it 'raises for an undeclared variable' do

+ 27
- 0
spec/ast/for_loop_spec.rb View File

@@ -18,4 +18,31 @@ RSpec.describe AST::ForLoop do
18 18
         .execute(Environment.new)
19 19
     }.to output("1.0\n2.0\n3.0\n").to_stdout
20 20
   end
21
+
22
+  it 'does not create a new scope inside the loop' do
23
+    env = Environment.new
24
+    AST::VariableDeclaration.new(AST::Identifier.new('x'), AST::Number.new(0.0))
25
+      .execute(env)
26
+    result =
27
+      AST::ForLoop.new(
28
+        AST::Identifier.new('y'),
29
+        AST::Array.new(
30
+          [AST::Number.new(1.0), AST::Number.new(2.0), AST::Number.new(3.0)]
31
+        ),
32
+        AST::Block.new(
33
+          [
34
+            AST::Assignment.new(
35
+              AST::Identifier.new('x'),
36
+              AST::Binary.new(
37
+                AST::Operators::ADD,
38
+                AST::Identifier.new('x'),
39
+                AST::Identifier.new('y')
40
+              )
41
+            )
42
+          ]
43
+        )
44
+      )
45
+        .execute(env)
46
+    expect(env.get('x')).to eq(6.0)
47
+  end
21 48
 end

+ 1
- 1
spec/ast/variable_declaration_spec.rb View File

@@ -3,7 +3,7 @@ RSpec.describe AST::VariableDeclaration do
3 3
     env = Environment.new
4 4
     AST::VariableDeclaration.new(AST::Identifier.new('x'), AST::Number.new(5.0))
5 5
       .execute(env)
6
-    expect(env.get('x').execute(env)).to eq(5.0)
6
+    expect(env.get('x')).to eq(5.0)
7 7
   end
8 8
 
9 9
   it 'raises if a variable is already defined' do

Loading…
Cancel
Save