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
[Crafting Interpreters](http://craftinginterpreters.com/), written in Ruby,
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)
if @values.has_key? name.lexeme
@values.put(name.lexeme, value)
@values[name.lexeme] = value
return
end
if @parent != nil
return @parent.assign(name, value)
end
raise ExecError.new(name.line, "Undefined variable '#{name.lexeme}'.")
raise ExecError.new(name.line,
"Cant assign undefined variable '#{name.lexeme}'.")
end
end

View File

@@ -1,20 +1,6 @@
def parenthesize(name, *exprs)
res = "(#{name}"
exprs.each { | expr |
res << " "
res << expr.dbg
}
res << ")"
res
end
def truthy(obj)
if obj == nil
def truthy?(obj)
if [nil, false, "", 0].include? obj
false
elsif obj.is_a? Boolean
obj
else
true
end
@@ -47,10 +33,6 @@ Assign = Struct.new(:name, :value) do
end
end
Binary = Struct.new(:left, :operator, :right) do
def dbg()
parenthesize(operator.lexeme, left, right)
end
def eval(env)
l = left.eval(env)
r = right.eval(env)
@@ -82,38 +64,27 @@ Binary = Struct.new(:left, :operator, :right) do
l <= r
when :bang_eq then l != r
when :eq_eq then l == r
when :comma then r
else nil
end
end
end
Grouping = Struct.new(:expression) do
def dbg()
parenthesize("group", expression)
end
def eval(env)
expression.eval(env)
end
end
Literal = Struct.new(:value) do
def dbg()
value.to_s
end
def eval(env)
value
end
end
Unary = Struct.new(:operator, :right) do
def dbg()
parenthesize(operator.lexeme, right)
end
def eval(env)
r = right.eval(env)
case operator.type
when :bang then is_truthy(r)
when :bang then truthy?(r)
when :minus then
check_number_op(operator, r)
-r
@@ -126,6 +97,25 @@ Var = Struct.new(:name) do
env.get(name)
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
def eval(env)
expression.eval(env)
@@ -158,3 +148,21 @@ Block = Struct.new(:stmts) do
env
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
def advance()
if !is_at_end()
if !is_at_end
@current += 1
end
return previous
@@ -54,9 +54,33 @@ class Parser
expr
end
def assignment()
def and_expr()
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)
equals = previous
value = assignment
@@ -126,7 +150,7 @@ class Parser
return Unary.new(operator, right)
end
primary()
primary
end
def primary()
@@ -166,7 +190,7 @@ class Parser
end
def expression()
comma()
comma
end
def synchronize()
@@ -210,9 +234,82 @@ class Parser
return stmts
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()
if match(:print)
if match(:if)
if_statement
elsif match(:for)
for_statement
elsif match(:print)
print_statement
elsif match(:while)
while_statement
elsif match(:left_brace)
Block.new(block)
else

View File

@@ -5,9 +5,9 @@ require './rlox/executor'
def prompt()
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 |
Readline::HISTORY.push line
Readline::HISTORY.push line.rstrip
}
begin
@@ -16,9 +16,10 @@ def prompt()
begin
exec.run buf
f.write("#{buf}\n")
rescue LoxError => err
STDERR.puts err
ensure
f.write("#{buf}\n")
end
exec.inc_line
end