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