# Copyright (C) 2007-2008, The Perl Foundation.
# $Id: /mirror/trunk/languages/lua/src/POSTGrammar.tg 35438 2009-01-11T12:18:24.831458Z fperrad  $

=head1 NAME

POSTGrammar.tg - A grammar for transforming the abstract syntax tree (PAST)
into an opcode syntax tree (POST).

=head1 DESCRIPTION


=cut

grammar Lua::POST::Grammar is TGE::Grammar;


transform post (PAST;Block) :language('PIR') {
    null $P0
    set_hll_global ['Lua';'POST'], '$?environ', $P0
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    new $P0, 'ResizableStringArray'
    set_hll_global ['Lua';'POST'], '@endlabels', $P0
    .local pmc outerpost
    outerpost = get_hll_global ['Lua';'POST'], '$?SUB'
    .local string name
    $S0 = node.'name'()
    unless $S0 == '' goto L1
    $S0 = 'anon'
  L1:
    .local pmc post
    $P0 = get_hll_global ['Lua';'POST'], 'Sub'
    post = $P0.'new'('node'=>node, 'outer'=>outerpost, 'pirflags'=>':anon :lex', 'blocktype'=>'declaration')
    $S1 = $S0 . '_'
    name = post.'unique'($S1)
    post.'name'(name)
    new $P0, 'Hash'
    post.'storage_lex'($P0)
    new $P0, 'Hash'
    post.'storage_const'($P0)
    $P0 = get_hll_global ['POST'], 'Ops'
    $P1 = $P0.'new'()
    post.'push'($P1)
    post.'ops_const'($P1)
    $P1 = $P0.'new'()
    post.'push'($P1)
    post.'ops_subr'($P1)
    set_hll_global ['Lua';'POST'], '$?SUB', post
    .local pmc iter, cpost
    iter = node.'iterator'()
  L2:
    unless iter goto L3
    $P0 = shift iter
    cpost = tree.'get'('post', $P0)
    post.'push'(cpost)
    goto L2
  L3:
    set_hll_global ['Lua';'POST'], '$?SUB', outerpost
    set_hll_global ['Lua';'POST'], '@endlabels', endlabels
    if null outerpost goto L4
    .local pmc ops_subr
    ops_subr = outerpost.'ops_subr'()
    $I0 = defined ops_subr
    unless $I0 goto L5
    ops_subr.'push_pirop'('inline', 'inline'=><<'PIRCODE')
    .local pmc subr
    subr = interpinfo .INTERPINFO_CURRENT_SUB
PIRCODE
    new $P0, 'Undef'
    outerpost.'ops_subr'($P0)
  L5:
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    ops.'push'(post)
    $S0 = post.'unique'('fct_')
    $S1 = post.'subid'()
    ops.'push_pirop'('inline', $S0, $S1, 'inline'=>'    .const "Sub" %0 = "%1"')
    .local string result
    result = ops.'unique'('$P')
    ops.'push_pirop'('newclosure', result, $S0)
    $S0 = ops.'unique'('$P')
    ops.'push_pirop'('callmethod', '"getfenv"', 'subr', 'result'=>$S0)
    ops.'push_pirop'('callmethod', '"setfenv"', result, $S0, 'result'=>'')
    ops.'result'(result)
    .return (ops)
  L4:
    .local pmc start
    $P0 = get_hll_global ['Lua'; 'POST'], 'Sub'
    start = $P0.'new'('name'=>'&start', 'pirflags'=>':anon :main')
    $S0 = post.'subid'()
    start.'push_pirop'('inline', $S0, 'inline'=><<'PIRCODE')
    .param pmc args :optional
#    print "start\n"
    load_bytecode "languages/lua/lua.pbc"
    lua_openlibs()
    .local pmc env
    env = get_hll_global "_G"
    .local pmc vararg
    vararg = argstolua(env, args)
    .const "Sub" main = "%0"
    main."setfenv"(env)
    ($I0, $P0) = docall(main, vararg :flat)
    unless $I0 goto L1
    printerr "luap: "
    printerr $P0
  L1:
PIRCODE
    post.'outer'(start)
    start.'push'(post)
    $P0 = get_hll_global ['Lua'; 'POST'], 'Chunk'
    .tailcall $P0.'new'(start, 'node'=>node, 'prologue'=><<'PIRCODE')
.include "interpinfo.pasm"
.HLL "lua"
.loadlib "lua_group"
PIRCODE
}


transform post (PAST;Stmts) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc iter
    iter = node.'iterator'()
  L1:
    unless iter goto L2
    $P0 = shift iter
    $P1 = tree.'get'('void', $P0)
    ops.'push'($P1)
    goto L1
  L2:
    null $P0
    set_hll_global ['Lua';'POST'], '$?environ', $P0
    .return (ops)
}


transform void (PAST;Stmts) :language('PIR') {
    .tailcall tree.'get'('post', node)
}


transform void (PAST;Op) :language('PIR') {
    $S0 = node.'pasttype'()
    .tailcall tree.'get'($S0, node)
}


transform cond (PAST;Op) :language('PIR') {
    $S0 = node.'pirop'()
    $I0 = index $S0, 'is'
    unless $I0 >= 0 goto L1
    .tailcall tree.'get'('cmp', node)
  L1:
    .tailcall tree.'get'('post', node)
}


transform post (PAST;Op) :language('PIR') {
    $S0 = node.'pasttype'()
    unless $S0 == 'call' goto L1
    .local pmc post
    post = tree.'get'($S0, node)
    $P0 = post.'pop'()
    .local string result
    result = post.'unique'('$P')
    post.'push_pirop'('new', result, '"LuaNil"')
    $P0.'result'(result)
    post.'push'($P0)
    post.'result'(result)
    .return (post)
  L1:
    .tailcall tree.'get'($S0, node)
}


transform pirop (PAST;Op) :language('PIR') {
    .local string result
    $S0 = node.'pirop'()
    $I0 = index $S0, 'is'
    unless $I0 >= 0 goto L1
    .local pmc post
    post = tree.'get'('cmp', node)
    $S0 = post.'result'()
    result = post.'unique'('$P')
    post.'push_pirop'('new', result, '"LuaBoolean"')
    post.'push_pirop'('set', result, $S0)
    post.'result'(result)
    .return (post)
  L1:
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc arglist, iter
    new arglist, 'ResizableStringArray'
    iter = node.'iterator'()
  L2:
    unless iter goto L3
    .local pmc cpost
    $P0 = shift iter
    cpost = tree.'get'('post', $P0)
    ops.'push'(cpost)
    $S1 = cpost.'result'()
    push arglist, $S1
    goto L2
  L3:
    result = ops.'unique'('$P')
    post = ops.'push_pirop'($S0, result, arglist :flat, 'node'=>node, 'result'=>result)
    ops.'result'(result)
    .return (ops)
}


transform cmp (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc expr1
    $P0 = node[0]
    expr1 = tree.'get'('post', $P0)
    ops.'push'(expr1)
    .local pmc expr2
    $P0 = node[1]
    expr2 = tree.'get'('post', $P0)
    ops.'push'(expr2)
    .local string pirop
    pirop = node.'pirop'()
    $S0 = ops.'unique'('$I')
    ops.'push_pirop'(pirop, $S0, expr1, expr2, 'node'=>node)
    ops.'result'($S0)
    .return (ops)
}


transform call (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc arglist, iter, cpost
    new arglist, 'ResizableStringArray'
    iter = node.'iterator'()
    .local int first
    first = 1
  L1:
    unless iter goto L2
    $P0 = shift iter
    cpost = tree.'get'('post', $P0)
    ops.'push'(cpost)
    $S0 = cpost.'result'()
    if first goto L3
    if iter goto L3
    ($I0, $P0) = cpost.'has_call_in_last_op'()
    unless $I0 goto L3
    $S1 = '(' . $S0
    $S1 .= ' :slurpy)'
    $P0.'result'($S1)
    $S0 .= ' :flat'
  L3:
    first = 0
    push arglist, $S0
    goto L1
  L2:
    ops.'push_pirop'('call', arglist :flat, 'node'=>node, 'result'=>'')
    null $P0
    set_hll_global ['Lua';'POST'], '$?environ', $P0
    .return (ops)
}


