done with 9

This commit is contained in:
2018-07-25 13:18:58 +02:00
parent 856edc4d17
commit 13106d1788
5 changed files with 156 additions and 43 deletions

View File

@@ -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 dont use code
generation for the expressions, and I dont use the visitor pattern. `var` and
`fun` are `let` and `fn`, respectively. And we dont need parentheses around
branching conditions, instead we require the bodies to be blocks.

View File

@@ -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,
"Cant assign undefined variable '#{name.lexeme}'.")
end end
end end

View File

@@ -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

View File

@@ -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

View File

@@ -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