Skip to content

Instantly share code, notes, and snippets.

@bryanwoods
Last active December 18, 2015 07:39
Show Gist options
  • Save bryanwoods/5748432 to your computer and use it in GitHub Desktop.
Save bryanwoods/5748432 to your computer and use it in GitHub Desktop.
require 'treetop'
Treetop.load('simple')
parse_tree = SimpleParser.new.parse('while (x < 5) { x = x * 3 }')
statement = parse_tree.to_ast
puts "THE RESULT: #{statement.evaluate({ x: Number.new(1) })}"
puts
puts "TRANSLATED TO RUBY: #{statement.to_ruby}"
__END__
THE RESULT: {:x=>«9»}
TRANSLATED TO RUBY: -> e {
while (-> e { (-> e { e[:x] }).call(e) < (-> e { 5 }).call(e) }).call(e)
e = (-> e { e.merge({ :x => (-> e { (-> e { e[:x] }).call(e) * (-> e { 3 }).call(e) }).call(e) }) }).call(e)
end
e
}
# encoding: UTF-8
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false
}
class Number < Struct.new(:value)
def reducible?
false
end
def evaluate(environment)
self
end
def to_s
value.to_s
end
def inspect
"«#{self}»"
end
def to_ruby
"-> e { #{value.inspect} }"
end
end
class Add < Struct.new(:left, :right)
def reducible?
true
end
def reduce(environment)
if left.reducible?
Add.new(left.reduce(environment), right)
elsif right.reducible?
Add.new(left, right.reduce(environment))
else
Number.new(left.value + right.value)
end
end
def evaluate(environment)
Number.new(
left.evaluate(environment).value + right.evaluate(environment).value
)
end
def to_s
"#{left} + #{right}"
end
def inspect
"«#{self}»"
end
def to_ruby
"-> e { (#{left.to_ruby}).call(e) + (#{right.to_ruby}).call(e) }"
end
end
class Multiply < Struct.new(:left, :right)
def reducible?
true
end
def reduce(environment)
if left.reducible?
Multiply.new(left.reduce(environment), right)
elsif right.reducible?
Multiply.new(left, right.reduce(environment))
else
Number.new(left.value * right.value)
end
end
def evaluate(environment)
Number.new(
left.evaluate(environment).value * right.evaluate(environment).value
)
end
def to_s
"#{left} * #{right}"
end
def inspect
"«#{self}»"
end
def to_ruby
"-> e { (#{left.to_ruby}).call(e) * (#{right.to_ruby}).call(e) }"
end
end
class Boolean < Struct.new(:value)
def to_s
value.to_s
end
def evaluate(environment)
self
end
def inspect
"«#{self}»"
end
def reducible?
false
end
def to_ruby
"-> e { #{value.inspect} }"
end
end
class LessThan < Struct.new(:left, :right)
def to_s
"#{left} < #{right}"
end
def inspect
"«#{self}»"
end
def reducible?
true
end
def reduce(environment)
if left.reducible?
LessThan.new(left.reduce(environment), right)
elsif right.reducible?
LessThan.new(left, right.reduce(environment))
else
Boolean.new(left.value < right.value)
end
end
def evaluate(environment)
Boolean.new(
left.evaluate(environment).value < right.evaluate(environment).value
)
end
def to_ruby
"-> e { (#{left.to_ruby}).call(e) < (#{right.to_ruby}).call(e) }"
end
end
class Variable < Struct.new(:name)
def to_s
name.to_s
end
def inspect
"«#{self}»"
end
def reducible?
true
end
def reduce(environment)
environment[name]
end
def evaluate(environment)
environment[name]
end
def to_ruby
"-> e { e[#{name.inspect}] }"
end
end
class If < Struct.new(:condition, :consequence, :alternative)
def to_s
"if (#{condition}) { #{consequence} } else { #{alternative} }"
end
def inspect
"«#{self}»"
end
def reducible?
true
end
def reduce(environment)
if condition.reducible?
[
If.new(
condition.reduce(environment),
consequence,
alternative
),
environment
]
else
case condition
when Boolean.new(true)
[consequence, environment]
when Boolean.new(false)
[alternative, environment]
end
end
end
def evaluate(environment)
case condition.evaluate(environment)
when Boolean.new(true)
consequence.evaluate(environment)
when Boolean.new(false)
alternative.evaluate(environment)
end
end
def to_ruby
<<-EOR
-> e {
if (#{condition.to_ruby}).call(e)
(#{consequence.to_ruby}).call(e)
else
(#{alternative.to_ruby}).call(e)
end
}
EOR
end
end
class DoNothing
def to_s
'do-nothing'
end
def inspect
"«#{self}»"
end
def ==(other_statement)
other_statement.instance_of?(DoNothing)
end
def reducible?
false
end
def evaluate(environment)
environment
end
def to_ruby
"-> e { e }"
end
end
class Assign < Struct.new(:name, :expression)
def to_s
"#{name} = #{expression}"
end
def reducible?
true
end
def reduce(environment)
if expression.reducible?
[Assign.new(name, expression.reduce(environment)), environment]
else
[DoNothing.new, environment.merge({ name => expression })]
end
end
def evaluate(environment)
environment.merge({ name => expression.evaluate(environment) })
end
def to_ruby
"-> e { e.merge({ #{name.inspect} => (#{expression.to_ruby}).call(e) }) }"
end
end
class Sequence < Struct.new(:first, :second)
def to_s
"#{first}; #{second}"
end
def inspect
"«#{self}»"
end
def reducible?
true
end
def reduce(environment)
case first
when DoNothing.new
[second, environment]
else
reduced_first, reduced_environment = first.reduce(environment)
[Sequence.new(reduced_first, second), reduced_environment]
end
end
def evaluate(environment)
second.evaluate(first.evaluate(environment))
end
def to_ruby
"-> e { (#{second.to_ruby}).call((#{first.to_ruby}).call(e)) }"
end
end
class While < Struct.new(:condition, :body)
def to_s
"while (#{condition}) { #{body} }"
end
def inspect
"«#{self}»"
end
def reducible?
true
end
def reduce(environment)
[
If.new(condition, Sequence.new(body, self), DoNothing.new),
environment
]
end
def evaluate(environment)
case condition.evaluate(environment)
when Boolean.new(true)
evaluate(body.evaluate(environment))
when Boolean.new(false)
environment
end
end
def to_ruby
<<-EOR
-> e {
while (#{condition.to_ruby}).call(e)
e = (#{body.to_ruby}).call(e)
end
e
}
EOR
end
end
class Machine < Struct.new(:statement, :environment)
def step
self.statement, self.environment = statement.reduce(environment)
end
def run
while statement.reducible?
puts "#{statement}, #{environment}"
step
end
puts "#{statement}, #{environment}"
end
end
grammar Simple
rule statement
while / assign
end
rule while
'while (' condition:expression ') { ' body:statement ' }' {
def to_ast
While.new(condition.to_ast, body.to_ast)
end
}
end
rule assign
name:[a-z]+ ' = ' expression {
def to_ast
Assign.new(name.text_value.to_sym, expression.to_ast)
end
}
end
rule expression
less_than
end
rule less_than
left:multiply ' < ' right:less_than {
def to_ast
LessThan.new(left.to_ast, right.to_ast)
end
}
/
multiply
end
rule multiply
left:term ' * ' right:multiply {
def to_ast
Multiply.new(left.to_ast, right.to_ast)
end
}
/
term
end
rule term
number / variable
end
rule number
[0-9]+ {
def to_ast
Number.new(text_value.to_i)
end
}
end
rule variable
[a-z]+ {
def to_ast
Variable.new(text_value.to_sym)
end
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment