3 PGE::Exp - base class for expressions
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
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)
49 =item C<compile(PMC adverbs :slurpy :named)>
51 Compile C<self> into PIR or a subroutine, according to the
56 .sub 'compile' :method
57 .param pmc adverbs :slurpy :named
60 target = adverbs['target']
61 target = downcase target
62 if target == 'parse' goto return_exp
63 if target == 'pge::exp' goto return_exp
66 code = new 'CodeString'
69 ns = adverbs['namespace']
70 if null ns goto ns_grammar
71 unless ns goto ns_grammar
73 code.'emit'('.namespace %0', $P0)
77 grammar = adverbs['grammar']
78 if grammar == '' goto ns_root
79 code.'emit'(".namespace [ '%0' ]", grammar)
82 code.'emit'('.namespace')
85 $P0 = self.'root_pir'(adverbs :flat :named)
87 if target != 'pir' goto bytecode
95 $P0 = new 'P6metaclass'
96 $P0.'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 ## Perform reduction/optimization on the
130 ## expression tree before generating PIR.
132 .local string explabel
134 set_hll_global ['PGE::Exp'], '$!group', exp
135 exp = exp.reduce(self)
137 ## we don't need a coroutine if :ratchet is set
139 $I0 = adverbs['ratchet']
140 cutrule = isne $I0, 0
142 ## generate the PIR for the expression tree.
144 expcode = new 'CodeString'
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
152 code.emit(<<" CODE", name, namecorou, .INTERPINFO_CURRENT_SUB)
154 .param pmc adverbs :slurpy :named
156 .const .Sub corou = %1
159 mob = $P0(self, adverbs)
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)
171 setattribute mob, '&!corou', $P0
173 lastpos = length target
174 if cpos > lastpos goto fail_rule
179 ## Initial code for a rule that cannot be backtracked into.
181 code.emit(<<" CODE", name)
183 .param pmc adverbs :unique_reg :slurpy :named
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)
191 lastpos = length target
192 if cpos > lastpos goto fail_rule
196 ## generate the ustack only if we need it
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'")
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'")
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")
219 code.emit(<<" CODE", PGE_CUT_RULE, returnop)
220 .local int pos, rep, cutmark :unique_reg
222 if cpos > lastpos goto fail_rule
226 local_branch cstack, R
227 if cutmark <= %0 goto fail_cut
229 if iscont goto try_match
233 mob.'_failcut'(cutmark)
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)
253 ## add the expression code, then close off the sub
260 .sub 'getargs' :method
263 .param pmc hash :slurpy :named
264 hash['L'] = label # %L = node's label
265 hash['S'] = next # %S = success target
267 $I0 = exists hash['quant']
269 quant = hash['quant']
272 $S2 = quant['backtrack']
273 hash['m'] = $I0 # %m = min repetitions
274 hash['n'] = $I1 # %n = max repetitions
282 hash['Q'] = $S0 # %Q = printable quant
286 if $I0 > 0 goto quant_max
287 hash['M'] = '### ' # %M = # if min==0
289 if $I1 != PGE_INF goto end
290 hash['N'] = '### ' # %N = # if max==INF
296 .sub 'gencapture' :method
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)
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]
318 $P0 = new 'ResizablePMCArray'
320 local_branch cstack, %1_cgen
326 .return (captgen, captsave, captback)
330 .namespace [ 'PGE::Exp::Literal' ]
332 .sub 'reduce' :method
343 args = self.'getargs'(label, next)
344 .local string literal
347 litlen = length literal
350 $I0 = self['ignorecase']
351 if $I0 == 0 goto ignorecase_end
352 args['I'] = 'downcase $S0'
353 literal = downcase literal
356 literal = code.escape(literal)
358 code.emit(<<" CODE", litlen, literal, args :named :flat)
361 if $I0 > lastpos goto fail
362 $S0 = substr target, pos, %0
364 if $S0 != %1 goto fail
372 .namespace [ 'PGE::Exp::Concat' ]
374 .sub 'reduce' :method
377 .local pmc children, exp
379 children = self.'list'()
380 n = elements children
382 if n <= 0 goto reduce_end
385 exp = exp.reduce(next)
391 .local pmc exp0, exp1
392 n = elements children
397 if i >= n goto concat_lit_end
399 $I1 = isa exp1, 'PGE::Exp::Literal'
400 if $I1 == 0 goto concat_lit_shift
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
410 exp0.'result_object'($S0)
414 if j >= i goto concat_lit_loop
434 code.emit(' %0: # concat', label)
436 iter = new 'Iterator', $P0
438 $S0 = code.unique('R')
440 unless iter goto iter_end
442 $S1 = code.unique('R')
443 exp.pir(code, $S0, $S1)
448 exp.pir(code, $S0, next)
453 .namespace [ 'PGE::Exp::Quant' ]
455 .sub 'reduce' :method
460 .local int backtrack, min, max
461 backtrack = self['backtrack']
464 if max != 1 goto reduce_exp0
465 if min != max goto reduce_max1
466 exp0['backtrack'] = backtrack
467 exp0 = exp0.reduce(next)
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
478 exp0 = exp0.reduce(next)
489 .local string explabel, replabel
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
500 args = self.'getargs'(label, next, 'quant' => self)
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
514 ## handle 0..Inf as a special case
516 if $I0 != 0 goto bt_greedy_none
518 if $I0 != PGE_INF goto bt_greedy_none
519 code.emit(<<" CODE", replabel, explabel, args :flat :named)
520 %L: # quant 0..Inf greedy
523 local_branch cstack, %1
525 if cutmark != 0 goto fail
534 ## handle 0..Inf as a special case
536 if $I0 != 0 goto bt_greedy_none
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
547 local_branch cstack, %1
549 if cutmark != 0 goto fail
550 local_branch cstack, %S
551 if cutmark != 0 goto fail
558 ## handle greedy or none
559 code.emit(<<" CODE", replabel, explabel, args :flat :named)
560 %L: # quant %Q greedy/none
562 local_branch cstack, %0
564 %Cif cutmark != %c goto fail
569 %Nif rep >= %n goto %L_1
574 local_branch cstack, %1
577 if cutmark != 0 goto fail
580 %Mif rep < %m goto fail
583 local_branch cstack, %S
586 if cutmark != 0 goto fail
593 ## handle eager backtracking
594 code.emit(<<" CODE", replabel, explabel, args :flat :named)
597 local_branch cstack, %0
602 %Mif rep < %m goto %L_1
606 local_branch cstack, %S
611 %Nif rep >= %n goto fail
618 exp.pir(code, explabel, replabel)
623 .namespace [ 'PGE::Exp::Group' ]
625 .sub 'reduce' :method
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.
634 $P0 = new 'PGE::Exp::Concat'
636 $P1 = new 'PGE::Exp::Cut'
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
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
667 cutmark = self['cutmark']
668 if cutmark > 0 goto has_cutmark
669 exp0.'pir'(code, label, next)
673 .local string exp0label
674 exp0label = code.unique('R')
675 code.emit(<<" CODE", label, exp0label, cutmark)
677 local_branch cstack, %1
678 if cutmark != %2 goto fail
682 exp0.'pir'(code, exp0label, next)
686 .namespace [ 'PGE::Exp::CGroup' ]
693 .local string explabel, expnext
694 explabel = code.unique('R')
695 expnext = concat label, '_close'
698 args = self.getargs(label, next)
700 .local string captgen, captsave, captback
701 (captgen, captsave, captback) = self.'gencapture'(label)
704 cutmark = self['cutmark']
707 if cutmark == 0 goto cutmark_end
712 isscope = self['isscope']
714 if isscope != 0 goto isscope_end
718 code.emit(<<" CODE", captgen, captsave, captback, 'E'=>explabel, args :flat :named)
721 captob = captscope.'new'(captscope, 'pos'=>pos)
725 local_branch cstack, %E
728 %Cif cutmark != %c goto fail
732 push ustack, captscope
735 $P1 = getattribute captob, '$.pos'
739 local_branch cstack, %S
744 captscope = pop ustack
749 exp.pir(code, explabel, expnext)
754 .namespace [ 'PGE::Exp::Subrule' ]
756 .sub 'reduce' :method
767 args = self.getargs(label, next)
771 $I0 = exists self['arg']
772 if $I0 == 0 goto subarg_end
774 subarg = code.escape(subarg)
775 subarg = concat ', ', subarg
779 .local string cname, captgen, captsave, captback
780 (captgen, captsave, captback) = self.gencapture(label)
782 .local string subname
783 subname = self['subname']
787 $I1 = index subname, '::', $I0
788 if $I1 == -1 goto 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
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')
803 $P0 = get_hll_global ['%0'], '%1'
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)
814 $P0 = getattribute captob, '$.pos'
817 if $I0 == 0 goto %L_1
818 $P0 = find_method mob, '%0'
822 unless null $P0 goto %L_2
823 say "Unable to find regex '%0'"
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
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
848 local_branch cstack, %1
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)
857 $P1 = getattribute captob, '&!corou'
860 local_branch cstack, %2
862 if cutmark != 0 goto fail
864 $P1 = getattribute captob, '$.pos'
866 if $P1 <= %0 goto fail_match
873 ## this handles zero-width subrule matches, either positive
877 $I0 = self['isnegated']
878 unless $I0 goto 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
887 $P1 = getattribute captob, '$.from'
896 .namespace [ 'PGE::Exp::Alt' ]
898 .sub 'reduce' :method
900 .local pmc exp0, exp1
902 exp0 = exp0.reduce(next)
905 exp1 = exp1.reduce(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)
922 local_branch cstack, %1
924 if cutmark != 0 goto fail
928 exp0.pir(code, exp0label, next)
930 exp1.pir(code, exp1label, next)
935 .namespace [ 'PGE::Exp::Anchor' ]
937 .sub 'reduce' :method
946 .local string token, test
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
958 if token == '\b' goto anchor_word
960 if token == '\B' goto anchor_word
964 code.emit(" %0: # anchor bos", label)
965 code.emit(" if pos == 0 goto %0", next)
966 code.emit(" goto fail")
970 code.emit(" %0: # anchor eos", label)
971 code.emit(" if pos == lastpos goto %0", next)
972 code.emit(" goto fail")
976 code.emit(<<" CODE", label, next, .CCLASS_NEWLINE)
979 if pos == lastpos goto fail
981 $I1 = is_cclass %2, target, $I0
988 code.emit(<<" CODE", label, next, .CCLASS_NEWLINE)
990 $I1 = is_cclass %2, target, pos
992 if pos != lastpos goto fail
995 $I1 = is_cclass %2, target, $I0
1002 code.emit(<<" CODE", label, next, .CCLASS_WORD, test)
1005 if pos == 0 goto %0_1
1007 $I0 = is_cclass %2, target, $I2
1010 if pos >= lastpos goto %0_2
1011 $I1 = is_cclass %2, target, pos
1013 if $I0 %3 $I1 goto %1
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
1026 $I0 = is_cclass %2, target, $I0
1033 code.emit(<<" CODE", label, next, .CCLASS_WORD)
1034 %0: # right word boundary
1035 if pos == 0 goto fail
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
1049 .namespace [ 'PGE::Exp::CCShortcut' ]
1051 .sub 'reduce' :method
1057 if token == '\D' goto digit
1058 if token == '\S' goto space
1059 if token == '\W' goto word
1060 if token == '\N' goto newline
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
1069 self['cclass'] = .CCLASS_NUMERIC
1072 self['cclass'] = .CCLASS_WHITESPACE
1075 self['cclass'] = .CCLASS_WORD
1078 self['cclass'] = .CCLASS_NEWLINE
1088 .local int cclass, negate
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)
1099 code.emit(" inc pos")
1100 code.emit(" goto %0", next)
1105 .sub 'pir_quant' :method
1112 .local int min, max, backtrack
1113 args = self.'getargs'(label, next, 'quant'=>quant)
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
1127 if negate == 0 goto emit_find
1130 code.emit(<<" CODE", negstr, cclass)
1131 $I0 = find%0_cclass %1, target, pos, lastpos
1136 code.emit(" rep = lastpos - pos")
1139 code.emit(<<" CODE", args :flat :named)
1140 %Mif rep < %m goto fail
1141 %Nif rep <= %n goto %L_1
1146 if backtrack == PGE_BACKTRACK_NONE goto bt_none
1147 if backtrack == PGE_BACKTRACK_EAGER goto bt_eager
1150 code.emit(<<" CODE", args :flat :named)
1153 if rep <= %m goto %S
1156 local_branch cstack, %S
1159 if cutmark != 0 goto fail
1167 code.emit(" pos += rep\n goto %0\n", next)
1171 code.emit(<<" CODE", args :flat :named)
1178 local_branch cstack, %S
1181 if cutmark != 0 goto fail
1189 .namespace [ 'PGE::Exp::Cut' ]
1191 .sub 'reduce' :method
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
1208 self['cutmark'] = 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
1231 code.emit(<<" CODE", label, next, cutmark)
1233 local_branch cstack, %1
1241 .namespace [ 'PGE::Exp::Scalar' ]
1243 .sub 'reduce' :method
1254 cname = self['cname']
1255 code.emit(<<" CODE", label, next, cname)
1258 $I0 = does $P0, 'array'
1259 if $I0 == 0 goto %0_1
1265 if $I0 > lastpos goto fail
1266 $S0 = substr target, pos, $I1
1267 if $S0 != $S1 goto fail
1275 .namespace [ 'PGE::Exp::EnumCharList' ]
1277 .sub 'reduce' :method
1287 .local string charlist
1289 charlist = code.escape($S0)
1293 $I0 = self['isnegated']
1294 if $I0 == 0 goto negated_end
1297 .local string incpos
1299 $I0 = self['iszerowidth']
1300 if $I0 == 0 goto zerowidth_end
1301 incpos = '### zero width'
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
1309 if $I0 %2 0 goto fail
1317 .namespace [ 'PGE::Exp::Newline' ]
1319 .sub 'reduce' :method
1328 code.emit(<<" CODE", label, next, .CCLASS_NEWLINE)
1330 $I0 = is_cclass %2, target, pos
1331 if $I0 == 0 goto fail
1332 $S0 = substr target, pos, 2
1334 if $S0 != "\\r\\n" goto %1
1342 .namespace [ 'PGE::Exp::Conj' ]
1344 .sub 'reduce' :method
1346 .local pmc exp0, exp1
1348 exp0 = exp0.reduce(next)
1351 exp1 = exp1.reduce(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)
1370 local_branch cstack, %2
1380 if $I0 != pos goto fail
1385 local_branch cstack, %1
1392 .local pmc exp0, exp1
1394 exp0.'pir'(code, exp0label, chk0label)
1396 exp1.'pir'(code, exp1label, chk1label)
1400 .namespace [ "PGE::Exp::Closure" ]
1402 .sub 'reduce' :method
1411 .local string value, 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
1422 code.emit(<<" CODE", label, next, lang, value)
1425 $P0 = get_hll_global ['PGE::Match'], '%!cache'
1427 unless null $P1 goto %0_1
1433 ($P0 :optional, $I0 :opt_flag) = $P1(mob)
1435 mob.'result_object'($P0)
1437 local_branch cstack, succeed
1440 mob.'result_object'($P0)
1446 .namespace [ "PGE::Exp::Action" ]
1448 .sub 'reduce' :method
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
1467 code.emit(<<" CODE", label, next, actionname, actionkey)
1468 $P1 = adverbs['action']
1485 # vim: expandtab shiftwidth=4 ft=pir: