fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / compilers / pge / PGE / Exp.pir
blob5794e9f09ec094b2bea3fb6a6e75b9a541e2d7ae
1 # Copyright (C) 2005-2009, Parrot Foundation.
2 # $Id$
4 =head1 TITLE
6 PGE::Exp - base class for expressions
8 =cut
10 .namespace [ 'PGE';'Exp' ]
12 .include "interpinfo.pasm"
13 .include "cclass.pasm"
14 .const int PGE_INF = 2147483647
15 .const int PGE_CUT_GROUP = -1
16 .const int PGE_CUT_RULE = -2
17 .const int PGE_CUT_MATCH = -3
18 .const int PGE_CUT_CUT = -4
19 .const int PGE_BACKTRACK_GREEDY = 1
20 .const int PGE_BACKTRACK_EAGER = 2
21 .const int PGE_BACKTRACK_NONE = 3
23 .sub "__onload" :load
24     .local pmc optable
25     .local pmc term
27     .local pmc p6meta, expproto
28     p6meta = get_hll_global 'P6metaclass'
29     expproto = p6meta.'new_class'('PGE::Exp', 'parent'=>'PGE::Match')
30     p6meta.'new_class'('PGE::Exp::Literal',      'parent'=>expproto)
31     p6meta.'new_class'('PGE::Exp::Scalar',       'parent'=>expproto)
32     p6meta.'new_class'('PGE::Exp::CCShortcut',   'parent'=>expproto)
33     p6meta.'new_class'('PGE::Exp::Newline',      'parent'=>expproto)
34     p6meta.'new_class'('PGE::Exp::EnumCharList', 'parent'=>expproto)
35     p6meta.'new_class'('PGE::Exp::Anchor',       'parent'=>expproto)
36     p6meta.'new_class'('PGE::Exp::Concat',       'parent'=>expproto)
37     p6meta.'new_class'('PGE::Exp::Alt',          'parent'=>expproto)
38     p6meta.'new_class'('PGE::Exp::Conj',         'parent'=>expproto)
39     p6meta.'new_class'('PGE::Exp::Group',        'parent'=>expproto)
40     p6meta.'new_class'('PGE::Exp::CGroup',       'parent'=>'PGE::Exp::Group')
41     p6meta.'new_class'('PGE::Exp::Subrule',      'parent'=>expproto)
42     p6meta.'new_class'('PGE::Exp::Cut',          'parent'=>expproto)
43     p6meta.'new_class'('PGE::Exp::Quant',        'parent'=>expproto)
44     p6meta.'new_class'('PGE::Exp::Modifier',     'parent'=>expproto)
45     p6meta.'new_class'('PGE::Exp::Closure',      'parent'=>expproto)
46     p6meta.'new_class'('PGE::Exp::Action',       'parent'=>expproto)
47 .end
50 =over 4
52 =item C<compile(PMC adverbs :slurpy :named)>
54 Compile C<self> into PIR or a subroutine, according to the
55 C<target> adverbs.
57 =cut
59 .sub 'compile' :method
60     .param pmc adverbs         :slurpy :named
62     .local string target
63     target = adverbs['target']
64     target = downcase target
65     if target == 'parse' goto return_exp
66     if target == 'pge::exp' goto return_exp
68     .local pmc code
69     code = new 'CodeString'
71     .local pmc ns
72     ns = adverbs['namespace']
73     unless null ns goto ns_emit
74   ns_grammar:
75     .local string grammar
76     grammar = adverbs['grammar']
77     ns = split '::', grammar
78   ns_emit:
79     $P0 = code.'key'(ns)
80     code.'emit'('.namespace %0', $P0)
81   ns_done:
83     $P0 = self.'root_pir'(adverbs :flat :named)
84     code .= $P0
85     if target != 'pir' goto bytecode
86     .return (code)
88   bytecode:
89     $P0 = compreg 'PIR'
90     $P1 = $P0(code)
91   make_grammar:
92     if grammar == '' goto end
93     .local pmc p6meta
94     p6meta = get_hll_global 'P6metaclass'
95     $P0 = p6meta.'get_proto'(grammar)
96     unless null $P0 goto end
97     $P0 = p6meta.'new_class'(grammar, 'parent'=>'PGE::Grammar')
98   end:
99     .return ($P1)
101   return_exp:
102     .return (self)
103 .end
106 =item C<root_pir(PMC adverbs)>
108 Return a CodeString object containing the entire expression
109 tree as a PIR code object that can be compiled.
111 =cut
113 .sub 'root_pir' :method
114     .param pmc adverbs         :slurpy :named
116     .local pmc code
117     code = new 'CodeString'
119     .local string name, namecorou
120     name = adverbs['name']
121     namecorou = concat name, '_corou'
122     if name > '' goto have_name
123     name = code.'unique'('_regex')
124     namecorou = concat name, '_corou'
125   have_name:
126     name = code.'escape'(name)
127     namecorou = code.'escape'(namecorou)
129     .local string pirflags
130     pirflags = adverbs['pirflags']
132     $I0 = index pirflags, ':subid'
133     if $I0 >= 0 goto have_subid
134     $P0 = adverbs['subid']
135     if null $P0 goto have_subid
136     $S0 = code.'escape'($P0)
137     pirflags = concat pirflags, ' :subid('
138     concat pirflags, $S0
139     concat pirflags, ')'
140   have_subid:
142     ##   Perform reduction/optimization on the
143     ##   expression tree before generating PIR.
144     .local pmc exp
145     .local string explabel
146     exp = self
147     set_hll_global ['PGE';'Exp'], '$!group', exp
148     exp = exp.'reduce'(self)
150     ##   we don't need a coroutine if :ratchet is set
151     .local int cutrule
152     $I0 = adverbs['ratchet']
153     cutrule = isne $I0, 0
155     ##   generate the PIR for the expression tree.
156     .local pmc expcode
157     expcode = new 'CodeString'
158     explabel = 'R'
159     exp.'pir'(expcode, explabel, 'succeed')
161     if cutrule goto code_cutrule
162     ##   Generate the initial PIR code for a backtracking (uncut) rule.
163     .local string returnop
164     returnop = '.yield'
165     code.'emit'(<<"        CODE", name, pirflags, namecorou, .INTERPINFO_CURRENT_SUB)
166       .sub %0 :method :nsentry %1
167           .param pmc adverbs   :slurpy :named
168           .local pmc mob
169           .const 'Sub' corou = %2
170           $P0 = corou
171           $P0 = clone $P0
172           mob = $P0(self, adverbs)
173           .return (mob)
174       .end
175       .sub '' :subid(%2)
176           .param pmc mob       :unique_reg
177           .param pmc adverbs   :unique_reg
178           .local string target :unique_reg
179           .local pmc mfrom, mpos :unique_reg
180           .local int cpos, iscont :unique_reg
181           $P0 = get_hll_global ['PGE'], '$!MATCH'
182           (mob, cpos, target, mfrom, mpos, iscont) = $P0.'new'(mob, adverbs :flat :named)
183           $P0 = interpinfo %3
184           setattribute mob, '&!corou', $P0
185           .local int lastpos
186           lastpos = length target
187           if cpos > lastpos goto fail_rule
188         CODE
189     goto code_body
191   code_cutrule:
192     ##   Initial code for a rule that cannot be backtracked into.
193     returnop = '.return'
194     code.'emit'(<<"        CODE", name, pirflags)
195       .sub %0 :method :nsentry %1
196           .param pmc adverbs      :unique_reg :slurpy :named
197           .local pmc mob
198           .local string target    :unique_reg
199           .local pmc mfrom, mpos  :unique_reg
200           .local int cpos, iscont :unique_reg
201           $P0 = get_hll_global ['PGE'], '$!MATCH'
202           (mob, cpos, target, mfrom, mpos, iscont) = $P0.'new'(self, adverbs :flat :named)
203           .local int lastpos
204           lastpos = length target
205           if cpos > lastpos goto fail_rule
206         CODE
208   code_body:
209     ##   generate the ustack only if we need it
210     .local string expstr
211     expstr = expcode
212     code.'emit'("          .local pmc cstack :unique_reg")
213     code.'emit'("          cstack = root_new ['parrot';'ResizableIntegerArray']")
214     $I0 = index expstr, 'ustack'
215     if $I0 < 0 goto code_body_1
216     code.'emit'("          .local pmc ustack :unique_reg")
217     code.'emit'("          ustack = root_new ['parrot';'ResizablePMCArray']")
218   code_body_1:
219     ##   generate the gpad only if we need it
220     $I0 = index expstr, 'gpad'
221     if $I0 < 0 goto code_body_2
222     code.'emit'("          .local pmc gpad :unique_reg")
223     code.'emit'("          gpad = root_new ['parrot';'ResizablePMCArray']")
224   code_body_2:
225     ##   set the captscope if we need it
226     $I0 = index expstr, 'captscope'
227     if $I0 < 0 goto code_body_3
228     code.'emit'("          .local pmc captscope, captob :unique_reg")
229     code.'emit'("          captscope = mob")
230   code_body_3:
232     code.'emit'(<<"        CODE", PGE_CUT_RULE, returnop)
233           .local int pos, rep, cutmark :unique_reg
234         try_match:
235           if cpos > lastpos goto fail_rule
236           mfrom = cpos
237           pos = cpos
238           cutmark = 0
239           local_branch cstack, R
240           if cutmark <= %0 goto fail_cut
241           inc cpos
242           if iscont goto try_match
243         fail_rule:
244           cutmark = %0
245         fail_cut:
246           mob.'_failcut'(cutmark)
247           %1 (mob)
248           goto fail_cut
249         succeed:
250           mpos = pos
251           %1 (mob)
252         fail:
253           local_return cstack
254         CODE
256     ##   add the "fail_match" target if we need it
257     $I0 = index expstr, 'fail_match'
258     if $I0 < 0 goto add_expcode
259     code.'emit'(<<"        CODE", PGE_CUT_MATCH)
260         fail_match:
261           cutmark = %0
262           goto fail_cut
263         CODE
265   add_expcode:
266     ##   add the expression code, then close off the sub
267     code .= expcode
268     code.'emit'("      .end")
269     .return (code)
270 .end
273 .sub 'getargs' :method
274     .param pmc label
275     .param pmc next
276     .param pmc hash            :slurpy :named
277     hash['L'] = label                                # %L = node's label
278     hash['S'] = next                                 # %S = success target
279     .local pmc quant
280     $I0 = exists hash['quant']
281     if $I0 == 0 goto end
282     quant = hash['quant']
283     $I0 = quant['min']
284     $I1 = quant['max']
285     $S2 = quant['backtrack']
286     hash['m'] = $I0                                  # %m = min repetitions
287     hash['n'] = $I1                                  # %n = max repetitions
288     $S0 = $I0
289     $S0 .= '..'
290     $S1 = $I1
291     $S0 .= $S1
292     $S0 .= ' ('
293     $S0 .= $S2
294     $S0 .= ')'
295     hash['Q'] = $S0                                  # %Q = printable quant
296     hash['M'] = ''
297     hash['N'] = ''
298   quant_min:
299     if $I0 > 0 goto quant_max
300     hash['M'] = '### '                               # %M = # if min==0
301   quant_max:
302     if $I1 != PGE_INF goto end
303     hash['N'] = '### '                               # %N = # if max==INF
304   end:
305     .return (hash)
306 .end
309 .sub 'gencapture' :method
310     .param pmc label
311     .local string cname
312     .local pmc captgen, captsave, captback
313     .local int iscapture, isarray
314     cname = self['cname']
315     iscapture = self['iscapture']
316     isarray = self['isarray']
317     captgen = new 'CodeString'
318     captsave = new 'CodeString'
319     captback = new 'CodeString'
320     if iscapture == 0 goto end
321     if isarray != 0 goto capt_array
322     captsave.'emit'("captscope[%0] = captob", cname)
323     captback.'emit'("delete captscope[%0]", cname)
324     goto end
325   capt_array:
326     captsave.'emit'("$P2 = captscope[%0]\n          push $P2, captob", cname)
327     captback.'emit'("$P2 = captscope[%0]\n          $P2 = pop $P2", cname)
328     captgen.'emit'(<<"        CODE", cname, label)
329           $I0 = defined captscope[%0]
330           if $I0 goto %1_cgen
331           $P0 = root_new ['parrot';'ResizablePMCArray']
332           captscope[%0] = $P0
333           local_branch cstack, %1_cgen
334           delete captscope[%0]
335           goto fail
336         %1_cgen:
337         CODE
338   end:
339     .return (captgen, captsave, captback)
340 .end
343 .namespace [ 'PGE';'Exp';'Literal' ]
345 .sub 'reduce' :method
346     .param pmc next
347     .return (self)
348 .end
350 .sub 'pir' :method
351     .param pmc code
352     .param string label
353     .param string next
355     .local pmc args
356     args = self.'getargs'(label, next)
357     .local string literal
358     .local int litlen
359     literal = self.'ast'()
360     litlen = length literal
362     args['I'] = ''
363     $I0 = self['ignorecase']
364     if $I0 == 0 goto ignorecase_end
365     args['I'] = '$S0 = downcase $S0'
366     literal = downcase literal
367   ignorecase_end:
369     literal = code.'escape'(literal)
371     code.'emit'(<<"        CODE", litlen, literal, args :named :flat)
372         %L: # literal
373           $I0 = pos + %0
374           if $I0 > lastpos goto fail
375           $S0 = substr target, pos, %0
376           %I
377           if $S0 != %1 goto fail
378           pos += %0
379           goto %S\n
380         CODE
381     .return ()
382 .end
385 .namespace [ 'PGE';'Exp';'Concat' ]
387 .sub 'reduce' :method
388     .param pmc next
390     .local pmc children, exp
391     .local int n
392     children = self.'list'()
393     n = elements children
394   reduce_loop:
395     if n <= 0 goto reduce_end
396     dec n
397     exp = self[n]
398     exp = exp.'reduce'(next)
399     self[n] = exp
400     next = exp
401     goto reduce_loop
402   reduce_end:
403     .local int i, j
404     .local pmc exp0, exp1
405     n = elements children
406     i = 0
407     j = 0
408   concat_lit_loop:
409     inc i
410     if i >= n goto concat_lit_end
411     exp1 = children[i]
412     $I1 = isa exp1, ['PGE';'Exp';'Literal']
413     if $I1 == 0 goto concat_lit_shift
414     exp0 = children[j]
415     $I0 = isa exp0, ['PGE';'Exp';'Literal']
416     if $I0 == 0 goto concat_lit_shift
417     $I0 = exp0['ignorecase']
418     $I1 = exp1['ignorecase']
419     if $I0 != $I1 goto concat_lit_shift
420     $S0 = exp0.'ast'()
421     $S1 = exp1.'ast'()
422     concat $S0, $S1
423     exp0.'!make'($S0)
424     goto concat_lit_loop
425   concat_lit_shift:
426     inc j
427     if j >= i goto concat_lit_loop
428     children[j] = exp1
429     goto concat_lit_loop
430   concat_lit_end:
431     inc j
432     children = j
433     if j > 1 goto end
434     exp = self[0]
435     .return (exp)
436   end:
437     .return (self)
438 .end
441 .sub 'pir' :method
442     .param pmc code
443     .param string label
444     .param string next
446     .local pmc it, exp
447     code.'emit'('        %0: # concat', label)
448     $P0 = self.'list'()
449     it  = iter $P0
450     exp = shift it
451     $S0 = code.'unique'('R')
452   iter_loop:
453     unless it goto iter_end
454     $P1 = shift it
455     $S1 = code.'unique'('R')
456     exp.'pir'(code, $S0, $S1)
457     exp = $P1
458     $S0 = $S1
459     goto iter_loop
460   iter_end:
461     exp.'pir'(code, $S0, next)
462     .return ()
463 .end
466 .namespace [ 'PGE';'Exp';'Quant' ]
468 .sub 'reduce' :method
469     .param pmc next
470     .local pmc exp0, sep
471     exp0 = self[0]
472     sep = self['sep']
474     .local int backtrack, min, max
475     backtrack = self['backtrack']
476     min = self['min']
477     max = self['max']
478     if max != 1 goto reduce_exp0
479     if min != max goto reduce_max1
480     exp0['backtrack'] = backtrack
481     exp0 = exp0.'reduce'(next)
482     .return (exp0)
484   reduce_max1:
485     ##  special case of 0..1?: node
486     if backtrack != PGE_BACKTRACK_NONE goto reduce_exp0
487     $I0 = exists exp0['backtrack']
488     if $I0 goto reduce_exp0
489     exp0['backtrack'] = backtrack
491   reduce_exp0:
492     exp0 = exp0.'reduce'(next)
493     self[0] = exp0
494     if null sep goto reduce_exp0_1
495     sep = sep.'reduce'(next)
496     self['sep'] = sep
497   reduce_exp0_1:
498     .return (self)
499 .end
501 .sub 'pir' :method
502     .param pmc code
503     .param string label
504     .param string next
506     .local pmc exp, sep
507     .local string explabel, seplabel, replabel, nextlabel
508     exp = self[0]
509     sep = self['sep']
511     unless null sep goto outer_quant
512     $I0 = can exp, 'pir_quant'
513     if $I0 == 0 goto outer_quant
514     $I0 = exp.'pir_quant'(code, label, next, self)
515     if $I0 == 0 goto outer_quant
516     .return ()
518   outer_quant:
519     .local pmc args
520     args = self.'getargs'(label, next, 'quant' => self)
522     .local int backtrack
523     backtrack = self['backtrack']
525     explabel = code.'unique'('R')
526     nextlabel = explabel
527     replabel = concat label, '_repeat'
528     if null sep goto outer_quant_1
529     seplabel = code.'unique'('R')
530     nextlabel = concat label, '_sep'
531   outer_quant_1:
533     if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
534     if backtrack == PGE_BACKTRACK_NONE goto bt_none
536   bt_greedy:
537     args['c'] = 0
538     args['C'] = '### '
539     ##   handle 0..Inf as a special case
540     $I0 = self['min']
541     if $I0 != 0 goto bt_greedy_none
542     $I0 = self['max']
543     if $I0 != PGE_INF goto bt_greedy_none
544     code.'emit'(<<"        CODE", replabel, nextlabel, args :flat :named)
545         %L:  # quant 0..Inf greedy
546         %0:
547           push ustack, pos
548           local_branch cstack, %1
549           pos = pop ustack
550           if cutmark != 0 goto fail
551           goto %S
552         CODE
553     goto end
555   bt_none:
556     $S0 = code.'unique'()
557     args['c'] = $S0
558     args['C'] = ''
559     ##   handle 0..Inf as a special case
560     $I0 = self['min']
561     if $I0 != 0 goto bt_greedy_none
562     $I0 = self['max']
563     if $I0 != PGE_INF goto bt_greedy_none
564     code.'emit'(<<"        CODE", replabel, nextlabel, args :flat :named)
565         %L:  # quant 0..Inf none
566           local_branch cstack, %0
567           if cutmark != %c goto fail
568           cutmark = 0
569           goto fail
570         %0:
571           push ustack, pos
572           local_branch cstack, %1
573           pos = pop ustack
574           if cutmark != 0 goto fail
575           local_branch cstack, %S
576           if cutmark != 0 goto fail
577           cutmark = %c
578           goto fail
579         CODE
580     goto end
582   bt_greedy_none:
583     ##   handle greedy or none
584     code.'emit'(<<"        CODE", replabel, nextlabel, args :flat :named)
585         %L:  # quant %Q greedy/none
586           push gpad, 0
587           local_branch cstack, %0
588           $I0 = pop gpad
589           %Cif cutmark != %c goto fail
590           %Ccutmark = 0
591           goto fail
592         %0:
593           rep = gpad[-1]
594           %Nif rep >= %n goto %L_1
595           inc rep
596           gpad[-1] = rep
597           push ustack, pos
598           push ustack, rep
599           local_branch cstack, %1
600           rep = pop ustack
601           pos = pop ustack
602           if cutmark != 0 goto fail
603           dec rep
604         %L_1:
605           %Mif rep < %m goto fail
606           $I0 = pop gpad
607           push ustack, rep
608           local_branch cstack, %S
609           rep = pop ustack
610           push gpad, rep
611           if cutmark != 0 goto fail
612           %Ccutmark = %c
613           goto fail\n
614         CODE
615     goto end
617   bt_eager:
618     ##   handle eager backtracking
619     code.'emit'(<<"        CODE", replabel, nextlabel, args :flat :named)
620         %L:  # quant %Q eager
621           push gpad, 0
622           local_branch cstack, %0
623           $I0 = pop gpad
624           goto fail
625         %0:
626           rep = gpad[-1]
627           %Mif rep < %m goto %L_1
628           $I0 = pop gpad
629           push ustack, pos
630           push ustack, rep
631           local_branch cstack, %S
632           rep = pop ustack
633           pos = pop ustack
634           push gpad, rep
635         %L_1:
636           %Nif rep >= %n goto fail
637           inc rep
638           gpad[-1] = rep
639           goto %1\n
640         CODE
642   end:
643     if null sep goto sep_done
644     code.'emit'(<<"        CODE", nextlabel, explabel, seplabel)
645         %0:
646           if rep == 1 goto %1
647           goto %2
648         CODE
649     sep.'pir'(code, seplabel, explabel)
650   sep_done:
651     exp.'pir'(code, explabel, replabel)
652     .return ()
653 .end
656 .namespace [ 'PGE';'Exp';'Group' ]
658 .sub 'reduce' :method
659     .param pmc next
660     .local pmc exp
662     $I0 = self['backtrack']
663     if $I0 != PGE_BACKTRACK_NONE goto reduce_exp0
664     ##   This group is non-backtracking, so concatenate a "cut group"
665     ##   node (PGE::Exp::Cut) to its child.
666     exp = self[0]
667     $P0 = new ['PGE';'Exp';'Concat']
668     $P0[0] = exp
669     $P1 = new ['PGE';'Exp';'Cut']
670     $P0[1] = $P1
671     self[0] = $P0
673   reduce_exp0:
674     .local pmc group
675     ##   Temporarily store this group as the current group.  Any
676     ##   cut nodes we encounter will set a cutmark into this group.
677     group = get_hll_global ['PGE';'Exp'], '$!group'
678     set_hll_global ['PGE';'Exp'], '$!group', self
679     exp = self[0]
680     exp = exp.'reduce'(next)
681     set_hll_global ['PGE';'Exp'], '$!group', group
682     $I0 = self['cutmark']
683     if $I0 > 0 goto keep_group
684     $I0 = self['iscapture']
685     if $I0 != 0 goto keep_group
686     .return (exp)
687   keep_group:
688     .return (self)
689 .end
691 .sub 'pir' :method
692     .param pmc code
693     .param string label
694     .param string next
696     .local pmc exp0
697     exp0 = self[0]
699     .local int cutmark
700     cutmark = self['cutmark']
701     if cutmark > 0 goto has_cutmark
702     exp0.'pir'(code, label, next)
703     .return ()
705   has_cutmark:
706     .local string exp0label
707     exp0label = code.'unique'('R')
708     code.'emit'(<<"        CODE", label, exp0label, cutmark)
709         %0:  # group %2
710           local_branch cstack, %1
711           if cutmark != %2 goto fail
712           cutmark = 0
713           goto fail\n
714         CODE
715     exp0.'pir'(code, exp0label, next)
716 .end
719 .namespace [ 'PGE';'Exp';'CGroup' ]
721 .sub 'pir' :method :nsentry
722     .param pmc code
723     .param string label
724     .param string next
726     .local string explabel, expnext
727     explabel = code.'unique'('R')
728     expnext = concat label, '_close'
730     .local pmc args
731     args = self.'getargs'(label, next)
733     .local string captgen, captsave, captback
734     (captgen, captsave, captback) = self.'gencapture'(label)
736     .local int cutmark
737     cutmark = self['cutmark']
738     args['c'] = cutmark
739     args['C'] = '### '
740     if cutmark == 0 goto cutmark_end
741     args['C'] = ''
742   cutmark_end:
744     .local int isscope
745     isscope = self['isscope']
746     args['X'] = ''
747     if isscope != 0 goto isscope_end
748     args['X'] = '### '
749   isscope_end:
751     code.'emit'(<<"        CODE", captgen, captsave, captback, 'E'=>explabel, args :flat :named)
752         %L: # capture
753           %0
754           captob = captscope.'new'(captscope, 'pos'=>pos)
755           push gpad, captscope
756           push gpad, captob
757           %Xcaptscope = captob
758           local_branch cstack, %E
759           captob = pop gpad
760           captscope = pop gpad
761           %Cif cutmark != %c goto fail
762           %Ccutmark = 0
763           goto fail
764         %L_close:
765           push ustack, captscope
766           captob = pop gpad
767           captscope = pop gpad
768           $P1 = getattribute captob, '$.pos'
769           $P1 = pos
770           %1
771           push ustack, captob
772           local_branch cstack, %S
773           captob = pop ustack
774           %2
775           push gpad, captscope
776           push gpad, captob
777           captscope = pop ustack
778           goto fail\n
779         CODE
780     .local pmc exp
781     exp = self[0]
782     exp.'pir'(code, explabel, expnext)
783     .return ()
784 .end
787 .namespace [ 'PGE';'Exp';'Subrule' ]
789 .sub 'reduce' :method
790     .param pmc next
791     .return (self)
792 .end
794 .sub 'pir' :method
795     .param pmc code
796     .param string label
797     .param string next
799     .local pmc args
800     args = self.'getargs'(label, next)
802     .local string subarg
803     subarg = ''
804     $I0 = exists self['arg']
805     if $I0 == 0 goto subarg_dba
806     subarg = self['arg']
807     subarg = code.'escape'(subarg)
808     subarg = concat ', ', subarg
809     args['A'] = $S0
810   subarg_dba:
811     $I0 = exists self['dba']
812     if $I0 == 0 goto subarg_end
813     $S0 = self['dba']
814     $S0 = code.'escape'($S0)
815     subarg .= ", 'dba'=>"
816     subarg .= $S0
817   subarg_end:
819     .local string cname, captgen, captsave, captback
820     (captgen, captsave, captback) = self.'gencapture'(label)
822     .local string subname
823     subname = self['subname']
824   subrule:
825     $I0 = 0
826   subrule_1:
827     $I1 = index subname, '::', $I0
828     if $I1 == -1 goto subrule_2
829     $I0 = $I1 + 2
830     goto subrule_1
831   subrule_2:
832     if $I0 == 0 goto subrule_simple_name
833     ##   The subrule is of the form <Grammar::rule>, which means we need
834     ##   to create a Match object of the appropriate grammar (class) for it.
835     .local string grammar, rname
836     rname = substr subname, $I0
837     $I0 -= 2
838     grammar = substr subname, 0, $I0
839     $P0 = split '::', grammar
840     $P0 = code.'key'($P0)
841     code.'emit'(<<"        CODE", grammar, rname, $P0, args :flat :named)
842         %L: # grammar subrule %0::%1
843           captob = captscope.'new'(captscope, 'grammar'=>'%0')
844           captob.'to'(pos)
845           $P0 = get_hll_global %2, '%1'
846         CODE
847     goto subrule_match
849   subrule_simple_name:
850     ##   The subrule is of the form <rule>, which means we first look
851     ##   for a method on the current match object, otherwise we do a
852     ##   normal name lookup.
853     code.'emit'(<<"        CODE", subname, args :flat :named)
854         %L: # subrule %0
855           captob = captscope
856           $P0 = getattribute captob, '$.pos'
857           $P0 = pos
858           $I0 = can mob, '%0'
859           if $I0 == 0 goto %L_1
860           $P0 = find_method mob, '%0'
861           goto %L_2
862         %L_1:
863           $P0 = find_name '%0'
864           unless null $P0 goto %L_2
865           die "Unable to find regex '%0'"
866         %L_2:
867         CODE
869   subrule_match:
870     $I0 = self['iszerowidth']
871     if $I0 goto subrule_zerowidth
872     $S0 = concat label, '_3'
873     $I0 = self['backtrack']
874     if $I0 != PGE_BACKTRACK_NONE goto subrule_match_1
875     $S0 = next
876   subrule_match_1:
877     ##   Perform the subrule match, capturing the result as needed.
878     ##   We either branch directly to the next node (PGE_BACKTRACK_NONE)
879     ##   or to a small subroutine below that will keep backtracking into
880     ##   the subrule until it no longer produces a match.
881     code.'emit'(<<"        CODE", PGE_CUT_MATCH, $S0, captgen, captsave, captback, subarg)
882           $P2 = adverbs['action']
883           captob = $P0(captob%5, 'action'=>$P2)
884           $P1 = getattribute captob, '$.pos'
885           if $P1 <= %0 goto fail_match
886           if $P1 < 0 goto fail
887           %2
888           %3
889           pos = $P1
890           local_branch cstack, %1
891           %4
892           goto fail
893         CODE
894     if $I0 == PGE_BACKTRACK_NONE goto end
895     ##   Repeatedly backtrack into the subrule until there are no matches.
896     code.'emit'(<<"        CODE", PGE_CUT_MATCH, $S0, next)
897         %1:
898           pos = $P1
899           $P1 = getattribute captob, '&!corou'
900           if null $P1 goto %2
901           push ustack, captob
902           local_branch cstack, %2
903           captob = pop ustack
904           if cutmark != 0 goto fail
905           captob.'next'()
906           $P1 = getattribute captob, '$.pos'
907           if $P1 >= 0 goto %1
908           if $P1 <= %0 goto fail_match
909           goto fail\n
910         CODE
911     goto end
912     .return()
914   subrule_zerowidth:
915     ##  this handles zero-width subrule matches, either positive
916     ##  or negative.
917     .local string test
918     test = 'if'
919     $I0 = self['isnegated']
920     unless $I0 goto have_test
921     test = 'unless'
922   have_test:
923     code.'emit'(<<"        CODE", PGE_CUT_MATCH, test, next, subarg)
924           captob = $P0(captob%3)
925           $P1 = getattribute captob, '$.pos'
926           if $P1 <= %0 goto fail_match
927           %1 $P1 < 0 goto fail
928           $P1 = pos
929           $P1 = getattribute captob, '$.from'
930           $P1 = pos
931           goto %2
932         CODE
933   end:
934     .return()
935 .end
938 .namespace [ 'PGE';'Exp';'Alt' ]
940 .sub 'reduce' :method
941     .param pmc next
942     .local pmc exp0, exp1
943     exp0 = self[0]
944     exp0 = exp0.'reduce'(next)
945     self[0] = exp0
946     exp1 = self[1]
947     exp1 = exp1.'reduce'(next)
948     self[1] = exp1
949     .return (self)
950 .end
953 .sub 'pir' :method
954     .param pmc code
955     .param string label
956     .param string next
957     .local pmc exp0, exp1
958     .local string exp0label, exp1label
959     exp0label = code.'unique'('R')
960     exp1label = code.'unique'('R')
961     code.'emit'(<<"        CODE", label, exp0label, exp1label)
962         %0:  # alt %1, %2
963           push ustack, pos
964           local_branch cstack, %1
965           pos = pop ustack
966           if cutmark != 0 goto fail
967           goto %2\n
968         CODE
969     exp0 = self[0]
970     exp0.'pir'(code, exp0label, next)
971     exp1 = self[1]
972     exp1.'pir'(code, exp1label, next)
973     .return ()
974 .end
977 .namespace [ 'PGE';'Exp';'Anchor' ]
979 .sub 'reduce' :method
980     .param pmc next
981     .return (self)
982 .end
984 .sub 'pir' :method
985     .param pmc code
986     .param pmc label
987     .param pmc next
988     .local string token, test
989     token = self.'ast'()
991     if token == '<?>' goto anchor_null
992     if token == '^' goto anchor_bos
993     if token == '$' goto anchor_eos
994     if token == '^^' goto anchor_bol
995     if token == '$$' goto anchor_eol
996     if token == '<<' goto anchor_word_left
997     if token == '>>' goto anchor_word_right
998     if token == unicode:"\xab" goto anchor_word_left
999     if token == unicode:"\xbb" goto anchor_word_right
1000     test = '!='
1001     if token == '\b' goto anchor_word
1002     test = '=='
1003     if token == '\B' goto anchor_word
1005   anchor_fail:
1006     code.'emit'("        %0: # anchor fail %1", label, token)
1007     code.'emit'("          goto fail")
1008     .return ()
1010   anchor_null:
1011     code.'emit'("        %0: # anchor null %1", label, token)
1012     code.'emit'("          goto %0", next)
1013     .return ()
1015   anchor_bos:
1016     code.'emit'("        %0: # anchor bos", label)
1017     code.'emit'("          if pos == 0 goto %0", next)
1018     code.'emit'("          goto fail")
1019     .return ()
1021   anchor_eos:
1022     code.'emit'("        %0: # anchor eos", label)
1023     code.'emit'("          if pos == lastpos goto %0", next)
1024     code.'emit'("          goto fail")
1025     .return ()
1027   anchor_bol:
1028     code.'emit'(<<"        CODE", label, next, .CCLASS_NEWLINE)
1029         %0: # anchor bol
1030           if pos == 0 goto %1
1031           if pos == lastpos goto fail
1032           $I0 = pos - 1
1033           $I1 = is_cclass %2, target, $I0
1034           if $I1 goto %1
1035           goto fail\n
1036         CODE
1037     .return ()
1039   anchor_eol:
1040     code.'emit'(<<"        CODE", label, next, .CCLASS_NEWLINE)
1041         %0: # anchor eol
1042           $I1 = is_cclass %2, target, pos
1043           if $I1 goto %1
1044           if pos != lastpos goto fail
1045           if pos < 1 goto %1
1046           $I0 = pos - 1
1047           $I1 = is_cclass %2, target, $I0
1048           if $I1 == 0 goto %1
1049           goto fail\n
1050         CODE
1051     .return ()
1053   anchor_word:
1054     code.'emit'(<<"        CODE", label, next, .CCLASS_WORD, test)
1055         %0: # anchor word
1056           $I0 = 0
1057           if pos == 0 goto %0_1
1058           $I2 = pos - 1
1059           $I0 = is_cclass %2, target, $I2
1060         %0_1:
1061           $I1 = 0
1062           if pos >= lastpos goto %0_2
1063           $I1 = is_cclass %2, target, pos
1064         %0_2:
1065           if $I0 %3 $I1 goto %1
1066           goto fail
1067         CODE
1068     .return ()
1070   anchor_word_left:
1071     code.'emit'(<<"        CODE", label, next, .CCLASS_WORD)
1072         %0: # left word boundary
1073           if pos >= lastpos goto fail
1074           $I0 = is_cclass %2, target, pos
1075           if $I0 == 0 goto fail
1076           if pos == 0 goto %1
1077           $I0 = pos - 1
1078           $I0 = is_cclass %2, target, $I0
1079           if $I0 goto fail
1080           goto %1
1081         CODE
1082     .return ()
1084   anchor_word_right:
1085     code.'emit'(<<"        CODE", label, next, .CCLASS_WORD)
1086         %0: # right word boundary
1087           if pos == 0 goto fail
1088           $I0 = pos - 1
1089           $I0 = is_cclass %2, target, $I0
1090           if $I0 == 0 goto fail
1091           if pos >= lastpos goto %1
1092           $I0 = is_cclass %2, target, pos
1093           if $I0 goto fail
1094           goto %1
1095         CODE
1096     .return ()
1098 .end
1101 .namespace [ 'PGE';'Exp';'CCShortcut' ]
1103 .sub 'reduce' :method
1104     .param pmc next
1106     .local string token
1107     token = self.'ast'()
1108     self['negate'] = 1
1109     if token == '\D' goto digit
1110     if token == '\S' goto space
1111     if token == '\W' goto word
1112     if token == '\N' goto newline
1113     self['negate'] = 0
1114     if token == '\d' goto digit
1115     if token == '\s' goto space
1116     if token == '\w' goto word
1117     if token == '\n' goto newline
1118     self['cclass'] = .CCLASS_ANY
1119     goto end
1120   digit:
1121     self['cclass'] = .CCLASS_NUMERIC
1122     goto end
1123   space:
1124     self['cclass'] = .CCLASS_WHITESPACE
1125     goto end
1126   word:
1127     self['cclass'] = .CCLASS_WORD
1128     goto end
1129   newline:
1130     self['cclass'] = .CCLASS_NEWLINE
1131   end:
1132     .return (self)
1133 .end
1136 .sub 'pir' :method
1137     .param pmc code
1138     .param string label
1139     .param string next
1140     .local int cclass, negate
1142     $S0 = self.'ast'()
1143     code.'emit'("        %0: # cclass %1", label, $S0)
1144     code.'emit'("          if pos >= lastpos goto fail")
1145     cclass = self['cclass']
1146     negate = self['negate']
1147     if cclass == .CCLASS_ANY goto end
1148     code.'emit'("          $I0 = is_cclass %0, target, pos", cclass)
1149     code.'emit'("          if $I0 == %0 goto fail", negate)
1150   end:
1151     code.'emit'("          inc pos")
1152     code.'emit'("          goto %0", next)
1153     .return ()
1154 .end
1157 .sub 'pir_quant' :method
1158     .param pmc code
1159     .param string label
1160     .param string next
1161     .param pmc quant
1163     .local pmc args
1164     .local int min, max, backtrack
1165     args = self.'getargs'(label, next, 'quant'=>quant)
1166     min = quant['min']
1167     max = quant['max']
1168     backtrack = quant['backtrack']
1170     ##  output initial label
1171     code.'emit'("        %L: # cclass %0 %Q", self, args :flat :named)
1173     .local int cclass, negate
1174     cclass = self['cclass']
1175     negate = self['negate']
1176     if cclass == .CCLASS_ANY goto emit_dot
1177     .local string negstr
1178     negstr = '_not'
1179     if negate == 0 goto emit_find
1180     negstr = ''
1181   emit_find:
1182     code.'emit'(<<"        CODE", negstr, cclass)
1183           $I0 = find%0_cclass %1, target, pos, lastpos
1184           rep = $I0 - pos
1185         CODE
1186     goto emit_pir
1187   emit_dot:
1188     code.'emit'("          rep = lastpos - pos")
1190   emit_pir:
1191     code.'emit'(<<"        CODE", args :flat :named)
1192           %Mif rep < %m goto fail
1193           %Nif rep <= %n goto %L_1
1194           %Nrep = %n
1195         %L_1:
1196         CODE
1198     if backtrack == PGE_BACKTRACK_NONE goto bt_none
1199     if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
1201   bt_greedy:
1202     code.'emit'(<<"        CODE", args :flat :named)
1203           pos += rep
1204         %L_2:
1205           if rep <= %m goto %S
1206           push ustack, pos
1207           push ustack, rep
1208           local_branch cstack, %S
1209           rep = pop ustack
1210           pos = pop ustack
1211           if cutmark != 0 goto fail
1212           dec pos
1213           dec rep
1214           goto %L_2
1215         CODE
1216     .return (1)
1218   bt_none:
1219     code.'emit'("          pos += rep\n          goto %0\n", next)
1220     .return (1)
1222   bt_eager:
1223     code.'emit'(<<"        CODE", args :flat :named)
1224           %Mpos += %m
1225           %Mrep -= %m
1226         %L_2:
1227           if rep == 0 goto %S
1228           push ustack, pos
1229           push ustack, rep
1230           local_branch cstack, %S
1231           rep = pop ustack
1232           pos = pop ustack
1233           if cutmark != 0 goto fail
1234           inc pos
1235           dec rep
1236           goto %L_2
1237         CODE
1239 .end
1241 .namespace [ 'PGE';'Exp';'Cut' ]
1243 .sub 'reduce' :method
1244     .param pmc next
1245     .local pmc group
1246     .local int cutmark
1248     cutmark = self['cutmark']
1249     if cutmark <= PGE_CUT_RULE goto end
1250     ##   This node is cutting a group.  We need to
1251     ##   get the current group's cutmark, or set
1252     ##   one if it doesn't already have one.
1253     group = get_hll_global ['PGE';'Exp'], '$!group'
1254     cutmark = group['cutmark']
1255     if cutmark > 0 goto has_cutmark
1256     $P1 = new 'CodeString'
1257     cutmark = $P1.'unique'()
1258     group['cutmark'] = cutmark
1259   has_cutmark:
1260     self['cutmark'] = cutmark
1261   end:
1262     .return (self)
1263 .end
1265 .sub 'pir' :method
1266     .param pmc code
1267     .param string label
1268     .param string next
1270     .local int cutmark
1271     cutmark = self['cutmark']
1273     if cutmark > 0 goto group_cutmark
1274     code.'emit'(<<"        CODE", label, next, cutmark)
1275         %0: # cut rule or match
1276           local_branch cstack, %1
1277           cutmark = %2
1278           goto fail_cut\n
1279         CODE
1280     .return ()
1282   group_cutmark:
1283     code.'emit'(<<"        CODE", label, next, cutmark)
1284         %0: # cut %2
1285           local_branch cstack, %1
1286           cutmark = %2
1287           goto fail\n
1288         CODE
1289     .return ()
1290 .end
1293 .namespace [ 'PGE';'Exp';'Scalar' ]
1295 .sub 'reduce' :method
1296     .param pmc next
1297     .return (self)
1298 .end
1300 .sub 'pir' :method
1301     .param pmc code
1302     .param string label
1303     .param string next
1305     .local string cname
1306     cname = self['cname']
1307     code.'emit'(<<"        CODE", label, next, cname)
1308         %0: # scalar %2
1309           $P0 = mob[%2]
1310           $I0 = does $P0, 'array'
1311           if $I0 == 0 goto %0_1
1312           $P0 = $P0[-1]
1313         %0_1:
1314           $S1 = $P0
1315           $I1 = length $S1
1316           $I0 = pos + $I1
1317           if $I0 > lastpos goto fail
1318           $S0 = substr target, pos, $I1
1319           if $S0 != $S1 goto fail
1320           pos += $I1
1321           goto %1
1322         CODE
1323     .return ()
1324 .end
1327 .namespace [ 'PGE';'Exp';'EnumCharList' ]
1329 .sub 'reduce' :method
1330     .param pmc next
1331     .return (self)
1332 .end
1334 .sub 'pir' :method
1335     .param pmc code
1336     .param string label
1337     .param string next
1339     .local string charlist
1340     $S0 = self.'ast'()
1341     charlist = code.'escape'($S0)
1343     .local string test
1344     test = '<'
1345     $I0 = self['isnegated']
1346     if $I0 == 0 goto negated_end
1347     test = '>='
1348   negated_end:
1349     .local string incpos
1350     incpos = 'inc pos'
1351     $I0 = self['iszerowidth']
1352     if $I0 == 0 goto zerowidth_end
1353     incpos = '###   zero width'
1354   zerowidth_end:
1356     code.'emit'(<<"        CODE", label, charlist, test, incpos, next)
1357         %0: # enumcharlist %1
1358           if pos >= lastpos goto fail
1359           $S0 = substr target, pos, 1
1360           $I0 = index %1, $S0
1361           if $I0 %2 0 goto fail
1362           %3
1363           goto %4
1364         CODE
1365     .return ()
1366 .end
1369 .namespace [ 'PGE';'Exp';'Newline' ]
1371 .sub 'reduce' :method
1372     .param pmc next
1373     .return (self)
1374 .end
1376 .sub 'pir' :method
1377     .param pmc code
1378     .param string label
1379     .param string next
1380     code.'emit'(<<"        CODE", label, next, .CCLASS_NEWLINE)
1381         %0: # newline
1382           $I0 = is_cclass %2, target, pos
1383           if $I0 == 0 goto fail
1384           $S0 = substr target, pos, 2
1385           inc pos
1386           if $S0 != "\\r\\n" goto %1
1387           inc pos
1388           goto %1
1389         CODE
1390     .return ()
1391 .end
1394 .namespace [ 'PGE';'Exp';'Conj' ]
1396 .sub 'reduce' :method
1397     .param pmc next
1398     .local pmc exp0, exp1
1399     exp0 = self[0]
1400     exp0 = exp0.'reduce'(next)
1401     self[0] = exp0
1402     exp1 = self[1]
1403     exp1 = exp1.'reduce'(next)
1404     self[1] = exp1
1405     .return (self)
1406 .end
1408 .sub 'pir' :method
1409     .param pmc code
1410     .param string label
1411     .param string next
1413     .local string exp0label, exp1label, chk0label, chk1label
1414     exp0label = code.'unique'('R')
1415     exp1label = code.'unique'('R')
1416     chk0label = concat label, '_chk0'
1417     chk1label = concat label, '_chk1'
1418     code.'emit'(<<"        CODE", label, next, exp0label, chk0label, exp1label, chk1label)
1419         %0: # conj %2, %4
1420           push gpad, pos
1421           push gpad, pos
1422           local_branch cstack, %2
1423           $I0 = pop gpad
1424           $I0 = pop gpad
1425           goto fail
1426         %3:
1427           gpad[-1] = pos
1428           pos = gpad[-2]
1429           goto %4
1430         %5:
1431           $I0 = gpad[-1]
1432           if $I0 != pos goto fail
1433           $I0 = pop gpad
1434           $I1 = pop gpad
1435           push ustack, $I1
1436           push ustack, $I0
1437           local_branch cstack, %1
1438           $I0 = pop ustack
1439           $I1 = pop ustack
1440           push gpad, $I1
1441           push gpad, $I0
1442           goto fail\n
1443         CODE
1444     .local pmc exp0, exp1
1445     exp0 = self[0]
1446     exp0.'pir'(code, exp0label, chk0label)
1447     exp1 = self[1]
1448     exp1.'pir'(code, exp1label, chk1label)
1449     .return ()
1450 .end
1452 .namespace [ 'PGE';'Exp';'Closure' ]
1454 .sub 'reduce' :method
1455     .param pmc next
1456     .return (self)
1457 .end
1459 .sub 'pir' :method
1460     .param pmc code
1461     .param string label
1462     .param string next
1463     .local string value, lang
1464     value = self.'ast'()
1465     lang = self['lang']
1466     value = code.'escape'(value)
1467     lang = code.'escape'(lang)
1468     ##  to prevent recompiling every execution, this code makes use of
1469     ##  a global %!cache, keyed on the inline closure source.  There
1470     ##  could be a (unlikely) problem if the same source is sent to
1471     ##  two different compilers.  Also, if the sources can be lengthy
1472     ##  we might be well served to use a hashed representation of
1473     ##  the source.
1474     code.'emit'(<<"        CODE", label, lang, value)
1475         %0: # closure
1476           $S1 = %2
1477           $P0 = get_hll_global ['PGE';'Match'], '%!cache'
1478           $P1 = $P0[$S1]
1479           unless null $P1 goto %0_1
1480           $P1 = compreg %1
1481           $P1 = $P1($S1)
1482           $P0[$S1] = $P1
1483         %0_1:
1484         CODE
1485     $I0 = self['iszerowidth']
1486     if $I0 goto closure_zerowidth
1487     code.'emit'(<<"        CODE", next)
1488           mpos = pos
1489           ($P0 :optional, $I0 :opt_flag) = $P1(mob)
1490           if $I0 == 0 goto %0
1491           mob.'!make'($P0)
1492           push ustack, pos
1493           local_branch cstack, succeed
1494           pos = pop ustack
1495           null $P0
1496           mob.'!make'($P0)
1497           goto fail
1498         CODE
1499     .return ()
1500   closure_zerowidth:
1501     ##  we're doing a <?{{ or <!{{ assertion.
1502     .local string test
1503     test = 'if'
1504     $I0 = self['isnegated']
1505     unless $I0 goto have_test
1506     test = 'unless'
1507   have_test:
1508     code.'emit'(<<"        CODE", test, next)
1509           mpos = pos
1510           $P0 = $P1(mob)
1511           %0 $P0 goto %1
1512           goto fail
1513         CODE
1514     .return ()
1515 .end
1517 .namespace [ 'PGE';'Exp';'Action' ]
1519 .sub 'reduce' :method
1520     .param pmc next
1521     .return (self)
1522 .end
1524 .sub 'pir' :method
1525     .param pmc code
1526     .param string label
1527     .param string next
1528     .local string actionname, actionkey
1529     code.'emit'("        %0: # action", label)
1530     actionname = self['actionname']
1531     if actionname == '' goto end
1532     actionname = code.'escape'(actionname)
1533     actionkey = self['actionkey']
1534     if actionkey == '' goto have_actionkey
1535     actionkey = code.'escape'(actionkey)
1536     actionkey = concat ', ', actionkey
1537   have_actionkey:
1538     code.'emit'(<<"        CODE", label, next, actionname, actionkey)
1539           $P1 = adverbs['action']
1540           if null $P1 goto %1
1541           $I1 = can $P1, %2
1542           if $I1 == 0 goto %1
1543           mpos = pos
1544           $P1.%2(mob%3)
1545           goto %1
1546         CODE
1547   end:
1548     .return ()
1549 .end
1552 # Local Variables:
1553 #   mode: pir
1554 #   fill-column: 100
1555 # End:
1556 # vim: expandtab shiftwidth=4 ft=pir: