8.5.1
FossilOrigin-Name: f2b21a08789cde121fca0488c7fbe95cb01b98589dc92ac383f4d42847b2433e
diff --git a/ruby/lib/lox/environment.rb b/ruby/lib/lox/environment.rb
index b568f69..15cf9cc 100644
--- a/ruby/lib/lox/environment.rb
+++ b/ruby/lib/lox/environment.rb
@@ -2,7 +2,8 @@ require_relative "error"
module Lox
class Environment
- def initialize
+ def initialize(enclosing = nil)
+ @enclosing = enclosing
@values = {}
end
@@ -13,13 +14,27 @@ module Lox
def get(token)
name = token.lexeme
- @values.fetch(name) { raise RuntimeError.new(token, "Undefined variable '#{name}'.") }
+ @values.fetch(name) {
+ raise RuntimeError.new(token, "Undefined variable '#{name}'.") if @enclosing.nil?
+
+ @enclosing.get(token)
+ }
end
def assign(name, value)
- raise RuntimeError.new(name, "Undefined variable '#{name.lexeme}'.") unless @values.has_key?(name.lexeme)
+ lexeme = name.lexeme
+
+ if @values.has_key?(lexeme)
+ @values[lexeme] = value
+ return
+ end
+
+ unless @enclosing.nil?
+ @enclosing.assign(name, value)
+ return
+ end
- @values[name.lexeme] = value
+ raise RuntimeError.new(name, "Undefined variable '#{lexeme}'.")
end
end
end
diff --git a/ruby/test/lox/test_environment.rb b/ruby/test/lox/test_environment.rb
index 8079a17..1838a3d 100644
--- a/ruby/test/lox/test_environment.rb
+++ b/ruby/test/lox/test_environment.rb
@@ -5,6 +5,8 @@ require "lox/error"
require "lox/token"
class TestEnvironment < Lox::Test
+ NAME_TOKEN = Lox::Token.new(:IDENTIFIER, "name", "name", 0)
+
def setup
@env = Lox::Environment.new
end
@@ -12,12 +14,34 @@ class TestEnvironment < Lox::Test
def test_define
@env.define("name", "value")
- assert_equal "value", @env.get(Lox::Token.new(:IDENTIFIER, "name", "name", 0))
+ assert_equal "value", @env.get(NAME_TOKEN)
end
def test_get
assert_raises Lox::RuntimeError, "Undefined variable name 'name'." do
- @env.get(Lox::Token.new(:IDENTIFIER, "name", "name", 0))
+ @env.get(NAME_TOKEN)
end
end
+
+ def test_enclosing_define
+ @env.define("name", "value")
+ enclosed = Lox::Environment.new(@env)
+ assert_equal "value", enclosed.get(NAME_TOKEN)
+
+ enclosed.define("name", "foo")
+ assert_equal "foo", enclosed.get(NAME_TOKEN)
+ end
+
+ def test_enclosing_assign
+ @env.define("name", "foo")
+ enclosed = Lox::Environment.new(@env)
+ enclosed.assign(NAME_TOKEN, "bar")
+ assert_equal "bar", enclosed.get(NAME_TOKEN)
+ assert_equal "bar", @env.get(NAME_TOKEN)
+
+ enclosed.define("name", "baz")
+ enclosed.assign(NAME_TOKEN, "qux")
+ assert_equal "qux", enclosed.get(NAME_TOKEN)
+ assert_equal "bar", @env.get(NAME_TOKEN)
+ end
end