grammar POST::Grammar is TGE::Grammar;

transform root (PAST::Block) :language('PIR') {
    .local pmc post
    post = tree.'get'('post', node)
    $I0 = isa post, 'POST::Sub'
    if $I0 goto end
    $S0 = post.'unique'('__anon_')
    post = post.'new'('POST::Sub', post, 'name'=>$S0)
  end:
    .return (post)
}


transform post (PAST::Block) :language('PIR') {
    .local string name, pragma, blocktype
    name = node.'name'()
    if name != '' goto with_name
    name = node.unique('_block')
  with_name:
    pragma = node.'pragma'()
    blocktype = node.'blocktype'()
    .local pmc outerpost
    outerpost = get_hll_global ['POST'], '$?SUB'
    .local pmc post
    post = new 'POST::Sub'
    post.'init'('node'=>node, 'name'=>name, 'outer'=>outerpost, 'pragma'=>pragma, 'blocktype'=>blocktype)
    set_hll_global ['POST'], '$?SUB', post
    .local string compiler
    compiler = node.'compiler'()
    if compiler goto hll_block
  past_block:
    .local pmc iter
    iter = node.'iterator'()
    unless iter goto iter_end
  iter_loop:
    .local pmc cpast, cpost
    cpast = shift iter
    cpost = tree.'get'('post', cpast)
    post.'push'(cpost)
    if iter goto iter_loop
    post.'result'(cpost)
  iter_end:
    goto end

  hll_block:
    post.'compiler'(compiler)
    iter = node.'iterator'()
  hll_loop:
    unless iter goto end
    $P0 = shift iter
    post.'push'($P0)
    goto hll_loop

  end:
    set_hll_global ['POST'], '$?SUB', outerpost
    if blocktype != 'immediate' goto blocktype_end
    post = post.'new'('POST::Op', post, 'node'=>node, 'pirop'=>'call', 'arglist'=>post)
  blocktype_end:
    .return (post)
}


transform post (PAST::Stmts) :language('PIR') {
    .local pmc ops
    ops =  new 'POST::Ops'
    ops.'init'('node'=>node)
    .local pmc iter
    iter = node.'iterator'()
    unless iter goto end
  iter_loop:
    .local pmc cpast, cpost
    cpast = shift iter
    cpost = tree.'get'('post', cpast)
    ops.'push'(cpost)
    if iter goto iter_loop
  iter_end:
    ops.'result'(cpost)
  end:
    .return (ops)
}


transform post (PAST::Op) :language('PIR') {
    .local string pasttype
    pasttype = node.'pasttype'()
    if pasttype goto post_pasttype

  post_pirop:
    .local string pirop
    pirop = node.'pirop'()
    if pirop goto have_pirop
    node.'pirop'('call')
  have_pirop:
    .return tree.'get'('pirop', node)

  post_pasttype:
    .return tree.'get'(pasttype, node)
}


transform pirop (PAST::Op) :language('PIR') {
    .local string pirop
    .local pmc post
    pirop = node.'pirop'()
    post = new 'POST::Op'
    post.'init'('node'=>node, 'pirop'=>pirop)

    .local pmc pirtable
    .local string signature
    pirtable = get_hll_global ['POST'], '%pirtable'
    signature = pirtable[pirop]
    if signature > '' goto have_signature
    signature = '%uppppppppppppp'                          # FIXME:
  have_signature:
    .local pmc arglist, iter
    .local int aindex
    aindex = 2
    arglist = new .ResizablePMCArray
    iter = node.'iterator'()
  iter_loop:
    unless iter goto iter_end
    .local pmc cpast, cpost
    cpast = shift iter
    $S0 = substr signature, aindex, 1
    cpast.'atype'($S0)
    cpost = tree.'get'('post', cpast)
    post.'push'(cpost)
    push arglist, cpost
    inc aindex
    goto iter_loop
  iter_end:
    .local int argresult
    argresult = 1

    if pirop == 'call' goto pirop_call
    if pirop == 'callmethod' goto pirop_call
    if pirop == 'inline' goto pirop_inline
    goto pirop_result

  pirop_call:
    .local string name
    argresult = 0
    name = node.'name'()
    unless name goto pirop_result
    name = node.'escape'(name)
    unshift arglist, name
    goto pirop_result

  pirop_inline:
    .local string inline
    argresult = 0
    inline = node.'inline'()
    unless inline goto pirop_result
    unshift arglist, inline
    signature = inline
    goto pirop_result

  pirop_result:
    $I0 = index signature, '%v'
    if $I0 >= 0 goto pirop_void
    $I0 = index signature, '%r'
    if $I0 >= 0 goto pirop_reg
    .local string returns
    returns = '.Undef'
    $S0 = node.'returns'()
    unless $S0 goto pirop_returns
    returns = $S0
  pirop_returns:
    $P0 = post.'push_new'('POST::Op', 'pirop'=>'new')
    $P0.'arglist'($P0, returns)
    post.'result'($P0)
    goto pirop_reg
  pirop_void:
    $P0 = post[0]
    post.'result'($P0)
    argresult = 0
  pirop_reg:
    unless argresult goto have_result
    unshift arglist, post
  have_result:
    post.'arglist'(arglist :flat)
    .return (post)
}


