Alpha Chen 2 years ago
parent 766f281a5e
commit f2501a1d41

@ -13,9 +13,11 @@ module Lox
expr :Assign, :name, :value
expr :Binary, :left, :op, :right
expr :Call, :callee, :paren, :args
expr :Get, :object, :name
expr :Grouping, :expr
expr :Literal, :value
expr :Logical, :left, :op, :right
expr :Set, :object, :name, :value
expr :Unary, :op, :right
expr :Variable, :name
end

@ -1,8 +1,22 @@
require_relative "error"
module Lox
class Instance
def initialize(klass)
@klass = klass
@fields = {}
end
def get(name)
raise RuntimeError.new(name, "Undefined property '#{name.lexeme}'.") unless @fields.has_key?(name.lexeme)
@fields.fetch(name.lexeme)
end
def set(name, value)
@fields[name.lexeme] = value
end
def to_s = "#{@klass.name} instance"

@ -121,6 +121,16 @@ module Lox
evaluate(expr.right)
end
def visit_set(expr)
object = evaluate(expr.object)
raise RuntimeError.new(expr.name, "Only instances have fields.") unless object.is_a?(Instance)
value = evaluate(expr.value)
object.set(expr.name, value)
value
end
def visit_unary(expr)
right = evaluate(expr.right)
@ -206,6 +216,13 @@ module Lox
func.call(self, args)
end
def visit_get(expr)
object = evaluate(expr.object)
raise RuntimeError.new(expr.name, "Only instances have properties.") unless object.is_a?(Instance)
object.get(expr.name)
end
private
def truthy?(value) = !!value

@ -16,7 +16,8 @@ module Lox
statements << declaration
end
statements
rescue ParseError
rescue ParseError => e
$stderr.puts e.message
synchronize!
end
@ -172,16 +173,19 @@ module Lox
def assignment
expr = or_
if match?(:EQUAL)
return expr unless match?(:EQUAL)
eq = prev
value = assignment
raise ParseError.new(eq, "Invalid assignment target.") unless expr.instance_of?(Expr::Variable)
return Expr::Assign.new(expr.name, value)
case expr
when Expr::Variable
Expr::Assign.new(expr.name, value)
when Expr::Get
Expr::Set.new(expr.object, expr.name, value)
else
raise ParseError.new(eq, "Invalid assignment target.")
end
expr
end
def or_
@ -272,6 +276,9 @@ module Lox
loop do
if match?(:LEFT_PAREN)
expr = finish_call(expr)
elsif match?(:DOT)
name = consume!(:IDENTIFIER, "Expect property name after '.'.")
expr = Expr::Get.new(expr, name)
else
break
end

@ -77,9 +77,22 @@ module Lox
def visit_binary(expr) = resolve(expr.left, expr.right)
def visit_call(expr) = resolve(expr.callee, *expr.args)
def visit_get(expr)
resolve(expr.object)
nil
end
def visit_grouping(expr) = resolve(expr.expr)
def visit_literal(expr) = nil
def visit_logical(expr) = resolve(expr.left, expr.right)
def visit_set(expr)
resolve(expr.value)
resolve(expr.object)
nil
end
def visit_unary(expr) = resolve(expr.right)
private

@ -336,6 +336,15 @@ class TestInterpreter < Lox::Test
SRC
end
def test_set
assert_interpreted "bar", <<~SRC
class Bagel {}
var bagel = Bagel();
bagel.foo = "bar";
print bagel.foo;
SRC
end
private
def assert_interpreted(expected, src)

Loading…
Cancel
Save