You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
2.4 KiB
99 lines
2.4 KiB
module Lox
|
|
class Interpreter
|
|
# The book does printing and error catching here, but
|
|
# we're going to do it in the runner instead.
|
|
def interpret(stmts)
|
|
stmts.each do |stmt|
|
|
evaluate(stmt)
|
|
end
|
|
end
|
|
|
|
def evaluate(stmt) = stmt.accept(self)
|
|
|
|
def visit_expr(expr)
|
|
evaluate(expr.expr)
|
|
nil
|
|
end
|
|
|
|
def visit_print(expr)
|
|
puts stringify(evaluate(expr.expr))
|
|
nil
|
|
end
|
|
|
|
def visit_grouping(expr) = evaluate(expr.expr)
|
|
def visit_literal(expr) = expr.value
|
|
|
|
def visit_unary(expr)
|
|
right = evaluate(expr.right)
|
|
|
|
case expr.op.type
|
|
when :MINUS
|
|
check_number_operand!(expr.op, right)
|
|
-right
|
|
when :BANG then !truthy?(right)
|
|
else fail
|
|
end
|
|
end
|
|
|
|
def visit_binary(expr)
|
|
left = evaluate(expr.left)
|
|
right = evaluate(expr.right)
|
|
|
|
case expr.op.type
|
|
when :GREATER
|
|
check_number_operands!(expr.op, left, right)
|
|
left > right
|
|
when :GREATER_EQUAL
|
|
check_number_operands!(expr.op, left, right)
|
|
left >= right
|
|
when :LESS
|
|
check_number_operands!(expr.op, left, right)
|
|
left < right
|
|
when :LESS_EQUAL
|
|
check_number_operands!(expr.op, left, right)
|
|
left <= right
|
|
when :BANG_EQUAL then left != right
|
|
when :EQUAL_EQUAL then left == right
|
|
when :MINUS
|
|
check_number_operands!(expr.op, left, right)
|
|
left - right
|
|
when :PLUS
|
|
unless left.is_a?(Float) && right.is_a?(Float) || left.is_a?(String) && right.is_a?(String)
|
|
raise RuntimeError.new(expr.op, "Operands must be two numbers or two strings.")
|
|
end
|
|
left + right
|
|
when :SLASH
|
|
check_number_operands!(expr.op, left, right)
|
|
left / right
|
|
when :STAR
|
|
check_number_operands!(expr.op, left, right)
|
|
left * right
|
|
else fail
|
|
end
|
|
|
|
end
|
|
|
|
private
|
|
|
|
def truthy?(value) = !!value
|
|
|
|
def check_number_operand!(token, operand)
|
|
return if operand.is_a?(Float)
|
|
|
|
raise RuntimeError.new(token, "Operand must be a number.")
|
|
end
|
|
|
|
def check_number_operands!(token, left, right)
|
|
return if left.is_a?(Float) && right.is_a?(Float)
|
|
|
|
raise RuntimeError.new(token, "Operands must be numbers.")
|
|
end
|
|
|
|
def stringify(value)
|
|
return "nil" if value.nil?
|
|
return value.to_s.sub(/\.0$/, "") if value.is_a?(Float)
|
|
value.to_s
|
|
end
|
|
end
|
|
end
|