transform call (PAST::Op) :language('PIR') {
    node.'pirop'('call')
    $P0 = tree.'get'('pirop', node)
    .return ($P0)
}


transform callmethod (PAST::Op) :language('PIR') {
    node.'pirop'('callmethod')
    $P0 = tree.'get'('pirop', node)
    .return ($P0)
}


transform inline (PAST::Op) :language('PIR') {
    node.'pirop'('inline')
    .return tree.'get'('pirop', node)
}


transform if (PAST::Op) :language('PIR') {
    .local pmc ops
    ops = new 'POST::Ops'
    ops.'init'('node'=>node)

    .local pmc exprpast, thenpast, elsepast
    .local pmc exprpost, thenpost, elsepost
    exprpast = node[0]
    thenpast = node[1]
    elsepast = node[2]

    .local pmc thenlabel, endlabel
    .local string pasttype, n
    pasttype = node.'pasttype'()
    n = node.'unique'('_')
    $S0 = concat pasttype, n
    thenlabel = ops.'new'('POST::Label', 'result'=>$S0)
    $S0 = concat $S0, '_end'
    endlabel = ops.'new'('POST::Label', 'result'=>$S0)

    exprpost = tree.'get'('post', exprpast)
    ops.'push'(exprpost)
    ops.'push_pirop'(pasttype, exprpost, thenlabel)
    elsepost = exprpost
    $I0 = defined elsepast
    if $I0 == 0 goto else_done
    elsepost = tree.'get'('post', elsepast)
    ops.'push'(elsepost)
  else_done:
    ops.'push_pirop'('set', ops, elsepost)
    ops.'push_pirop'('goto', endlabel)
    ops.'push'(thenlabel)
    thenpost = exprpost
    $I0 = defined thenpast
    if $I0 == 0 goto then_done
    thenpost = tree.'get'('post', thenpast)
    ops.'push'(thenpost)
  then_done:
    ops.'push_pirop'('set', ops, thenpost)
    ops.'push'(endlabel)
    .return (ops)
}


transform unless (PAST::Op) :language('PIR') {
    .return tree.'get'('if', node)
}


transform while (PAST::Op) :language('PIR') {
    .local pmc ops
    ops = new 'POST::Ops'
    ops.'init'('node'=>node)

    .local pmc exprpast, blockpast
    .local pmc exprpost, blockpost
    exprpast = node[0]
    blockpast = node[1]

    .local pmc looplabel, endlabel
    .local string pasttype, n
    pasttype = node.'pasttype'()
    n = node.'unique'('_')
    $S0 = concat pasttype, n
    looplabel = ops.'new'('POST::Label', 'result'=>$S0)
    $S0 = concat $S0, '_end'
    endlabel = ops.'new'('POST::Label', 'result'=>$S0)

    ##   determine if we need an 'if' or an 'unless' on
    ##   the conditional (while => if, until => unless)
    .local string iftype
    iftype = 'if'
    if $S0 == 'until' goto done_iftype
    iftype = 'unless'
  done_iftype:

    ops.'push'(looplabel)
    exprpost = tree.'get'('post', exprpast)
    ops.'push'(exprpost)
    ops.'push_pirop'(iftype, exprpost, endlabel)
    blockpost = tree.'get'('post', blockpast)
    ops.'push'(blockpost)
    ops.'push_pirop'('goto', looplabel)
    ops.'push'(endlabel)
    ops.'result'(exprpost)
    .return (ops)
}


transform until (PAST::Op) :language('PIR') {
    .return tree.'get'('while', node)
}


transform try (PAST::Op) :language('PIR') {
    .local pmc trypast, trypost, post, traplabel, endlabel
    trypast = node[0]
    trypost = tree.'get'('post', trypast)
    post = trypost.'new'('POST::Ops', 'node'=>node, 'result'=>trypost)
    $S0 = node.'unique'('trap_')
    traplabel = post.'new'('POST::Label', 'result'=>$S0)
    $S0 = concat $S0, '_end'
    endlabel = post.'new'('POST::Label', 'result'=>$S0)
    post.'push_pirop'('push_eh', traplabel)
    post.'push'(trypost)
    post.'push_pirop'('clear_eh')
    post.'push_pirop'('goto', endlabel)
    post.'push'(traplabel)
    .local pmc catchnode, catchpast
    catchnode = node[1]
    unless catchnode goto have_catch
    catchpast = tree.'get'('post', catchnode)
    post.'push'(catchpast)
  have_catch:
    post.'push'(endlabel)
    .return (post)
}