transform len (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc expr
    $P0 = node[0]
    expr = tree.'get'('post', $P0)
    ops.'push'(expr)
    .local string result
    result = ops.'unique'('$P')
    $S0 = ops.'escape'('len')
    ops.'push_pirop'('callmethod', $S0, expr, 'node'=>node, 'result'=>result)
    ops.'result'(result)
    .return (ops)
}


transform and (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string type
    type = node.'pasttype'()
    .local pmc expr1
    $P0 = node[0]
    expr1 = tree.'get'('post', $P0)
    ops.'push'(expr1)
    .local string label, endlabel
    $S1 = type . '_'
    label = ops.'unique'($S1)
    endlabel = label . '_end'
    $S0 = ops.'unique'('$P')
    unless type == 'and' goto L1
    ops.'push_pirop'('if', expr1, label)
    goto L2
  L1:
    ops.'push_pirop'('unless', expr1, label)
  L2:
    ops.'push_pirop'('clone', $S0, expr1)
    ops.'push_pirop'('goto', endlabel)
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>label)
    ops.'push'($P0)
    .local pmc expr2
    $P0 = node[1]
    expr2 = tree.'get'('post', $P0)
    ops.'push'(expr2)
    ops.'push_pirop'('clone', $S0, expr2)
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    ops.'result'($S0)
    null $P0
    set_hll_global ['Lua';'POST'], '$?environ', $P0
    .return (ops)
}


transform or (PAST;Op) :language('PIR') {
    .tailcall tree.'get'('and', node)
}


transform vararg (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string result
    result = ops.'unique'('$P')
    ops.'push_pirop'('new', result, '"LuaNil"')
    ops.'push_pirop'('call', 'mkarg', 'vararg', 'result'=>result)
    ops.'result'(result)
    .return (ops)
}


transform if (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string thenlabel, endlabel
    thenlabel = ops.'unique'('if_')
    endlabel = thenlabel . '_end'
    .local pmc expr
    $P0 = node[0]
    expr = tree.'get'('cond', $P0)
    ops.'push'(expr)
    ops.'push_pirop'('if', expr, thenlabel)
    .local pmc else
    $P0 = node[2]
    $I0 = defined $P0
    if $I0 == 0 goto L1
    else = tree.'get'('post', $P0)
    ops.'push'(else)
  L1:
    ops.'push_pirop'('goto', endlabel)
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>thenlabel)
    ops.'push'($P0)
    .local pmc then
    $P0 = node[1]
    $I0 = defined $P0
    if $I0 == 0 goto L2
    then = tree.'get'('post', $P0)
    ops.'push'(then)
  L2:
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    .return (ops)
}


transform while (PAST;Op) :language('PIR') {
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string looplabel, endlabel
    looplabel = ops.'unique'('while_')
    endlabel = looplabel . '_end'
    unshift endlabels, endlabel
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>looplabel)
    ops.'push'($P0)
    .local pmc expr
    $P0 = node[0]
    expr = tree.'get'('cond', $P0)
    ops.'push'(expr)
    ops.'push_pirop'('unless', expr, endlabel)
    .local pmc blk
    $P0 = node[1]
    blk = tree.'get'('post', $P0)
    ops.'push'(blk)
    ops.'push_pirop'('goto', looplabel)
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    $P0 = shift endlabels
    .return (ops)
}


transform repeat (PAST;Op) :language('PIR') {
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string looplabel, endlabel
    looplabel = ops.'unique'('repeat_')
    endlabel = looplabel . '_end'
    unshift endlabels, endlabel
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>looplabel)
    ops.'push'($P0)
    .local pmc blk
    $P0 = node[0]
    blk = tree.'get'('post', $P0)
    ops.'push'(blk)
    .local pmc expr
    $P0 = node[1]
    expr = tree.'get'('cond', $P0)
    ops.'push'(expr)
    ops.'push_pirop'('if', expr, endlabel)
    ops.'push_pirop'('goto', looplabel)
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    $P0 = shift endlabels
    .return (ops)
}


transform fornum (PAST;Op) :language('PIR') {
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc e_var, e_limit, e_step
    $P0 = node[1]
    $P1 = $P0[0]
    e_var = tree.'get'('post', $P1)
    ops.'push'(e_var)
    $P1 = $P0[1]
    e_limit = tree.'get'('post', $P1)
    ops.'push'(e_limit)
    $P1 = $P0[2]
    e_step = tree.'get'('post', $P1)
    ops.'push'(e_step)
    .local string loc_v
    $P0 = node[0]
    $P0 = tree.'get'('post', $P0)
    $P1 = $P0.'pop'()
    ops.'push'($P0)
    $P1 = $P0.'pop'()
    $P0.'push'($P1)
    loc_v = $P1.'result'()
    .local string var, limit, step
    var = ops.'unique'('$P')
    limit = ops.'unique'('$P')
    step = ops.'unique'('$P')
    ops.'push_pirop'('inline', var, limit, step, e_var, e_limit, e_step, 'inline'=>'    (%0, %1, %2) = checkforloop(%3, %4, %5)')
    .local string incr
    $S0 = ops.'unique'('$N')
    ops.'push_pirop'('set', $S0, step)
    incr = ops.'unique'('$I')
    ops.'push_pirop'('isgt', incr, $S0, '0.0')
    .local string looplabel, endlabel, orlabel, blklabel
    looplabel = ops.'unique'('fornum_')
    orlabel = looplabel . '_or'
    blklabel = looplabel . '_blk'
    endlabel = looplabel . '_end'
    unshift endlabels, endlabel
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>looplabel)
    ops.'push'($P0)
    ops.'push_pirop'('unless', incr, orlabel)
    ops.'push_pirop'('gt', var, limit, endlabel)
    ops.'push_pirop'('goto', blklabel)
    $P0 = $P1.'new'('result'=>orlabel)
    ops.'push'($P0)
    ops.'push_pirop'('lt', var, limit, endlabel)
    $P0 = $P1.'new'('result'=>blklabel)
    ops.'push'($P0)
    ops.'push_pirop'('clone', loc_v, var)
    .local pmc blk
    $P0 = node[2]
    blk = tree.'get'('post', $P0)
    ops.'push'(blk)
    ops.'push_pirop'('add', var, step)
    ops.'push_pirop'('goto', looplabel)
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    $P0 = shift endlabels
    .return (ops)
}


