include 'parser/sexp'
sh = import('parser/string_helper', :string_helper)
to_id = { name | sh.escape_identifier name }
node = object.new
node.prototype [
init: { name |
my.node_name = name
export my, name
}
to_s: { "<#{my.node_name}: #{my.matched}>" }
]
each_ast = { elements, block |
elements.each { e |
true? { e.has_method?(:ast)} { block e.ast }
{ true? e.has_method?(:elements) { each_ast e.elements, ->block } }
}
}
add_ast = { node, list |
each_ast node.elements, { ast |
true? ast
{ list << ast }
}
list
}
ast = { name, block |
n = node.new name
n.ast = ->block
}
ast :program {
out = s[:program]
add_ast my, out
}
ast :simple_exp {
a = null
each_ast my.elements, { ast | a = ast }
a
}
ast :bnumber {
s[:number text.to_f]
}
ast :empty_array {
s[:array]
}
ast :barray {
s[:array].concat inner.ast
}
ast :array_inner {
list = []
add_ast my, list
list
}
ast :empty_hash {
s[:hash]
}
ast :bhash {
s[:hash].concat inner.ast
}
ast :hash_arg {
[my.key.ast, my.value.ast]
}
ast :hash_key_arg {
[s[:simple_string my.key.text], my.value.ast]
}
ast :bregex {
s[:regex my.body.text, my.opts.text]
}
ast :simple_symbol {
s[:simple_symbol my.svalue.text]
}
ast :empty_symbol {
s[:symbol]
}
ast :double_symbol {
s[:symbol my.svalue.text]
}
ast :simple_string {
s[:simple_string my.svalue.text]
}
ast :empty_string {
s[:string]
}
ast :string_interp {
list = []
values.matched.each { e |
true? e.has_method?(:node_name) && { e.node_name == :interp_value }
{ list << e.ast }
{
# Combine consecutive strings into one string
true? sexp?(list.last) && { list.last.name == :string }
{ list.last.last << e.text }
{ list << s[:string e.text] }
}
}
# If just one simple string, return that
true? list.length == 1 && { list[0].name == :string }
{ list[0] }
{ true? list.length == 0
{ s[:string] }
{ s[:string_interp].concat list }
}
}
ast :interp_value {
value = s[:string_eval]
true? first, {
add_ast first, value
}
add_ast rest, value
}
ast :bfunction {
list = s[:function]
arg_list = []
add_ast args, arg_list
body_list = []
add_ast body, body_list
list << arg_list << body_list
}
ast :plain_arg {
s[:arg to_id my.text]
}
ast :default_arg {
s[:def_arg, to_id(my.arg_var.text), my.arg_val.ast]
}
ast :variable_args {
s[:var_arg to_id(my.arg_var.text)]
}
ast :unary_op {
list = s[:call]
add_ast my, list
list << to_id(my.elements[1].text)
}
ast :inner_arg_list {
list = []
add_ast my, list
reg_args = []
named_args = s[:hash]
list.each { arg |
true? arg.name == :named_arg
{ named_args << [arg.key, arg.value] }
{ reg_args << arg }
}
true? named_args.nodes.empty?
{ reg_args }
{ reg_args << named_args }
}
ast :named_argument {
a = s[:named_arg]
true? my.key.rule_name == :identifier
{ a << s[:string my.key.text] }
{ a << my.key.ast }
a << my.value.ast
}
ast :named_argument_call {
s[:named_arg, s[:get_value, my.key.text], my.value.ast]
}
ast :simple_call {
args = []
add_ast my.alist, args
var = to_id my.m_name.text
true? args.empty?
{
true? my.alist.text.empty? # simple call with no args and no parens
{ s[:get_value, var] }
{ s[:call, null, var] }
}
{ s[:call, null, var, args.first] }
}
ast :simpler_call {
args = []
add_ast my.alist, args
var = to_id my.m_name.text
true? args.empty?
{ s[:call, null, var] }
{ s[:call, null, var, args.first] }
}
ast :simplest_call {
s[:get_value, to_id(my.elements.first.text)]
}
ast :chain_call {
list = s[:call]
list << my.elements.first.ast
list << to_id(my.elements[1].text)
add_ast my.elements.last, list
}
ast :simple_meth_lhs {
args = []
index_args = []
add_ast my.args, args
true? my.has_method?(:index_args) {
add_ast my.index_args, index_args
}
var = to_id(my.elements.first.text)
lhs_call = true? args.empty?
{
true? my.args.text.empty? # simple call with no args and no parens
{ s[:get_value, var] }
{ s[:call, null, var] }
}
{ s[:call, null, var, args.first] }
false? index_args.empty?
{
lhs_call = s[:call, lhs_call, :get, index_args]
}
lhs_call
}
ast :array_index_lhs {
my.indexes.elements.reduce my.target.ast, { m, i |
add_ast i, s[:call, m, :get]
}
}
ast :access_meth_lhs {
s[:meth_access, null, to_id(my.var.text)]
}
ast :method_chain {
list = []
add_ast my, list
list.reduce { memo, call |
true? call.name == :get_value
{ c = s[:call]; c.nodes = call.nodes; c.nodes.insert(0, memo); call = c }
{ null? call.nodes.first
{ call.nodes[0] = memo }
{ true? call.name == :call && { call.method == :get }
{
#Call is treated as target instead of a call on the target
#and [] is on the result of the call instead of the call on
#the target. So flip things around.
args = call.nodes.pop
target = call.target
true? target.name == :call
{
call.nodes[1] = target.method
call.nodes[2] = target.args
}
{
call.nodes[1] = target.value
}
call.nodes[0] = memo
call = s[:call, call, :get, args]
}
{
p "wut #{call}"
}
}
}
call
}
}
ast :paren_call {
list = s[:invoke]
add_ast my, list
}
ast :func_lit_call {
list = s[:invoke_function]
add_ast my, list
}
ast :var_assign {
s[:var_assign to_id(my.var.text), my.elements.last.ast]
}
ast :field_assign {
list = s[:field_assign]
add_ast my, list
}
ast :field_access {
list = s[:field_access]
add_ast my, list
list << to_id(my.elements.last.text)
}
ast :binary_op {
list = s[:binop].concat my.elements.first.ast
list << my.elements.last.ast
}
ast :binary_op_chain {
list = []
my.elements.each { e |
list << e.elements.first.ast
list << to_id(e.elements[2].text)
}
list
}
ast :simple_meth_access {
s[:meth_access, null, to_id(my.meth.text)]
}
ast :simple_paren_meth_access {
list = s[:meth_access null]
add_ast my, list
}
ast :paren_meth_access {
list = s[:meth_access]
add_ast my, list
list << to_id(my.meth.text)
}
ast :meth_access {
list = s[:meth_access]
add_ast my, list
list << to_id(my.meth.text)
}
ast :index_get {
list = s[:call, my.elements.first.ast, :get]
index_args = add_ast my.gindexes, []
list << index_args.deq
list = index_args.reduce(list, { memo, index |
s[:call, memo, :get, index]
})
args = add_ast(my.iargs, []).first
true? list.args.length == 1 && { list.args.first.name == :simple_symbol || { list.args.first.name == :simple_string } }
{ list.name = :simple_index_get }
null? args
{ list }
{ s[:invoke_index_get, list, args] }
}
ast :index_set {
list = s[:call, my.elements.first.ast]
true? my.sindexes.elements.length > 1
{
list << :get
index_args = add_ast my.sindexes, []
list << [index_args.deq]
final_arg = index_args.pop
list = index_args.reduce(list, { memo, index |
s[:call, memo, :get, [index]]
})
value = my.elements.last.ast
s[:call, list, :set, [final_arg, value]]
}
{
args = add_ast my.sindexes, []
args << my.elements.last.ast
true? args.first.name == :simple_symbol || { args.first.name == :simple_string }
{ list.name = :simple_index_set }
list << :set << args
}
}