includes 'parser/walker' 'parser/sexp' 'parser/env' :set
a = walker.new
a.init = { ast, interactive = false |
my.ast = ast
my.functions = []
my.inner_functions = [[]]
my.env = env.new
my.interactive? = interactive
}
a.prototype.assign = {
my.walk_sexps(my.ast.nodes)
my.ast
}
a.prototype.set_upvar_assign = {
my.functions.each { f | f.upvar_assign? = true }
}
a.prototype.set_upvar_access = { access |
my.functions.each { f | f.upvar_access? = true }
f = my.functions.last
env_index = -1
var = true? (access.name == :invoke_up || { access.name == :meth_access_up })
{ access.method }
{ access.value }
not_found_local = true
c = my
c.set = set
my.functions.reverse_each_while { func |
false? func == f # skip current function
{
env_index = env_index - 1
false? c.env.variables[env_index][var]
{
null? func.upvars, { func.upvars = c.set.new }
func.upvars << var
}
{
not_found_local = false
}
}
not_found_local
}
null? f.upvars
{ f.upvars = c.set.new }
f.upvars << var
}
a.prototype.var_type = { var |
true? my.env.local_var?(var)
{ :local }
{
true? my.env[var]
{ :up }
{ :new }
}
}
a.unhandled = { node |
walk_sexps node.nodes
}
a.walk :var_assign { node |
var = node.lhs
t = var_type var
true? t == :local
{ node.name = "local_var_reassign" }
{
true? t == :up
{
node.name = "upvar_assign"
set_upvar_assign
}
{
true? (my.functions.empty? && my.interactive?)
{ node.name = "local_var_assign_interactive" }
{ node.name = "local_var_assign" }
my.env[var] = true
}
}
process node.rhs
}
a.prototype.check_potentials = {
false? my.interactive?
{
ps = my.inner_functions.pop
false? my.functions.empty?
{
true? ps.any?({ f | f.upvar_assign? })
{ ps.each { f | false? (f.upvar_access? || { f.upvar_assign? }) { f.name = :liftable_function }}}
{ ps.each { f | false? f.upvar_assign?, { f.name = :liftable_function }}}
}
}
}
a.walk :function { node |
my.env.new_scope
my.functions << node
my.inner_functions << []
node.upvar_access? = false
node.upvar_assign? = false
node.upvars = null
walk_sexps node.args
walk_sexps node.body
check_potentials
my.functions.pop
my.env.pop_scope
my.inner_functions.last << node
}
a.walk :call { node |
target = node.target
true? target
{ process node.target }
true? node.args
{ walk_sexps node.args }
meth = node.method
null? target
{
t = var_type meth
true? t == :local
{ node.name = :invoke_local }
{ true? t == :up
{ node.name = :invoke_up; set_upvar_access node }
{ node.name = :invoke_self }
}
}
}
a.walk :get_value { node |
t = var_type(node.value)
true? t == :local
{ node.name = :get_local_value }
{ true? t == :up
{ node.name = :get_up_value; set_upvar_access node }
}
}
a.walk :simple_index_get { node |
my.proc_call node
}
a.walk :simple_index_set { node |
my.proc_call node
}
a.walk :arg { node |
my.env[node.id] = true
}
a.walk :def_arg { node |
my.env[node.id] = true
process node.value
}
a.walk :var_arg { node |
my.env[node.id] = true
}
a.walk :meth_access { node |
null? node.target
{
t = var_type(node.method)
true? t == :local
{ node.name = :meth_access_local }
{ true? t == :up
{ node.name = :meth_access_up; set_upvar_access node }
{ node.name = :meth_access_self }
}
}
{
process node.target
}
}
export a, :var_assigner