5 PAST::Compiler - PAST Compiler
9 PAST::Compiler implements a basic compiler for PAST nodes.
10 By default PAST::Compiler transforms a PAST tree into POST.
12 =head2 Signature Flags
14 Throughout the compiler PAST uses a number of 1-character
15 "flags" to indicate allowable register types and conversions.
16 This helps the compiler generate more efficient code and know
17 what sorts of conversions are allowed (or desired). The
20 P,S,I,N PMC, string, int, or num register
21 Q keyed PMC, next flag indicates type of key
22 s string register or constant
23 i int register or constant
24 n num register or constant
27 * any result type except void
28 + PMC, int register, num register, or numeric constant
29 ~ PMC, string register, or string constant
30 : argument (same as '*'), possibly with :named or :flat
31 0-9 use the nth input operand as the output result of this operation
33 These flags are used to describe signatures and desired return
34 types for various operations. For example, if an opcode is
35 specified with a signature of C<I~P*>, then the opcode places
36 its result in an int register, its first child is coerced into
37 some sort of string value, its second child is coerced into a
38 PMC register, and the third and subsequent children can return
43 .include "cclass.pasm"
44 .include "except_types.pasm"
45 .include "interpinfo.pasm"
47 .namespace [ 'PAST';'Compiler' ]
49 .sub 'onload' :anon :load :init
50 load_bytecode 'PCT/HLLCompiler.pbc'
51 .local pmc p6meta, cproto
52 p6meta = new 'P6metaclass'
53 cproto = p6meta.'new_class'('PAST::Compiler', 'parent'=>'PCT::HLLCompiler', 'attr'=>'%!symtable')
54 cproto.'language'('PAST')
55 $P1 = split ' ', 'post pir evalpmc'
58 ## %piropsig is a table of common opcode signatures
61 piropsig['add'] = 'PP+'
62 piropsig['band'] = 'PPP'
63 piropsig['bxor'] = 'PPP'
64 piropsig['bnot'] = 'PP'
65 piropsig['bor'] = 'PPP'
66 piropsig['can'] = 'IPs'
67 piropsig['chr'] = 'Si'
68 piropsig['clone'] = 'PP'
69 piropsig['concat'] = 'PP~'
70 piropsig['copy'] = '0PP'
71 piropsig['defined'] = 'IP'
72 piropsig['delete'] = 'vQ*'
73 piropsig['die'] = 'v~'
74 piropsig['div'] = 'PP+'
75 piropsig['does'] = 'IPs'
76 piropsig['downcase'] = 'Ss'
77 piropsig['elements'] = 'IP'
78 piropsig['exists'] = 'IQ*'
79 piropsig['exit'] = 'vi'
80 piropsig['fdiv'] = 'PP+'
81 piropsig['find_codepoint'] = 'Is'
82 piropsig['find_dynamic_lex'] = 'Ps'
83 piropsig['find_name'] = 'Ps'
84 piropsig['getinterp'] = 'P'
85 piropsig['getprop'] = 'P~P'
86 piropsig['getstderr'] = 'P'
87 piropsig['getstdin'] = 'P'
88 piropsig['getstdout'] = 'P'
89 piropsig['index'] = 'Issi'
90 piropsig['isa'] = 'IP~'
91 piropsig['isfalse'] = 'IP'
92 piropsig['isnull'] = 'IP'
93 piropsig['issame'] = 'IPP'
94 piropsig['istrue'] = 'IP'
95 piropsig['join'] = 'SsP'
96 piropsig['length'] = 'Is'
97 piropsig['load_bytecode'] = 'vs'
98 piropsig['load_language'] = 'vs'
99 piropsig['loadlib'] = 'P~'
100 piropsig['mod'] = 'PP+'
101 piropsig['mul'] = 'PP+'
102 piropsig['neg'] = 'PP'
103 piropsig['new'] = 'P~'
104 piropsig['newclosure'] = 'PP'
105 piropsig['not'] = 'PP'
106 piropsig['ord'] = 'Isi'
107 piropsig['pop'] = 'PP'
108 piropsig['pow'] = 'NN+'
109 piropsig['print'] = 'v*'
110 piropsig['printerr'] = 'v*'
111 piropsig['push'] = '0P*'
112 piropsig['repeat'] = 'Ssi'
113 piropsig['replace'] = 'Ssiis'
114 piropsig['say'] = 'v*'
115 piropsig['set'] = 'PP'
116 piropsig['setprop'] = '0P~P'
117 piropsig['setattribute'] = '0P~P'
118 piropsig['shift'] = 'PP'
119 piropsig['shl'] = 'PP+'
120 piropsig['shr'] = 'PP+'
121 piropsig['sleep'] = 'v+'
122 piropsig['splice'] = '0PPii'
123 piropsig['split'] = 'Pss'
124 piropsig['sub'] = 'PP+'
125 piropsig['substr'] = 'Ssii'
126 piropsig['titlecase'] = 'Ss'
127 piropsig['trace'] = 'vi'
128 piropsig['typeof'] = 'SP'
129 piropsig['unshift'] = '0P*'
130 piropsig['upcase'] = 'Ss'
131 set_global '%piropsig', piropsig
133 ## %valflags specifies when PAST::Val nodes are allowed to
134 ## be used as a constant. The 'e' flag indicates that the
135 ## value must be quoted+escaped in PIR code.
137 valflags = new 'Hash'
138 valflags['String'] = 's~*:e'
139 valflags['Integer'] = 'i+*:'
140 valflags['Float'] = 'n+*:'
141 valflags['!cconst'] = 'i+*:c'
142 valflags['!except_severity'] = 'i+*:c'
143 valflags['!except_types'] = 'i+*:c'
144 valflags['!iterator'] = 'i+*:c'
145 valflags['!socket'] = 'i+*:c'
146 set_global '%valflags', valflags
148 ## %!controltypes holds the list of exception types for each
149 ## type of exception handler we support
150 .local pmc controltypes
151 controltypes = new 'Hash'
152 controltypes['CONTROL'] = '.CONTROL_RETURN, .CONTROL_OK, .CONTROL_BREAK, .CONTROL_CONTINUE, .CONTROL_TAKE, .CONTROL_LEAVE, .CONTROL_EXIT, .CONTROL_LOOP_NEXT, .CONTROL_LOOP_LAST, .CONTROL_LOOP_REDO'
153 controltypes['RETURN'] = '.CONTROL_RETURN'
154 controltypes['OK'] = '.CONTROL_OK'
155 controltypes['BREAK'] = '.CONTROL_BREAK'
156 controltypes['CONTINUE'] = '.CONTROL_CONTINUE'
157 controltypes['ERROR'] = '.CONTROL_ERROR'
158 controltypes['GATHER'] = '.CONTROL_TAKE'
159 controltypes['LEAVE'] = '.CONTROL_LEAVE'
160 controltypes['EXIT'] = '.CONTROL_EXIT'
161 controltypes['NEXT'] = '.CONTROL_NEXT'
162 controltypes['LAST'] = '.CONTROL_LAST'
163 controltypes['REDO'] = '.CONTROL_REDO'
164 set_global '%!controltypes', controltypes
167 set_global '$!serno', $P0
172 =head2 Compiler methods
176 =item to_post(node [, 'option'=>option, ...])
178 Compile the abstract syntax tree given by C<past> into POST.
182 .sub 'to_post' :method
184 .param pmc options :slurpy :named
187 symtable = new 'Hash'
188 setattribute self, '%!symtable', symtable
191 blockpast = get_global '@?BLOCK'
192 unless null blockpast goto have_blockpast
193 blockpast = new 'ResizablePMCArray'
194 set_global '@?BLOCK', blockpast
196 .lex '@*BLOCKPAST', blockpast
199 $P1 = self.'as_post'(past, 'rtype'=>'v')
205 Return C<str> as a PIR constant string.
209 .sub 'escape' :method
213 $I0 = index estr, "\\x"
214 if $I0 >= 0 goto unicode_prefix
215 $I0 = index estr, "\\u"
216 if $I0 >= 0 goto unicode_prefix
217 estr = concat '"', estr
220 estr = concat 'unicode:"', estr
222 estr = concat estr, '"'
226 =item unique([STR fmt])
228 Generate a unique number that can be used as an identifier.
229 If C<fmt> is provided, then it will be used as a prefix to the
234 .sub 'unique' :method
235 .param string fmt :optional
236 .param int has_fmt :opt_flag
238 if has_fmt goto unique_1
241 $P0 = get_global '$!serno'
243 $S0 = concat fmt, $S0
248 =item uniquereg(rtype)
250 Generate a unique register based on C<rtype>, where C<rtype>
251 is one of the signature flags described above.
255 .sub 'uniquereg' :method
257 unless rtype goto err_nortype
258 if rtype == 'v' goto reg_void
261 $I0 = index 'Ss~Nn+Ii', rtype
262 if $I0 < 0 goto reg_psin
263 reg = substr 'SSSNNNII', $I0, 1
265 reg = concat '$', reg
266 .tailcall self.'unique'(reg)
270 self.'panic'('rtype not set')
273 =item coerce(post, rtype)
275 Return a POST tree that coerces the result of C<post> to have a
276 return value compatible with C<rtype>. C<rtype> can also be
277 a specific register, in which case the result of C<post> is
278 forced into that register (with conversions as needed).
282 .sub 'coerce' :method
286 unless rtype goto err_nortype
288 .local string pmctype, result, rrtype
292 ## if rtype is a register, then set result and use the register
294 $S0 = substr rtype, 0, 1
295 unless $S0 == '$' goto have_rtype
297 rtype = substr result, 1, 1
300 ## these rtypes allow any return value, so no coercion needed.
301 $I0 = index 'v*:', rtype
304 ## figure out what type of result we already have
306 source = post.'result'()
307 $S0 = substr source, 0, 1
308 if $S0 == '$' goto source_reg
309 if $S0 == '"' goto source_str
310 if $S0 == '.' goto source_int_or_num
311 if $S0 == '-' goto source_int_or_num
312 $I0 = is_cclass .CCLASS_NUMERIC, source, 0
313 if $I0 goto source_int_or_num
314 $S0 = substr source, 0, 8
315 if $S0 == 'unicode:' goto source_str
316 ## assume that whatever is left acts like a PMC
320 ## source is some sort of register
321 ## if a register is all we need, we're done
322 if rtype == 'r' goto end
323 $S0 = substr source, 1, 1
324 ## if we have the correct register type already, we're done
325 if $S0 != rtype goto source_reg_1
326 unless result goto end
330 if $S0 == rtype goto end
331 ## figure it out based on the register type
332 if $S0 == 's' goto source_str
333 if rtype == '+' goto end
334 if $S0 == 'i' goto source_int
335 if $S0 == 'n' goto source_num
337 $I0 = index 'SINsin', rtype
342 if rtype == '~' goto end
343 if rtype == 's' goto end
349 if rtype == '+' goto end
350 ## existence of an 'e' or '.' implies num
351 $I0 = index source, '.'
352 if $I0 >= 0 goto source_num
353 $I0 = index source, 'E'
354 if $I0 >= 0 goto source_num
357 if rtype == 'i' goto end
359 pmctype = "'Integer'"
363 if rtype == 'n' goto end
368 ## okay, we know we have to do a coercion.
369 ## If we just need the value in a register (rtype == 'r'),
370 ## then create result based on the preferred register type (rrtype).
371 if rtype != 'r' goto coerce_reg_1
372 result = self.'uniquereg'(rrtype)
374 ## if we haven't set the result target yet, then generate one
375 ## based on rtype. (The case of rtype == 'r' was handled above.)
376 if result goto coerce_reg_2
377 result = self.'uniquereg'(rtype)
379 ## create a new ops node to hold the coercion, put C<post> in it.
380 $P0 = get_hll_global ['POST'], 'Ops'
381 post = $P0.'new'(post, 'result'=>result)
382 ## if we need a new pmc (rtype == 'P' && pmctype defined), create it
383 if rtype != 'P' goto have_result
384 unless pmctype goto have_result
385 post.'push_pirop'('new', result, pmctype)
387 ## store the value into the target register
388 post.'push_pirop'('set', result, source)
394 self.'panic'('rtype not set')
398 =item post_children(node [, 'signature'=>signature] )
400 Return the POST representation of evaluating all of C<node>'s
401 children in sequence. The C<signature> option is a string of
402 flags as described in "Signature Flags" above. Since we're
403 just evaluating children nodes, the first character of
404 C<signature> (return value type) is ignored. Thus a C<signature>
405 of C<v~P*> says that the first child needs to be something
406 in string context, the second child should be a PMC, and the
407 third and subsequent children can be any value they wish.
411 .sub 'post_children' :method
413 .param pmc options :slurpy :named
416 $P0 = get_hll_global ['POST'], 'Ops'
417 ops = $P0.'new'('node'=>node)
419 ## get any conversion types
420 .local string signature
421 signature = options['signature']
422 if signature goto have_signature
425 .local int sigmax, sigidx
426 sigmax = length signature
429 ## if the signature contains a ':', then we're doing
430 ## flagged arguments (:flat, :named)
431 .local pmc posargs, namedargs
432 posargs = new 'ResizableStringArray'
434 $I0 = index signature, ':'
435 if $I0 < 0 goto nocolon
436 namedargs = new 'ResizableStringArray'
441 iter = node.'iterator'()
443 rtype = substr signature, sigidx, 1
445 if rtype == 'Q' goto keyed_pos
446 unless iter goto iter_end
447 .local pmc cpast, cpost
449 cpost = self.'as_post'(cpast, 'rtype'=>rtype)
450 cpost = self.'coerce'(cpost, rtype)
452 $I0 = isa cpast, ['PAST';'Node']
453 unless $I0 goto cpost_pos
455 isflat = cpast.'flat'()
456 if rtype != ':' goto iter_pos
457 .local pmc npast, npost
458 npast = cpast.'named'()
459 unless npast goto iter_pos
461 if isflat goto flat_named
462 npost = self.'as_post'(npast, 'rtype'=>'~')
465 concat $S0, ' :named('
470 concat $S0, ' :named :flat'
475 if isflat goto flat_pos
484 unless sigidx < sigmax goto iter_loop
486 rtype = substr signature, sigidx, 1
489 # rtype is 'Q', so construct a keyed pmc argument
490 # first, get the base PMC
491 unless iter goto iter_end
493 cpost = self.'as_post'(cpast, 'rtype'=>'P')
494 cpost = self.'coerce'(cpost, 'P')
495 # now process the key arg
496 unless iter goto iter_end
497 .local pmc kpast, kpost
500 rtype = substr signature, sigidx, 1
501 kpost = self.'as_post'(kpast, 'rtype'=>rtype)
502 kpost = self.'coerce'(kpost, rtype)
505 # now construct the keyed PMC
514 .return (ops, posargs, namedargs)
519 =head2 Methods on C<PAST::Node> arguments
521 The methods below are used to transform PAST nodes into their
528 =item as_post(node) (General)
530 Return a POST representation of C<node>. Note that C<post> is
531 a multimethod based on the type of its first argument, this is
532 the method that is called when no other methods match.
536 This is the "fallback" method for any unrecognized node types.
537 We use this to throw a more useful exception in case any non-PAST
538 nodes make it into the tree.
542 .sub 'as_post' :method :multi(_, _)
544 .param pmc options :slurpy :named
545 unless null node goto not_null_node
546 self.'panic'("PAST::Compiler can't compile a null node")
550 self.'panic'("PAST::Compiler can't compile node of type ", $S0)
555 Return an empty POST node that can be used to hold a (PMC) result.
559 .sub 'as_post' :method :multi(_, Undef)
561 .param pmc options :slurpy :named
563 $P0 = get_hll_global ['POST'], 'Ops'
564 result = self.'uniquereg'('P')
565 .tailcall $P0.'new'('result'=>result)
569 =item as_post(Integer)
573 =item as_post(String)
575 Handle Integer, Float, and String nodes in the PAST tree, by
576 generating a constant or an appropriate register setting.
580 .sub 'as_post' :method :multi(_, Integer)
582 .param pmc options :slurpy :named
583 $P0 = get_hll_global ['POST'], 'Ops'
584 $P0 = $P0.'new'( 'result'=>node )
585 $S0 = options['rtype']
586 .tailcall self.'coerce'($P0, $S0)
589 .sub 'as_post' :method :multi(_, Float)
591 .param pmc options :slurpy :named
592 $P0 = get_hll_global ['POST'], 'Ops'
593 $P0 = $P0.'new'( 'result'=>node )
594 $S0 = options['rtype']
595 .tailcall self.'coerce'($P0, $S0)
598 .sub 'as_post' :method :multi(_, String)
600 .param pmc options :slurpy :named
602 value = self.'escape'(node)
603 $P0 = get_hll_global ['POST'], 'Ops'
604 $P0 = $P0.'new'( 'result'=>value )
605 $S0 = options['rtype']
606 .tailcall self.'coerce'($P0, $S0)
610 =item as_vivipost(String class)
612 Generate POST to create a new object of type C<class>. This
613 is typically invoked by the various vivification methods below
614 (e.g., in a PAST::Var node to default a variable to a given type).
618 .sub 'as_vivipost' :method :multi(_, String)
620 .param pmc options :slurpy :named
623 $P0 = get_hll_global ['POST'], 'Op'
624 result = self.'uniquereg'('P')
625 $S0 = self.'escape'(node)
626 .tailcall $P0.'new'(result, $S0, 'pirop'=>'new', 'result'=>result)
629 =item as_vivipost(PAST::Node node)
633 .sub 'as_vivipost' :method :multi(_, _)
635 .param pmc options :slurpy :named
636 .tailcall self.'as_post'(node, options :flat :named)
639 =item as_post(PAST::Node node)
641 Return the POST representation of executing C<node>'s children in
642 sequence. The result of the final child is used as the result
645 N.B.: This method is also the one that is invoked for converting
646 nodes of type C<PAST::Stmts>.
650 .sub 'as_post' :method :multi(_, ['PAST';'Node']) :subid('Node.as_post')
652 .param pmc options :slurpy :named
656 rtype = options['rtype']
659 $S0 = repeat 'v', $I0
661 ops = self.'post_children'(node, 'signature'=>$S0)
665 eh = node.'handlers'()
667 ops = self.'wrap_handlers'(ops, eh, 'rtype'=>rtype)
674 =head3 C<PAST::Control>
678 =item as_post(PAST::Control node)
680 Return the POST representation of a C<PAST::Control>.
684 .sub 'as_post' :method :multi(_, ['PAST';'Control'])
686 .param pmc options :slurpy :named
688 .local pmc ops, children, ishandled, nothandled
689 .local string handled
690 $P0 = get_hll_global ['POST'], 'Label'
691 $S0 = self.'unique'('handled_')
692 ishandled = $P0.'new'('result'=>$S0)
693 $S0 = self.'unique'('nothandled_')
694 nothandled = $P0.'new'('result'=>$S0)
695 $P0 = get_hll_global ['POST'], 'Ops'
696 ops = $P0.'new'('node'=>node)
698 rtype = options['rtype']
701 $S0 = repeat 'v', $I0
703 ops.'push_pirop'('.local pmc exception')
704 ops.'push_pirop'('.get_results (exception)')
705 children = self.'post_children'(node, 'signature'=>$S0)
707 handled = self.'uniquereg'('I')
708 ops.'push_pirop'('set', handled, 'exception["handled"]')
709 ops.'push_pirop'('ne', handled, 1, nothandled)
710 ops.'push'(ishandled)
711 ops.'push_pirop'('return', 'exception')
712 ops.'push'(nothandled)
713 ops.'push_pirop'('rethrow', 'exception')
717 .sub 'wrap_handlers' :method
720 .param pmc options :slurpy :named
723 rtype = options['rtype']
725 .local pmc it, node, ops, pops, tail, skip
726 $P0 = get_hll_global ['POST'], 'Ops'
727 ops = $P0.'new'('node'=>node)
728 $P0 = get_hll_global ['POST'], 'Ops'
729 pops = $P0.'new'('node'=>node)
730 $P0 = get_hll_global ['POST'], 'Ops'
731 tail = $P0.'new'('node'=>node)
732 $P0 = get_hll_global ['POST'], 'Label'
733 $S0 = self.'unique'('skip_handler_')
734 skip = $P0.'new'('result'=>$S0)
738 unless it, handler_loop_done
741 .local pmc ehpir, label, controltypes, subpost
742 .local string ehreg, type
743 $P0 = get_hll_global ['POST'], 'Label'
744 $S0 = self.'unique'('control_')
745 label = $P0.'new'('result'=>$S0)
747 subpost = find_dynamic_lex '$*SUB'
749 ehreg = self.'uniquereg'('P')
750 ops.'push_pirop'('new', ehreg, "'ExceptionHandler'")
751 ops.'push_pirop'('set_addr', ehreg, label)
752 controltypes = get_global '%!controltypes'
753 type = node.'handle_types'()
754 unless type, handle_types_done
755 type = controltypes[type]
756 unless type, handle_types_done
757 $P0 = split ',', type
758 ops.'push_pirop'('callmethod', '"handle_types"', ehreg, $P0 :flat)
759 subpost.'add_directive'('.include "except_types.pasm"')
761 type = node.'handle_types_except'()
762 unless type, handle_types_except_done
763 type = controltypes[type]
764 unless type, handle_types_except_done
765 $P0 = split ',', type
766 ops.'push_pirop'('callmethod', '"handle_types_except"', ehreg, $P0 :flat)
767 subpost.'add_directive'('.include "except_types.pasm"')
768 handle_types_except_done:
769 ops.'push_pirop'('push_eh', ehreg)
771 # Add one pop_eh for every handler we push_eh
772 pops.'push_pirop'('pop_eh')
774 # Push the handler itself
776 ehpir = self.'as_post'(node, 'rtype'=>rtype)
786 ops.'push_pirop'('goto', skip)
795 =head3 C<PAST::Block>
799 =item as_post(PAST::Block node)
801 Return the POST representation of a C<PAST::Block>.
805 .sub 'as_post' :method :multi(_, ['PAST';'Block'])
807 .param pmc options :slurpy :named
809 ## add current block node to @*BLOCKPAST
811 blockpast = find_dynamic_lex '@*BLOCKPAST'
812 unshift blockpast, node
814 .local string name, pirflags, blocktype
815 .local pmc nsentry, subid, ns, hll, multi
817 pirflags = node.'pirflags'()
818 blocktype = node.'blocktype'()
819 nsentry = node.'nsentry'()
820 subid = node.'subid'()
821 ns = node.'namespace'()
823 multi = node.'multi'()
825 ## handle nsentry attribute
826 $I0 = defined nsentry
827 unless $I0 goto nsentry_done
828 unless nsentry goto nsentry_anon
829 $S0 = self.'escape'(nsentry)
830 pirflags = concat pirflags, ' :nsentry('
831 pirflags = concat pirflags, $S0
832 pirflags = concat pirflags, ')'
835 pirflags = concat pirflags, ' :anon'
838 ## handle anonymous blocks
839 if name goto have_name
840 name = self.'unique'('_block')
842 if nsentry goto have_name
843 pirflags = concat pirflags, ' :anon'
846 ## create a POST::Sub node for this block
848 $P0 = get_hll_global ['POST'], 'Sub'
849 bpost = $P0.'new'('node'=>node, 'name'=>name, 'blocktype'=>blocktype, 'namespace'=>ns, 'hll'=>hll, 'subid'=>subid, 'multi'=>multi)
850 unless pirflags goto pirflags_done
851 bpost.'pirflags'(pirflags)
854 ## pir-encode name and namespace
855 .local string blockreg, blockref
856 blockreg = self.'uniquereg'('P')
858 blockref = concat ".const 'Sub' ", blockreg
859 concat blockref, ' = '
860 $P0 = bpost.'subid'()
861 $S0 = self.'escape'($P0)
865 $P0 = get_hll_global ['POST'], 'Compiler'
866 blockref = concat 'get_hll_global ', blockreg
867 $S0 = $P0.'key_pir'(ns)
868 concat blockref, ', '
870 $S0 = self.'escape'(name)
871 concat blockref, ', '
875 ## determine the outer POST::Sub for the new one
877 outerpost = find_dynamic_lex '$*SUB'
881 islexical = node.'lexical'()
882 unless islexical goto outer_done
883 bpost.'outer'(outerpost)
885 ## add block setup code (cpost) to outer block if needed
886 if null outerpost goto outer_done
888 $P0 = get_hll_global ['POST'], 'Ops'
889 cpost = $P0.'new'( 'result'=>blockreg )
890 cpost.'push_pirop'(blockref)
891 cpost.'push_pirop'('capture_lex', blockreg)
892 outerpost.'unshift'(cpost)
895 ## merge the node's symtable with the master
896 .local pmc outersym, symtable
897 outersym = getattribute self, '%!symtable'
899 ## if the Block doesn't have a symtable, re-use the existing one
900 $P0 = node.'symtable'()
901 unless $P0 goto have_symtable
902 ## if the Block has a default ('') entry, use the Block's symtable as-is
904 $I0 = defined symtable['']
905 if $I0 goto have_symtable
906 ## merge the Block's symtable with outersym
907 symtable = clone symtable
912 unless it goto have_symtable
914 $I0 = exists symtable[$S0]
915 if $I0 goto symtable_merge_loop
918 goto symtable_merge_loop
920 setattribute self, '%!symtable', symtable
923 compiler = node.'compiler'()
924 if compiler goto children_compiler
926 ## control exception handler
927 .local pmc ctrlpast, ctrllabel
928 ctrlpast = node.'control'()
929 unless ctrlpast goto children_past
930 $P0 = get_hll_global ['POST'], 'Label'
931 $S0 = self.'unique'('control_')
932 ctrllabel = $P0.'new'('result'=>$S0)
933 $S0 = self.'uniquereg'('P')
934 bpost.'push_pirop'('new', $S0, "'ExceptionHandler'")
935 bpost.'push_pirop'('set_addr', $S0, ctrllabel)
936 bpost.'push_pirop'('callmethod', '"handle_types"', $S0, '.CONTROL_RETURN')
937 bpost.'push_pirop'('push_eh', $S0)
938 bpost.'add_directive'('.include "except_types.pasm"')
941 ## all children but last are void context, last returns anything
944 $S0 = repeat 'v', $I0
946 ## convert children to post
947 .local pmc ops, retval
948 ops = self.'post_children'(node, 'signature'=>$S0)
949 ## wrap the child with appropriate exception handlers, if any
951 eh = node.'handlers'()
953 $S0 = options['rtype']
955 ops = self.'wrap_handlers'(ops, eh, 'rtype'=>$S0)
958 ## result of last child is return from block
962 bpost.'push_pirop'('return', retval)
964 unless ctrlpast goto sub_done
965 bpost.'push'(ctrllabel)
966 bpost.'push_pirop'('.local pmc exception')
967 bpost.'push_pirop'('.get_results (exception)')
968 $I0 = isa ctrlpast, ['PAST';'Node']
969 if $I0 goto control_past
970 if ctrlpast == 'return_pir' goto control_return
971 self.'panic'("Unrecognized control handler '", ctrlpast, "'")
973 ## handle 'return' exceptions
974 $S0 = self.'uniquereg'('P')
975 bpost.'push_pirop'('getattribute', $S0, 'exception', '"payload"')
976 bpost.'push_pirop'('return', $S0)
979 $P0 = self.'as_post'(ctrlpast, 'rtype'=>'*')
984 ## set the compiler to use for the POST::Sub node, pass on
985 ## and compiler arguments and add this block's child to it.
986 bpost.'compiler'(compiler)
987 $P0 = node.'compiler_args'()
988 bpost.'compiler_args'($P0)
993 ## generate any loadinit code for the sub
994 $I0 = exists node['loadinit']
995 unless $I0 goto loadinit_done
997 $P0 = get_hll_global ['POST'], 'Sub'
998 lisub = $P0.'new'('outer'=>bpost, 'pirflags'=>':load :init')
999 lisub.'push_pirop'(blockref)
1000 lisub.'push_pirop'('.local pmc', 'block')
1001 lisub.'push_pirop'('set', 'block', blockreg)
1002 .local pmc lipast, lipost
1003 lipast = node.'loadinit'()
1004 lipost = self.'as_post'(lipast, 'rtype'=>'v')
1005 lisub.'push'(lipost)
1006 bpost['loadinit'] = lisub
1009 ## restore previous outer scope and symtable
1010 setattribute self, '%!symtable', outersym
1012 ## return block or block result
1013 .local string rtype, result
1014 rtype = options['rtype']
1016 if blocktype == 'immediate' goto block_immediate
1017 if rtype == 'v' goto block_done
1018 $P0 = get_hll_global ['POST'], 'Ops'
1019 bpost = $P0.'new'( bpost, 'node'=>node, 'result'=>blockreg)
1020 bpost.'push_pirop'( blockref, 'result'=>blockreg )
1021 unless islexical goto block_done
1022 $I0 = node.'closure'()
1023 if $I0 goto block_closure
1024 bpost.'push_pirop'('capture_lex', blockreg)
1028 ## return a reference to a clone of the block with captured outer context
1029 result = self.'uniquereg'('P')
1030 bpost.'push_pirop'('newclosure', result, blockreg)
1031 bpost.'result'(result)
1036 arglist = options['arglist']
1037 unless null arglist goto have_arglist
1038 arglist = new 'ResizablePMCArray'
1040 result = self.'uniquereg'(rtype)
1041 $P0 = get_hll_global ['POST'], 'Ops'
1042 bpost = $P0.'new'(bpost, 'node'=>node, 'result'=>result)
1043 bpost.'push_pirop'(blockref)
1044 unless islexical goto block_immediate_capture_skip
1045 bpost.'push_pirop'('capture_lex', blockreg)
1046 block_immediate_capture_skip:
1047 bpost.'push_pirop'('call', blockreg, arglist :flat, 'result'=>result)
1050 ## remove current block from @*BLOCKPAST
1051 $P99 = shift blockpast
1062 =item as_post(PAST::Op node)
1064 Return the POST representation of a C<PAST::Op> node. Normally
1065 this is handled by redispatching to a method corresponding to
1066 the node's "pasttype" attribute.
1070 .sub 'as_post' :method :multi(_, ['PAST';'Op'])
1072 .param pmc options :slurpy :named
1074 ## see if we set first child's lvalue
1075 $I0 = node.'lvalue'()
1076 unless $I0 goto have_lvalue
1078 if null $P0 goto have_lvalue
1079 $I1 = exists $P0['lvalue']
1080 if $I1 goto have_lvalue
1084 .local string pasttype
1085 pasttype = node.'pasttype'()
1086 unless pasttype goto post_pirop
1087 $P0 = find_method self, pasttype
1088 .tailcall self.$P0(node, options :flat :named)
1092 pirop = node.'pirop'()
1093 unless pirop goto post_inline
1094 .tailcall self.'pirop'(node, options :flat :named)
1098 inline = node.'inline'()
1099 unless inline goto post_call
1100 .tailcall self.'inline'(node, options :flat :named)
1103 .tailcall self.'call'(node, options :flat :named)
1107 =item pirop(PAST::Op node)
1109 Return the POST representation of a C<PAST::Op> node with
1110 a 'pasttype' of 'pirop'.
1114 .sub 'pirop' :method :multi(_, ['PAST';'Op'])
1116 .param pmc options :slurpy :named
1118 .local string pirop, signature
1119 pirop = node.'pirop'()
1120 ## see if pirop is of form "pirop signature"
1121 $I0 = index pirop, ' '
1122 if $I0 < 0 goto pirop_0
1124 signature = substr pirop, $I1
1125 pirop = substr pirop, 0, $I0
1128 ## see if pirop is of form "pirop__signature"
1129 $I0 = index pirop, '__'
1130 if $I0 < 0 goto pirop_1
1132 signature = substr pirop, $I1
1133 pirop = substr pirop, 0, $I0
1136 $P0 = get_global '%piropsig'
1137 signature = $P0[pirop]
1138 if signature goto have_signature
1142 .local pmc ops, posargs
1143 (ops, posargs) = self.'post_children'(node, 'signature'=>signature)
1146 arglist = ops.'list'()
1148 $S0 = substr signature, 0, 1
1149 if $S0 == 'v' goto pirop_void
1150 $I0 = index '0123456789', $S0
1151 if $I0 < 0 goto pirop_reg
1156 .local string result
1157 result = self.'uniquereg'($S0)
1158 ops.'result'(result)
1159 ops.'push_pirop'(pirop, result, posargs :flat)
1162 ops.'push_pirop'(pirop, posargs :flat)
1167 =item call(PAST::Op node)
1169 Return the POST representation of a C<PAST::Op> node
1174 .sub 'call' :method :multi(_, ['PAST';'Op'])
1176 .param pmc options :slurpy :named
1177 .local string pasttype
1178 pasttype = node.'pasttype'()
1179 if pasttype goto have_pasttype
1183 .local string signature
1185 ## for callmethod, the invocant (child) must be a PMC
1186 if pasttype != 'callmethod' goto have_signature
1190 .local pmc name, ops, posargs, namedargs
1191 name = node.'name'()
1192 if name goto call_by_name
1193 ## our first child is the thing to be invoked, so make sure it's a PMC
1194 signature = replace signature, 1, 0, 'P'
1195 (ops, posargs, namedargs) = self.'post_children'(node, 'signature'=>signature)
1198 (ops, posargs, namedargs) = self.'post_children'(node, 'signature'=>signature)
1199 $I0 = isa name, ['PAST';'Node']
1200 if $I0 goto call_by_name_past
1201 $S0 = self.'escape'(name)
1202 unshift posargs, $S0
1205 .local pmc name_post
1206 name_post = self.'as_post'(name, 'rtype'=>'s')
1207 name_post = self.'coerce'(name_post, 's')
1208 ops.'push'(name_post)
1209 unshift posargs, name_post
1212 ## generate the call itself
1213 .local string result, rtype
1214 rtype = options['rtype']
1215 result = self.'uniquereg'(rtype)
1216 ops.'push_pirop'(pasttype, posargs :flat, namedargs :flat, 'result'=>result)
1217 ops.'result'(result)
1222 =item callmethod(PAST::Op node)
1224 Return the POST representation of a C<PAST::Op> node
1225 to invoke a method on a PMC.
1229 .sub 'callmethod' :method :multi(_, ['PAST';'Op'])
1231 .param pmc options :slurpy :named
1232 .tailcall self.'call'(node, options :flat :named)
1236 =item if(PAST::Op node)
1238 =item unless(PAST::Op node)
1240 Return the POST representation of C<PAST::Op> nodes with
1241 a 'pasttype' of if/unless.
1245 .sub 'if' :method :multi(_,['PAST';'Op'])
1247 .param pmc options :slurpy :named
1249 .local pmc opsclass, ops
1250 opsclass = get_hll_global ['POST'], 'Ops'
1251 ops = opsclass.'new'('node'=>node)
1253 .local string rtype, result
1254 rtype = options['rtype']
1255 result = self.'uniquereg'(rtype)
1256 ops.'result'(result)
1258 .local string pasttype
1259 pasttype = node.'pasttype'()
1261 .local pmc exprpast, thenpast, elsepast, childpast
1262 .local pmc exprpost, thenpost, elsepost, childpost
1267 .local pmc thenlabel, endlabel
1268 $P0 = get_hll_global ['POST'], 'Label'
1269 $S0 = concat pasttype, '_'
1270 $S0 = self.'unique'($S0)
1271 thenlabel = $P0.'new'('result'=>$S0)
1272 $S0 = concat $S0, '_end'
1273 endlabel = $P0.'new'('result'=>$S0)
1275 .local string exprrtype, childrtype
1277 if rtype != 'v' goto have_exprrtype
1281 $I0 = index '*:', rtype
1282 if $I0 < 0 goto have_childrtype
1286 exprpost = self.'as_post'(exprpast, 'rtype'=>exprrtype)
1289 jmpstack = new 'ResizableIntegerArray'
1290 childpast = thenpast
1291 local_branch jmpstack, make_childpost
1292 thenpost = childpost
1293 childpast = elsepast
1294 local_branch jmpstack, make_childpost
1295 elsepost = childpost
1297 if null elsepost goto no_elsepost
1299 ops.'push'(exprpost)
1300 ops.'push_pirop'(pasttype, exprpost, thenlabel)
1301 if null elsepost goto else_done
1302 ops.'push'(elsepost)
1304 ops.'push_pirop'('goto', endlabel)
1305 ops.'push'(thenlabel)
1306 if null thenpost goto then_done
1307 ops.'push'(thenpost)
1309 ops.'push'(endlabel)
1314 unless pasttype == $S0 goto no_elsepost_1
1317 ops.'push'(exprpost)
1318 ops.'push_pirop'($S0, exprpost, endlabel)
1319 if null thenpost goto no_elsepost_2
1320 ops.'push'(thenpost)
1322 ops.'push'(endlabel)
1327 $I0 = defined childpast
1328 unless $I0 goto no_childpast
1330 arglist = new 'ResizablePMCArray'
1331 $I0 = childpast.'arity'()
1332 unless $I0 > 0 goto have_arglist
1333 push arglist, exprpost
1335 childpost = self.'as_post'(childpast, 'rtype'=>childrtype, 'arglist'=>arglist)
1336 goto childpost_coerce
1338 if rtype == 'v' goto ret_childpost
1339 childpost = opsclass.'new'('result'=>exprpost)
1341 unless result goto ret_childpost
1342 childpost = self.'coerce'(childpost, result)
1344 local_return jmpstack
1347 .sub 'unless' :method :multi(_, ['PAST';'Op'])
1349 .param pmc options :slurpy :named
1350 .tailcall self.'if'(node, options :flat :named)
1356 Generate a standard loop with NEXT/LAST/REDO exception handling.
1360 .sub 'loop_gen' :method
1361 .param pmc options :slurpy :named
1363 .local pmc testlabel, prelabel, redolabel, nextlabel, donelabel, handlabel
1364 $P0 = get_hll_global ['POST'], 'Label'
1365 .local string loopname
1366 loopname = self.'unique'('loop')
1367 $S0 = concat loopname, '_test'
1368 testlabel = $P0.'new'('result'=>$S0)
1369 $S0 = concat loopname, '_redo'
1370 redolabel = $P0.'new'('result'=>$S0)
1371 $S0 = concat loopname, '_next'
1372 nextlabel = $P0.'new'('result'=>$S0)
1373 $S0 = concat loopname, '_done'
1374 donelabel = $P0.'new'('result'=>$S0)
1375 $S0 = concat loopname, '_handler'
1376 handlabel = $P0.'new'('result'=>$S0)
1378 .local pmc testpost, prepost, bodypost, nextpost
1379 .local string testop
1380 .local int bodyfirst
1381 testop = options['testop']
1382 testpost = options['test']
1383 prepost = options['pre']
1384 bodypost = options['body']
1385 nextpost = options['next']
1386 bodyfirst = options['bodyfirst']
1388 if testop goto have_testop
1393 $P0 = get_hll_global ['POST'], 'Ops'
1396 $P0 = find_dynamic_lex '$*SUB'
1397 $P0.'add_directive'('.include "except_types.pasm"')
1399 .local string handreg
1400 handreg = self.'uniquereg'('P')
1401 ops.'push_pirop'('new', handreg, "'ExceptionHandler'")
1402 ops.'push_pirop'('set_addr', handreg, handlabel)
1403 ops.'push_pirop'('callmethod', '"handle_types"', handreg, '.CONTROL_LOOP_NEXT', '.CONTROL_LOOP_REDO', '.CONTROL_LOOP_LAST')
1404 ops.'push_pirop'('push_eh', handreg)
1406 unless bodyfirst goto bodyfirst_done
1407 ops.'push_pirop'('goto', redolabel)
1409 ops.'push'(testlabel)
1410 if null testpost goto test_done
1411 ops.'push'(testpost)
1412 ops.'push_pirop'(testop, testpost, donelabel)
1414 if null prepost goto pre_done
1417 ops.'push'(redolabel)
1418 if null bodypost goto body_done
1419 ops.'push'(bodypost)
1421 ops.'push'(nextlabel)
1422 if null nextpost goto next_done
1423 ops.'push'(nextpost)
1425 ops.'push_pirop'('goto', testlabel)
1426 ops.'push'(handlabel)
1427 ops.'push_pirop'('.local pmc exception')
1428 ops.'push_pirop'('.get_results (exception)')
1429 $S0 = self.'uniquereg'('P')
1430 ops.'push_pirop'('getattribute', $S0, 'exception', "'type'")
1431 ops.'push_pirop'('eq', $S0, '.CONTROL_LOOP_NEXT', nextlabel)
1432 ops.'push_pirop'('eq', $S0, '.CONTROL_LOOP_REDO', redolabel)
1433 ops.'push'(donelabel)
1434 ops.'push_pirop'('pop_eh')
1439 =item while(PAST::Op node)
1441 =item until(PAST::Op node)
1443 =item repeat_while(PAST::Op node)
1445 =item repeat_until(PAST::Op node)
1447 Return the POST representation of a C<while> or C<until> loop.
1451 .sub 'while' :method :multi(_, ['PAST';'Op'])
1453 .param pmc options :slurpy :named
1454 .local pmc exprpast, bodypast, nextpast
1459 .local pmc exprpost, bodypost, nextpost
1460 exprpost = self.'as_post'(exprpast, 'rtype'=>'r')
1463 arglist = new 'ResizablePMCArray'
1464 $I0 = bodypast.'arity'()
1465 if $I0 < 1 goto have_arglist
1466 push arglist, exprpost
1468 bodypost = self.'as_post'(bodypast, 'rtype'=>'v', 'arglist'=>arglist)
1471 if null nextpast goto have_nextpost
1472 nextpost = self.'as_post'(nextpast, 'rtype'=>'v')
1475 .local string testop
1476 testop = options['testop']
1477 .local int bodyfirst
1478 bodyfirst = options['bodyfirst']
1481 ops = self.'loop_gen'('testop'=>testop, 'test'=>exprpost, 'body'=>bodypost, 'bodyfirst'=>bodyfirst, 'next'=>nextpost)
1482 ops.'result'(exprpost)
1487 .sub 'until' :method :multi(_, ['PAST';'Op'])
1489 .param pmc options :slurpy :named
1490 .tailcall self.'while'(node, options :flat :named, 'testop'=>'if')
1493 .sub 'repeat_while' :method :multi(_, ['PAST';'Op'])
1495 .param pmc options :slurpy :named
1496 .tailcall self.'while'(node, options :flat :named, 'bodyfirst'=>1)
1499 .sub 'repeat_until' :method :multi(_, ['PAST';'Op'])
1501 .param pmc options :slurpy :named
1502 .tailcall self.'while'(node, options :flat :named, 'testop'=>'if', 'bodyfirst'=>1)
1506 =item for(PAST::Op node)
1508 Return the POST representation of the C<for> loop given
1513 .sub 'for' :method :multi(_, ['PAST';'Op'])
1515 .param pmc options :slurpy :named
1517 .local pmc ops, prepost, testpost
1518 $P0 = get_hll_global ['POST'], 'Ops'
1519 ops = $P0.'new'('node'=>node)
1520 prepost = $P0.'new'()
1521 $S0 = self.'uniquereg'('P')
1522 testpost = $P0.'new'('result'=>$S0)
1524 .local pmc collpast, bodypast
1529 collpost = self.'as_post'(collpast, 'rtype'=>'P')
1530 ops.'push'(collpost)
1532 ## don't try to iterate undefined values
1533 .local pmc undeflabel
1534 $P0 = get_hll_global ['POST'], 'Label'
1535 undeflabel = $P0.'new'('name'=>'for_undef_')
1536 $S0 = self.'uniquereg'('I')
1537 ops.'push_pirop'('defined', $S0, collpost)
1538 ops.'push_pirop'('unless', $S0, undeflabel)
1540 ops.'push_pirop'('iter', testpost, collpost)
1542 ## determine the arity of the loop. We check arity of the 'for'
1543 ## node itself, and if not set we use the arity of the body.
1546 $P0 = node.'arity'()
1548 unless $I0 goto arity_child
1552 $P0 = bodypast.'arity'()
1554 unless $I0 goto have_arity
1558 ## build the argument list to pass to the body
1560 arglist = new 'ResizablePMCArray'
1562 .local string nextarg
1563 nextarg = self.'uniquereg'('P')
1564 prepost.'push_pirop'('shift', nextarg, testpost)
1565 if arity < 1 goto arity_end
1566 push arglist, nextarg
1568 if arity > 0 goto arity_loop
1571 ## now build the body itself
1573 bodypost = self.'as_post'(bodypast, 'rtype'=>'v', 'arglist'=>arglist)
1575 ## generate the loop and return
1576 $P0 = self.'loop_gen'('test'=>testpost, 'pre'=>prepost, 'body'=>bodypost)
1578 ops.'push'(undeflabel)
1579 ops.'result'(testpost)
1584 =item list(PAST::Op node)
1586 Build a list from the children. The type of list constructed
1587 is determined by the C<returns> attribute, which defaults
1588 to C<ResizablePMCArray> if not set.
1592 .sub 'list' :method :multi(_, ['PAST';'Op'])
1594 .param pmc options :slurpy :named
1596 .local pmc ops, posargs
1597 (ops, posargs) = self.'post_children'(node, 'signature'=>'v*')
1600 returns = node.'returns'()
1601 if returns goto have_returns
1602 returns = box 'ResizablePMCArray'
1605 .local pmc listpost, it
1606 listpost = self.'as_vivipost'(returns, 'rtype'=>'P')
1607 ops.'result'(listpost)
1608 ops.'push'(listpost)
1611 unless it goto iter_end
1613 ops.'push_pirop'('push', listpost, $S0)
1620 =item stmts(PAST::Op node)
1622 Treat the node like a PAST::Stmts node -- i.e., invoke all the
1623 children and return the value of the last one.
1627 .sub 'stmts' :method :multi(_, ['PAST';'Op'])
1629 .param pmc options :slurpy :named
1631 .const 'Sub' $P0 = 'Node.as_post'
1632 .tailcall self.$P0(node, options :flat :named)
1636 =item null(PAST::Op node)
1638 A "no-op" node -- none of the children are processed, and
1639 no statements are generated.
1643 .sub 'null' :method :multi(_, ['PAST';'Op'])
1645 .param pmc options :slurpy :named
1646 $P0 = get_hll_global ['POST'], 'Ops'
1647 .tailcall $P0.'new'('node'=>node)
1651 =item return(PAST::Op node)
1653 Generate a return exception, using the first child (if any) as
1658 .sub 'return' :method :multi(_, ['PAST';'Op'])
1660 .param pmc options :slurpy :named
1663 $P0 = get_hll_global ['POST'], 'Ops'
1664 ops = $P0.'new'('node'=>node)
1666 .local string exreg, extype
1667 exreg = self.'uniquereg'('P')
1668 extype = concat exreg, "['type']"
1669 ops.'push_pirop'('new', exreg, '"Exception"')
1670 ops.'push_pirop'('set', extype, '.CONTROL_RETURN')
1671 $P0 = find_dynamic_lex '$*SUB'
1672 $P0.'add_directive'('.include "except_types.pasm"')
1674 .local pmc cpast, cpost
1676 unless cpast goto cpast_done
1677 cpost = self.'as_post'(cpast, 'rtype'=>'P')
1678 cpost = self.'coerce'(cpost, 'P')
1680 ops.'push_pirop'('setattribute', exreg, "'payload'", cpost)
1682 ops.'push_pirop'('throw', exreg)
1687 =item try(PAST::Op node)
1689 Return the POST representation of a C<PAST::Op>
1690 node with a 'pasttype' of bind. The first child
1691 is the code to be surrounded by an exception handler,
1692 the second child (if any) is the code to process the
1697 .sub 'try' :method :multi(_, ['PAST';'Op'])
1699 .param pmc options :slurpy :named
1702 $P0 = get_hll_global ['POST'], 'Ops'
1703 ops = $P0.'new'('node'=>node)
1705 .local pmc catchlabel, endlabel
1706 $P0 = get_hll_global ['POST'], 'Label'
1707 $S0 = self.'unique'('catch_')
1708 catchlabel = $P0.'new'('result'=>$S0)
1709 $S0 = concat $S0, '_end'
1710 endlabel = $P0.'new'('result'=>$S0)
1713 rtype = options['rtype']
1715 .local pmc trypast, trypost
1717 trypost = self.'as_post'(trypast, 'rtype'=>rtype)
1718 ops.'push_pirop'('push_eh', catchlabel)
1720 ops.'push_pirop'('pop_eh')
1721 .local pmc elsepast, elsepost
1723 if null elsepast goto else_done
1724 elsepost = self.'as_post'(elsepast, 'rtype'=>'v')
1725 ops.'push'(elsepost)
1727 ops.'push_pirop'('goto', endlabel)
1728 ops.'push'(catchlabel)
1729 .local pmc catchpast, catchpost
1731 if null catchpast goto catch_done
1732 catchpost = self.'as_post'(catchpast, 'rtype'=>'v')
1733 ops.'push'(catchpost)
1734 ops.'push_pirop'('pop_eh') # FIXME: should be before catchpost
1736 ops.'push'(endlabel)
1737 ops.'result'(trypost)
1742 =item chain(PAST::Op node)
1744 A short-circuiting chain of operations. In a sequence of nodes
1745 with pasttype 'chain', the right operand of a node serves as
1746 the left operand of its parent. Each node is evaluated only
1747 once, and the first false result short-circuits the chain.
1748 In other words, C<< $x < $y < $z >> is true only if
1749 $x < $y and $y < $z, but $y only gets evaluated once.
1753 .sub 'chain' :method :multi(_, ['PAST';'Op'])
1755 .param pmc options :slurpy :named
1756 .local pmc clist, cpast
1758 ## first, we build up the list of nodes in the chain
1759 clist = new 'ResizablePMCArray'
1762 $I0 = isa cpast, ['PAST';'Op']
1763 if $I0 == 0 goto chain_end
1764 .local string pasttype
1765 pasttype = cpast.'pasttype'()
1766 if pasttype != 'chain' goto chain_end
1772 .local pmc ops, endlabel
1773 $P0 = get_hll_global ['POST'], 'Ops'
1774 ops = $P0.'new'('node'=>node)
1775 $S0 = self.'unique'('$P')
1777 $P0 = get_hll_global ['POST'], 'Label'
1778 endlabel = $P0.'new'('name'=>'chain_end_')
1780 .local pmc apast, apost
1783 apost = self.'as_post'(apast, 'rtype'=>'P')
1787 .local pmc bpast, bpost
1789 bpost = self.'as_post'(bpast, 'rtype'=>'P')
1792 name = cpast.'name'()
1793 name = self.'escape'(name)
1794 ops.'push_pirop'('call', name, apost, bpost, 'result'=>ops)
1795 unless clist goto clist_end
1796 ops.'push_pirop'('unless', ops, endlabel)
1801 ops.'push'(endlabel)
1806 =item def_or(PAST::Op node)
1808 The short-circuiting default operator (e.g., Perl 6's C<< infix:<//> >>).
1809 Returns its first child if its defined, otherwise it evaluates and returns
1810 the second child. (N.B.: This particular pasttype is a candidate for
1811 being refactored out using thunks of some sort.)
1815 .sub 'def_or' :method :multi(_, ['PAST';'Op'])
1817 .param pmc options :slurpy :named
1820 $P0 = get_hll_global ['POST'], 'Ops'
1821 $S0 = self.'unique'('$P')
1822 ops = $P0.'new'('node'=>node, 'result'=>$S0)
1824 .local pmc lpast, lpost
1826 lpost = self.'as_post'(lpast, 'rtype'=>'P')
1828 ops.'push_pirop'('set', ops, lpost)
1831 $P0 = get_hll_global ['POST'], 'Label'
1832 $S0 = self.'unique'('default_')
1833 endlabel = $P0.'new'('result'=>$S0)
1835 $S0 = self.'unique'('$I')
1836 ops.'push_pirop'('defined', $S0, ops)
1837 ops.'push_pirop'('if', $S0, endlabel)
1838 .local pmc rpast, rpost
1840 rpost = self.'as_post'(rpast, 'rtype'=>'P')
1842 ops.'push_pirop'('set', ops, rpost)
1843 ops.'push'(endlabel)
1848 =item xor(PAST::Op node)
1850 A short-circuiting exclusive-or operation. Each child is evaluated,
1851 if exactly one child evaluates to true then its value is returned,
1852 otherwise return Undef. Short-circuits with Undef as soon as
1853 a second child is found that evaluates as true.
1857 .sub 'xor' :method :multi(_,['PAST';'Op'])
1859 .param pmc options :slurpy :named
1862 $P0 = get_hll_global ['POST'], 'Ops'
1863 ops = $P0.'new'('node'=>node)
1864 $S0 = self.'unique'('$P')
1867 .local pmc labelproto, endlabel, falselabel
1868 labelproto = get_hll_global ['POST'], 'Label'
1869 falselabel = labelproto.'new'('name'=>'xor_false')
1870 endlabel = labelproto.'new'('name'=>'xor_end')
1872 .local pmc iter, apast, apost, i, t, u
1873 i = self.'unique'('$I')
1874 t = self.'unique'('$I')
1875 u = self.'unique'('$I')
1876 iter = node.'iterator'()
1878 apost = self.'as_post'(apast, 'rtype'=>'P')
1880 ops.'push_pirop'('set', ops, apost)
1881 ops.'push_pirop'('istrue', t, apost)
1883 .local pmc bpast, bpost
1885 bpost = self.'as_post'(bpast, 'rtype'=>'P')
1887 ops.'push_pirop'('istrue', u, bpost)
1888 ops.'push_pirop'('and', i, t, u)
1889 ops.'push_pirop'('if', i, falselabel)
1890 unless iter goto last_child
1891 .local pmc truelabel
1892 truelabel = labelproto.'new'('name'=>'xor_true')
1893 ops.'push_pirop'('if', t, truelabel)
1894 ops.'push_pirop'('set', ops, bpost)
1895 ops.'push_pirop'('set', t, u)
1896 ops.'push'(truelabel)
1899 ops.'push_pirop'('if', t, endlabel)
1900 ops.'push_pirop'('set', ops, bpost)
1901 ops.'push_pirop'('goto', endlabel)
1902 ops.'push'(falselabel)
1903 ops.'push_pirop'('new', ops, '"Undef"')
1904 ops.'push'(endlabel)
1909 =item bind(PAST::Op node)
1911 Return the POST representation of a C<PAST::Op>
1912 node with a 'pasttype' of bind.
1916 .sub 'bind' :method :multi(_, ['PAST';'Op'])
1918 .param pmc options :slurpy :named
1920 .local pmc ops, lpast, rpast, lpost, rpost
1924 $P0 = get_hll_global ['POST'], 'Ops'
1925 ops = $P0.'new'('node'=>node)
1926 rpost = self.'as_post'(rpast, 'rtype'=>'P')
1927 rpost = self.'coerce'(rpost, 'P')
1931 lpost = self.'as_post'(lpast, 'bindpost'=>rpost)
1938 =item copy(PAST::Op node)
1940 Implement a 'copy' assignment (at least until we get the 'copy' opcode).
1944 .sub 'copy' :method :multi(_, ['PAST';'Op'])
1946 .param pmc options :slurpy :named
1947 .local pmc rpast, rpost, lpast, lpost
1950 rpost = self.'as_post'(rpast, 'rtype'=>'P')
1951 lpost = self.'as_post'(lpast, 'rtype'=>'P')
1952 .local pmc ops, alabel
1953 $P0 = get_hll_global ['POST'], 'Ops'
1954 ops = $P0.'new'(rpost, lpost, 'node'=>node, 'result'=>lpost)
1955 ops.'push_pirop'('copy', lpost, rpost)
1960 =item inline(PAST::Op node)
1962 Return the POST representation of a C<PAST::Op>
1963 node with a 'pasttype' of inline.
1967 .sub 'inline' :method :multi(_, ['PAST';'Op'])
1969 .param pmc options :slurpy :named
1972 ops = self.'post_children'(node, 'signature'=>'vP')
1974 .local pmc inline_pmc
1975 .local string inline
1976 inline_pmc = node.'inline'()
1977 $I0 = does inline_pmc, 'array'
1978 if $I0 goto inline_array
1982 inline = join "\n", inline_pmc
1985 .local string result
1987 $I0 = index inline, '%t'
1988 if $I0 >= 0 goto result_new
1989 $I0 = index inline, '%r'
1990 unless $I0 >= 0 goto have_result
1991 result = self.'unique'('$P')
1992 ops.'result'(result)
1995 result = self.'unique'('$P')
1996 ops.'push_pirop'('new', result, "'Undef'")
1997 ops.'result'(result)
2001 arglist = ops.'list'()
2002 ops.'push_pirop'('inline', arglist :flat, 'inline'=>inline, 'result'=>result)
2003 $S0 = options['rtype']
2014 =item as_post(PAST::Var node)
2016 Return the POST representation of a C<PAST::Var>. Generally we
2017 redispatch to an appropriate handler based on the node's 'scope'
2022 .sub 'as_post' :method :multi(_, ['PAST';'Var'])
2024 .param pmc options :slurpy :named
2028 bindpost = options['bindpost']
2029 unless null bindpost goto have_bindpost
2030 bindpost = new 'Undef'
2033 ## determine the node's scope. First, check the node itself
2035 scope = node.'scope'()
2036 if scope goto have_scope
2037 ## otherwise, check the current symbol table under the variable's name
2039 name = node.'name'()
2041 symtable = getattribute self, '%!symtable'
2042 $P0 = symtable[name]
2043 if null $P0 goto default_scope
2044 scope = $P0['scope']
2045 if scope goto have_scope
2047 ## see if an outer block has set a default scope
2049 if null $P0 goto scope_error
2050 scope = $P0['scope']
2051 unless scope goto scope_error
2053 push_eh scope_error_ex
2054 $P0 = find_method self, scope
2055 .tailcall self.$P0(node, bindpost)
2059 unless scope goto scope_error_1
2060 scope = concat " in '", scope
2061 scope = concat scope, "' scope"
2063 # Find the nearest named block
2064 .local string blockname
2067 $P0 = find_dynamic_lex '@*BLOCKPAST'
2069 scope_error_block_loop:
2070 unless it goto scope_error_2
2072 blockname = $P0.'name'()
2073 unless blockname goto scope_error_block_loop
2075 if blockname goto have_blockname
2076 blockname = '<anonymous>'
2078 # Find the source location, if available
2079 .local string sourceline
2080 .local pmc source, pos, files
2082 source = node['source']
2084 if null source goto scope_error_3
2085 files = find_caller_lex '$?FILES'
2086 if null files goto scope_error_3
2088 sourceline = concat ' (', $S0
2089 concat sourceline, ':'
2090 $I0 = self.'lineof'(source, pos)
2093 concat sourceline, $S0
2094 concat sourceline, ')'
2096 .tailcall self.'panic'("Symbol '", name, "' not predeclared", scope, " in ", blockname, sourceline)
2100 .sub 'vivify' :method
2106 .local pmc viviself, vivipost, vivilabel
2107 viviself = node.'viviself'()
2108 vivipost = self.'as_vivipost'(viviself, 'rtype'=>'P')
2109 .local string result
2110 result = vivipost.'result'()
2111 unless result == '' goto have_result
2112 result = self.'uniquereg'('P')
2114 ops.'result'(result)
2116 unless viviself goto vivipost_done
2117 $P0 = get_hll_global ['POST'], 'Label'
2118 vivilabel = $P0.'new'('name'=>'vivify_')
2119 ops.'push_pirop'('unless_null', ops, vivilabel)
2120 ops.'push'(vivipost)
2121 $I0 = node.'lvalue'()
2122 unless $I0 goto vivipost_stored
2125 ops.'push'(vivilabel)
2131 .sub 'parameter' :method :multi(_, ['PAST';'Var'])
2135 ## get the current sub
2137 subpost = find_dynamic_lex '$*SUB'
2139 ## determine lexical, register, and parameter names
2140 .local string named, pname, has_pname
2142 name = node.'name'()
2143 named = node.'named'()
2144 pname = self.'unique'('param_')
2145 has_pname = concat 'has_', pname
2147 ## returned post node
2149 $P0 = get_hll_global ['POST'], 'Ops'
2150 ops = $P0.'new'('node'=>node, 'result'=>pname)
2152 ## handle optional params
2153 .local pmc viviself, vivipost, vivilabel
2154 viviself = node.'viviself'()
2155 unless viviself goto param_required
2156 vivipost = self.'as_vivipost'(viviself, 'rtype'=>'P')
2157 $P0 = get_hll_global ['POST'], 'Label'
2158 vivilabel = $P0.'new'('name'=>'optparam_')
2159 subpost.'add_param'(pname, 'named'=>named, 'optional'=>1)
2160 ops.'push_pirop'('if', has_pname, vivilabel)
2161 ops.'push'(vivipost)
2162 ops.'push_pirop'('set', ops, vivipost)
2163 ops.'push'(vivilabel)
2167 .local int call_sig, slurpy
2168 call_sig = node.'call_sig'()
2169 slurpy = node.'slurpy'()
2170 subpost.'add_param'(pname, 'named'=>named, 'slurpy'=>slurpy, 'call_sig'=>call_sig)
2174 unless $I0 goto param_lex_done
2175 name = self.'escape'(name)
2176 ops.'push_pirop'('.lex', name, ops)
2182 .sub 'package' :method :multi(_, ['PAST';'Var'])
2186 .local pmc ops, fetchop, storeop
2187 $P0 = get_hll_global ['POST'], 'Ops'
2188 ops = $P0.'new'('node'=>node)
2191 name = node.'name'()
2192 name = self.'escape'(name)
2194 $P0 = get_hll_global ['POST'], 'Op'
2196 ns = node.'namespace'()
2198 if $I0 goto package_hll
2199 if bindpost goto package_bind
2200 fetchop = $P0.'new'(ops, name, 'pirop'=>'get_global')
2201 storeop = $P0.'new'(name, ops, 'pirop'=>'set_global')
2202 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2204 .tailcall $P0.'new'(name, bindpost, 'pirop'=>'set_global', 'result'=>bindpost)
2207 if ns goto package_ns
2208 if bindpost goto package_hll_bind
2209 fetchop = $P0.'new'(ops, name, 'pirop'=>'get_hll_global')
2210 storeop = $P0.'new'(name, ops, 'pirop'=>'set_hll_global')
2211 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2213 .tailcall $P0.'new'(name, bindpost, 'pirop'=>'set_hll_global', 'result'=>bindpost)
2216 $P1 = get_hll_global ['POST'], 'Compiler'
2217 ns = $P1.'key_pir'(ns)
2218 if bindpost goto package_ns_bind
2219 fetchop = $P0.'new'(ops, ns, name, 'pirop'=>'get_hll_global')
2220 storeop = $P0.'new'(ns, name, ops, 'pirop'=>'set_hll_global')
2221 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2223 .tailcall $P0.'new'(ns, name, bindpost, 'pirop'=>'set_hll_global', 'result'=>bindpost)
2227 .sub 'lexical' :method :multi(_, ['PAST';'Var'])
2232 $P0 = get_hll_global ['POST'], 'Ops'
2233 name = node.'name'()
2234 name = self.'escape'(name)
2237 isdecl = node.'isdecl'()
2239 if bindpost goto lexical_bind
2242 if isdecl goto lexical_decl
2243 .local pmc ops, fetchop, storeop
2244 ops = $P0.'new'('node'=>node)
2245 $P0 = get_hll_global ['POST'], 'Op'
2246 fetchop = $P0.'new'(ops, name, 'pirop'=>'find_lex')
2247 storeop = $P0.'new'(name, ops, 'pirop'=>'store_lex')
2248 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2251 ops = $P0.'new'('node'=>node)
2252 .local pmc viviself, vivipost
2253 viviself = node.'viviself'()
2254 vivipost = self.'as_vivipost'(viviself, 'rtype'=>'P')
2255 ops.'push'(vivipost)
2256 ops.'push_pirop'('.lex', name, vivipost)
2257 ops.'result'(vivipost)
2261 $P0 = get_hll_global ['POST'], 'Op'
2262 if isdecl goto lexical_bind_decl
2263 .tailcall $P0.'new'(name, bindpost, 'pirop'=>'store_lex', 'result'=>bindpost)
2265 .tailcall $P0.'new'(name, bindpost, 'pirop'=>'.lex', 'result'=>bindpost)
2269 .sub 'contextual' :method :multi(_, ['PAST';'Var'])
2272 # If we've requested a contextual in a block that
2273 # explicitly declares the variable as a different type,
2274 # treat it as that type.
2276 name = node.'name'()
2277 $P0 = find_dynamic_lex '@*BLOCKPAST'
2279 $P0 = $P0.'symtable'()
2280 unless $P0 goto contextual
2282 if null $P0 goto contextual
2284 unless $S0 goto contextual
2285 if $S0 == 'contextual' goto contextual
2286 .tailcall self.$S0(node, bindpost)
2289 # If this is a declaration, treat it like a normal lexical
2291 isdecl = node.'isdecl'()
2292 if isdecl goto contextual_lex
2294 name = self.'escape'(name)
2295 if bindpost goto contextual_bind
2298 .local pmc ops, fetchop, storeop
2299 $P0 = get_hll_global ['POST'], 'Ops'
2300 ops = $P0.'new'('node'=>node)
2301 $P0 = get_hll_global ['POST'], 'Op'
2302 fetchop = $P0.'new'(ops, name, 'pirop'=>'find_dynamic_lex')
2303 storeop = $P0.'new'(name, ops, 'pirop'=>'store_dynamic_lex')
2304 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2307 $P0 = get_hll_global ['POST'], 'Op'
2308 .tailcall $P0.'new'(name, bindpost, 'pirop'=>'store_dynamic_lex', 'result'=>bindpost)
2311 .tailcall self.'lexical'(node, bindpost)
2315 .sub 'keyed' :method :multi(_, ['PAST';'Var'])
2318 .param string keyrtype :optional
2319 .param int has_keyrtype :opt_flag
2322 $P0 = get_hll_global ['POST'], 'Ops'
2323 ops = $P0.'new'('node'=>node)
2325 if has_keyrtype goto have_keyrtype
2329 .local pmc keypast, keypost
2331 keypost = self.'as_post'(keypast, 'rtype'=>keyrtype)
2332 keypost = self.'coerce'(keypost, keyrtype)
2335 .local pmc basepast, basepost
2338 $P0 = node.'vivibase'()
2339 unless $P0 goto have_vivibase
2340 $I0 = can basepast, 'viviself'
2341 unless $I0 goto have_vivibase
2342 $P1 = basepast.'viviself'()
2343 unless $P1 goto vivibase_1
2344 if $P1 != 'Undef' goto have_vivibase
2346 basepast.'viviself'($P0)
2349 # if the keyed node is an lvalue, its base is an lvalue also
2350 $I0 = node.'lvalue'()
2351 unless $I0 goto have_lvalue
2352 basepast.'lvalue'($I0)
2355 basepost = self.'as_post'(basepast, 'rtype'=>'P')
2356 ops.'push'(basepost)
2358 $S0 = basepost.'result'()
2359 name = concat $S0, '['
2360 $S0 = keypost.'result'()
2363 .local pmc fetchop, storeop
2364 $P0 = get_hll_global ['POST'], 'Op'
2365 if bindpost goto keyed_bind
2366 fetchop = $P0.'new'(ops, name, 'pirop'=>'set')
2367 storeop = $P0.'new'(name, ops, 'pirop'=>'set')
2368 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2370 ops.'result'(bindpost)
2371 ops.'push_pirop'('set', name, ops)
2376 .sub 'keyed_int' :method :multi(_, ['PAST';'Var'])
2379 .tailcall self.'keyed'(node, bindpost, 'i')
2383 .sub 'attribute' :method :multi(_, ['PAST';'Var'])
2388 $P0 = get_hll_global ['POST'], 'Ops'
2389 ops = $P0.'new'('node'=>node)
2391 name = node.'name'()
2392 name = self.'escape'(name)
2396 if null call_on goto use_self
2397 call_on = self.'as_post'(call_on, 'rtype'=>'P')
2401 call_on = box 'self'
2404 if bindpost goto attribute_bind
2407 .local pmc fetchop, storeop
2408 $P0 = get_hll_global ['POST'], 'Op'
2409 fetchop = $P0.'new'(ops, call_on, name, 'pirop'=>'getattribute')
2410 storeop = $P0.'new'(call_on, name, ops, 'pirop'=>'setattribute')
2411 .tailcall self.'vivify'(node, ops, fetchop, storeop)
2414 ops.'push_pirop'('setattribute', call_on, name, bindpost)
2415 ops.'result'(bindpost)
2420 .sub 'register' :method :multi(_, ['PAST';'Var'])
2425 name = node.'name'()
2426 if name goto have_name
2427 name = self.'uniquereg'('P')
2432 $P0 = get_hll_global ['POST'], 'Ops'
2433 ops = $P0.'new'('result'=>name, 'node'=>node)
2436 isdecl = node.'isdecl'()
2437 unless isdecl goto decl_done
2438 ops.'push_pirop'('.local pmc', ops)
2441 if bindpost goto register_bind
2443 .local pmc viviself, vivipost
2444 viviself = node.'viviself'()
2445 unless viviself goto end
2446 vivipost = self.'as_vivipost'(viviself, 'rtype'=>'P')
2447 ops.'push'(vivipost)
2448 ops.'push_pirop'('set', ops, vivipost)
2452 ops.'push_pirop'('set', ops, bindpost)
2465 =item as_post(PAST::Val node [, 'rtype'=>rtype])
2467 Return the POST representation of the constant value given
2468 by C<node>. The C<rtype> parameter advises the method whether
2469 the value may be returned directly as a PIR constant or needs
2470 to have a PMC generated containing the constant value.
2474 .sub 'as_post' :method :multi(_, ['PAST';'Val'])
2476 .param pmc options :slurpy :named
2479 $P0 = get_hll_global ['POST'], 'Ops'
2480 ops = $P0.'new'('node'=>node)
2482 .local pmc value, returns
2483 value = node['value']
2484 if null value goto err_novalue
2485 $I0 = isa value, ['PAST';'Block']
2486 if $I0 goto value_block
2487 returns = node.'returns'()
2488 if returns goto have_returns
2493 .local string valflags
2494 $P0 = get_global '%valflags'
2495 valflags = $P0[returns]
2497 $I0 = index valflags, 'e'
2498 if $I0 < 0 goto escape_done
2499 value = self.'escape'(value)
2502 # See if this is a pasm constant type
2503 $I0 = index valflags, 'c'
2504 if $I0 < 0 goto const_done
2505 # Add the directive for the appropriate .include statement.
2507 $S0 = replace $S0, 0, 1, '.include "'
2508 $S0 = concat $S0, '.pasm"'
2509 $P0 = find_dynamic_lex '$*SUB'
2510 $P0.'add_directive'($S0)
2511 # Add a leading dot to the value if one isn't already there.
2512 $S0 = substr value, 0, 1
2513 if $S0 == '.' goto const_done
2515 value = concat $P0, value
2519 rtype = options['rtype']
2520 $I0 = index valflags, rtype
2521 if $I0 < 0 goto result_convert
2526 # handle int-to-num conversion here
2527 if rtype != 'n' goto result_pmc
2528 $I0 = index valflags, 'i'
2529 if $I0 < 0 goto result_pmc
2530 value = concat value, '.0'
2535 .local string result
2536 result = self.'uniquereg'('P')
2537 returns = self.'escape'(returns)
2538 ops.'push_pirop'('new', result, returns)
2539 ops.'push_pirop'('assign', result, value)
2540 ops.'result'(result)
2544 .local string blockreg, blockref
2545 blockreg = self.'uniquereg'('P')
2546 blockref = concat ".const 'Sub' ", blockreg
2547 concat blockref, ' = '
2548 $P0 = value.'subid'()
2549 $S0 = self.'escape'($P0)
2550 concat blockref, $S0
2551 ops.'push_pirop'(blockref)
2552 ops.'result'(blockreg)
2556 self.'panic'('PAST::Val node missing :value attribute')
2564 Patrick Michaud <pmichaud@pobox.com> is the author and maintainer.
2565 Please send patches and suggestions to the Parrot porters or
2566 Perl 6 compilers mailing lists.
2570 2006-11-20 Patrick Michaud added first draft of POD documentation.
2571 2006-11-27 Significant refactor into separate modules.
2575 Copyright (C) 2006-2008, Parrot Foundation.
2583 # vim: expandtab shiftwidth=4 ft=pir: