88 lines
1.8 KiB
Ruby
88 lines
1.8 KiB
Ruby
# parse pseudo Ruby code and generate AST
|
|
|
|
require_relative "ast-def"
|
|
require "ripper"
|
|
|
|
Subroutines = {}
|
|
|
|
def expr(e)
|
|
case e[0]
|
|
when :binary then PBinary[e[2], expr(e[1]), expr(e[3])]
|
|
when :var_ref, :vcall
|
|
name = e[1][1]
|
|
case name
|
|
when "nop" then PNop[]
|
|
when "minus1" then PImm[-1]
|
|
when "Zero" then PImm[0]
|
|
else PRead[name]
|
|
end
|
|
when :aref then PARef[expr(e[1]), expr(e[2][1][0])]
|
|
when :assign then PASet[expr(e[1][1]), expr(e[1][2][1][0]), expr(e[2])]
|
|
when :@int then PImm[e[1].to_i]
|
|
when :paren
|
|
PSequence[statements(e[1][0..-2]), expr(e[1][-1])]
|
|
when :ifop
|
|
PIfThenElse[expr(e[1]), expr(e[2]), expr(e[3])]
|
|
when :method_add_arg
|
|
case e[1][1][1]
|
|
when "inc", "dec"
|
|
r = e[2][1][1][0][1][1]
|
|
PSequence[
|
|
[PAssign[r, PBinary[e[1][1][1] == "inc" ? :+ : :-, PRead[r], PImm[1]]]],
|
|
PRead[r]
|
|
]
|
|
else
|
|
pp e
|
|
raise e[1][1][1]
|
|
end
|
|
when Array then expr(e[0])
|
|
else
|
|
pp e
|
|
raise
|
|
end
|
|
end
|
|
|
|
def statement(e)
|
|
case e[0]
|
|
when :assign then PAssign[e[1][1][1], expr(e[2])]
|
|
when :method_add_block
|
|
if e[1][1][1][1] == "loop"
|
|
PLoop[expr(e[1][2][1][1]), statements(e[2][2])]
|
|
else
|
|
pp e
|
|
raise
|
|
end
|
|
when :command
|
|
case e[1][1]
|
|
when "call" then Subroutines[e[2][1][0][1][1]]
|
|
when "exit" then PExit[expr(e[2][1][0])]
|
|
when "out" then POut[expr(e[2][1][0])]
|
|
when "test" then PTest[expr(e[2][1][0])]
|
|
else
|
|
pp e
|
|
exit
|
|
end
|
|
when :void_stmt then PNop[]
|
|
else
|
|
pp e
|
|
raise e[0].to_s
|
|
end
|
|
end
|
|
|
|
def statements(s)
|
|
s.map {|e| statement(e) }
|
|
end
|
|
|
|
def program(e)
|
|
e[1].each do |e|
|
|
Subroutines[e[1][1][1][1]] = statements(e[2][2])
|
|
end
|
|
end
|
|
|
|
if ARGV.size != 2
|
|
puts "usage: #$0 <src.rb> <ast.txt>"
|
|
else
|
|
program(Ripper.sexp(File.read(ARGV[0])))
|
|
File.write(ARGV[1], Subroutines["main"].pretty_inspect)
|
|
end
|