transform forlist (PAST;Op) :language('PIR') {
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc explist, rpost
    explist = node[1]
    .local string func, state, var
    .local pmc iter
    new iter, 'Iterator', explist
    $P0 = shift iter
    rpost = tree.'get'('post', $P0)
    ops.'push'(rpost)
    func = rpost.'result'()
    if iter goto L1
    $I0 = rpost.'has_call_in_last_op'()
    unless $I0 goto L1
    $P0 = rpost.'pop'()
    $S0 = '(' . func
    state = rpost.'unique'('$P')
    rpost.'push_pirop'('new', state, '"LuaNil"')
    $S0 .= ', '
    $S0 .= state
    var = rpost.'unique'('$P')
    rpost.'push_pirop'('new', var, '"LuaNil"')
    $S0 .= ', '
    $S0 .= var
    $S0 .= ')'
    $P0.'result'($S0)
    rpost.'push'($P0)
  L1:
    unless iter goto L4
    $P0 = shift iter
    rpost = tree.'get'('post', $P0)
    ops.'push'(rpost)
    state = rpost.'result'()
    if iter goto L2
    $I0 = rpost.'has_call_in_last_op'()
    unless $I0 goto L2
    $P0 = rpost.'pop'()
    $S0 = '(' . state
    var = rpost.'unique'('$P')
    rpost.'push_pirop'('new', var, '"LuaNil"')
    $S0 .= ', '
    $S0 .= var
    $S0 .= ')'
    $P0.'result'($S0)
    rpost.'push'($P0)
  L2:
    unless iter goto L4
    $P0 = shift iter
    rpost = tree.'get'('post', $P0)
    ops.'push'(rpost)
    var = rpost.'result'()
  L3:
    unless iter goto L4
    $P0 = shift iter
    rpost = tree.'get'('post', $P0)
    ops.'push'(rpost)
    goto L3
  L4:
    unless state == '' goto L5
    var = ops.'unique'('$P')
    ops.'push_pirop'('new', var, '"LuaNil"')
  L5:
    unless var == '' goto L6
    var = ops.'unique'('$P')
    ops.'push_pirop'('new', var, '"LuaNil"')
  L6:
    .local pmc namelist, lpost, tmp
    namelist = node[0]
    new iter, 'Iterator', namelist
    new tmp, 'ResizableStringArray'
  L7:
    unless iter goto L8
    $P0 = shift iter
    lpost = tree.'get'('post', $P0)
    lpost.'pop'()
    ops.'push'(lpost)
    $P1 = lpost.'pop'()
    lpost.'push'($P1)
    $S1 = $P1.'result'()
    push tmp, $S1
    goto L7
  L8:
    .local string looplabel, endlabel
    looplabel = ops.'unique'('forlist_')
    endlabel = looplabel . '_end'
    unshift endlabels, endlabel
    $P1 = get_hll_global ['POST'], 'Label'
    $P0 = $P1.'new'('result'=>looplabel)
    ops.'push'($P0)
    new iter, 'Iterator', tmp
    $S0 = '('
  L9:
    unless iter goto L10
    $S1 = shift iter
    ops.'push_pirop'('new', $S1, '"LuaNil"')
    if $S0 == '(' goto L11
    $S0 .= ', '
  L11:
    $S0 .= $S1
    goto L9
  L10:
    $S0 .= ')'
    ops.'push_pirop'('call', func, state, var, 'node'=>node, 'result'=>$S0)
    $S0 = tmp[0]
    ops.'push_pirop'('assign', var, $S0)
    $S0 = ops.'unique'('$I')
    ops.'push_pirop'('isa', $S0, var, '"LuaNil"')
    ops.'push_pirop'('if', $S0, endlabel)
    .local pmc blk
    $P0 = node[2]
    blk = tree.'get'('post', $P0)
    ops.'push'(blk)
    ops.'push_pirop'('goto', looplabel)
    $P0 = $P1.'new'('result'=>endlabel)
    ops.'push'($P0)
    $P0 = shift endlabels
    .return (ops)
}


transform return (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc arglist
    new arglist, 'ResizableStringArray'
    .local pmc iter, cpost
    iter = node.'iterator'()
  L1:
    unless iter goto L2
    $P0 = shift iter
    cpost = tree.'get'('post', $P0)
    ops.'push'(cpost)
    $S0 = cpost.'result'()
    if iter goto L3
    ($I0, $P0) = cpost.'has_call_in_last_op'()
    unless $I0 goto L3
    if arglist goto L4
    $P0.'pirop'('tailcall')
    # remove useless new LuaNil
    $P1 = cpost.'pop'()
    $P2 = cpost.'pop'()
    cpost.'push'($P1)
    .return (ops)
  L4:
    $S1 = '(' . $S0
    $S1 .= ' :slurpy)'
    $P0.'result'($S1)
    $S0 .= ' :flat'
  L3:
    push arglist, $S0
    goto L1
  L2:
    ops.'push_pirop'('return', arglist :flat)
    .return (ops)
}


