1 # Copyright (C) 2007-2008, The Perl Foundation.
6 POSTGrammar.tg - A grammar for transforming the abstract syntax tree (PAST)
7 into an opcode syntax tree (POST).
14 grammar Lua::POST::Grammar is TGE::Grammar;
17 transform post (PAST::Block) :language('PIR') {
19 set_hll_global ['Lua::POST'], '$?environ', $P0
21 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
22 new $P0, 'ResizableStringArray'
23 set_hll_global ['Lua::POST'], '@endlabels', $P0
25 outerpost = get_hll_global ['Lua::POST'], '$?SUB'
28 unless $S0 == '' goto L1
32 $P0 = get_hll_global ['Lua'; 'POST'], 'Sub'
33 post = $P0.'new'('node'=>node, 'outer'=>outerpost, 'pirflags'=>':anon :lex', 'blocktype'=>'declaration')
35 name = post.'unique'($S1)
38 post.'storage_lex'($P0)
40 post.'storage_const'($P0)
41 $P0 = post.'push_new'('POST::Ops')
43 $P0 = post.'push_new'('POST::Ops')
45 set_hll_global ['Lua::POST'], '$?SUB', post
46 .local pmc iter, cpost
47 iter = node.'iterator'()
51 cpost = tree.'get'('post', $P0)
55 set_hll_global ['Lua::POST'], '$?SUB', outerpost
56 set_hll_global ['Lua::POST'], '@endlabels', endlabels
57 .local string esc_name
58 esc_name = post.'escape'(name)
59 if null outerpost goto L4
61 ops_subr = outerpost.'ops_subr'()
62 $I0 = defined ops_subr
64 ops_subr.'push_pirop'('inline', 'inline'=><<'PIRCODE')
66 subr = interpinfo .INTERPINFO_CURRENT_SUB
69 outerpost.'ops_subr'($P0)
72 $P0 = get_hll_global ['POST'], 'Ops'
73 ops = $P0.'new'('node'=>node)
75 $S0 = post.'unique'('fct_')
76 ops.'push_pirop'('inline', $S0, esc_name, 'inline'=>' .const .Sub %0 = %1')
78 result = ops.'unique'('$P')
79 ops.'push_pirop'('newclosure', result, $S0)
80 $S0 = ops.'unique'('$P')
81 ops.'push_pirop'('callmethod', 'getfenv', 'subr', 'result'=>$S0)
82 ops.'push_pirop'('callmethod', 'setfenv', result, $S0, 'result'=>'')
87 $P0 = get_hll_global ['Lua'; 'POST'], 'Sub'
88 start = $P0.'new'('name'=>'&start', 'pirflags'=>':anon :main')
89 start.'push_pirop'('inline', esc_name, 'inline'=><<'PIRCODE')
90 .param pmc args :optional
92 load_bytecode "languages/lua/lua.pbc"
95 env = get_hll_global "_G"
97 vararg = argstolua(env, args)
100 ($I0, $P0) = docall(main, vararg :flat)
108 $P0 = get_hll_global ['Lua'; 'POST'], 'Chunk'
109 .return $P0.'new'(start, 'node'=>node, 'prologue'=><<'PIRCODE')
110 .include "interpinfo.pasm"
111 .HLL "Lua", "lua_group"
116 transform post (PAST::Stmts) :language('PIR') {
118 $P0 = get_hll_global ['POST'], 'Ops'
119 ops = $P0.'new'('node'=>node)
121 iter = node.'iterator'()
125 $P1 = tree.'get'('void', $P0)
130 set_hll_global ['Lua::POST'], '$?environ', $P0
135 transform void (PAST::Stmts) :language('PIR') {
136 .return tree.'get'('post', node)
140 transform void (PAST::Op) :language('PIR') {
141 $S0 = node.'pasttype'()
142 .return tree.'get'($S0, node)
146 transform cond (PAST::Op) :language('PIR') {
148 $I0 = index $S0, 'is'
149 unless $I0 >= 0 goto L1
150 .return tree.'get'('cmp', node)
152 .return tree.'get'('post', node)
156 transform post (PAST::Op) :language('PIR') {
157 $S0 = node.'pasttype'()
158 unless $S0 == 'call' goto L1
160 post = tree.'get'($S0, node)
163 result = post.'unique'('$P')
164 post.'push_pirop'('new', result, '"LuaNil"')
167 post.'result'(result)
170 .return tree.'get'($S0, node)
174 transform pirop (PAST::Op) :language('PIR') {
177 $I0 = index $S0, 'is'
178 unless $I0 >= 0 goto L1
180 post = tree.'get'('cmp', node)
181 $S0 = post.'result'()
182 result = post.'unique'('$P')
183 post.'push_pirop'('new', result, '"LuaBoolean"')
184 post.'push_pirop'('set', result, $S0)
185 post.'result'(result)
189 $P0 = get_hll_global ['POST'], 'Ops'
190 ops = $P0.'new'('node'=>node)
191 .local pmc arglist, iter
192 new arglist, 'ResizableStringArray'
193 iter = node.'iterator'()
198 cpost = tree.'get'('post', $P0)
200 $S1 = cpost.'result'()
204 result = ops.'unique'('$P')
205 post = ops.'push_pirop'($S0, result, arglist :flat, 'node'=>node, 'result'=>result)
211 transform cmp (PAST::Op) :language('PIR') {
213 $P0 = get_hll_global ['POST'], 'Ops'
214 ops = $P0.'new'('node'=>node)
217 expr1 = tree.'get'('post', $P0)
221 expr2 = tree.'get'('post', $P0)
224 pirop = node.'pirop'()
225 $S0 = ops.'unique'('$I')
226 ops.'push_pirop'(pirop, $S0, expr1, expr2, 'node'=>node)
232 transform call (PAST::Op) :language('PIR') {
234 $P0 = get_hll_global ['POST'], 'Ops'
235 ops = $P0.'new'('node'=>node)
236 .local pmc arglist, iter, cpost
237 new arglist, 'ResizableStringArray'
238 iter = node.'iterator'()
244 cpost = tree.'get'('post', $P0)
246 $S0 = cpost.'result'()
249 ($I0, $P0) = cpost.'has_call_in_last_op'()
260 ops.'push_pirop'('call', arglist :flat, 'node'=>node, 'result'=>'')
262 set_hll_global ['Lua::POST'], '$?environ', $P0
267 transform len (PAST::Op) :language('PIR') {
269 $P0 = get_hll_global ['POST'], 'Ops'
270 ops = $P0.'new'('node'=>node)
273 expr = tree.'get'('post', $P0)
276 result = ops.'unique'('$P')
277 $S0 = ops.'escape'('len')
278 ops.'push_pirop'('callmethod', $S0, expr, 'node'=>node, 'result'=>result)
284 transform and (PAST::Op) :language('PIR') {
286 $P0 = get_hll_global ['POST'], 'Ops'
287 ops = $P0.'new'('node'=>node)
289 type = node.'pasttype'()
292 expr1 = tree.'get'('post', $P0)
294 .local string label, endlabel
296 label = ops.'unique'($S1)
297 endlabel = label . '_end'
298 $S0 = ops.'unique'('$P')
299 unless type == 'and' goto L1
300 ops.'push_pirop'('if', expr1, label)
303 ops.'push_pirop'('unless', expr1, label)
305 ops.'push_pirop'('clone', $S0, expr1)
306 ops.'push_pirop'('goto', endlabel)
307 ops.'push_new'('POST::Label', 'result'=>label)
310 expr2 = tree.'get'('post', $P0)
312 ops.'push_pirop'('clone', $S0, expr2)
313 ops.'push_new'('POST::Label', 'result'=>endlabel)
319 transform or (PAST::Op) :language('PIR') {
320 .return tree.'get'('and', node)
324 transform vararg (PAST::Op) :language('PIR') {
326 $P0 = get_hll_global ['POST'], 'Ops'
327 ops = $P0.'new'('node'=>node)
329 result = ops.'unique'('$P')
330 ops.'push_pirop'('new', result, '"LuaNil"')
331 ops.'push_pirop'('call', 'mkarg', 'vararg', 'result'=>result)
337 transform if (PAST::Op) :language('PIR') {
339 $P0 = get_hll_global ['POST'], 'Ops'
340 ops = $P0.'new'('node'=>node)
341 .local string thenlabel, endlabel
342 thenlabel = ops.'unique'('if_')
343 endlabel = thenlabel . '_end'
346 expr = tree.'get'('cond', $P0)
348 ops.'push_pirop'('if', expr, thenlabel)
353 else = tree.'get'('post', $P0)
356 ops.'push_pirop'('goto', endlabel)
357 ops.'push_new'('POST::Label', 'result'=>thenlabel)
362 then = tree.'get'('post', $P0)
365 ops.'push_new'('POST::Label', 'result'=>endlabel)
370 transform while (PAST::Op) :language('PIR') {
372 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
374 $P0 = get_hll_global ['POST'], 'Ops'
375 ops = $P0.'new'('node'=>node)
376 .local string looplabel, endlabel
377 looplabel = ops.'unique'('while_')
378 endlabel = looplabel . '_end'
379 unshift endlabels, endlabel
380 ops.'push_new'('POST::Label', 'result'=>looplabel)
383 expr = tree.'get'('cond', $P0)
385 ops.'push_pirop'('unless', expr, endlabel)
388 blk = tree.'get'('post', $P0)
390 ops.'push_pirop'('goto', looplabel)
391 ops.'push_new'('POST::Label', 'result'=>endlabel)
392 $P0 = shift endlabels
397 transform repeat (PAST::Op) :language('PIR') {
399 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
401 $P0 = get_hll_global ['POST'], 'Ops'
402 ops = $P0.'new'('node'=>node)
403 .local string looplabel, endlabel
404 looplabel = ops.'unique'('repeat_')
405 endlabel = looplabel . '_end'
406 unshift endlabels, endlabel
407 ops.'push_new'('POST::Label', 'result'=>looplabel)
410 blk = tree.'get'('post', $P0)
414 expr = tree.'get'('cond', $P0)
416 ops.'push_pirop'('if', expr, endlabel)
417 ops.'push_pirop'('goto', looplabel)
418 ops.'push_new'('POST::Label', 'result'=>endlabel)
419 $P0 = shift endlabels
424 transform fornum (PAST::Op) :language('PIR') {
426 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
428 $P0 = get_hll_global ['POST'], 'Ops'
429 ops = $P0.'new'('node'=>node)
430 .local pmc e_var, e_limit, e_step
433 e_var = tree.'get'('post', $P1)
436 e_limit = tree.'get'('post', $P1)
439 e_step = tree.'get'('post', $P1)
443 $P0 = tree.'get'('post', $P0)
449 .local string limit, step
450 limit = ops.'unique'('$P')
451 step = ops.'unique'('$P')
452 ops.'push_pirop'('inline', var, limit, step, e_var, e_limit, e_step, 'inline'=>' (%0, %1, %2) = checkforloop(%3, %4, %5)')
454 $S0 = ops.'unique'('$N')
455 ops.'push_pirop'('set', $S0, step)
456 incr = ops.'unique'('$I')
457 ops.'push_pirop'('isgt', incr, $S0, '0.0')
458 .local string looplabel, endlabel, orlabel, blklabel
459 looplabel = ops.'unique'('fornum_')
460 orlabel = looplabel . '_or'
461 blklabel = looplabel . '_blk'
462 endlabel = looplabel . '_end'
463 unshift endlabels, endlabel
464 ops.'push_new'('POST::Label', 'result'=>looplabel)
465 ops.'push_pirop'('unless', incr, orlabel)
466 ops.'push_pirop'('gt', var, limit, endlabel)
467 ops.'push_pirop'('goto', blklabel)
468 ops.'push_new'('POST::Label', 'result'=>orlabel)
469 ops.'push_pirop'('lt', var, limit, endlabel)
470 ops.'push_new'('POST::Label', 'result'=>blklabel)
473 blk = tree.'get'('post', $P0)
475 ops.'push_pirop'('add', var, step)
476 ops.'push_pirop'('goto', looplabel)
477 ops.'push_new'('POST::Label', 'result'=>endlabel)
478 $P0 = shift endlabels
483 transform forlist (PAST::Op) :language('PIR') {
485 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
487 $P0 = get_hll_global ['POST'], 'Ops'
488 ops = $P0.'new'('node'=>node)
489 .local pmc explist, rpost
491 .local string func, state, var
493 new iter, 'Iterator', explist
495 rpost = tree.'get'('post', $P0)
497 func = rpost.'result'()
499 $I0 = rpost.'has_call_in_last_op'()
503 state = rpost.'unique'('$P')
504 rpost.'push_pirop'('new', state, '"LuaNil"')
507 var = rpost.'unique'('$P')
508 rpost.'push_pirop'('new', var, '"LuaNil"')
517 rpost = tree.'get'('post', $P0)
519 state = rpost.'result'()
521 $I0 = rpost.'has_call_in_last_op'()
525 var = rpost.'unique'('$P')
526 rpost.'push_pirop'('new', var, '"LuaNil"')
535 rpost = tree.'get'('post', $P0)
537 var = rpost.'result'()
541 rpost = tree.'get'('post', $P0)
545 unless state == '' goto L5
546 var = ops.'unique'('$P')
547 ops.'push_pirop'('new', var, '"LuaNil"')
549 unless var == '' goto L6
550 var = ops.'unique'('$P')
551 ops.'push_pirop'('new', var, '"LuaNil"')
553 .local pmc namelist, lpost, tmp
555 new iter, 'Iterator', namelist
556 new tmp, 'ResizableStringArray'
560 lpost = tree.'get'('post', $P0)
569 .local string looplabel, endlabel
570 looplabel = ops.'unique'('forlist_')
571 endlabel = looplabel . '_end'
572 unshift endlabels, endlabel
573 ops.'push_new'('POST::Label', 'result'=>looplabel)
574 new iter, 'Iterator', tmp
579 ops.'push_pirop'('new', $S1, '"LuaNil"')
580 if $S0 == '(' goto L11
587 ops.'push_pirop'('call', func, state, var, 'node'=>node, 'result'=>$S0)
589 ops.'push_pirop'('assign', var, $S0)
590 $S0 = ops.'unique'('$I')
591 ops.'push_pirop'('isa', $S0, var, '"LuaNil"')
592 ops.'push_pirop'('if', $S0, endlabel)
595 blk = tree.'get'('post', $P0)
597 ops.'push_pirop'('goto', looplabel)
598 ops.'push_new'('POST::Label', 'result'=>endlabel)
599 $P0 = shift endlabels
604 transform return (PAST::Op) :language('PIR') {
606 $P0 = get_hll_global ['POST'], 'Ops'
607 ops = $P0.'new'('node'=>node)
609 new arglist, 'ResizableStringArray'
610 .local pmc iter, cpost
611 iter = node.'iterator'()
615 cpost = tree.'get'('post', $P0)
617 $S0 = cpost.'result'()
619 ($I0, $P0) = cpost.'has_call_in_last_op'()
622 $P0.'pirop'('tailcall')
633 ops.'push_pirop'('return', arglist :flat)
638 transform break (PAST::Op) :language('PIR') {
640 endlabels = get_hll_global ['Lua::POST'], '@endlabels'
642 $P0 = get_hll_global ['POST'], 'Op'
643 .return $P0.'new'($S0, 'node'=>node, 'pirop'=>'goto')
647 transform assign (PAST::Op) :language('PIR') {
649 $P0 = get_hll_global ['POST'], 'Ops'
650 ops = $P0.'new'('node'=>node)
653 rpost = tree.'get'('post', $P0)
655 val = rpost.'result'()
659 lpost = tree.'get'('post', $P0)
669 transform assignlist (PAST::Op) :language('PIR') {
671 $P0 = get_hll_global ['POST'], 'Ops'
672 ops = $P0.'new'('node'=>node)
673 .local pmc namelist, explist
676 .local pmc iter, tmp, rpost
677 new tmp, 'ResizableStringArray'
678 new iter, 'Iterator', explist
682 rpost = tree.'get'('post', $P0)
684 $S1 = rpost.'result'()
687 $I0 = rpost.'has_call_in_last_op'()
696 unless $I1 > 0 goto L4
697 $S1 = ops.'unique'('$P')
698 rpost.'push_pirop'('new', $S1, '"LuaNil"')
709 .local pmc ass, lpost
710 $P0 = get_hll_global ['POST'], 'Ops'
711 ass = $P0.'new'('node'=>node)
712 new iter, 'Iterator', namelist
720 $S1 = ops.'unique'('$P')
721 ops.'push_pirop'('new', $S1, '"LuaNil"')
723 lpost = tree.'get'('post', $P0)
735 transform parenthese (PAST::Op) :language('PIR') {
737 $P0 = get_hll_global ['POST'], 'Ops'
738 ops = $P0.'new'('node'=>node)
741 expr = tree.'get'('post', $P0)
748 transform cond (PAST::Val) :language('PIR') {
749 .return tree.'get'('post', node)
753 transform post (PAST::Val) :language('PIR') {
756 $I0 = index type, 'Lua'
759 $P0 = get_hll_global ['POST'], 'Ops'
760 ops = $P0.'new'('node'=>node)
762 result = ops.'unique'('$P')
763 $S0 = ops.'escape'(type)
764 ops.'push_pirop'('new', result, $S0)
766 value = node.'value'()
767 if value == '' goto L2
768 unless type == 'LuaString' goto L3
769 value = ops.'escape'(value)
771 ops.'push_pirop'('set', result, value)
776 .return tree.'get'(type, node)
780 transform key (PAST::Val) :language('PIR') {
782 subpost = get_hll_global ['Lua::POST'], '$?SUB'
783 .local pmc storage_const
784 storage_const = subpost.'storage_const'()
786 name = node.'value'()
787 $I0 = exists storage_const[name]
790 ops_const = subpost.'ops_const'()
792 $S1 = ops_const.'escape'(name)
793 $P0 = ops_const.'push_pirop'('inline', $S0, $S1, 'inline'=>' .const .LuaString %0 = %1')
794 storage_const[name] = $S0
797 $P0 = get_hll_global ['POST'], 'Ops'
798 ops = $P0.'new'('node'=>node)
799 $S0 = storage_const[name]
805 transform constructor (PAST::Val) :language('PIR') {
807 $P0 = get_hll_global ['POST'], 'Ops'
808 ops = $P0.'new'('node'=>node)
810 table = ops.'unique'('$P')
811 ops.'push_pirop'('new', table, '"LuaTable"')
813 .local pmc iter, kpost, vpost
814 .local string key, result
817 iter = node.'iterator'()
821 $I0 = does $P0, 'array'
823 $P2 = get_hll_global ['POST'], 'Ops'
824 kpost = $P2.'new'('node'=>node)
825 result = kpost.'unique'('$P')
826 kpost.'push_pirop'('new', result, '"LuaNumber"')
829 kpost.'push_pirop'('set', result, $S0)
830 kpost.'result'(result)
831 vpost = tree.'get'('post', $P0)
833 $I0 = vpost.'has_call_in_last_op'()
844 $P1 = ops.'push_pirop'('call', 'tconstruct', table, kpost, $S0)
849 kpost = tree.'get'('post', $P1)
851 vpost = tree.'get'('post', $P1)
857 $S0 = kpost.'result'()
860 ops.'push_pirop'('set', key, vpost)
867 transform cond (PAST::Var) :language('PIR') {
868 .return tree.'get'('post', node)
872 transform post (PAST::Var) :language('PIR') {
874 .return tree.'get'($S0, node)
878 transform parameter (PAST::Var) :language('PIR') {
880 subpost = get_hll_global ['Lua::POST'], '$?SUB'
882 $P0 = get_hll_global ['POST'], 'Ops'
883 ops = $P0.'new'('node'=>node)
886 $I0 = node.'slurpy'()
888 subpost.'add_param'(name, 'slurpy'=>1)
891 .local pmc storage_lex
892 storage_lex = subpost.'storage_lex'()
894 pname = 'param_' . name
895 subpost.'add_param'(pname, 'optional'=>1)
896 $S0 = ops.'escape'(name)
897 ops.'push_pirop'('.lex', $S0, pname)
898 .local string vivilabel
899 vivilabel = ops.'unique'('vivify_')
900 ops.'push_pirop'('unless_null', pname, vivilabel)
901 ops.'push_pirop'('new', pname, '"LuaNil"')
902 ops.'push_new'('POST::Label', 'result'=>vivilabel)
904 storage_lex[name] = pname
909 transform lexical (PAST::Var) :language('PIR') {
911 subpost = get_hll_global ['Lua::POST'], '$?SUB'
912 .local pmc storage_lex
913 storage_lex = subpost.'storage_lex'()
915 $P0 = get_hll_global ['POST'], 'Ops'
916 ops = $P0.'new'('node'=>node)
917 .local string name, lname
919 lname = ops.'escape'(name)
920 $I0 = node.'lvalue'()
922 $I0 = exists storage_lex[name]
925 result = ops.'unique'('$P')
926 ops.'push_pirop'('find_lex', result, lname)
930 $S0 = storage_lex[name]
934 $I0 = node.'isdecl'()
936 $I0 = exists storage_lex[name]
939 pname = 'var_' . name
940 ops.'push_pirop'('.local pmc', pname)
941 ops.'push_pirop'('.lex', lname, pname, 'result'=>pname)
942 storage_lex[name] = pname
944 ops.'push_pirop'('store_lex', lname, 'node'=>node)
950 transform package (PAST::Var) :language('PIR') {
953 $P0 = get_hll_global ['POST'], 'Ops'
954 ops = $P0.'new'('node'=>node)
955 .local pmc basepost, keypost
957 unless $S0 == 'keyed' goto L1
959 basepost = tree.'get'('post', $P0)
962 keypost = tree.'get'('post', $P0)
967 subpost = get_hll_global ['Lua::POST'], '$?SUB'
968 basepost = get_hll_global ['Lua::POST'], '$?environ'
969 unless null basepost goto L3
971 ops_subr = subpost.'ops_subr'()
972 $I0 = defined ops_subr
974 ops_subr.'push_pirop'('inline', 'inline'=><<'PIRCODE')
976 subr = interpinfo .INTERPINFO_CURRENT_SUB
979 subpost.'ops_subr'($P0)
981 result = ops.'unique'('$P')
982 basepost = ops.'push_pirop'('callmethod', 'getfenv', 'subr', 'result'=>result)
983 set_hll_global ['Lua::POST'], '$?environ', basepost
985 .local pmc storage_const
986 storage_const = subpost.'storage_const'()
989 $I0 = exists storage_const[name]
992 ops_const = subpost.'ops_const'()
994 $S1 = ops_const.'escape'(name)
995 $P0 = ops_const.'push_pirop'('inline', $S0, $S1, 'inline'=>' .const .LuaString %0 = %1')
996 storage_const[name] = $S0
998 $P0 = get_hll_global ['POST'], 'Ops'
999 keypost = $P0.'new'('node'=>node)
1000 $S0 = storage_const[name]
1001 keypost.'result'($S0)
1004 key = basepost.'result'()
1006 $S0 = keypost.'result'()
1010 $I0 = node.'lvalue'()
1012 result = ops.'unique'('$P')
1013 ops.'push_pirop'('set', result, key, 'result'=>result)
1014 ops.'result'(result)
1017 ops.'push_pirop'('set', key, 'node'=>node)
1023 transform keyed (PAST::Var) :language('PIR') {
1024 .return tree.'get'('package', node)