8.5.1
FossilOrigin-Name: f2b21a08789cde121fca0488c7fbe95cb01b98589dc92ac383f4d42847b2433e
change pyxztuuzrvkyxnyrnqwoymyxqltxlwkk
commit 21ab732204c80799c12f7f7d31b718a214fae87b
author alpha <alpha@kejadlen.dev>
date
parent pvqqwuqn
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