transform xor (PAST::Op) :language('PIR') {
    .local pmc ops
    ops = new 'POST::Ops'
    ops.'init'('node'=>node)

    .local pmc endlabel, falselabel
    falselabel = ops.'new'('POST::Label', 'name'=>'xor_false')
    endlabel = ops.'new'('POST::Label', 'name'=>'xor_end')

    .local pmc iter, apast, apost, i, t, u
    i = ops.unique('$I')
    t = ops.unique('$I')
    u = ops.unique('$I')
    iter = node.'iterator'()
    apast = shift iter
    apost = tree.'get'('post', apast)
    ops.'push'(apost)
    ops.'push_pirop'('set', ops, apost)
    ops.'push_pirop'('istrue', t, apost)
  middle_child:
    .local pmc bpast, bpost
    bpast = shift iter
    bpost = tree.'get'('post', bpast)
    ops.'push'(bpost)
    ops.'push_pirop'('istrue', u, bpost)
    ops.'push_pirop'('and', i, t, u)
    ops.'push_pirop'('if', i, falselabel)
    unless iter goto last_child
    .local pmc s
    s = ops.'new'('POST::Label', 'name'=>'xor_skip')
    ops.'push_pirop'('if', t, s)
    ops.'push_pirop'('set', ops, bpost)
    ops.'push_pirop'('set', t, u)
    ops.'push'(s)
    goto middle_child
  last_child:
    ops.'push_pirop'('if', t, endlabel)
    ops.'push_pirop'('set', ops, bpost)
    ops.'push_pirop'('goto', endlabel)
    ops.'push'(falselabel)
    ops.'push_pirop'('new', ops, '.Undef')
    ops.'push'(endlabel)
    .return (ops)
}


transform bind (PAST::Op) :language('PIR') {
    .local pmc lpast, rpast, lpost, rpost, post
    rpast = node[1]
    rpost = tree.'get'('post', rpast)
    lpast = node[0]
    lpast.'bindvalue'(rpost)
    lpost = tree.'get'('bind', lpast)
    post = lpost.'new'('POST::Ops', rpost, lpost, 'node'=>node)
    .return (post)
}


transform assign (PAST::Op) :language('PIR') {
    .local pmc lpast, rpast, lpost, rpost, post, alabel
    rpast = node[1]
    rpost = tree.'get'('post', rpast)
    lpast = node[0]
    lpost = tree.'get'('post', lpast)
    post = lpost.'new'('POST::Ops', rpost, lpost, 'node'=>node, 'result'=>lpost)
    alabel = post.'new'('POST::Label', 'name'=>'assign_')
    post.'push_pirop'('eq_addr', lpost, rpost, alabel)
    post.'push_pirop'('morph', lpost, '.Undef')
    post.'push_pirop'('assign', lpost, rpost)
    post.'push'(alabel)
    .return (post)
}


transform chain (PAST::Op) :language('PIR') {
    .local pmc clist, cpast
    clist = new .ResizablePMCArray
    cpast = node
  chain_loop:
    $I0 = isa cpast, 'PAST::Op'
    if $I0 == 0 goto chain_end
    .local string pasttype
    pasttype = cpast.'pasttype'()
    if pasttype != 'chain' goto chain_end
    push clist, cpast
    cpast = cpast[0]
    goto chain_loop
  chain_end:

    .local pmc ops, endlabel, apast, apost
    ops = new 'POST::Ops'
    ops.'init'('node'=>node)
    cpast = pop clist
    endlabel = ops.'new'('POST::Label', 'name'=>'chain_end_')
    apast = cpast[0]
    apost = tree.'get'('post', apast)
    ops.'push'(apost)

  clist_loop:
    .local pmc bpast, bpost
    bpast = cpast[1]
    bpost = tree.'get'('post', bpast)
    .local string name
    name = cpast.'name'()
    name = cpast.'escape'(name)
    ops.'push'(bpost)
    $P0 = ops.'push_pirop'('call', name, apost, bpost)
    $P0.'result'(ops)
    unless clist goto clist_end
    ops.'push_pirop'('unless', ops, endlabel)
    cpast = pop clist
    apost = bpost
    goto clist_loop
  clist_end:
    ops.'push'(endlabel)
    .return (ops)
}


transform post (PAST::Val) :language('PIR') {
    .local string vtype, ctype, name
    vtype = node.'vtype'()
    ctype = node.'ctype'()
    name = node.'name'()

    $I0 = index ctype, '~'
    if $I0 < 0 goto skip_escape
    name = node.'escape'(name)
  skip_escape:

    .local pmc post
    post = new 'POST::Ops'
    post.'init'('node'=>node)

    .local string atype
    atype = node.'atype'()
    if ctype == '' goto return_pmc
    if atype == '*' goto return_native
    $I0 = index ctype, atype
    if $I0 >= 0 goto return_native

  return_pmc:
    $P0 = post.'push_new'('POST::Op', 'pirop'=>'new')
    $P0.'arglist'($P0, vtype)
    $P1 = post.'push_pirop'('assign', $P0, name, 'result'=>$P0)
    post.'result'($P1)
    .return (post)

  return_native:
    post.'result'(name)
    .return (post)
}


transform keypost (PAST::Var) :language('PIR') {
    .local pmc post
    post = new 'POST::Ops'
    post.'init'('node'=>node)
    .local pmc basepast, keypast
    .local pmc basepost, keypost
    basepast = node[0]
    keypast = node[1]
    keypast.'atype'('*')
    keypost = tree.'get'('post', keypast)
    post.'push'(keypost)
    basepost = tree.'get'('post', basepast)
    post.'push'(basepost)
    .local string key
    key = basepost.'result'()
    key = concat key, '['
    $S0 = keypost.'result'()
    key .= $S0
    key .= ']'
    post.'result'(key)
    .return (post)
}


transform bind (PAST::Var) :language('PIR') {
    .local pmc bindvalue
    .local string scope
    bindvalue = node.'bindvalue'()
    scope = node.'scope'()
    if scope == 'keyed' goto keyed

    .local string name
    name = node.'name'()
    name = node.'escape'(name)
    .local pmc post
    post = new 'POST::Op'
    post.'init'('node'=>node, 'result'=>bindvalue, 'pirop'=>'set_global')
    post.'arglist'(name, bindvalue)
    if scope == 'lexical' goto lexical
    .return (post)

  lexical:
    .local int isdecl
    isdecl = node.'ismy'()
    if isdecl goto lexical_decl
    post.'pirop'('store_lex')
    .return (post)

  lexical_decl:
    post.'pirop'('.lex')
    .return (post)

  keyed:
    .local pmc post, keypost
    keypost = tree.'get'('keypost', node)
    post = keypost.'new'('POST::Op', keypost, 'node'=>node, 'pirop'=>'set', 'result'=>bindvalue)
    post.'arglist'(keypost, bindvalue)
    .return (post)
}


transform vivipost (PAST::Var) :language('PIR') {
    .local pmc vivipost, vivilabel
    .local string viviself
    viviself = node.'viviself'()
    if viviself goto have_viviself
    viviself = '.Undef'
  have_viviself:
    vivipost = new 'POST::Op'
    vivipost.'init'('node'=>node, 'pirop'=>'new', 'result'=>vivipost)
    vivipost.'arglist'(vivipost, viviself)
    .return (vivipost)
}


transform post (PAST::Var) :language('PIR') {
    .local pmc vivipost, vivilabel, post
    vivipost = tree.'get'('vivipost', node)
    post = vivipost.'new'('POST::Ops', 'node'=>node, 'result'=>vivipost)
    vivilabel = post.'new'('POST::Label', 'name'=>'vivify_')

    .local string scope, name, fetchop, storeop
    scope = node.'scope'()
    if scope == 'keyed' goto scope_keyed
    name = node.'name'()
    name = node.'escape'(name)
    if scope == 'lexical' goto scope_lexical
    if scope == 'parameter' goto scope_parameter
  scope_package:
    fetchop = 'get_global'
    storeop = 'set_global'
    goto have_scope

  scope_keyed:
    .local pmc keypost, basepost
    keypost = tree.'get'('keypost', node)
    post.'push'(keypost)
    name = keypost.'result'()
    fetchop = 'set'
    storeop = 'set'
    goto have_scope

  scope_lexical:
    fetchop = 'find_lex'
    storeop = 'store_lex'
    $I0 = node.'ismy'()
    if $I0 == 0 goto have_scope
    post.'push'(vivipost)
    post.'push_pirop'('.lex', name, vivipost)
    .return (post)

  scope_parameter:
    post = new 'POST::Op'
    post.'pirop'('.lex')
    .local string pname
    pname = post.'unique'('param_')
    post.'arglist'(name, pname)
    post.'result'(pname)
    .local pmc subpost
    subpost = get_hll_global ['POST'], '$?SUB'
    subpost.'push_param'('pmc', pname)
    .return (post)

  have_scope:
    post.'push_pirop'(fetchop, vivipost, name)
    post.'push_pirop'('unless_null', post, vivilabel)
    post.'push'(vivipost)
    .local int islvalue
    islvalue = node.'islvalue'()
    unless islvalue goto done_lvalue
    post.'push_pirop'(storeop, name, post)
  done_lvalue:
    post.'push'(vivilabel)
    .return (post)
}

