done with 9
This commit is contained in:
@@ -3,3 +3,8 @@
|
|||||||
`rlox` is an unpronouncable variant of the tree-walk interpreter laid out in
|
`rlox` is an unpronouncable variant of the tree-walk interpreter laid out in
|
||||||
[Crafting Interpreters](http://craftinginterpreters.com/), written in Ruby,
|
[Crafting Interpreters](http://craftinginterpreters.com/), written in Ruby,
|
||||||
because I want to learn it.
|
because I want to learn it.
|
||||||
|
|
||||||
|
It deviates a little from the “canonical” version of Lox. I don’t use code
|
||||||
|
generation for the expressions, and I don’t use the visitor pattern. `var` and
|
||||||
|
`fun` are `let` and `fn`, respectively. And we don’t need parentheses around
|
||||||
|
branching conditions, instead we require the bodies to be blocks.
|
||||||
|
@@ -24,11 +24,13 @@ class Environment
|
|||||||
|
|
||||||
def assign(name, value)
|
def assign(name, value)
|
||||||
if @values.has_key? name.lexeme
|
if @values.has_key? name.lexeme
|
||||||
@values.put(name.lexeme, value)
|
@values[name.lexeme] = value
|
||||||
|
return
|
||||||
end
|
end
|
||||||
if @parent != nil
|
if @parent != nil
|
||||||
return @parent.assign(name, value)
|
return @parent.assign(name, value)
|
||||||
end
|
end
|
||||||
raise ExecError.new(name.line, "Undefined variable '#{name.lexeme}'.")
|
raise ExecError.new(name.line,
|
||||||
|
"Can’t assign undefined variable '#{name.lexeme}'.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,20 +1,6 @@
|
|||||||
def parenthesize(name, *exprs)
|
def truthy?(obj)
|
||||||
res = "(#{name}"
|
if [nil, false, "", 0].include? obj
|
||||||
|
|
||||||
exprs.each { | expr |
|
|
||||||
res << " "
|
|
||||||
res << expr.dbg
|
|
||||||
}
|
|
||||||
res << ")"
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def truthy(obj)
|
|
||||||
if obj == nil
|
|
||||||
false
|
false
|
||||||
elsif obj.is_a? Boolean
|
|
||||||
obj
|
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
@@ -47,10 +33,6 @@ Assign = Struct.new(:name, :value) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
Binary = Struct.new(:left, :operator, :right) do
|
Binary = Struct.new(:left, :operator, :right) do
|
||||||
def dbg()
|
|
||||||
parenthesize(operator.lexeme, left, right)
|
|
||||||
end
|
|
||||||
|
|
||||||
def eval(env)
|
def eval(env)
|
||||||
l = left.eval(env)
|
l = left.eval(env)
|
||||||
r = right.eval(env)
|
r = right.eval(env)
|
||||||
@@ -82,38 +64,27 @@ Binary = Struct.new(:left, :operator, :right) do
|
|||||||
l <= r
|
l <= r
|
||||||
when :bang_eq then l != r
|
when :bang_eq then l != r
|
||||||
when :eq_eq then l == r
|
when :eq_eq then l == r
|
||||||
|
when :comma then r
|
||||||
else nil
|
else nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Grouping = Struct.new(:expression) do
|
Grouping = Struct.new(:expression) do
|
||||||
def dbg()
|
|
||||||
parenthesize("group", expression)
|
|
||||||
end
|
|
||||||
|
|
||||||
def eval(env)
|
def eval(env)
|
||||||
expression.eval(env)
|
expression.eval(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Literal = Struct.new(:value) do
|
Literal = Struct.new(:value) do
|
||||||
def dbg()
|
|
||||||
value.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def eval(env)
|
def eval(env)
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Unary = Struct.new(:operator, :right) do
|
Unary = Struct.new(:operator, :right) do
|
||||||
def dbg()
|
|
||||||
parenthesize(operator.lexeme, right)
|
|
||||||
end
|
|
||||||
|
|
||||||
def eval(env)
|
def eval(env)
|
||||||
r = right.eval(env)
|
r = right.eval(env)
|
||||||
|
|
||||||
case operator.type
|
case operator.type
|
||||||
when :bang then is_truthy(r)
|
when :bang then truthy?(r)
|
||||||
when :minus then
|
when :minus then
|
||||||
check_number_op(operator, r)
|
check_number_op(operator, r)
|
||||||
-r
|
-r
|
||||||
@@ -126,6 +97,25 @@ Var = Struct.new(:name) do
|
|||||||
env.get(name)
|
env.get(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Logical = Struct.new(:left, :operator, :right) do
|
||||||
|
def eval(env)
|
||||||
|
l = left.eval(env)
|
||||||
|
|
||||||
|
case operator.type
|
||||||
|
when :or then
|
||||||
|
if truthy?(l)
|
||||||
|
return l
|
||||||
|
end
|
||||||
|
when :and then
|
||||||
|
if not truthy?(l)
|
||||||
|
return l
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
right.eval(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Expression = Struct.new(:expression) do
|
Expression = Struct.new(:expression) do
|
||||||
def eval(env)
|
def eval(env)
|
||||||
expression.eval(env)
|
expression.eval(env)
|
||||||
@@ -158,3 +148,21 @@ Block = Struct.new(:stmts) do
|
|||||||
env
|
env
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
If = Struct.new(:cond, :thn, :els) do
|
||||||
|
def eval(env)
|
||||||
|
if truthy? cond.eval(env)
|
||||||
|
thn.eval(env)
|
||||||
|
elsif els != nil
|
||||||
|
els.eval(env)
|
||||||
|
end
|
||||||
|
env
|
||||||
|
end
|
||||||
|
end
|
||||||
|
While = Struct.new(:cond, :body) do
|
||||||
|
def eval(env)
|
||||||
|
while truthy? cond.eval(env)
|
||||||
|
body.eval(env)
|
||||||
|
end
|
||||||
|
env
|
||||||
|
end
|
||||||
|
end
|
||||||
|
107
rlox/parse.rb
107
rlox/parse.rb
@@ -24,7 +24,7 @@ class Parser
|
|||||||
end
|
end
|
||||||
|
|
||||||
def advance()
|
def advance()
|
||||||
if !is_at_end()
|
if !is_at_end
|
||||||
@current += 1
|
@current += 1
|
||||||
end
|
end
|
||||||
return previous
|
return previous
|
||||||
@@ -54,9 +54,33 @@ class Parser
|
|||||||
expr
|
expr
|
||||||
end
|
end
|
||||||
|
|
||||||
def assignment()
|
def and_expr()
|
||||||
expr = equality
|
expr = equality
|
||||||
|
|
||||||
|
if match(:and)
|
||||||
|
operator = previous
|
||||||
|
right = equality
|
||||||
|
expr = Logical.new(expr, operator, right)
|
||||||
|
end
|
||||||
|
|
||||||
|
expr
|
||||||
|
end
|
||||||
|
|
||||||
|
def or_expr()
|
||||||
|
expr = and_expr
|
||||||
|
|
||||||
|
if match(:or)
|
||||||
|
operator = previous
|
||||||
|
right = and_expr
|
||||||
|
expr = Logical.new(expr, operator, right)
|
||||||
|
end
|
||||||
|
|
||||||
|
expr
|
||||||
|
end
|
||||||
|
|
||||||
|
def assignment()
|
||||||
|
expr = or_expr
|
||||||
|
|
||||||
if match(:eq)
|
if match(:eq)
|
||||||
equals = previous
|
equals = previous
|
||||||
value = assignment
|
value = assignment
|
||||||
@@ -126,7 +150,7 @@ class Parser
|
|||||||
return Unary.new(operator, right)
|
return Unary.new(operator, right)
|
||||||
end
|
end
|
||||||
|
|
||||||
primary()
|
primary
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary()
|
def primary()
|
||||||
@@ -166,7 +190,7 @@ class Parser
|
|||||||
end
|
end
|
||||||
|
|
||||||
def expression()
|
def expression()
|
||||||
comma()
|
comma
|
||||||
end
|
end
|
||||||
|
|
||||||
def synchronize()
|
def synchronize()
|
||||||
@@ -210,9 +234,82 @@ class Parser
|
|||||||
return stmts
|
return stmts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def if_statement()
|
||||||
|
cond = expression
|
||||||
|
|
||||||
|
consume(:left_brace, "Expect '{' after 'if' condition.")
|
||||||
|
thn = Block.new(block)
|
||||||
|
els = nil
|
||||||
|
|
||||||
|
if match(:else)
|
||||||
|
consume(:left_brace, "Expect '{' after 'else'.")
|
||||||
|
els = Block.new(block)
|
||||||
|
end
|
||||||
|
|
||||||
|
If.new(cond, thn, els)
|
||||||
|
end
|
||||||
|
|
||||||
|
def while_statement()
|
||||||
|
cond = expression
|
||||||
|
|
||||||
|
consume(:left_brace, "Expect '{' after 'if' condition.")
|
||||||
|
body = Block.new(block)
|
||||||
|
|
||||||
|
While.new(cond, body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_statement()
|
||||||
|
init = if match(:semicolon)
|
||||||
|
nil
|
||||||
|
elsif match(:var)
|
||||||
|
var_declaration
|
||||||
|
else
|
||||||
|
expression_statement
|
||||||
|
end
|
||||||
|
|
||||||
|
cond = nil
|
||||||
|
|
||||||
|
if !check(:semicolon)
|
||||||
|
cond = expression
|
||||||
|
end
|
||||||
|
|
||||||
|
consume(:semicolon, "Expect ';' after loop condition.")
|
||||||
|
|
||||||
|
inc = nil
|
||||||
|
|
||||||
|
if !check(:semicolon)
|
||||||
|
inc = expression
|
||||||
|
end
|
||||||
|
|
||||||
|
consume(:left_brace, "Expect '{' after 'if' condition.")
|
||||||
|
body = Block.new(block)
|
||||||
|
|
||||||
|
if inc
|
||||||
|
body = Block.new([body, Expr.new(inc)])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not cond
|
||||||
|
cond = Literal.new(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if init
|
||||||
|
body = Block.new([init, body])
|
||||||
|
end
|
||||||
|
|
||||||
|
body = While.new(cond, body)
|
||||||
|
|
||||||
|
body
|
||||||
|
end
|
||||||
|
|
||||||
def statement()
|
def statement()
|
||||||
if match(:print)
|
if match(:if)
|
||||||
|
if_statement
|
||||||
|
elsif match(:for)
|
||||||
|
for_statement
|
||||||
|
elsif match(:print)
|
||||||
print_statement
|
print_statement
|
||||||
|
elsif match(:while)
|
||||||
|
while_statement
|
||||||
elsif match(:left_brace)
|
elsif match(:left_brace)
|
||||||
Block.new(block)
|
Block.new(block)
|
||||||
else
|
else
|
||||||
|
@@ -5,9 +5,9 @@ require './rlox/executor'
|
|||||||
def prompt()
|
def prompt()
|
||||||
exec = Executor.new
|
exec = Executor.new
|
||||||
|
|
||||||
f = File.new("#{Dir.home}/.rlox", File::CREAT|File::RDWR)
|
f = File.new("#{Dir.home}/.rlox", "a+")
|
||||||
File.readlines("#{Dir.home}/.rlox").each { | line |
|
File.readlines("#{Dir.home}/.rlox").each { | line |
|
||||||
Readline::HISTORY.push line
|
Readline::HISTORY.push line.rstrip
|
||||||
}
|
}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@@ -16,9 +16,10 @@ def prompt()
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
exec.run buf
|
exec.run buf
|
||||||
f.write("#{buf}\n")
|
|
||||||
rescue LoxError => err
|
rescue LoxError => err
|
||||||
STDERR.puts err
|
STDERR.puts err
|
||||||
|
ensure
|
||||||
|
f.write("#{buf}\n")
|
||||||
end
|
end
|
||||||
exec.inc_line
|
exec.inc_line
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user