include "parser/sexp"
h = object.new
precedence = [
"_or_or" : 1,
"_and_and" : 2,
"_less_equal_greater" : 3,
"_equal_equal" : 3,
"_not_equal" : 3,
"_tilde" : 3,
"_bang_tilde" : 3,
"_less_equal" : 4,
"_greater_equal" : 4,
"_less" : 4,
"_greater" : 4,
"_less_less" : 5,
"_greater_greater" : 6,
"_plus" : 7,
"_minus" : 8,
"_star" : 9,
"_forward" : 9,
"_percent" : 9,
"_up" : 10
]
op_escape = [ "!" : "_bang",
"*" : "_star",
"-" : "_minus",
"+" : "_plus",
"|" : "_or",
"&" : "_and",
"@" : "_at",
"~" : "_tilde",
"^" : "_up",
"/" : "_forward",
"\\" : "_back",
"?" : "_question",
"<" : "_less",
">" : "_greater",
"=" : "_equal",
"%" : "_percent",
"_" : "_under",
"$" : "_dollar"
]
op_unescape = [ "bang" : "!",
"star" : "*",
"minus" : "-",
"plus" : "+",
"or" : "|" ,
"and" : "&",
"at" : "@",
"tilde" : "~",
"up" : "^",
"forward" : "/",
"back" : "\\\\",
"question" : "?",
"less" : "<",
"greater" : ">",
"notequal" : "!=",
"equal" : "=",
"percent" : "%",
"under" : "_",
"dollar" : "$"
]
ID_CONVERT_RE_OP = /_(bang|star|minus|plus|oror|or|andand|and|at|tilde|up|forward|back|question|less|greater|notequal|equal|percent|under|dollar)/
ID_CONVERT_RE_KW = /__(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)/
prec = { op |
precedence[op] || 0
}
h.unescape_op = { op |
op.sub ID_CONVERT_RE_OP, { x | op_unescape[x] }
}
h.number? = { exp |
sexp?(exp) && { exp.name == :number }
}
# Reorder binary operations to RPN using the shunting yard algorithm
h.reorder_ops = { node |
output = []
stack = []
w = my
node.nodes.each { n |
true? n.string?
{
o1 = n
o2 = null
while { o2 = stack.last; o2.string? && { prec(o1) <= prec(o2) }}
{ output.push stack.pop }
stack.push o1
}
{
true? sexp?(n) && { n.name == :binop }
{ output.push h.reorder_ops(n) }
{ output.push n }
}
}
until { stack.empty? }
{
output.push stack.pop
}
rewrite_binop rewrite_to_binops output
}
# Rewrite RPN to binary operations
h.rewrite_to_binops = { input |
stack = []
input.each { i |
true? i.string?
{
rhs = stack.pop
lhs = stack.pop
stack.push s[:binop, lhs, i, rhs]
}
{
stack.push i
}
}
stack.pop
}
native_ops = ["_percent", "_plus", "_minus", "_forward", "_star", "_up"]
compare_ops = ["_less", "_greater", "_equal_equal", "_less_equal", "_greater_equal"]
# Rewrite binary operations to method invocations,
# including special ones for numbers
h.rewrite_binop = { node |
true? sexp?(node) && { node.name == :binop }
{
lhs = rewrite_binop node.lhs
rhs = rewrite_binop node.rhs
true? h.number?(lhs)
{
true? h.number?(rhs)
{ s[:invoke_numbers, lhs, node.op, rhs] }
{ s[:invoke_number, lhs, node.op, rhs] }
}
{
n = my
true? (n.number?(rhs) && { native_ops.include?(node.op) || { compare_ops.include?(node.op) }})
{ s[:invoke_number_rhs, lhs, node.op, rhs] }
{ s[:call, lhs, node.op, [rhs]] }
}
}
{
node
}
}
export h, :binop_helper