tagged release 0.7.1
[parrot.git] / compilers / pge / PGE / Exp.pir
bloba7e2b84fe8c573276b57f19ec31519a5b9dddc05
1 =head1 TITLE
3 PGE::Exp - base class for expressions
5 =cut
7 .namespace [ "PGE::Exp" ]
9 .include "interpinfo.pasm"
10 .include "cclass.pasm"
11 .const int PGE_INF = 2147483647
12 .const int PGE_CUT_GROUP = -1
13 .const int PGE_CUT_RULE = -2
14 .const int PGE_CUT_MATCH = -3
15 .const int PGE_CUT_CUT = -4
16 .const int PGE_BACKTRACK_GREEDY = 1
17 .const int PGE_BACKTRACK_EAGER = 2
18 .const int PGE_BACKTRACK_NONE = 3
20 .sub "__onload" :load
21     .local pmc optable
22     .local pmc term
24     .local pmc p6meta, expproto
25     p6meta = new 'P6metaclass'
26     expproto = p6meta.'new_class'('PGE::Exp', 'parent'=>'PGE::Match')
27     p6meta.'new_class'('PGE::Exp::Literal',      'parent'=>expproto)
28     p6meta.'new_class'('PGE::Exp::Scalar',       'parent'=>expproto)
29     p6meta.'new_class'('PGE::Exp::CCShortcut',   'parent'=>expproto)
30     p6meta.'new_class'('PGE::Exp::Newline',      'parent'=>expproto)
31     p6meta.'new_class'('PGE::Exp::EnumCharList', 'parent'=>expproto)
32     p6meta.'new_class'('PGE::Exp::Anchor',       'parent'=>expproto)
33     p6meta.'new_class'('PGE::Exp::Concat',       'parent'=>expproto)
34     p6meta.'new_class'('PGE::Exp::Alt',          'parent'=>expproto)
35     p6meta.'new_class'('PGE::Exp::Conj',         'parent'=>expproto)
36     p6meta.'new_class'('PGE::Exp::Group',        'parent'=>expproto)
37     p6meta.'new_class'('PGE::Exp::CGroup',       'parent'=>'PGE::Exp::Group')
38     p6meta.'new_class'('PGE::Exp::Subrule',      'parent'=>expproto)
39     p6meta.'new_class'('PGE::Exp::Cut',          'parent'=>expproto)
40     p6meta.'new_class'('PGE::Exp::Quant',        'parent'=>expproto)
41     p6meta.'new_class'('PGE::Exp::Modifier',     'parent'=>expproto)
42     p6meta.'new_class'('PGE::Exp::Closure',      'parent'=>expproto)
43     p6meta.'new_class'('PGE::Exp::Action',       'parent'=>expproto)
44 .end
47 =over 4
49 =item C<compile(PMC adverbs :slurpy :named)>
51 Compile C<self> into PIR or a subroutine, according to the
52 C<target> adverbs.
54 =cut
56 .sub 'compile' :method
57     .param pmc adverbs         :slurpy :named
59     .local string target
60     target = adverbs['target']
61     target = downcase target
62     if target == 'parse' goto return_exp
63     if target == 'pge::exp' goto return_exp
65     .local pmc code
66     code = new 'CodeString'
68     .local pmc ns
69     ns = adverbs['namespace']
70     if null ns goto ns_grammar
71     unless ns goto ns_grammar
72     $P0 = code.'key'(ns)
73     code.'emit'('.namespace %0', $P0)
74     goto ns_done
75   ns_grammar:
76     .local string grammar
77     grammar = adverbs['grammar']
78     if grammar == '' goto ns_root
79     code.'emit'(".namespace [ '%0' ]", grammar)
80     goto ns_done
81   ns_root:
82     code.'emit'('.namespace')
83   ns_done:
85     $P0 = self.'root_pir'(adverbs :flat :named)
86     code .= $P0
87     if target != 'pir' goto bytecode
88     .return (code)
90   bytecode:
91     $P0 = compreg 'PIR'
92     $P1 = $P0(code)
93   make_grammar:
94     push_eh end
95     $P0 = new 'P6metaclass'
96     $P0.'new_class'(grammar, 'parent'=>'PGE::Grammar')
97     pop_eh
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     ##   Perform reduction/optimization on the
130     ##   expression tree before generating PIR.
131     .local pmc exp
132     .local string explabel
133     exp = self
134     set_hll_global ['PGE::Exp'], '$!group', exp
135     exp = exp.reduce(self)
137     ##   we don't need a coroutine if :ratchet is set
138     .local int cutrule
139     $I0 = adverbs['ratchet']
140     cutrule = isne $I0, 0
142     ##   generate the PIR for the expression tree.
143     .local pmc expcode
144     expcode = new 'CodeString'
145     explabel = 'R'
146     exp.pir(expcode, explabel, 'succeed')
148     if cutrule goto code_cutrule
149     ##   Generate the initial PIR code for a backtracking (uncut) rule.
150     .local string returnop
151     returnop = '.yield'
152     code.emit(<<"        CODE", name, namecorou, .INTERPINFO_CURRENT_SUB)
153       .sub %0 :method
154           .param pmc adverbs   :slurpy :named
155           .local pmc mob
156           .const .Sub corou = %1
157           $P0 = corou
158           $P0 = clone $P0
159           mob = $P0(self, adverbs)
160           .return (mob)
161       .end
162       .sub %1
163           .param pmc mob       :unique_reg
164           .param pmc adverbs   :unique_reg
165           .local string target :unique_reg
166           .local pmc mfrom, mpos :unique_reg
167           .local int cpos, iscont :unique_reg
168           $P0 = get_hll_global ['PGE'], 'Match'
169           (mob, cpos, target, mfrom, mpos, iscont) = $P0.'new'(mob, adverbs :flat :named)
170           $P0 = interpinfo %2
171           setattribute mob, '&!corou', $P0
172           .local int lastpos
173           lastpos = length target
174           if cpos > lastpos goto fail_rule
175         CODE
176     goto code_body
178   code_cutrule:
179     ##   Initial code for a rule that cannot be backtracked into.
180     returnop = '.return'
181     code.emit(<<"        CODE", name)
182       .sub %0 :method
183           .param pmc adverbs      :unique_reg :slurpy :named
184           .local pmc mob
185           .local string target    :unique_reg
186           .local pmc mfrom, mpos  :unique_reg
187           .local int cpos, iscont :unique_reg
188           $P0 = get_hll_global ['PGE'], 'Match'
189           (mob, cpos, target, mfrom, mpos, iscont) = $P0.'new'(self, adverbs :flat :named)
190           .local int lastpos
191           lastpos = length target
192           if cpos > lastpos goto fail_rule
193         CODE
195   code_body:
196     ##   generate the ustack only if we need it
197     .local string expstr
198     expstr = expcode
199     code.emit("          .local pmc cstack :unique_reg")
200     code.emit("          cstack = new 'ResizableIntegerArray'")
201     $I0 = index expstr, 'ustack'
202     if $I0 < 0 goto code_body_1
203     code.emit("          .local pmc ustack :unique_reg")
204     code.emit("          ustack = new 'ResizablePMCArray'")
205   code_body_1:
206     ##   generate the gpad only if we need it
207     $I0 = index expstr, 'gpad'
208     if $I0 < 0 goto code_body_2
209     code.emit("          .local pmc gpad :unique_reg")
210     code.emit("          gpad = new 'ResizablePMCArray'")
211   code_body_2:
212     ##   set the captscope if we need it
213     $I0 = index expstr, 'captscope'
214     if $I0 < 0 goto code_body_3
215     code.emit("          .local pmc captscope, captob :unique_reg")
216     code.emit("          captscope = mob")
217   code_body_3:
219     code.emit(<<"        CODE", PGE_CUT_RULE, returnop)
220           .local int pos, rep, cutmark :unique_reg
221         try_match:
222           if cpos > lastpos goto fail_rule
223           mfrom = cpos
224           pos = cpos
225           cutmark = 0
226           local_branch cstack, R
227           if cutmark <= %0 goto fail_cut
228           inc cpos
229           if iscont goto try_match
230         fail_rule:
231           cutmark = %0
232         fail_cut:
233           mob.'_failcut'(cutmark)
234           %1 (mob)
235           goto fail_cut
236         succeed:
237           mpos = pos
238           %1 (mob)
239         fail:
240           local_return cstack
241         CODE
243     ##   add the "fail_match" target if we need it
244     $I0 = index expstr, 'fail_match'
245     if $I0 < 0 goto add_expcode
246     code.emit(<<"        CODE", PGE_CUT_MATCH)
247         fail_match:
248           cutmark = %0
249           goto fail_cut
250         CODE
252   add_expcode:
253     ##   add the expression code, then close off the sub
254     code .= expcode
255     code.emit("      .end")
256     .return (code)
257 .end
260 .sub 'getargs' :method
261     .param pmc label
262     .param pmc next
263     .param pmc hash            :slurpy :named
264     hash['L'] = label                                # %L = node's label
265     hash['S'] = next                                 # %S = success target
266     .local pmc quant
267     $I0 = exists hash['quant']
268     if $I0 == 0 goto end
269     quant = hash['quant']
270     $I0 = quant['min']
271     $I1 = quant['max']
272     $S2 = quant['backtrack']
273     hash['m'] = $I0                                  # %m = min repetitions
274     hash['n'] = $I1                                  # %n = max repetitions
275     $S0 = $I0
276     $S0 .= '..'
277     $S1 = $I1
278     $S0 .= $S1
279     $S0 .= ' ('
280     $S0 .= $S2
281     $S0 .= ')'
282     hash['Q'] = $S0                                  # %Q = printable quant
283     hash['M'] = ''
284     hash['N'] = ''
285   quant_min:
286     if $I0 > 0 goto quant_max
287     hash['M'] = '### '                               # %M = # if min==0
288   quant_max:
289     if $I1 != PGE_INF goto end
290     hash['N'] = '### '                               # %N = # if max==INF
291   end:
292     .return (hash)
293 .end
296 .sub 'gencapture' :method
297     .param pmc label
298     .local string cname
299     .local pmc captgen, captsave, captback
300     .local int iscapture, isarray
301     cname = self['cname']
302     iscapture = self['iscapture']
303     isarray = self['isarray']
304     captgen = new 'CodeString'
305     captsave = new 'CodeString'
306     captback = new 'CodeString'
307     if iscapture == 0 goto end
308     if isarray != 0 goto capt_array
309     captsave.emit("captscope[%0] = captob", cname)
310     captback.emit("delete captscope[%0]", cname)
311     goto end
312   capt_array:
313     captsave.emit("$P2 = captscope[%0]\n          push $P2, captob", cname)
314     captback.emit("$P2 = captscope[%0]\n          $P2 = pop $P2", cname)
315     captgen.emit(<<"        CODE", cname, label)
316           $I0 = defined captscope[%0]
317           if $I0 goto %1_cgen
318           $P0 = new 'ResizablePMCArray'
319           captscope[%0] = $P0
320           local_branch cstack, %1_cgen
321           delete captscope[%0]
322           goto fail
323         %1_cgen:
324         CODE
325   end:
326     .return (captgen, captsave, captback)
327 .end
330 .namespace [ 'PGE::Exp::Literal' ]
332 .sub 'reduce' :method
333     .param pmc next
334     .return (self)
335 .end
337 .sub 'pir' :method
338     .param pmc code
339     .param string label
340     .param string next
342     .local pmc args
343     args = self.'getargs'(label, next)
344     .local string literal
345     .local int litlen
346     literal = self
347     litlen = length literal
349     args['I'] = ''
350     $I0 = self['ignorecase']
351     if $I0 == 0 goto ignorecase_end
352     args['I'] = 'downcase $S0'
353     literal = downcase literal
354   ignorecase_end:
356     literal = code.escape(literal)
358     code.emit(<<"        CODE", litlen, literal, args :named :flat)
359         %L: # literal
360           $I0 = pos + %0
361           if $I0 > lastpos goto fail
362           $S0 = substr target, pos, %0
363           %I
364           if $S0 != %1 goto fail
365           pos += %0
366           goto %S\n
367         CODE
368     .return ()
369 .end
372 .namespace [ 'PGE::Exp::Concat' ]
374 .sub 'reduce' :method
375     .param pmc next
377     .local pmc children, exp
378     .local int n
379     children = self.'list'()
380     n = elements children
381   reduce_loop:
382     if n <= 0 goto reduce_end
383     dec n
384     exp = self[n]
385     exp = exp.reduce(next)
386     self[n] = exp
387     next = exp
388     goto reduce_loop
389   reduce_end:
390     .local int i, j
391     .local pmc exp0, exp1
392     n = elements children
393     i = 0
394     j = 0
395   concat_lit_loop:
396     inc i
397     if i >= n goto concat_lit_end
398     exp1 = children[i]
399     $I1 = isa exp1, 'PGE::Exp::Literal'
400     if $I1 == 0 goto concat_lit_shift
401     exp0 = children[j]
402     $I0 = isa exp0, 'PGE::Exp::Literal'
403     if $I0 == 0 goto concat_lit_shift
404     $I0 = exp0['ignorecase']
405     $I1 = exp1['ignorecase']
406     if $I0 != $I1 goto concat_lit_shift
407     $S0 = exp0
408     $S1 = exp1
409     concat $S0, $S1
410     exp0.'result_object'($S0)
411     goto concat_lit_loop
412   concat_lit_shift:
413     inc j
414     if j >= i goto concat_lit_loop
415     children[j] = exp1
416     goto concat_lit_loop
417   concat_lit_end:
418     inc j
419     children = j
420     if j > 1 goto end
421     exp = self[0]
422     .return (exp)
423   end:
424     .return (self)
425 .end
428 .sub 'pir' :method
429     .param pmc code
430     .param string label
431     .param string next
433     .local pmc iter, exp
434     code.emit('        %0: # concat', label)
435     $P0 = self.'list'()
436     iter = new 'Iterator', $P0
437     exp = shift iter
438     $S0 = code.unique('R')
439   iter_loop:
440     unless iter goto iter_end
441     $P1 = shift iter
442     $S1 = code.unique('R')
443     exp.pir(code, $S0, $S1)
444     exp = $P1
445     $S0 = $S1
446     goto iter_loop
447   iter_end:
448     exp.pir(code, $S0, next)
449     .return ()
450 .end
453 .namespace [ 'PGE::Exp::Quant' ]
455 .sub 'reduce' :method
456     .param pmc next
457     .local pmc exp0
458     exp0 = self[0]
460     .local int backtrack, min, max
461     backtrack = self['backtrack']
462     min = self['min']
463     max = self['max']
464     if max != 1 goto reduce_exp0
465     if min != max goto reduce_max1
466     exp0['backtrack'] = backtrack
467     exp0 = exp0.reduce(next)
468     .return (exp0)
470   reduce_max1:
471     ##  special case of 0..1?: node
472     if backtrack != PGE_BACKTRACK_NONE goto reduce_exp0
473     $I0 = exists exp0['backtrack']
474     if $I0 goto reduce_exp0
475     exp0['backtrack'] = backtrack
477   reduce_exp0:
478     exp0 = exp0.reduce(next)
479     self[0] = exp0
480     .return (self)
481 .end
483 .sub 'pir' :method
484     .param pmc code
485     .param string label
486     .param string next
488     .local pmc exp
489     .local string explabel, replabel
490     exp = self[0]
492     $I0 = can exp, 'pir_quant'
493     if $I0 == 0 goto outer_quant
494     $I0 = exp.'pir_quant'(code, label, next, self)
495     if $I0 == 0 goto outer_quant
496     .return ()
498   outer_quant:
499     .local pmc args
500     args = self.'getargs'(label, next, 'quant' => self)
502     .local int backtrack
503     backtrack = self['backtrack']
505     explabel = code.unique('R')
506     replabel = concat label, '_repeat'
508     if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
509     if backtrack == PGE_BACKTRACK_NONE goto bt_none
511   bt_greedy:
512     args['c'] = 0
513     args['C'] = '### '
514     ##   handle 0..Inf as a special case
515     $I0 = self['min']
516     if $I0 != 0 goto bt_greedy_none
517     $I0 = self['max']
518     if $I0 != PGE_INF goto bt_greedy_none
519     code.emit(<<"        CODE", replabel, explabel, args :flat :named)
520         %L:  # quant 0..Inf greedy
521         %0:
522           push ustack, pos
523           local_branch cstack, %1
524           pos = pop ustack
525           if cutmark != 0 goto fail
526           goto %S
527         CODE
528     goto end
530   bt_none:
531     $S0 = code.unique()
532     args['c'] = $S0
533     args['C'] = ''
534     ##   handle 0..Inf as a special case
535     $I0 = self['min']
536     if $I0 != 0 goto bt_greedy_none
537     $I0 = self['max']
538     if $I0 != PGE_INF goto bt_greedy_none
539     code.emit(<<"        CODE", replabel, explabel, args :flat :named)
540         %L:  # quant 0..Inf none
541           local_branch cstack, %0
542           if cutmark != %c goto fail
543           cutmark = 0
544           goto fail
545         %0:
546           push ustack, pos
547           local_branch cstack, %1
548           pos = pop ustack
549           if cutmark != 0 goto fail
550           local_branch cstack, %S
551           if cutmark != 0 goto fail
552           cutmark = %c
553           goto fail
554         CODE
555     goto end
557   bt_greedy_none:
558     ##   handle greedy or none
559     code.emit(<<"        CODE", replabel, explabel, args :flat :named)
560         %L:  # quant %Q greedy/none
561           push gpad, 0
562           local_branch cstack, %0
563           $I0 = pop gpad
564           %Cif cutmark != %c goto fail
565           %Ccutmark = 0
566           goto fail
567         %0:
568           rep = gpad[-1]
569           %Nif rep >= %n goto %L_1
570           inc rep
571           gpad[-1] = rep
572           push ustack, pos
573           push ustack, rep
574           local_branch cstack, %1
575           rep = pop ustack
576           pos = pop ustack
577           if cutmark != 0 goto fail
578           dec rep
579         %L_1:
580           %Mif rep < %m goto fail
581           $I0 = pop gpad
582           push ustack, rep
583           local_branch cstack, %S
584           rep = pop ustack
585           push gpad, rep
586           if cutmark != 0 goto fail
587           %Ccutmark = %c
588           goto fail\n
589         CODE
590     goto end
592   bt_eager:
593     ##   handle eager backtracking
594     code.emit(<<"        CODE", replabel, explabel, args :flat :named)
595         %L:  # quant %Q eager
596           push gpad, 0
597           local_branch cstack, %0
598           $I0 = pop gpad
599           goto fail
600         %0:
601           rep = gpad[-1]
602           %Mif rep < %m goto %L_1
603           $I0 = pop gpad
604           push ustack, pos
605           push ustack, rep
606           local_branch cstack, %S
607           rep = pop ustack
608           pos = pop ustack
609           push gpad, rep
610         %L_1:
611           %Nif rep >= %n goto fail
612           inc rep
613           gpad[-1] = rep
614           goto %1\n
615         CODE
617   end:
618     exp.pir(code, explabel, replabel)
619     .return ()
620 .end
623 .namespace [ 'PGE::Exp::Group' ]
625 .sub 'reduce' :method
626     .param pmc next
627     .local pmc exp
629     $I0 = self['backtrack']
630     if $I0 != PGE_BACKTRACK_NONE goto reduce_exp0
631     ##   This group is non-backtracking, so concatenate a "cut group"
632     ##   node (PGE::Exp::Cut) to its child.
633     exp = self[0]
634     $P0 = new 'PGE::Exp::Concat'
635     $P0[0] = exp
636     $P1 = new 'PGE::Exp::Cut'
637     $P0[1] = $P1
638     self[0] = $P0
640   reduce_exp0:
641     .local pmc group
642     ##   Temporarily store this group as the current group.  Any
643     ##   cut nodes we encounter will set a cutmark into this group.
644     group = get_hll_global ['PGE::Exp'], '$!group'
645     set_hll_global ['PGE::Exp'], '$!group', self
646     exp = self[0]
647     exp = exp.reduce(next)
648     set_hll_global ['PGE::Exp'], '$!group', group
649     $I0 = self['cutmark']
650     if $I0 > 0 goto keep_group
651     $I0 = self['iscapture']
652     if $I0 != 0 goto keep_group
653     .return (exp)
654   keep_group:
655     .return (self)
656 .end
658 .sub 'pir' :method
659     .param pmc code
660     .param string label
661     .param string next
663     .local pmc exp0
664     exp0 = self[0]
666     .local int cutmark
667     cutmark = self['cutmark']
668     if cutmark > 0 goto has_cutmark
669     exp0.'pir'(code, label, next)
670     .return ()
672   has_cutmark:
673     .local string exp0label
674     exp0label = code.unique('R')
675     code.emit(<<"        CODE", label, exp0label, cutmark)
676         %0:  # group %2
677           local_branch cstack, %1
678           if cutmark != %2 goto fail
679           cutmark = 0
680           goto fail\n
681         CODE
682     exp0.'pir'(code, exp0label, next)
683 .end
686 .namespace [ 'PGE::Exp::CGroup' ]
688 .sub 'pir' :method
689     .param pmc code
690     .param string label
691     .param string next
693     .local string explabel, expnext
694     explabel = code.unique('R')
695     expnext = concat label, '_close'
697     .local pmc args
698     args = self.getargs(label, next)
700     .local string captgen, captsave, captback
701     (captgen, captsave, captback) = self.'gencapture'(label)
703     .local int cutmark
704     cutmark = self['cutmark']
705     args['c'] = cutmark
706     args['C'] = '### '
707     if cutmark == 0 goto cutmark_end
708     args['C'] = ''
709   cutmark_end:
711     .local int isscope
712     isscope = self['isscope']
713     args['X'] = ''
714     if isscope != 0 goto isscope_end
715     args['X'] = '### '
716   isscope_end:
718     code.emit(<<"        CODE", captgen, captsave, captback, 'E'=>explabel, args :flat :named)
719         %L: # capture
720           %0
721           captob = captscope.'new'(captscope, 'pos'=>pos)
722           push gpad, captscope
723           push gpad, captob
724           %Xcaptscope = captob
725           local_branch cstack, %E
726           captob = pop gpad
727           captscope = pop gpad
728           %Cif cutmark != %c goto fail
729           %Ccutmark = 0
730           goto fail
731         %L_close:
732           push ustack, captscope
733           captob = pop gpad
734           captscope = pop gpad
735           $P1 = getattribute captob, '$.pos'
736           $P1 = pos
737           %1
738           push ustack, captob
739           local_branch cstack, %S
740           captob = pop ustack
741           %2
742           push gpad, captscope
743           push gpad, captob
744           captscope = pop ustack
745           goto fail\n
746         CODE
747     .local pmc exp
748     exp = self[0]
749     exp.pir(code, explabel, expnext)
750     .return ()
751 .end
754 .namespace [ 'PGE::Exp::Subrule' ]
756 .sub 'reduce' :method
757     .param pmc next
758     .return (self)
759 .end
761 .sub 'pir' :method
762     .param pmc code
763     .param string label
764     .param string next
766     .local pmc args
767     args = self.getargs(label, next)
769     .local string subarg
770     subarg = ''
771     $I0 = exists self['arg']
772     if $I0 == 0 goto subarg_end
773     subarg = self['arg']
774     subarg = code.escape(subarg)
775     subarg = concat ', ', subarg
776     args['A'] = $S0
777   subarg_end:
779     .local string cname, captgen, captsave, captback
780     (captgen, captsave, captback) = self.gencapture(label)
782     .local string subname
783     subname = self['subname']
784   subrule:
785     $I0 = 0
786   subrule_1:
787     $I1 = index subname, '::', $I0
788     if $I1 == -1 goto subrule_2
789     $I0 = $I1 + 2
790     goto subrule_1
791   subrule_2:
792     if $I0 == 0 goto subrule_simple_name
793     ##   The subrule is of the form <Grammar::rule>, which means we need
794     ##   to create a Match object of the appropriate grammar (class) for it.
795     .local string grammar, rname
796     rname = substr subname, $I0
797     $I0 -= 2
798     grammar = substr subname, 0, $I0
799     code.emit(<<"        CODE", grammar, rname, args :flat :named)
800         %L: # grammar subrule %0::%1
801           captob = captscope.'new'(captscope, 'grammar'=>'%0')
802           captob.'to'(pos)
803           $P0 = get_hll_global ['%0'], '%1'
804         CODE
805     goto subrule_match
807   subrule_simple_name:
808     ##   The subrule is of the form <rule>, which means we first look
809     ##   for a method on the current match object, otherwise we do a
810     ##   normal name lookup.
811     code.emit(<<"        CODE", subname, args :flat :named)
812         %L: # subrule %0
813           captob = captscope
814           $P0 = getattribute captob, '$.pos'
815           $P0 = pos
816           $I0 = can mob, '%0'
817           if $I0 == 0 goto %L_1
818           $P0 = find_method mob, '%0'
819           goto %L_2
820         %L_1:
821           $P0 = find_name '%0'
822           unless null $P0 goto %L_2
823           say "Unable to find regex '%0'"
824         %L_2:
825         CODE
827   subrule_match:
828     $I0 = self['iszerowidth']
829     if $I0 goto subrule_zerowidth
830     $S0 = concat label, '_3'
831     $I0 = self['backtrack']
832     if $I0 != PGE_BACKTRACK_NONE goto subrule_match_1
833     $S0 = next
834   subrule_match_1:
835     ##   Perform the subrule match, capturing the result as needed.
836     ##   We either branch directly to the next node (PGE_BACKTRACK_NONE)
837     ##   or to a small subroutine below that will keep backtracking into
838     ##   the subrule until it no longer produces a match.
839     code.emit(<<"        CODE", PGE_CUT_MATCH, $S0, captgen, captsave, captback, subarg)
840           $P2 = adverbs['action']
841           captob = $P0(captob%5, 'action'=>$P2)
842           $P1 = getattribute captob, '$.pos'
843           if $P1 <= %0 goto fail_match
844           if $P1 < 0 goto fail
845           %2
846           %3
847           pos = $P1
848           local_branch cstack, %1
849           %4
850           goto fail
851         CODE
852     if $I0 == PGE_BACKTRACK_NONE goto end
853     ##   Repeatedly backtrack into the subrule until there are no matches.
854     code.emit(<<"        CODE", PGE_CUT_MATCH, $S0, next)
855         %1:
856           pos = $P1
857           $P1 = getattribute captob, '&!corou'
858           if null $P1 goto %2
859           push ustack, captob
860           local_branch cstack, %2
861           captob = pop ustack
862           if cutmark != 0 goto fail
863           captob.next()
864           $P1 = getattribute captob, '$.pos'
865           if $P1 >= 0 goto %1
866           if $P1 <= %0 goto fail_match
867           goto fail\n
868         CODE
869     goto end
870     .return()
872   subrule_zerowidth:
873     ##  this handles zero-width subrule matches, either positive
874     ##  or negative.
875     .local string test
876     test = 'if'
877     $I0 = self['isnegated']
878     unless $I0 goto have_test
879     test = 'unless'
880   have_test:
881     code.emit(<<"        CODE", PGE_CUT_MATCH, test, next, subarg)
882           captob = $P0(captob%3)
883           $P1 = getattribute captob, '$.pos'
884           if $P1 <= %0 goto fail_match
885           %1 $P1 < 0 goto fail
886           $P1 = pos
887           $P1 = getattribute captob, '$.from'
888           $P1 = pos
889           goto %2
890         CODE
891   end:
892     .return()
893 .end
896 .namespace [ 'PGE::Exp::Alt' ]
898 .sub 'reduce' :method
899     .param pmc next
900     .local pmc exp0, exp1
901     exp0 = self[0]
902     exp0 = exp0.reduce(next)
903     self[0] = exp0
904     exp1 = self[1]
905     exp1 = exp1.reduce(next)
906     self[1] = exp1
907     .return (self)
908 .end
911 .sub 'pir' :method
912     .param pmc code
913     .param string label
914     .param string next
915     .local pmc exp0, exp1
916     .local string exp0label, exp1label
917     exp0label = code.unique('R')
918     exp1label = code.unique('R')
919     code.emit(<<"        CODE", label, exp0label, exp1label)
920         %0:  # alt %1, %2
921           push ustack, pos
922           local_branch cstack, %1
923           pos = pop ustack
924           if cutmark != 0 goto fail
925           goto %2\n
926         CODE
927     exp0 = self[0]
928     exp0.pir(code, exp0label, next)
929     exp1 = self[1]
930     exp1.pir(code, exp1label, next)
931     .return ()
932 .end
935 .namespace [ 'PGE::Exp::Anchor' ]
937 .sub 'reduce' :method
938     .param pmc next
939     .return (self)
940 .end
942 .sub 'pir' :method
943     .param pmc code
944     .param pmc label
945     .param pmc next
946     .local string token, test
947     token = self
949     if token == '^' goto anchor_bos
950     if token == '$' goto anchor_eos
951     if token == '^^' goto anchor_bol
952     if token == '$$' goto anchor_eol
953     if token == '<<' goto anchor_word_left
954     if token == '>>' goto anchor_word_right
955     if token == unicode:"\xab" goto anchor_word_left
956     if token == unicode:"\xbb" goto anchor_word_right
957     test = '!='
958     if token == '\b' goto anchor_word
959     test = '=='
960     if token == '\B' goto anchor_word
963   anchor_bos:
964     code.emit("        %0: # anchor bos", label)
965     code.emit("          if pos == 0 goto %0", next)
966     code.emit("          goto fail")
967     .return ()
969   anchor_eos:
970     code.emit("        %0: # anchor eos", label)
971     code.emit("          if pos == lastpos goto %0", next)
972     code.emit("          goto fail")
973     .return ()
975   anchor_bol:
976     code.emit(<<"        CODE", label, next, .CCLASS_NEWLINE)
977         %0: # anchor bol
978           if pos == 0 goto %1
979           if pos == lastpos goto fail
980           $I0 = pos - 1
981           $I1 = is_cclass %2, target, $I0
982           if $I1 goto %1
983           goto fail\n
984         CODE
985     .return ()
987   anchor_eol:
988     code.emit(<<"        CODE", label, next, .CCLASS_NEWLINE)
989         %0: # anchor eol
990           $I1 = is_cclass %2, target, pos
991           if $I1 goto %1
992           if pos != lastpos goto fail
993           if pos < 1 goto %1
994           $I0 = pos - 1
995           $I1 = is_cclass %2, target, $I0
996           if $I1 == 0 goto %1
997           goto fail\n
998         CODE
999     .return ()
1001   anchor_word:
1002     code.emit(<<"        CODE", label, next, .CCLASS_WORD, test)
1003         %0: # anchor word
1004           $I0 = 0
1005           if pos == 0 goto %0_1
1006           $I2 = pos - 1
1007           $I0 = is_cclass %2, target, $I2
1008         %0_1:
1009           $I1 = 0
1010           if pos >= lastpos goto %0_2
1011           $I1 = is_cclass %2, target, pos
1012         %0_2:
1013           if $I0 %3 $I1 goto %1
1014           goto fail
1015         CODE
1016     .return ()
1018   anchor_word_left:
1019     code.emit(<<"        CODE", label, next, .CCLASS_WORD)
1020         %0: # left word boundary
1021           if pos >= lastpos goto fail
1022           $I0 = is_cclass %2, target, pos
1023           if $I0 == 0 goto fail
1024           if pos == 0 goto %1
1025           $I0 = pos - 1
1026           $I0 = is_cclass %2, target, $I0
1027           if $I0 goto fail
1028           goto %1
1029         CODE
1030     .return ()
1032   anchor_word_right:
1033     code.emit(<<"        CODE", label, next, .CCLASS_WORD)
1034         %0: # right word boundary
1035           if pos == 0 goto fail
1036           $I0 = pos - 1
1037           $I0 = is_cclass %2, target, $I0
1038           if $I0 == 0 goto fail
1039           if pos >= lastpos goto %1
1040           $I0 = is_cclass %2, target, pos
1041           if $I0 goto fail
1042           goto %1
1043         CODE
1044     .return ()
1046 .end
1049 .namespace [ 'PGE::Exp::CCShortcut' ]
1051 .sub 'reduce' :method
1052     .param pmc next
1054     .local string token
1055     token = self
1056     self['negate'] = 1
1057     if token == '\D' goto digit
1058     if token == '\S' goto space
1059     if token == '\W' goto word
1060     if token == '\N' goto newline
1061     self['negate'] = 0
1062     if token == '\d' goto digit
1063     if token == '\s' goto space
1064     if token == '\w' goto word
1065     if token == '\n' goto newline
1066     self['cclass'] = .CCLASS_ANY
1067     goto end
1068   digit:
1069     self['cclass'] = .CCLASS_NUMERIC
1070     goto end
1071   space:
1072     self['cclass'] = .CCLASS_WHITESPACE
1073     goto end
1074   word:
1075     self['cclass'] = .CCLASS_WORD
1076     goto end
1077   newline:
1078     self['cclass'] = .CCLASS_NEWLINE
1079   end:
1080     .return (self)
1081 .end
1084 .sub 'pir' :method
1085     .param pmc code
1086     .param string label
1087     .param string next
1088     .local int cclass, negate
1090     $S0 = self
1091     code.emit("        %0: # cclass %1", label, $S0)
1092     code.emit("          if pos >= lastpos goto fail")
1093     cclass = self['cclass']
1094     negate = self['negate']
1095     if cclass == .CCLASS_ANY goto end
1096     code.emit("          $I0 = is_cclass %0, target, pos", cclass)
1097     code.emit("          if $I0 == %0 goto fail", negate)
1098   end:
1099     code.emit("          inc pos")
1100     code.emit("          goto %0", next)
1101     .return ()
1102 .end
1105 .sub 'pir_quant' :method
1106     .param pmc code
1107     .param string label
1108     .param string next
1109     .param pmc quant
1111     .local pmc args
1112     .local int min, max, backtrack
1113     args = self.'getargs'(label, next, 'quant'=>quant)
1114     min = quant['min']
1115     max = quant['max']
1116     backtrack = quant['backtrack']
1118     ##  output initial label
1119     code.'emit'("        %L: # cclass %0 %Q", self, args :flat :named)
1121     .local int cclass, negate
1122     cclass = self['cclass']
1123     negate = self['negate']
1124     if cclass == .CCLASS_ANY goto emit_dot
1125     .local string negstr
1126     negstr = '_not'
1127     if negate == 0 goto emit_find
1128     negstr = ''
1129   emit_find:
1130     code.emit(<<"        CODE", negstr, cclass)
1131           $I0 = find%0_cclass %1, target, pos, lastpos
1132           rep = $I0 - pos
1133         CODE
1134     goto emit_pir
1135   emit_dot:
1136     code.emit("          rep = lastpos - pos")
1138   emit_pir:
1139     code.emit(<<"        CODE", args :flat :named)
1140           %Mif rep < %m goto fail
1141           %Nif rep <= %n goto %L_1
1142           %Nrep = %n
1143         %L_1:
1144         CODE
1146     if backtrack == PGE_BACKTRACK_NONE goto bt_none
1147     if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
1149   bt_greedy:
1150     code.emit(<<"        CODE", args :flat :named)
1151           pos += rep
1152         %L_2:
1153           if rep <= %m goto %S
1154           push ustack, pos
1155           push ustack, rep
1156           local_branch cstack, %S
1157           rep = pop ustack
1158           pos = pop ustack
1159           if cutmark != 0 goto fail
1160           dec pos
1161           dec rep
1162           goto %L_2
1163         CODE
1164     .return (1)
1166   bt_none:
1167     code.emit("          pos += rep\n          goto %0\n", next)
1168     .return (1)
1170   bt_eager:
1171     code.emit(<<"        CODE", args :flat :named)
1172           %Mpos += %m
1173           %Mrep -= %m
1174         %L_2:
1175           if rep == 0 goto %S
1176           push ustack, pos
1177           push ustack, rep
1178           local_branch cstack, %S
1179           rep = pop ustack
1180           pos = pop ustack
1181           if cutmark != 0 goto fail
1182           inc pos
1183           dec rep
1184           goto %L_2
1185         CODE
1187 .end
1189 .namespace [ 'PGE::Exp::Cut' ]
1191 .sub 'reduce' :method
1192     .param pmc next
1193     .local pmc group
1194     .local int cutmark
1196     cutmark = self['cutmark']
1197     if cutmark <= PGE_CUT_RULE goto end
1198     ##   This node is cutting a group.  We need to
1199     ##   get the current group's cutmark, or set
1200     ##   one if it doesn't already have one.
1201     group = get_hll_global ['PGE::Exp'], '$!group'
1202     cutmark = group['cutmark']
1203     if cutmark > 0 goto has_cutmark
1204     $P1 = new 'CodeString'
1205     cutmark = $P1.unique()
1206     group['cutmark'] = cutmark
1207   has_cutmark:
1208     self['cutmark'] = cutmark
1209   end:
1210     .return (self)
1211 .end
1213 .sub 'pir' :method
1214     .param pmc code
1215     .param string label
1216     .param string next
1218     .local int cutmark
1219     cutmark = self['cutmark']
1221     if cutmark > 0 goto group_cutmark
1222     code.emit(<<"        CODE", label, next, cutmark)
1223         %0: # cut rule or match
1224           local_branch cstack, %1
1225           cutmark = %2
1226           goto fail_cut\n
1227         CODE
1228     .return ()
1230   group_cutmark:
1231     code.emit(<<"        CODE", label, next, cutmark)
1232         %0: # cut %2
1233           local_branch cstack, %1
1234           cutmark = %2
1235           goto fail\n
1236         CODE
1237     .return ()
1238 .end
1241 .namespace [ 'PGE::Exp::Scalar' ]
1243 .sub 'reduce' :method
1244     .param pmc next
1245     .return (self)
1246 .end
1248 .sub 'pir' :method
1249     .param pmc code
1250     .param string label
1251     .param string next
1253     .local string cname
1254     cname = self['cname']
1255     code.emit(<<"        CODE", label, next, cname)
1256         %0: # scalar %2
1257           $P0 = mob[%2]
1258           $I0 = does $P0, 'array'
1259           if $I0 == 0 goto %0_1
1260           $P0 = $P0[-1]
1261         %0_1:
1262           $S1 = $P0
1263           $I1 = length $S1
1264           $I0 = pos + $I1
1265           if $I0 > lastpos goto fail
1266           $S0 = substr target, pos, $I1
1267           if $S0 != $S1 goto fail
1268           pos += $I1
1269           goto %1
1270         CODE
1271     .return ()
1272 .end
1275 .namespace [ 'PGE::Exp::EnumCharList' ]
1277 .sub 'reduce' :method
1278     .param pmc next
1279     .return (self)
1280 .end
1282 .sub 'pir' :method
1283     .param pmc code
1284     .param string label
1285     .param string next
1287     .local string charlist
1288     $S0 = self
1289     charlist = code.escape($S0)
1291     .local string test
1292     test = '<'
1293     $I0 = self['isnegated']
1294     if $I0 == 0 goto negated_end
1295     test = '>='
1296   negated_end:
1297     .local string incpos
1298     incpos = 'inc pos'
1299     $I0 = self['iszerowidth']
1300     if $I0 == 0 goto zerowidth_end
1301     incpos = '###   zero width'
1302   zerowidth_end:
1304     code.emit(<<"        CODE", label, charlist, test, incpos, next)
1305         %0: # enumcharlist %1
1306           if pos >= lastpos goto fail
1307           $S0 = substr target, pos, 1
1308           $I0 = index %1, $S0
1309           if $I0 %2 0 goto fail
1310           %3
1311           goto %4
1312         CODE
1313     .return ()
1314 .end
1317 .namespace [ 'PGE::Exp::Newline' ]
1319 .sub 'reduce' :method
1320     .param pmc next
1321     .return (self)
1322 .end
1324 .sub 'pir' :method
1325     .param pmc code
1326     .param string label
1327     .param string next
1328     code.emit(<<"        CODE", label, next, .CCLASS_NEWLINE)
1329         %0: # newline
1330           $I0 = is_cclass %2, target, pos
1331           if $I0 == 0 goto fail
1332           $S0 = substr target, pos, 2
1333           inc pos
1334           if $S0 != "\\r\\n" goto %1
1335           inc pos
1336           goto %1
1337         CODE
1338     .return ()
1339 .end
1342 .namespace [ 'PGE::Exp::Conj' ]
1344 .sub 'reduce' :method
1345     .param pmc next
1346     .local pmc exp0, exp1
1347     exp0 = self[0]
1348     exp0 = exp0.reduce(next)
1349     self[0] = exp0
1350     exp1 = self[1]
1351     exp1 = exp1.reduce(next)
1352     self[1] = exp1
1353     .return (self)
1354 .end
1356 .sub 'pir' :method
1357     .param pmc code
1358     .param string label
1359     .param string next
1361     .local string exp0label, exp1label, chk0label, chk1label
1362     exp0label = code.unique('R')
1363     exp1label = code.unique('R')
1364     chk0label = concat label, '_chk0'
1365     chk1label = concat label, '_chk1'
1366     code.emit(<<"        CODE", label, next, exp0label, chk0label, exp1label, chk1label)
1367         %0: # conj %2, %4
1368           push gpad, pos
1369           push gpad, pos
1370           local_branch cstack, %2
1371           $I0 = pop gpad
1372           $I0 = pop gpad
1373           goto fail
1374         %3:
1375           gpad[-1] = pos
1376           pos = gpad[-2]
1377           goto %4
1378         %5:
1379           $I0 = gpad[-1]
1380           if $I0 != pos goto fail
1381           $I0 = pop gpad
1382           $I1 = pop gpad
1383           push ustack, $I1
1384           push ustack, $I0
1385           local_branch cstack, %1
1386           $I0 = pop ustack
1387           $I1 = pop ustack
1388           push gpad, $I1
1389           push gpad, $I0
1390           goto fail\n
1391         CODE
1392     .local pmc exp0, exp1
1393     exp0 = self[0]
1394     exp0.'pir'(code, exp0label, chk0label)
1395     exp1 = self[1]
1396     exp1.'pir'(code, exp1label, chk1label)
1397     .return ()
1398 .end
1400 .namespace [ "PGE::Exp::Closure" ]
1402 .sub 'reduce' :method
1403     .param pmc next
1404     .return (self)
1405 .end
1407 .sub 'pir' :method
1408     .param pmc code
1409     .param string label
1410     .param string next
1411     .local string value, lang
1412     value = self
1413     lang = self['lang']
1414     value = code.'escape'(value)
1415     lang = code.'escape'(lang)
1416     ##  to prevent recompiling every execution, this code makes use of
1417     ##  a global %!cache, keyed on the inline closure source.  There
1418     ##  could be a (unlikely) problem if the same source is sent to
1419     ##  two different compilers.  Also, if the sources can be lengthy
1420     ##  we might be well served to use a hashed representation of
1421     ##  the source.
1422     code.emit(<<"        CODE", label, next, lang, value)
1423         %0: # closure
1424           $S1 = %3
1425           $P0 = get_hll_global ['PGE::Match'], '%!cache'
1426           $P1 = $P0[$S1]
1427           unless null $P1 goto %0_1
1428           $P1 = compreg %2
1429           $P1 = $P1($S1)
1430           $P0[$S1] = $P1
1431         %0_1:
1432           mpos = pos
1433           ($P0 :optional, $I0 :opt_flag) = $P1(mob)
1434           if $I0 == 0 goto %1
1435           mob.'result_object'($P0)
1436           push ustack, pos
1437           local_branch cstack, succeed
1438           pos = pop ustack
1439           null $P0
1440           mob.'result_object'($P0)
1441           goto fail
1442         CODE
1443     .return ()
1444 .end
1446 .namespace [ "PGE::Exp::Action" ]
1448 .sub 'reduce' :method
1449     .param pmc next
1450     .return (self)
1451 .end
1453 .sub 'pir' :method
1454     .param pmc code
1455     .param string label
1456     .param string next
1457     .local string actionname, actionkey
1458     code.emit("        %0: # action", label)
1459     actionname = self['actionname']
1460     if actionname == '' goto end
1461     actionname = code.'escape'(actionname)
1462     actionkey = self['actionkey']
1463     if actionkey == '' goto have_actionkey
1464     actionkey = code.'escape'(actionkey)
1465     actionkey = concat ', ', actionkey
1466   have_actionkey:
1467     code.emit(<<"        CODE", label, next, actionname, actionkey)
1468           $P1 = adverbs['action']
1469           if null $P1 goto %1
1470           $I1 = can $P1, %2
1471           if $I1 == 0 goto %1
1472           mpos = pos
1473           $P1.%2(mob%3)
1474           goto %1
1475         CODE
1476   end:
1477     .return ()
1478 .end
1481 # Local Variables:
1482 #   mode: pir
1483 #   fill-column: 100
1484 # End:
1485 # vim: expandtab shiftwidth=4 ft=pir: