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
 
12
 
13
   def execute(env)
13
   def execute(env)
14
     if env.get(@name.name)
14
     if env.get(@name.name)
15
-      env.set(@name.name, @value)
15
+      env.set(@name.name, @value.execute(env))
16
     else
16
     else
17
       raise "Invalid assignment to undeclared variable #{@name.name}"
17
       raise "Invalid assignment to undeclared variable #{@name.name}"
18
     end
18
     end

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

15
 
15
 
16
   def execute(env)
16
   def execute(env)
17
     @iterable.execute(env).each do |i|
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
     end
20
     end
22
   end
21
   end
23
 end
22
 end

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

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

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

6
     AST::Assignment.new(AST::Identifier.new('x'), AST::Number.new(6.0)).execute(
6
     AST::Assignment.new(AST::Identifier.new('x'), AST::Number.new(6.0)).execute(
7
       env
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
   end
10
   end
11
 
11
 
12
   it 'raises for an undeclared variable' do
12
   it 'raises for an undeclared variable' do

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

18
         .execute(Environment.new)
18
         .execute(Environment.new)
19
     }.to output("1.0\n2.0\n3.0\n").to_stdout
19
     }.to output("1.0\n2.0\n3.0\n").to_stdout
20
   end
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
 end
48
 end

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

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

Loading…
Cancel
Save