Last active
December 18, 2015 07:39
-
-
Save bryanwoods/5748432 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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