Alpha Chen 2 years ago
parent 766f281a5e
commit f2501a1d41

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

@ -1,8 +1,22 @@
require_relative "error"
module Lox module Lox
class Instance class Instance
def initialize(klass) def initialize(klass)
@klass = 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 end
def to_s = "#{@klass.name} instance" def to_s = "#{@klass.name} instance"

@ -121,6 +121,16 @@ module Lox
evaluate(expr.right) evaluate(expr.right)
end 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) def visit_unary(expr)
right = evaluate(expr.right) right = evaluate(expr.right)
@ -206,6 +216,13 @@ module Lox
func.call(self, args) func.call(self, args)
end 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 private
def truthy?(value) = !!value def truthy?(value) = !!value

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

@ -77,9 +77,22 @@ module Lox
def visit_binary(expr) = resolve(expr.left, expr.right) def visit_binary(expr) = resolve(expr.left, expr.right)
def visit_call(expr) = resolve(expr.callee, *expr.args) 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_grouping(expr) = resolve(expr.expr)
def visit_literal(expr) = nil def visit_literal(expr) = nil
def visit_logical(expr) = resolve(expr.left, expr.right) 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) def visit_unary(expr) = resolve(expr.right)
private private

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

Loading…
Cancel
Save