includes 'parser/walker' 'parser/sexp' 'parser/env' 'parser/compiler_helper'
c = walker.new
c.squish compiler_helper
c.init = { ast |
my.ast = ast
my.env = env.new
my.liftable_functions = []
my.liftable_strings = []
my.lifted_string_index = [:]
x = 0
my.next_liftable = { x = x + 1 }
}
c.walk :program { node |
c = my
my.results = node.nodes.map({ n |
c.process(n)
})
my.results = my.lifted_strings + my.liftable_functions + my.results
}
c.walk :local_var_assign { node |
temp = my.env.new_var node.lhs
rhs = process node.rhs, temp
my.env.set_type temp, rhs[:type]
true? temp == rhs.var
{ r temp, "local #{temp}\n#{rhs.out}" }
{ r temp, "#{rhs.out}\nlocal #{temp} = #{rhs.var}" }
}
c.walk :local_var_assign_interactive { node |
temp = my.env.new_var node.lhs
rhs = process node.rhs, temp
my.env.set_type temp, rhs[:type]
r temp, "#{rhs.out}\n#{temp} = #{rhs.var}"
}
c.walk :local_var_reassign, c->var_reassign
c.walk :upvar_assign, c->var_reassign
c.walk :field_assign { node, var = null |
lhs = process node.lhs.target
var = node.lhs.field
rhs = process node.rhs
out = lhs.out << "\n" << rhs.out << "
if _type(#{lhs.var}) == 'table' then
#{lhs.var}['#{var}'] = #{rhs.var}
else
_error('Cannot set method on ' .. #{lhs.var})
end
"
r rhs.var, out
}
c.walk :get_local_value, c->get_a_value
c.walk :get_up_value, c->get_a_value
c.walk :get_value { node, var = null |
res = set_result var
res.out << get_value(node.value, res.var)
res
}
c.walk :invoke_local { node, var = null |
invoke node, null, ->invoke_local, var
}
c.walk :invoke_up { node, var = null |
invoke node, null, ->invoke_local, var
}
conds = [:true_question, :false_question, :null_question]
inlineable_types = [:function, :liftable_function, :string, :simple_string, :symbol, :number, :array, :hash]
inlineable? = { n |
n.null? || { inlineable_types.include? n.name }
}
inlineable_when? = { node |
node.method == :when &&
{ node.args.all? ->inlineable? }
}
c.walk :invoke_self { node, var = null |
meth = node.method
true? conds.include?(meth) && { inlineable?(node.args[1]) && { inlineable?(node.args[2]) }}
{
true? meth == :true_question
{ true_if(node, var) }
{ true? meth == :false_question
{ false_if(node, var) }
{ null_if(node, var) }
}
}
{
true? inlineable_when?(node)
{
inline_when(node, var)
}
{
invoke node, null, ->invoke_self, var
}
}
}
c.walk :invoke_index_get { node, var = null |
target = process node.target, var
node.method = null
invoke node, target, ->invoke_index_get, var
}
c.walk :simple_index_get { node, var = null |
target = process node.target
invoke_index_get_direct node, target, var
}
c.walk :simple_index_set { node, var = null |
target = process node.target
invoke_index_set_direct node, target, var
}
c.walk :invoke_function { node, var = null |
target = process node.target, var
node.method = null
invoke node, target, ->invoke_function, var
}
c.walk :call { node, var = null |
target = process node.target
invoke node, target, ->invoke_method, var
}
c.walk :number { node |
val = node.value.to_f
r val, '', type: :number
}
c.walk :array { node, var = null |
true? node.nodes.empty?
{
set_result var, "array:new()", type: :array
}
{
res = set_result var
ares_var = res.var
temp_var = my.env.next_temp
out = "#{res.out}\ndo\nlocal #{temp_var}\n#{ares_var} = {}\n"
comp = my
node.nodes.each_with_index { n, i |
elem = comp.process n, temp_var
out << elem.out << "\n" <<
"#{ares_var}[#{i + 1}] = #{elem.var}\n"
}
out << "#{ares_var} = array:new(#{ares_var})\nend\n"
r ares_var, out, type: :array
}
}
c.walk :hash { node, var = null |
true? node.nodes.empty?
{
set_result var, "hash:new()", type: :hash
}
{
res = set_result var, "{}"
hres_var = res.var
key_temp = my.env.next_temp
val_temp = my.env.next_temp
out = "#{res.out}\ndo\nlocal #{key_temp};local #{val_temp}\n"
w = my
node.nodes.each { pair |
key = w.process pair.first, key_temp
val = w.process pair.last, val_temp
out << "#{key.out}\n#{val.out}\n#{hres_var}[#{key.var}] = #{val.var}\n"
}
my.env.unset key_temp
my.env.unset val_temp
out << "\n#{hres_var} = hash:new(#{hres_var})\nend\n"
r hres_var, out, type: :hash
}
}
c.walk :string { node, var = null |
true? node.nodes.empty?
{
set_result var, 'string:new("")', type: :string
}
{
set_result var, "string:new(\"#{escape_string node.value}\")", type: :string
}
}
c.walk :simple_string { node, var = null |
escaped = node.value.sub("\\[0a-z\"]", "\\%1").sub("\n", '\n')
set_result var, "string:new('#{escaped}')", type: :string
}
c.walk :simple_symbol { node, var = null |
escaped = node.value.sub("\\[0a-z\"]", "\\%1").sub("\n", '\n')
lift_string escaped, var
}
c.walk :symbol { node, var = null |
true? node.nodes.empty?
{
lift_string '', var
}
{
lift_string escape_string(node.value), var, :double
}
}
c.walk :function { node, var = null |
w = my
my.env.new_scope
args = do_args node
res = set_result var, "function(#{args[:arg_list]})"
res.out << args[:out]
body = node.body.copy
last = body.pop
body_res = body.map({ n | w.process(n, '_dummy').out }).join("\n")
res.out << body_res
true? last
{
last_res = my.process last
res.out << last_res.out << "\n" << "return #{last_res.var}\n"
}
{
true? body.empty?
{ res.out << "return object:null()" }
}
my.env.pop_scope
res.out << "\nend\n"
res[:type] = :function
my.env.set_type res.var, :function
res
}
c.walk :liftable_function { node, var = null |
w = my
my.env.new_scope
args = do_args node
res = true? node.upvars,
{ set_result "_lifted[#{my.next_liftable}]", "function(_argtable, #{args[:arg_list]})" }
{ set_result "_lifted[#{my.next_liftable}]", "function(#{args[:arg_list]})" } # just _self and real args
true? node.upvars
{
res.out << node.upvars.map({ u |
"local #{w.env[u]} = _argtable['#{w.env[u]}']"
}).join("\n")
}
res.out << args[:out]
body = node.body.copy
last = body.pop
body_res = body.map({ n | w.process(n, '_dummy').out }).join("\n")
res.out << body_res
true? last
{
last_res = my.process last
res.out << last_res.out << "\n" << "return #{last_res.var}\n"
}
{
true? body.empty?
{ res.out << "return object:null()" }
}
my.env.pop_scope
res.out << "\nend\n"
res[:type] = :function
my.liftable_functions << res
lifted_call = true? node.upvars
{
"_lifted_call(#{res.var}, {})"
}
{
res.var
}
lifted = set_result var, lifted_call
my.env.set_type lifted.var, :function
lifted[:type] = :function
true? node.upvars
{
e = my.env
lifted.out << node.upvars.map({ u |
"#{lifted.var}.arg_table['#{w.env[u]}'] = #{w.env[u]}"
}).join("\n")
}
lifted
}
c.walk :arg { node, var = null |
temp = my.env.new_var node.id
r temp, '', arg_type: :arg
}
c.walk :var_arg { node, var = null |
temp = my.env.new_var node.id
r '...', "local #{temp} = array:new(...)\n", arg_type: :var_arg
}
c.walk :def_arg { node, var = null |
temp = my.env.new_var node.id
rhs = process node.value, temp
out = "
if #{temp} == nil then
#{rhs.out}
"
false? rhs.var == temp
{ out << "#{temp} = #{rhs.var}\n" }
out << "end\n"
r temp, out, arg_type: :def_arg
}
c.walk :meth_access_local { node, var = null |
res = set_result var
res.out << get_method_local node.method, res.var
res
}
c.walk :meth_access_up { node, var = null |
res = set_result var
res.out << get_method_local node.method, res.var
res
}
c.walk :meth_access_self { node, var = null |
res = set_result var
res.out << get_method_self node.method, res.var
res
}
c.walk :meth_access { node, var = null |
res = set_result var
target = process node.target
res.out << target.out
res.out << get_method target.var, node.method, res.var
res
}
c.walk :regex { node, var = null |
body = node.body.sub("\\", "\\\\").sub(/"/, "\\\"")
opts = node.opts.dice.unique.join.sub(/m/, 's')
set_result var, "regex:new(\"#{body}\", \"#{opts}\")", type: :regex
}
c.walk :string_interp { node, var = null |
res = set_result var
temp = my.env.next_temp
res.out << "\ndo\nlocal #{temp} = {}\n"
w = my
node.nodes.each_with_index { n, i |
true? n.name == :string
{ res.out << "#{temp}[#{i + 1}] = \"#{w.escape_string n.value}\"\n" }
{
o = w.process n, "#{temp}[#{i + 1}]"
res.out << o.out
res.out << "#{temp}[#{i + 1}] = _tostring(#{o.var})\n"
}
}
res.out << "#{res.var} = string:new(_table.concat(#{temp}))\nend\n"
my.env.unset temp
my.env.set_type res.var, :string
res[:type] = :string
res
}
c.walk :string_eval { node, var = null |
res = set_result var
w = my
values = node.nodes.map { n | w.process(n, res.var) }
res.out << values.map(:out).join("\n")
true? res.var != values.last.var
{ res.out << "\n#{res.var} = #{values.last.var}\n" }
res
}
c.walk :binop { node, var = null |
res = reorder_ops node
process res
}
c.walk :invoke_numbers { node, var = null |
res = set_result var
res.out << invoke_numbers node.lhs.value, node.op, node.rhs.value, res.var
res
}
c.walk :invoke_number { node, var = null |
res = set_result var
rhs = process node.rhs
res.out << rhs.out << "\n"
res.out << invoke_number_lhs node.lhs.value, node.op, rhs.var, res.var
res
}
c.walk :invoke_number_rhs { node, var = null |
res = set_result var
lhs = process node.lhs
res.out << lhs.out << "\n"
res.out << invoke_number_rhs lhs.var, node.op, node.rhs.value, res.var
res
}
export c, :compiler