Author: pmichaud
Date: Sat May 7 06:12:28 2005
New Revision: 8000
Modified:
trunk/compilers/pge/PGE/Exp.pir
trunk/compilers/pge/PGE/Match.pir
trunk/compilers/pge/PGE/P6Rule.pir
trunk/config/gen/makefiles/pge.in
trunk/runtime/parrot/library/PGE/Dumper.pir
trunk/t/p6rules/capture.t
Log:
Lots of updates:
Added capture aliases.
Added backreferences.
Fixed a bug with nested captures.
Added new tests for backreferences, nested captures, aliases.
Modified creation of PGE.pbc file.
Modified: trunk/compilers/pge/PGE/Exp.pir
==============================================================================
--- trunk/compilers/pge/PGE/Exp.pir (original)
+++ trunk/compilers/pge/PGE/Exp.pir Sat May 7 06:12:28 2005
@@ -11,6 +11,7 @@
PGE::Start - start of rule
PGE::End - (successful) end of rule
PGE::Literal - match a literal string
+ PGE::Scalar - match a scalar
PGE::Dot - match any character
PGE::CCShortcut - character class shortcuts (\d, \D, \w, etc.)
PGE::Anchor - matching of ^, ^^, $, $$, \b, \B anchors
@@ -39,6 +40,7 @@
$P0 = subclass expclass, "PGE::Exp::Start"
$P0 = subclass expclass, "PGE::Exp::End"
$P0 = subclass expclass, "PGE::Exp::Literal"
+ $P0 = subclass expclass, "PGE::Exp::Scalar"
$P0 = subclass expclass, "PGE::Exp::Dot"
$P0 = subclass expclass, "PGE::Exp::CCShortcut"
$P0 = subclass expclass, "PGE::Exp::Anchor"
@@ -136,14 +138,16 @@
.return ($S0)
.end
-=item C<analyze(PMC next, int isarray)>
+=item C<analyze(PMC next, PMC pad)>
The analyze method is used to walk an expression tree and perform
a variety of optimizations and pre-processing in preparation for
generating the rule code. The C<next> parameter identifies the
-expression that will come after this one. The C<isarray> parameter
-indicates if capture objects will produce an array of captures or a single
-capture.
+expression that will come after this one. The C<pad> parameter
+is a work pad hash that is carried from one object to the next.
+Typical entries in the pad include:
+ isarray: does this (group) object cause its subelements to repeat?
+ reps: a hash of lexically repeated capture names
=cut
@@ -314,14 +318,14 @@
greedy:
emit(code, " rep = 0")
unless isgreedy goto lazy
- emit(code, " %s_1:", label)
- emit(code, " if rep >= %d goto %s_2", max, label)
+ emit(code, " %s_lit1:", label)
+ emit(code, " if rep >= %d goto %s_lit2", max, label)
emit(code, " $S0 = substr target, pos, litlen")
- emit(code, " if $S0 != lit goto %s_2", label)
+ emit(code, " if $S0 != lit goto %s_lit2", label)
emit(code, " inc rep")
emit(code, " pos += litlen")
- emit(code, " goto %s_1", label)
- emit(code, " %s_2:", label)
+ emit(code, " goto %s_lit1", label)
+ emit(code, " %s_lit2:", label)
emit(code, " if rep < %d goto fail", min)
unless iscut goto greedy_1
emit(code, " goto %s", next)
@@ -330,12 +334,12 @@
self.emitsub(code, next, "pos", "rep", "litlen")
emit(code, " dec rep")
emit(code, " pos -= litlen")
- emit(code, " goto %s_2", label)
+ emit(code, " goto %s_lit2", label)
.return()
lazy:
- emit(code, " %s_1:", label)
- emit(code, " if rep < %d goto %s_2", min, label)
+ emit(code, " %s_lit1:", label)
+ emit(code, " if rep < %d goto %s_lit2", min, label)
unless iscut goto lazy_1
emit(code, " goto %s", next)
goto lazy_2
@@ -343,22 +347,23 @@
emit(code, " if rep >= %d goto %s", max, next)
self.emitsub(code, next, "pos", "rep", "lit", "litlen")
lazy_2:
- emit(code, " %s_2:", label)
+ emit(code, " %s_lit2:", label)
emit(code, " $S0 = substr target, pos, litlen")
emit(code, " if $S0 != lit goto fail")
emit(code, " inc rep")
emit(code, " pos += litlen")
- emit(code, " goto %s_1", label)
+ emit(code, " goto %s_lit1", label)
.end
.namespace [ "PGE::Exp::Start" ]
.sub analyze method
.param pmc next
- .param int isarray
+ .param pmc pad
+ pad = new Hash # create a new workpad
$P0 = self["exp1"]
- $P0.analyze(self, isarray)
- self.firstchars($P0)
+ $P0.analyze(self, pad) # analyze our subexp
+ self.firstchars($P0) # set firstchars
.end
.sub "gen" method
@@ -450,7 +455,7 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param pmc pad
$S0 = self["literal"] # set up firstchars
$S0 = substr $S0, 0, 1
self["firstchars"] = $S0
@@ -473,6 +478,43 @@
self.genliteral(code, label, next)
.end
+
+.namespace [ "PGE::Exp::Scalar" ]
+
+.sub "gen" method
+ .param pmc code
+ .param string label
+ .param string next
+ .local pmc emit
+ .local pmc cname
+ .local int subp
+ emit = find_global "PGE::Exp", "emit"
+ $S0 = self."quant"()
+ cname = self["cname"]
+ $I0 = isa cname, "Integer"
+ unless $I0 goto named
+ subp = cname
+ emit(code, "\n %s: # backref $%d %s", label, subp, $S0)
+ emit(code, " lit = ''")
+ emit(code, " $P0 = getattribute mob, \"PGE::Match\\x0@:capt\"")
+ emit(code, " isnull $P0, %s_1", label)
+ emit(code, " $P1 = $P0[%d]", subp)
+ emit(code, " lit = $P1[-1]")
+ emit(code, " %s_1:", label)
+ .return self.genliteral(code, label, next)
+ named:
+ $S1 = cname
+ emit(code, "\n %s: # backref $<%s> %s", label, subp, $S1)
+ emit(code, " lit = ''")
+ emit(code, " $P0 = getattribute mob, \"PGE::Match\\x0%:capt\"")
+ emit(code, " isnull $P0, %s_1", label)
+ emit(code, " $P1 = $P0[\"%s\"]", $S1) # XXX: quote $S1
+ emit(code, " lit = $P1[-1]")
+ emit(code, " %s_1:", label)
+ .return self.genliteral(code, label, next)
+.end
+
+
.namespace [ "PGE::Exp::Dot" ]
.sub "gen" method
@@ -593,7 +635,7 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param pmc pad
self.firstchars(next)
.end
@@ -646,12 +688,12 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param pmc pad
.local pmc exp1, exp2
exp2 = self["exp2"]
- exp2.analyze(next, isarray)
+ exp2.analyze(next, pad)
exp1 = self["exp1"]
- exp1.analyze(exp2, isarray)
+ exp1.analyze(exp2, pad)
self.firstchars(exp1)
.end
@@ -675,7 +717,7 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param int pad
self.firstchars(next)
.end
@@ -702,12 +744,17 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param pmc pad
.local pmc exp1, exp2
+ .local pmc creps
+
+ creps = pad["creps"]
+ creps = clone creps
exp1 = self["exp1"]
exp2 = self["exp2"]
- exp1.analyze(next, isarray)
- exp2.analyze(next, isarray)
+ exp2.analyze(next, pad)
+ pad["creps"] = creps
+ exp1.analyze(next, pad)
self.firstchars(exp1, exp2)
.end
@@ -734,28 +781,60 @@
.sub "analyze" method
.param pmc next
- .param int isarray
+ .param pmc pad
.local pmc exp1
-
- self["firstchars"] = ""
- $I0 = self["isarray"] # see if this is an array
- isarray |= $I0
+ .local int isarray
+ .local pmc creps
+ .local string cname
+
+ self["firstchars"] = "" # no firstchars default
+
+ $I0 = exists pad["creps"] # create creps hash array
+ if $I0 goto creps_1 # if not exists
+ $P0 = new Hash
+ pad["creps"] = $P0
+ creps_1:
+ creps = pad["creps"] # load creps hash
+ $I0 = exists self["cname"]
+ unless $I0 goto isarray_0 # skip if no capture
+ $P0 = self["cname"]
+ cname = $P0
+ cname = concat "%", cname
+ $I0 = isa $P0, "Integer" # Integer = subpattern cap
+ if $I0 goto creps_2
+ creps_2:
+ $I0 = exists creps[cname] # have seen capture name?
+ unless $I0 goto creps_3 #
+ $P0 = creps[cname] # yes, so prev is now
+ $P0["isarray"] = 1 # an array capture
+ self["isarray"] = 1 # and so is self
+ creps_3:
+ creps[cname] = self # mark us for future ref
+
+ isarray_0:
+ isarray = pad["isarray"] # set group's isarray
+ $I0 = self["isarray"] # and pass along to
+ isarray |= $I0 # nested objects
self["isarray"] = isarray
$I0 = self["cscope"]
unless $I0 goto isarray_1
- isarray = 0
+ isarray = 0 # each capt obj is single
+ delete pad["creps"] # new lexical name scope
isarray_1:
$I0 = defined self["exp1"]
unless $I0 goto end
exp1 = self["exp1"]
- exp1.analyze(next, isarray)
+ pad["isarray"] = isarray
+ exp1.analyze(next, pad)
+ fc:
$I0 = self["min"] # set up firstchars
- if $I0 > 0 goto isarray_2
+ if $I0 > 0 goto fc_2
self.firstchars(exp1, next)
goto end
- isarray_2:
+ fc_2:
self.firstchars(exp1)
end:
+ pad["creps"] = creps
.end
.sub "gen" method
@@ -831,8 +910,9 @@
emit(code, " $P0 = pop gpad")
emit(code, " $P0 = pop cpad")
unless iscapture goto init_2
- emit(code, " unless iscreator goto fail")
+ emit(code, " unless iscreator goto %s_i4", label)
emit(code, " delete cobcapt[%s]", captname)
+ emit(code, " %s_i4:", label)
init_2:
emit(code, " if cutting != 1 goto fail")
emit(code, " cutting = 0")
@@ -910,7 +990,9 @@
emit(code, " cpad[-1] = $P0")
subpat_2:
emit(code, " push capt, $P0")
+ emit(code, " save capt")
emit(code, " bsr %s_s1", sublabel)
+ emit(code, " restore capt")
emit(code, " $P0 = pop capt")
emit(code, " ret")
exp1 = self["exp1"]
Modified: trunk/compilers/pge/PGE/Match.pir
==============================================================================
--- trunk/compilers/pge/PGE/Match.pir (original)
+++ trunk/compilers/pge/PGE/Match.pir Sat May 7 06:12:28 2005
@@ -191,8 +191,8 @@
.param string b2
.local pmc capt
.local int spi, spc
+ .local pmc iter
.local string prefix1, prefix2
- .local int matchidx
unless argcS < 3 goto start
b2 = "]"
unless argcS < 2 goto start
@@ -208,6 +208,7 @@
$I0 = self."from"()
print $I0
print "> "
+
subpats:
$I0 = self
print $I0
@@ -222,7 +223,11 @@
$S0 = spi
concat prefix1, $S0
concat prefix1, b2
+ $I0 = defined capt[spi]
+ unless $I0 goto subpats_3
$P0 = capt[spi]
+ $I0 = defined $P0
+ unless $I0 goto subpats_3
$I0 = 0
$I1 = elements $P0
unless $I0 < $I1 goto subpats_3
@@ -244,7 +249,40 @@
subpats_3:
inc spi
goto subpats_1
+
subrules:
+ capt = getattribute self, "PGE::Match\x0%:capt"
+ isnull capt, end
+ iter = new Iterator, capt
+ iter = 0
+ subrules_1:
+ unless iter goto end
+ $S0 = shift iter
+ prefix1 = concat prefix, "<"
+ concat prefix1, $S0
+ concat prefix1, ">"
+ $I0 = defined capt[$S0]
+ unless $I0 goto subrules_1
+ $P0 = capt[$S0]
+ $I0 = 0
+ $I1 = elements $P0
+ unless $I0 < $I1 goto subrules_1
+ $P1 = getprop "isarray", $P0
+ if $P1 goto subrules_2
+ $P1 = $P0[-1]
+ $P1."dump"(prefix1, b1, b2)
+ goto subrules_1
+ subrules_2:
+ unless $I0 < $I1 goto subrules_1
+ $P1 = $P0[$I0]
+ prefix2 = concat prefix1, b1
+ $S0 = $I0
+ concat prefix2, $S0
+ concat prefix2, b2
+ $P1."dump"(prefix2, b1, b2)
+ inc $I0
+ goto subrules_2
+ end:
.end
=head1 AUTHOR
Modified: trunk/compilers/pge/PGE/P6Rule.pir
==============================================================================
--- trunk/compilers/pge/PGE/P6Rule.pir (original)
+++ trunk/compilers/pge/PGE/P6Rule.pir Sat May 7 06:12:28 2005
@@ -54,6 +54,17 @@
p6meta[']'] = u
p6meta['('] = $P0
p6meta[')'] = u
+ $P0 = find_global "PGE::P6Rule", "p6rule_parse_alias"
+ p6meta['$<'] = $P0
+ p6meta['$1'] = $P0
+ p6meta['$2'] = $P0
+ p6meta['$3'] = $P0
+ p6meta['$4'] = $P0
+ p6meta['$5'] = $P0
+ p6meta['$6'] = $P0
+ p6meta['$7'] = $P0
+ p6meta['$8'] = $P0
+ p6meta['$9'] = $P0
# $P0 = find_global "PGE::P6Rule", "p6rule_parse_assert" # XXX: TODO
# p6meta['<'] = $P0
# p6meta['>'] = u
@@ -85,6 +96,10 @@
plen = lex["plen"]
pos = lex["pos"]
pos += skip
+ lex["ws"] = 0
+ unless pos < plen goto end
+ $I0 = is_whitespace pattern, pos
+ lex["ws"] = $I0
skipws:
unless pos < plen goto end
$I0 = is_whitespace pattern, pos
@@ -105,6 +120,44 @@
.return (pos)
.end
+=item C<p6rule_parse_string(STR pattern, PMC lex, STR stopat)>
+
+Grab a sequence of characters, processing backslash escapes,
+until one of the characters in C<stopat> is found. Advances
+the C<lex["pos"]> pointer as it goes.
+
+=cut
+
+.sub p6rule_parse_string
+ .param string pattern
+ .param pmc lex
+ .param string stopat
+ .param int pos
+ .param int plen
+ .param string val
+ pos = lex["pos"]
+ plen = lex["plen"]
+ val = ""
+ string_1:
+ unless pos < plen goto end
+ $S0 = substr pattern, pos, 1
+ unless $S0 == "\\" goto string_2
+ inc pos
+ unless pos < plen goto end
+ $S0 = substr pattern, pos, 1
+ goto string_3
+ string_2:
+ $I0 = index stopat, $S0
+ if $I0 >= 0 goto end
+ string_3:
+ concat val, $S0
+ inc pos
+ goto string_1
+ end:
+ lex["pos"] = pos
+ .return (val)
+.end
+
=item C<p6rule_parse_error(STR pattern, PMC lex, STR message)>
Generates error messages during parsing. Gracefully recovering
@@ -209,7 +262,6 @@
$P0 = find_global "PGE::Exp", "new"
exp = $P0("PGE::Exp::Anchor")
exp["token"] = token
- if token != '^' goto end
end:
.return (exp)
.end
@@ -231,34 +283,41 @@
.local int pos
.local string ket
.local int subp
- p6rule_parse_skip(pattern, lex, $I0)
- $P0 = find_global "PGE::Exp", "new"
+ .local pmc cname
+ .local int isaliased
+
+ p6rule_parse_skip(pattern, lex, 1) # skip '(' or ']'
+ $P0 = find_global "PGE::Exp", "new" # create group exp object
exp = $P0("PGE::Exp::Group")
- if token == '(' goto group_1
- ket = ']'
- goto group_2
+ isaliased = exists lex["cname"]
+ unless isaliased goto group_1 # are we aliased?
+ cname = lex["cname"] # yes, use that capture name
+ exp["cname"] = cname
+ delete lex["cname"] # and then remove it
group_1:
- ket = ')'
- subp = lex["subp"]
- exp["cname"] = subp
- exp["cscope"] = 1
- lex["subp"] = 0
+ subp = lex["subp"] # current subpattern count
+ unless token == '(' goto group_2
+ exp["cscope"] = 1 # '(' == scoped capture
+ lex["subp"] = 0 # restart subpattern #'s
+ if isaliased goto group_2 # if not aliased
+ exp["cname"] = subp # use subpattern number
+ inc subp # and increase
group_2:
- $P1 = "p6rule_parse_exp"(pattern, lex)
- exp["exp1"] = $P1
- pos = lex['pos']
- $S0 = substr pattern, pos, 1
- if $S0 != ket goto error
- p6rule_parse_skip(pattern, lex, 1)
- unless token == '(' goto end
- inc subp
- lex["subp"] = subp
+ $P1 = "p6rule_parse_exp"(pattern, lex) # parse subexpression
+ exp["exp1"] = $P1 # store in group exp
+ pos = lex['pos'] # update pattern pos
+ $S0 = substr pattern, pos, 1 # get closing char
+ unless token == '(' goto group_3 # if scoped capture '('
+ lex["subp"] = subp # set next subpattern #
+ if $S0 == ')' goto group_4 # check for closing char
+ p6rule_parse_error(pattern, lex, "group missing ')'")
goto end
- error:
- $S0 = "missing '"
- concat $S0, ket
- concat $S0, "'"
- p6rule_parse_error(pattern, lex, $S0)
+ group_3: # unscoped capture
+ if $S0 == ']' goto group_4
+ p6rule_parse_error(pattern, lex, "group missing ']'")
+ goto end
+ group_4:
+ p6rule_parse_skip(pattern, lex, 1) # skip closing token
end:
.return (exp)
.end
@@ -304,6 +363,66 @@
.return (exp)
.end
+=item C<p6rule_parse_alias(STR pattern, PMC lex, STR token)>
+
+Parse an alias or backreference.
+
+=cut
+
+.sub p6rule_parse_alias
+ .param string pattern
+ .param pmc lex
+ .param string token
+ .local int pos, plen
+ .local int subp
+ .local pmc exp
+
+
+ pos = lex["pos"] # get current position
+ inc pos # skip past '$'
+ if token == '$<' goto name # $< == named capture
+ $I0 = pos # aha, numeric capture
+ plen = lex["plen"] # now let's scan for digits
+ num_0:
+ if $I0 >= plen goto num_1
+ $I1 = is_digit pattern, $I0
+ unless $I1 goto num_1
+ inc $I0
+ goto num_0
+ num_1: # we have digits
+ lex["pos"] = $I0 # save new scan position
+ $I0 -= pos # get length of digit seq.
+ $S0 = substr pattern, pos, $I0 # extract digit seq.
+ subp = $S0 # convert to integer
+ lex["subp"] = subp # store next subpattern #
+ dec subp # compute index of
+ lex["cname"] = subp # this capture
+ p6rule_parse_skip(pattern, lex, 0) # skip ws
+ goto alias
+ name:
+ inc pos # skip over '<'
+ lex["pos"] = pos # set position
+ $S0 = p6rule_parse_string(pattern, lex, '>') # now get named alias
+ lex["cname"] = $S0 # capture to this alias
+ p6rule_parse_skip(pattern, lex, 1) # skip closing '>' (XXX)
+ alias:
+ pos = lex["pos"] # get current pos
+ $S0 = substr pattern, pos, 2 # check for ':='
+ unless $S0 == ':=' goto backref
+ p6rule_parse_skip(pattern, lex, 2) # skip ':='
+ exp = p6rule_parse_term(pattern, lex) # parse a term to capture
+ goto end
+ backref:
+ $P0 = find_global "PGE::Exp", "new" # create a backreference
+ exp = $P0("PGE::Exp::Scalar")
+ $P0 = lex["cname"]
+ exp["cname"] = $P0
+ end:
+ delete lex["cname"] # destroy any capture name
+ .return (exp)
+.end
+
+
=item C<p6rule_parse_charclass(STR pattern, PMC lex)>
Parses a character class of some sort, including the \n, \N, \s, \S,
@@ -606,7 +725,7 @@
exp = $P1("PGE::Exp::Concat", exp, $P2)
exp = $P1("PGE::Exp::Start", exp)
- exp.analyze(0)
+ exp.analyze()
exp.serno(0)
code = new String
Modified: trunk/config/gen/makefiles/pge.in
==============================================================================
--- trunk/config/gen/makefiles/pge.in (original)
+++ trunk/config/gen/makefiles/pge.in Sat May 7 06:12:28 2005
@@ -17,7 +17,7 @@
$(CP) PGE.pbc $(PARROT_LIBRARY)
PGE.pbc: PGE.pir PGE/Exp.pir PGE/Match.pir PGE/P6Rule.pir PGE/TokenHash.pir
- $(PARROT) --output-pbc PGE.pir >PGE.pbc
+ $(PARROT) -o PGE.pbc --output-pbc PGE.pir
# This is a listing of all targets, that are meant to be called by users
help:
Modified: trunk/runtime/parrot/library/PGE/Dumper.pir
==============================================================================
--- trunk/runtime/parrot/library/PGE/Dumper.pir (original)
+++ trunk/runtime/parrot/library/PGE/Dumper.pir Sat May 7 06:12:28 2005
@@ -57,6 +57,21 @@
print "\n"
.end
+.namespace [ "PGE::Exp::Scalar" ]
+
+.sub "dump" method
+ .param int indent
+ .local pmc cname
+ cname = self["cname"]
+ self."dumpindent"(indent)
+ print "BACKREF �"
+ print cname
+ print "� "
+ $S0 = self."quant"()
+ print $S0
+ print "\n"
+.end
+
.namespace [ "PGE::Exp::Dot" ]
.sub "dump" method
Modified: trunk/t/p6rules/capture.t
==============================================================================
--- trunk/t/p6rules/capture.t (original)
+++ trunk/t/p6rules/capture.t Sat May 7 06:12:28 2005
@@ -13,13 +13,54 @@
p6rule_like('abcd', '(a(b(c))(d))', qr/mob 0 0 0: <c @ 2>/, 'nested match');
p6rule_like('abcd', '(a(b(c))(d))', qr/mob 0 1: <d @ 3>/, 'nested match');
-# backreferences not implemented yet
-#p6rule_is ('bookkeeper', '(((.)$3)+)', 'backreference');
-#p6rule_like('bookkeeper', '(((.)$3)+)',
-# qr/0: <ookkee @ 1>/, 'backref $0');
-#p6rule_like('bookkeeper', '(((.)$3)+)',
-# qr/1: <ookkee @ 1>/, 'backref $1');
-#p6rule_like('bookkeeper', '(((.)$3)+)',
-# qr/2: <oo @ 1> <kk @ 3> <ee @ 5>/, 'backref $2');
-#p6rule_like('bookkeeper', '(((.)$3)+)',
-# qr/3: <o @ 1> <k @ 3> <e @ 5>/, 'backref $2');
+p6rule_like('abcdefg', '(a) [ (bc) (d) | .* (ef) ] .* (g)',
+ qr/mob 0: <a @ 0>/, 'alt subpattern before group');
+p6rule_like('abcdefg', '(a) [ (bc) (d) | .* (ef) ] .* (g)',
+ qr/mob 1: <bc @ 1>/, 'alt subpattern in group');
+p6rule_like('abcdefg', '(a) [ (bc) (d) | .* (ef) ] .* (g)',
+ qr/mob 2: <d @ 3>/, 'alt subpattern in group');
+p6rule_like('abcdefg', '(a) [ (bc) (d) | .* (ef) ] .* (g)',
+ qr/mob 3: <g @ 6>/, 'alt subpattern after group');
+p6rule_like('abcdefg', '(a) [ (bc) (x) | .* (ef) ] .* (g)',
+ qr/mob 1: <ef @ 4>/, '2nd alt subpattern in group');
+p6rule_like('abcdefg', '(a) [ (bc) (x) | .* (ef) ] .* (g)',
+ qr/mob 3: <g @ 6>/, '2nd alt subpattern after group');
+
+p6rule_like('abc', '( (.) )*', qr/mob 0 1 0: <b @ 1>/,
+ 'nested repeated captures');
+p6rule_like('abc', '[ (.) ]*', qr/mob 0 1: <b @ 1>/,
+ 'nested repeated captures');
+p6rule_like('abc', '( [.] )*', qr/mob 0 1: <b @ 1>/,
+ 'nested repeated captures');
+
+p6rule_like('abcdefg', '(.) (.) $7:=(.) (.) $4:=(.)', qr/mob 0: <a @ 0>/,
+ 'numbered aliases $1');
+p6rule_like('abcdefg', '(.) (.) $7:=(.) (.) $4:=(.)', qr/mob 1: <b @ 1>/,
+ 'numbered aliases $2');
+p6rule_like('abcdefg', '(.) (.) $7:=(.) (.) $4:=(.)', qr/mob 6: <c @ 2>/,
+ 'numbered aliases $7');
+p6rule_like('abcdefg', '(.) (.) $7:=(.) (.) $4:=(.)', qr/mob 7: <d @ 3>/,
+ 'numbered aliases $8');
+p6rule_like('abcdefg', '(.) (.) $7:=(.) (.) $4:=(.)', qr/mob 3: <e @ 4>/,
+ 'numbered aliases $4');
+
+
+p6rule_like('abcdefg', '$1:=[ (.) (.) (.) ] (.)', qr/mob 0: <abc @ 0>/,
+ 'perl5 numbered captures $1');
+p6rule_like('abcdefg', '$1:=[ (.) (.) (.) ] (.)', qr/mob 1: <a @ 0>/,
+ 'perl5 numbered captures $1');
+p6rule_like('abcdefg', '$1:=[ (.) (.) (.) ] (.)', qr/mob 2: <b @ 1>/,
+ 'perl5 numbered captures $1');
+p6rule_like('abcdefg', '$1:=[ (.) (.) (.) ] (.)', qr/mob 3: <c @ 2>/,
+ 'perl5 numbered captures $1');
+p6rule_like('abcdefg', '$1:=[ (.) (.) (.) ] (.)', qr/mob 4: <d @ 3>/,
+ 'perl5 numbered captures $1');
+
+p6rule_is ('bookkeeper', '[(.)$1]+', 'backreference');
+p6rule_like('bookkeeper', '[(.)$1]+',
+ qr/mob 0 0: <o @ 1>/, 'backref $1');
+p6rule_like('bookkeeper', '[(.)$1]+',
+ qr/mob 0 1: <k @ 3>/, 'backref $1');
+p6rule_like('bookkeeper', '[(.)$1]+',
+ qr/mob 0 2: <e @ 5>/, 'backref $1');
+