transform break (PAST;Op) :language('PIR') {
    .local pmc endlabels
    endlabels = get_hll_global ['Lua';'POST'], '@endlabels'
    $S0 = endlabels[0]
    $P0 = get_hll_global ['POST'], 'Op'
    .tailcall $P0.'new'($S0, 'node'=>node, 'pirop'=>'goto')
}


transform assign (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc rpost
    $P0 = node[1]
    rpost = tree.'get'('post', $P0)
    .local string val
    val = rpost.'result'()
    ops.'push'(rpost)
    .local pmc lpost
    $P0 = node[0]
    lpost = tree.'get'('post', $P0)
    .local pmc ass
    ass = lpost.'pop'()
    ops.'push'(lpost)
    ass.'push'(val)
    ops.'push'(ass)
    .return (ops)
}


transform assignlist (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc namelist, explist
    namelist = node[0]
    explist = node[1]
    .local pmc iter, tmp, rpost
    new tmp, 'ResizableStringArray'
    new iter, 'Iterator', explist
  L1:
    unless iter goto L2
    $P0 = shift iter
    rpost = tree.'get'('post', $P0)
    ops.'push'(rpost)
    $S1 = rpost.'result'()
    $S2 = ops.'unique'('$P')
    ops.'push_pirop'('clone', $S2, $S1)
    push tmp, $S2
    if iter goto L1
    $I0 = rpost.'has_call_in_last_op'()
    unless $I0 goto L1
    $I1 = namelist
    $I2 = explist
    $I1 -= $I2
    unless $I1 goto L1
    $S0 = '(' . $S1
    $P0 = rpost.'pop'()
  L3:
    unless $I1 > 0 goto L4
    $S1 = ops.'unique'('$P')
    rpost.'push_pirop'('new', $S1, '"LuaNil"')
    push tmp, $S1
    $S0 .= ', '
    $S0 .= $S1
    dec $I1
    goto L3
  L4:
    $S0 .= ')'
    $P0.'result'($S0)
    rpost.'push'($P0)
  L2:
    .local pmc ass, lpost
    $P0 = get_hll_global ['POST'], 'Ops'
    ass = $P0.'new'('node'=>node)
    new iter, 'Iterator', namelist
  L5:
    unless iter goto L6
    $P0 = shift iter
    unless tmp goto L7
    $S1 = shift tmp
    goto L8
  L7:
    $S1 = ops.'unique'('$P')
    ops.'push_pirop'('new', $S1, '"LuaNil"')
  L8:
    lpost = tree.'get'('post', $P0)
    $P0 = lpost.'pop'()
    $P0.'push'($S1)
    ops.'push'(lpost)
    ass.'push'($P0)
    goto L5
  L6:
    ops.'push'(ass)
    .return (ops)
}


transform parenthese (PAST;Op) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc expr
    $P0 = node[0]
    expr = tree.'get'('post', $P0)
    ops.'push'(expr)
    ops.'result'(expr)
    .return (ops)
}


transform cond (PAST;Val) :language('PIR') {
    .tailcall tree.'get'('post', node)
}


transform post (PAST;Val) :language('PIR') {
    .local string type
    type = node.'name'()
    $I0 = index type, 'Lua'
    if $I0 < 0 goto L1
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string result
    result = ops.'unique'('$P')
    $S0 = ops.'escape'(type)
    ops.'push_pirop'('new', result, $S0)
    .local string value
    value = node.'value'()
    if value == '' goto L2
    unless type == 'LuaString' goto L3
    value = ops.'escape'(value)
  L3:
    ops.'push_pirop'('set', result, value)
  L2:
    ops.'result'(result)
    .return (ops)
  L1:
    .tailcall tree.'get'(type, node)
}


