1 # Copyright (C) 2005-2009, Parrot Foundation.
6 PGE::Exp - base class for expressions
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
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)
52 =item C<compile(PMC adverbs :slurpy :named)>
54 Compile C<self> into PIR or a subroutine, according to the
59 .sub 'compile' :method
60 .param pmc adverbs :slurpy :named
63 target = adverbs['target']
64 target = downcase target
65 if target == 'parse' goto return_exp
66 if target == 'pge::exp' goto return_exp
69 code = new 'CodeString'
72 ns = adverbs['namespace']
73 unless null ns goto ns_emit
76 grammar = adverbs['grammar']
77 ns = split '::', grammar
80 code.'emit'('.namespace %0', $P0)
83 $P0 = self.'root_pir'(adverbs :flat :named)
85 if target != 'pir' goto bytecode
92 if grammar == '' goto end
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')
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.
113 .sub 'root_pir' :method
114 .param pmc adverbs :slurpy :named
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'
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('
142 ## Perform reduction/optimization on the
143 ## expression tree before generating PIR.
145 .local string explabel
147 set_hll_global ['PGE';'Exp'], '$!group', exp
148 exp = exp.'reduce'(self)
150 ## we don't need a coroutine if :ratchet is set
152 $I0 = adverbs['ratchet']
153 cutrule = isne $I0, 0
155 ## generate the PIR for the expression tree.
157 expcode = new 'CodeString'
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
165 code.'emit'(<<" CODE", name, pirflags, namecorou, .INTERPINFO_CURRENT_SUB)
166 .sub %0 :method :nsentry %1
167 .param pmc adverbs :slurpy :named
169 .const 'Sub' corou = %2
172 mob = $P0(self, adverbs)
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)
184 setattribute mob, '&!corou', $P0
186 lastpos = length target
187 if cpos > lastpos goto fail_rule
192 ## Initial code for a rule that cannot be backtracked into.
194 code.'emit'(<<" CODE", name, pirflags)
195 .sub %0 :method :nsentry %1
196 .param pmc adverbs :unique_reg :slurpy :named
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)
204 lastpos = length target
205 if cpos > lastpos goto fail_rule
209 ## generate the ustack only if we need it
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']")
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']")
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")
232 code.'emit'(<<" CODE", PGE_CUT_RULE, returnop)
233 .local int pos, rep, cutmark :unique_reg
235 if cpos > lastpos goto fail_rule
239 local_branch cstack, R
240 if cutmark <= %0 goto fail_cut
242 if iscont goto try_match
246 mob.'_failcut'(cutmark)
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)
266 ## add the expression code, then close off the sub
273 .sub 'getargs' :method
276 .param pmc hash :slurpy :named
277 hash['L'] = label # %L = node's label
278 hash['S'] = next # %S = success target
280 $I0 = exists hash['quant']
282 quant = hash['quant']
285 $S2 = quant['backtrack']
286 hash['m'] = $I0 # %m = min repetitions
287 hash['n'] = $I1 # %n = max repetitions
295 hash['Q'] = $S0 # %Q = printable quant
299 if $I0 > 0 goto quant_max
300 hash['M'] = '### ' # %M = # if min==0
302 if $I1 != PGE_INF goto end
303 hash['N'] = '### ' # %N = # if max==INF
309 .sub 'gencapture' :method
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)
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]
331 $P0 = root_new ['parrot';'ResizablePMCArray']
333 local_branch cstack, %1_cgen
339 .return (captgen, captsave, captback)
343 .namespace [ 'PGE';'Exp';'Literal' ]
345 .sub 'reduce' :method
356 args = self.'getargs'(label, next)
357 .local string literal
359 literal = self.'ast'()
360 litlen = length literal
363 $I0 = self['ignorecase']
364 if $I0 == 0 goto ignorecase_end
365 args['I'] = '$S0 = downcase $S0'
366 literal = downcase literal
369 literal = code.'escape'(literal)
371 code.'emit'(<<" CODE", litlen, literal, args :named :flat)
374 if $I0 > lastpos goto fail
375 $S0 = substr target, pos, %0
377 if $S0 != %1 goto fail
385 .namespace [ 'PGE';'Exp';'Concat' ]
387 .sub 'reduce' :method
390 .local pmc children, exp
392 children = self.'list'()
393 n = elements children
395 if n <= 0 goto reduce_end
398 exp = exp.'reduce'(next)
404 .local pmc exp0, exp1
405 n = elements children
410 if i >= n goto concat_lit_end
412 $I1 = isa exp1, ['PGE';'Exp';'Literal']
413 if $I1 == 0 goto concat_lit_shift
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
427 if j >= i goto concat_lit_loop
447 code.'emit'(' %0: # concat', label)
451 $S0 = code.'unique'('R')
453 unless it goto iter_end
455 $S1 = code.'unique'('R')
456 exp.'pir'(code, $S0, $S1)
461 exp.'pir'(code, $S0, next)
466 .namespace [ 'PGE';'Exp';'Quant' ]
468 .sub 'reduce' :method
474 .local int backtrack, min, max
475 backtrack = self['backtrack']
478 if max != 1 goto reduce_exp0
479 if min != max goto reduce_max1
480 exp0['backtrack'] = backtrack
481 exp0 = exp0.'reduce'(next)
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
492 exp0 = exp0.'reduce'(next)
494 if null sep goto reduce_exp0_1
495 sep = sep.'reduce'(next)
507 .local string explabel, seplabel, replabel, nextlabel
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
520 args = self.'getargs'(label, next, 'quant' => self)
523 backtrack = self['backtrack']
525 explabel = code.'unique'('R')
527 replabel = concat label, '_repeat'
528 if null sep goto outer_quant_1
529 seplabel = code.'unique'('R')
530 nextlabel = concat label, '_sep'
533 if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
534 if backtrack == PGE_BACKTRACK_NONE goto bt_none
539 ## handle 0..Inf as a special case
541 if $I0 != 0 goto bt_greedy_none
543 if $I0 != PGE_INF goto bt_greedy_none
544 code.'emit'(<<" CODE", replabel, nextlabel, args :flat :named)
545 %L: # quant 0..Inf greedy
548 local_branch cstack, %1
550 if cutmark != 0 goto fail
556 $S0 = code.'unique'()
559 ## handle 0..Inf as a special case
561 if $I0 != 0 goto bt_greedy_none
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
572 local_branch cstack, %1
574 if cutmark != 0 goto fail
575 local_branch cstack, %S
576 if cutmark != 0 goto fail
583 ## handle greedy or none
584 code.'emit'(<<" CODE", replabel, nextlabel, args :flat :named)
585 %L: # quant %Q greedy/none
587 local_branch cstack, %0
589 %Cif cutmark != %c goto fail
594 %Nif rep >= %n goto %L_1
599 local_branch cstack, %1
602 if cutmark != 0 goto fail
605 %Mif rep < %m goto fail
608 local_branch cstack, %S
611 if cutmark != 0 goto fail
618 ## handle eager backtracking
619 code.'emit'(<<" CODE", replabel, nextlabel, args :flat :named)
622 local_branch cstack, %0
627 %Mif rep < %m goto %L_1
631 local_branch cstack, %S
636 %Nif rep >= %n goto fail
643 if null sep goto sep_done
644 code.'emit'(<<" CODE", nextlabel, explabel, seplabel)
649 sep.'pir'(code, seplabel, explabel)
651 exp.'pir'(code, explabel, replabel)
656 .namespace [ 'PGE';'Exp';'Group' ]
658 .sub 'reduce' :method
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.
667 $P0 = new ['PGE';'Exp';'Concat']
669 $P1 = new ['PGE';'Exp';'Cut']
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
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
700 cutmark = self['cutmark']
701 if cutmark > 0 goto has_cutmark
702 exp0.'pir'(code, label, next)
706 .local string exp0label
707 exp0label = code.'unique'('R')
708 code.'emit'(<<" CODE", label, exp0label, cutmark)
710 local_branch cstack, %1
711 if cutmark != %2 goto fail
715 exp0.'pir'(code, exp0label, next)
719 .namespace [ 'PGE';'Exp';'CGroup' ]
721 .sub 'pir' :method :nsentry
726 .local string explabel, expnext
727 explabel = code.'unique'('R')
728 expnext = concat label, '_close'
731 args = self.'getargs'(label, next)
733 .local string captgen, captsave, captback
734 (captgen, captsave, captback) = self.'gencapture'(label)
737 cutmark = self['cutmark']
740 if cutmark == 0 goto cutmark_end
745 isscope = self['isscope']
747 if isscope != 0 goto isscope_end
751 code.'emit'(<<" CODE", captgen, captsave, captback, 'E'=>explabel, args :flat :named)
754 captob = captscope.'new'(captscope, 'pos'=>pos)
758 local_branch cstack, %E
761 %Cif cutmark != %c goto fail
765 push ustack, captscope
768 $P1 = getattribute captob, '$.pos'
772 local_branch cstack, %S
777 captscope = pop ustack
782 exp.'pir'(code, explabel, expnext)
787 .namespace [ 'PGE';'Exp';'Subrule' ]
789 .sub 'reduce' :method
800 args = self.'getargs'(label, next)
804 $I0 = exists self['arg']
805 if $I0 == 0 goto subarg_dba
807 subarg = code.'escape'(subarg)
808 subarg = concat ', ', subarg
811 $I0 = exists self['dba']
812 if $I0 == 0 goto subarg_end
814 $S0 = code.'escape'($S0)
815 subarg .= ", 'dba'=>"
819 .local string cname, captgen, captsave, captback
820 (captgen, captsave, captback) = self.'gencapture'(label)
822 .local string subname
823 subname = self['subname']
827 $I1 = index subname, '::', $I0
828 if $I1 == -1 goto 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
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')
845 $P0 = get_hll_global %2, '%1'
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)
856 $P0 = getattribute captob, '$.pos'
859 if $I0 == 0 goto %L_1
860 $P0 = find_method mob, '%0'
864 unless null $P0 goto %L_2
865 die "Unable to find regex '%0'"
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
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
890 local_branch cstack, %1
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)
899 $P1 = getattribute captob, '&!corou'
902 local_branch cstack, %2
904 if cutmark != 0 goto fail
906 $P1 = getattribute captob, '$.pos'
908 if $P1 <= %0 goto fail_match
915 ## this handles zero-width subrule matches, either positive
919 $I0 = self['isnegated']
920 unless $I0 goto 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
929 $P1 = getattribute captob, '$.from'
938 .namespace [ 'PGE';'Exp';'Alt' ]
940 .sub 'reduce' :method
942 .local pmc exp0, exp1
944 exp0 = exp0.'reduce'(next)
947 exp1 = exp1.'reduce'(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)
964 local_branch cstack, %1
966 if cutmark != 0 goto fail
970 exp0.'pir'(code, exp0label, next)
972 exp1.'pir'(code, exp1label, next)
977 .namespace [ 'PGE';'Exp';'Anchor' ]
979 .sub 'reduce' :method
988 .local string token, test
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
1001 if token == '\b' goto anchor_word
1003 if token == '\B' goto anchor_word
1006 code.'emit'(" %0: # anchor fail %1", label, token)
1007 code.'emit'(" goto fail")
1011 code.'emit'(" %0: # anchor null %1", label, token)
1012 code.'emit'(" goto %0", next)
1016 code.'emit'(" %0: # anchor bos", label)
1017 code.'emit'(" if pos == 0 goto %0", next)
1018 code.'emit'(" goto fail")
1022 code.'emit'(" %0: # anchor eos", label)
1023 code.'emit'(" if pos == lastpos goto %0", next)
1024 code.'emit'(" goto fail")
1028 code.'emit'(<<" CODE", label, next, .CCLASS_NEWLINE)
1031 if pos == lastpos goto fail
1033 $I1 = is_cclass %2, target, $I0
1040 code.'emit'(<<" CODE", label, next, .CCLASS_NEWLINE)
1042 $I1 = is_cclass %2, target, pos
1044 if pos != lastpos goto fail
1047 $I1 = is_cclass %2, target, $I0
1054 code.'emit'(<<" CODE", label, next, .CCLASS_WORD, test)
1057 if pos == 0 goto %0_1
1059 $I0 = is_cclass %2, target, $I2
1062 if pos >= lastpos goto %0_2
1063 $I1 = is_cclass %2, target, pos
1065 if $I0 %3 $I1 goto %1
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
1078 $I0 = is_cclass %2, target, $I0
1085 code.'emit'(<<" CODE", label, next, .CCLASS_WORD)
1086 %0: # right word boundary
1087 if pos == 0 goto fail
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
1101 .namespace [ 'PGE';'Exp';'CCShortcut' ]
1103 .sub 'reduce' :method
1107 token = self.'ast'()
1109 if token == '\D' goto digit
1110 if token == '\S' goto space
1111 if token == '\W' goto word
1112 if token == '\N' goto newline
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
1121 self['cclass'] = .CCLASS_NUMERIC
1124 self['cclass'] = .CCLASS_WHITESPACE
1127 self['cclass'] = .CCLASS_WORD
1130 self['cclass'] = .CCLASS_NEWLINE
1140 .local int cclass, negate
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)
1151 code.'emit'(" inc pos")
1152 code.'emit'(" goto %0", next)
1157 .sub 'pir_quant' :method
1164 .local int min, max, backtrack
1165 args = self.'getargs'(label, next, 'quant'=>quant)
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
1179 if negate == 0 goto emit_find
1182 code.'emit'(<<" CODE", negstr, cclass)
1183 $I0 = find%0_cclass %1, target, pos, lastpos
1188 code.'emit'(" rep = lastpos - pos")
1191 code.'emit'(<<" CODE", args :flat :named)
1192 %Mif rep < %m goto fail
1193 %Nif rep <= %n goto %L_1
1198 if backtrack == PGE_BACKTRACK_NONE goto bt_none
1199 if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
1202 code.'emit'(<<" CODE", args :flat :named)
1205 if rep <= %m goto %S
1208 local_branch cstack, %S
1211 if cutmark != 0 goto fail
1219 code.'emit'(" pos += rep\n goto %0\n", next)
1223 code.'emit'(<<" CODE", args :flat :named)
1230 local_branch cstack, %S
1233 if cutmark != 0 goto fail
1241 .namespace [ 'PGE';'Exp';'Cut' ]
1243 .sub 'reduce' :method
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
1260 self['cutmark'] = 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
1283 code.'emit'(<<" CODE", label, next, cutmark)
1285 local_branch cstack, %1
1293 .namespace [ 'PGE';'Exp';'Scalar' ]
1295 .sub 'reduce' :method
1306 cname = self['cname']
1307 code.'emit'(<<" CODE", label, next, cname)
1310 $I0 = does $P0, 'array'
1311 if $I0 == 0 goto %0_1
1317 if $I0 > lastpos goto fail
1318 $S0 = substr target, pos, $I1
1319 if $S0 != $S1 goto fail
1327 .namespace [ 'PGE';'Exp';'EnumCharList' ]
1329 .sub 'reduce' :method
1339 .local string charlist
1341 charlist = code.'escape'($S0)
1345 $I0 = self['isnegated']
1346 if $I0 == 0 goto negated_end
1349 .local string incpos
1351 $I0 = self['iszerowidth']
1352 if $I0 == 0 goto zerowidth_end
1353 incpos = '### zero width'
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
1361 if $I0 %2 0 goto fail
1369 .namespace [ 'PGE';'Exp';'Newline' ]
1371 .sub 'reduce' :method
1380 code.'emit'(<<" CODE", label, next, .CCLASS_NEWLINE)
1382 $I0 = is_cclass %2, target, pos
1383 if $I0 == 0 goto fail
1384 $S0 = substr target, pos, 2
1386 if $S0 != "\\r\\n" goto %1
1394 .namespace [ 'PGE';'Exp';'Conj' ]
1396 .sub 'reduce' :method
1398 .local pmc exp0, exp1
1400 exp0 = exp0.'reduce'(next)
1403 exp1 = exp1.'reduce'(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)
1422 local_branch cstack, %2
1432 if $I0 != pos goto fail
1437 local_branch cstack, %1
1444 .local pmc exp0, exp1
1446 exp0.'pir'(code, exp0label, chk0label)
1448 exp1.'pir'(code, exp1label, chk1label)
1452 .namespace [ 'PGE';'Exp';'Closure' ]
1454 .sub 'reduce' :method
1463 .local string value, lang
1464 value = self.'ast'()
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
1474 code.'emit'(<<" CODE", label, lang, value)
1477 $P0 = get_hll_global ['PGE';'Match'], '%!cache'
1479 unless null $P1 goto %0_1
1485 $I0 = self['iszerowidth']
1486 if $I0 goto closure_zerowidth
1487 code.'emit'(<<" CODE", next)
1489 ($P0 :optional, $I0 :opt_flag) = $P1(mob)
1493 local_branch cstack, succeed
1501 ## we're doing a <?{{ or <!{{ assertion.
1504 $I0 = self['isnegated']
1505 unless $I0 goto have_test
1508 code.'emit'(<<" CODE", test, next)
1517 .namespace [ 'PGE';'Exp';'Action' ]
1519 .sub 'reduce' :method
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
1538 code.'emit'(<<" CODE", label, next, actionname, actionkey)
1539 $P1 = adverbs['action']
1556 # vim: expandtab shiftwidth=4 ft=pir: