12.6.1
change rntmpqspyxloonxtpkwmsnkslyzowmtl
commit 186a8e13a8913eadc5d89da237eccdb7e70f88a4
author Alpha Chen <alpha@kejadlen.dev>
date
parent zwkmzsxs
diff --git a/ruby/lib/lox/resolver.rb b/ruby/lib/lox/resolver.rb
index 7cdf3b6..c52819c 100644
--- a/ruby/lib/lox/resolver.rb
+++ b/ruby/lib/lox/resolver.rb
@@ -8,6 +8,7 @@ module Lox
 
       @scopes = []
       @current_func = :NONE
+      @current_class = :NONE
     end
 
     def resolve(*resolvees)
@@ -25,16 +26,18 @@ module Lox
     end
 
     def visit_class(stmt)
-      declare(stmt.name)
+      with_current_class(:CLASS) do
+        declare(stmt.name)
 
-      with_scope do
-        @scopes.last["this"] = true
+        with_scope do
+          @scopes.last["this"] = true
 
-        stmt.methods.each do |method|
-          resolve_function(method, :METHOD)
-        end
+          stmt.methods.each do |method|
+            resolve_function(method, :METHOD)
+          end
 
-        define(stmt.name)
+          define(stmt.name)
+        end
       end
 
       nil
@@ -104,6 +107,8 @@ module Lox
     end
 
     def visit_this(expr)
+      raise ResolverError.new(expr.keyword, "Can't use 'this' outside of a class.") if @current_class == :NONE
+
       resolve_local(expr, expr.keyword)
       nil
     end
@@ -163,5 +168,14 @@ module Lox
       @current_func = enclosing_func
     end
 
+    def with_current_class(type)
+      enclosing_class = @current_class
+      @current_class = type
+
+      yield
+
+      @current_class = enclosing_class
+    end
+
   end
 end
diff --git a/ruby/test/lox/test_interpreter.rb b/ruby/test/lox/test_interpreter.rb
index 6463058..73e42df 100644
--- a/ruby/test/lox/test_interpreter.rb
+++ b/ruby/test/lox/test_interpreter.rb
@@ -402,6 +402,20 @@ class TestInterpreter < Lox::Test
     SRC
   end
 
+  def test_invalid_this
+    assert_raises Lox::ResolverError, "Can't use 'this' outside of a class." do
+      interpret("print this;")
+    end
+
+    assert_raises Lox::ResolverError, "Can't use 'this' outside of a class." do
+      interpret(<<~SRC)
+        fun notAMethod() {
+          print this;
+        }
+      SRC
+    end
+  end
+
   private
 
   def assert_interpreted(expected, src)