transform key (PAST;Val) :language('PIR') {
    .local pmc subpost
    subpost = get_hll_global ['Lua';'POST'], '$?SUB'
    .local pmc storage_const
    storage_const = subpost.'storage_const'()
    .local string name
    name = node.'value'()
    $I0 = exists storage_const[name]
    if $I0 goto L1
    .local pmc ops_const
    ops_const = subpost.'ops_const'()
    $S0 = 'k_' . name
    $S1 = ops_const.'escape'(name)
    $P0 = ops_const.'push_pirop'('inline', $S0, $S1, 'inline'=>'    .const "LuaString" %0 = %1')
    storage_const[name] = $S0
  L1:
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    $S0 = storage_const[name]
    ops.'result'($S0)
    .return (ops)
}


transform constructor (PAST;Val) :language('PIR') {
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string table
    table = ops.'unique'('$P')
    ops.'push_pirop'('new', table, '"LuaTable"')
    ops.'result'(table)
    .local pmc iter, kpost, vpost
    .local string key, result
    .local int i
    i = 1
    iter = node.'iterator'()
  L1:
    unless iter goto L2
    $P0 = shift iter
    $I0 = does $P0, 'array'
    if $I0 goto L3
    $P2 = get_hll_global ['POST'], 'Ops'
    kpost = $P2.'new'('node'=>node)
    result = kpost.'unique'('$P')
    kpost.'push_pirop'('new', result, '"LuaNumber"')
    $S0 = i
    inc i
    kpost.'push_pirop'('set', result, $S0)
    kpost.'result'(result)
    vpost = tree.'get'('post', $P0)
    if iter goto L4
    $I0 = vpost.'has_call_in_last_op'()
    unless $I0 goto L4
    ops.'push'(kpost)
    $P0 = vpost.'pop'()
    vpost.'push'($P0)
    $S0 = $P0.'result'()
    $S1 = '(' . $S0
    $S1 .= ' :slurpy)'
    $P0.'result'($S1)
    ops.'push'(vpost)
    $S0 .= ' :flat'
    $P1 = ops.'push_pirop'('call', 'tconstruct', table, kpost, $S0)
    $P1.'result'($S1)
    goto L2
  L3:
    $P1 = $P0[0]
    kpost = tree.'get'('post', $P1)
    $P1 = $P0[1]
    vpost = tree.'get'('post', $P1)
  L4:
    ops.'push'(kpost)
    ops.'push'(vpost)
    key = clone table
    key .= '['
    $S0 = kpost.'result'()
    key .= $S0
    key .= ']'
    ops.'push_pirop'('set', key, vpost)
    goto L1
  L2:
    .return (ops)
}


transform cond (PAST;Var) :language('PIR') {
    .tailcall tree.'get'('post', node)
}


transform post (PAST;Var) :language('PIR') {
    $S0 = node.'scope'()
    .tailcall tree.'get'($S0, node)
}


transform parameter (PAST;Var) :language('PIR') {
    .local pmc subpost
    subpost = get_hll_global ['Lua';'POST'], '$?SUB'
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string name
    name = node.'name'()
    $I0 = node.'slurpy'()
    unless $I0 goto L1
    subpost.'add_param'(name, 'slurpy'=>1)
    .return (ops)
  L1:
    .local pmc storage_lex
    storage_lex = subpost.'storage_lex'()
    .local string pname
    pname = 'param_' . name
    subpost.'add_param'(pname, 'optional'=>1)
    $S0 = ops.'escape'(name)
    ops.'push_pirop'('.lex', $S0, pname)
    .local string vivilabel
    vivilabel = ops.'unique'('vivify_')
    $S0 = concat 'has_', pname
    ops.'push_pirop'('if', $S0, vivilabel)
    ops.'push_pirop'('new', pname, '"LuaNil"')
    $P0 = get_hll_global ['POST'], 'Label'
    $P0 = $P0.'new'('result'=>vivilabel)
    ops.'push'($P0)
    ops.'result'(pname)
    storage_lex[name] = pname
    .return (ops)
}


transform lexical (PAST;Var) :language('PIR') {
    .local pmc subpost
    subpost = get_hll_global ['Lua';'POST'], '$?SUB'
    .local pmc storage_lex
    storage_lex = subpost.'storage_lex'()
    .local pmc ops
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local string name, lname
    name = node.'name'()
    lname = ops.'escape'(name)
    $I0 = node.'lvalue'()
    if $I0 goto L1
    $I0 = exists storage_lex[name]
    if $I0 goto L2
    .local string result
    result = ops.'unique'('$P')
    ops.'push_pirop'('find_lex', result, lname)
    ops.'result'(result)
    .return (ops)
  L2:
    $S0 = storage_lex[name]
    ops.'result'($S0)
    .return (ops)
  L1:
    $I0 = node.'isdecl'()
    unless $I0 goto L3
    $I0 = exists storage_lex[name]
    if $I0 goto L3
    .local string pname
    pname = 'var_' . name
    ops.'push_pirop'('.local pmc', pname)
    ops.'push_pirop'('.lex', lname, pname, 'result'=>pname)
    storage_lex[name] = pname
    ops.'push_pirop'('set', pname, 'node'=>node)
    ops.'result'(pname)
    .return (ops)
  L3:
    ops.'push_pirop'('store_lex', lname, 'node'=>node)
    ops.'result'(lname)
    .return (ops)
}


transform package (PAST;Var) :language('PIR') {
    .local pmc ops
    .local string result
    $P0 = get_hll_global ['POST'], 'Ops'
    ops = $P0.'new'('node'=>node)
    .local pmc basepost, keypost
    $S0 = node.'scope'()
    unless $S0 == 'keyed' goto L1
    $P0 = node[0]
    basepost = tree.'get'('post', $P0)
    ops.'push'(basepost)
    $P0 = node[1]
    keypost = tree.'get'('post', $P0)
    ops.'push'(keypost)
    goto L2
  L1:
    .local pmc subpost
    subpost = get_hll_global ['Lua';'POST'], '$?SUB'
    basepost = get_hll_global ['Lua';'POST'], '$?environ'
    unless null basepost goto L3
    .local pmc ops_subr
    ops_subr = subpost.'ops_subr'()
    $I0 = defined ops_subr
    unless $I0 goto L4
    ops_subr.'push_pirop'('inline', 'inline'=><<'PIRCODE')
    .local pmc subr
    subr = interpinfo .INTERPINFO_CURRENT_SUB
PIRCODE
    new $P0, 'Undef'
    subpost.'ops_subr'($P0)
  L4:
    result = ops.'unique'('$P')
    basepost = ops.'push_pirop'('callmethod', '"getfenv"', 'subr', 'result'=>result)
    set_hll_global ['Lua';'POST'], '$?environ', basepost
  L3:
    .local pmc storage_const
    storage_const = subpost.'storage_const'()
    .local string name
    name = node.'name'()
    $I0 = exists storage_const[name]
    if $I0 goto L5
    .local pmc ops_const
    ops_const = subpost.'ops_const'()
    $S0 = 'k_' . name
    $S1 = ops_const.'escape'(name)
    $P0 = ops_const.'push_pirop'('inline', $S0, $S1, 'inline'=>'    .const "LuaString" %0 = %1')
    storage_const[name] = $S0
  L5:
    $P0 = get_hll_global ['POST'], 'Ops'
    keypost = $P0.'new'('node'=>node)
    $S0 = storage_const[name]
    keypost.'result'($S0)
  L2:
    .local string key
    key = basepost.'result'()
    key .= '['
    $S0 = keypost.'result'()
    key .= $S0
    key .= ']'
    ops.'result'(key)
    $I0 = node.'lvalue'()
    if $I0 goto L6
    result = ops.'unique'('$P')
    ops.'push_pirop'('set', result, key, 'result'=>result)
    ops.'result'(result)
    .return (ops)
  L6:
    ops.'push_pirop'('set', key, 'node'=>node)
    ops.'result'(key)
    .return (ops)
}


transform keyed (PAST;Var) :language('PIR') {
    .tailcall tree.'get'('package', node)
}

