Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Implement $<foo> = [bar]
This required a bit of restructuring to support synthetic subrules,
which might not always trap captures or cut operators.
  • Loading branch information
sorear committed Oct 20, 2010
1 parent 9b64deb commit 4c83f15
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 39 deletions.
24 changes: 18 additions & 6 deletions lib/Cursor.cs
Expand Up @@ -91,12 +91,15 @@ public sealed class RxFrame {
// don't remove this on backtracking, and quit if we would back into it
public readonly Choice rootf;

public RxFrame(string name, Cursor csr) {
public RxFrame(string name, Cursor csr, bool passcap, bool passcut) {
global = csr.global;
orig = global.orig_a;
end = orig.Length;
rootf = bt = csr.xact;
st.ns = new NState(rootf, "RULE " + name, csr.nstate);
st.ns = passcut ? csr.nstate :
new NState(rootf, "RULE " + name, csr.nstate);
if (passcap)
st.captures = csr.captures;
st.ns.klass = csr.mo;
st.pos = csr.pos;
from = csr.pos;
Expand Down Expand Up @@ -142,6 +145,10 @@ public sealed class RxFrame {
return st.subrule_iter;
}

public void SetCapturesFrom(Cursor inp) {
st.captures = inp.captures;
}

public void SetPos(int pos) {
st.pos = pos;
}
Expand Down Expand Up @@ -284,7 +291,7 @@ public sealed class RxFrame {
}

public Cursor MakeCursor() {
return new Cursor(global, st.ns.klass, st.ns, bt, st.pos);
return new Cursor(global, st.ns.klass, st.ns, bt, st.pos, st.captures);
}

public Cursor MakeMatch() {
Expand Down Expand Up @@ -356,7 +363,7 @@ public class Cursor : IP6 {
public string GetBacking() { return global.orig_s; }

public Cursor(IP6 proto, string text)
: this(new GState(text), proto.mo, null, null, 0) { }
: this(new GState(text), proto.mo, null, null, 0, null) { }

public Cursor(GState g, DynMetaObject klass, int from, int pos, CapInfo captures) {
this.global = g;
Expand All @@ -367,20 +374,25 @@ public Cursor(IP6 proto, string text)
this.save_klass = klass;
}

public Cursor(GState g, DynMetaObject klass, NState ns, Choice xact, int pos) {
public Cursor(GState g, DynMetaObject klass, NState ns, Choice xact, int pos, CapInfo captures) {
this.mo = klass;
this.xact = xact;
this.nstate = ns;
this.global = g;
this.pos = pos;
this.captures = captures;
}

public override bool IsDefined() {
return true;
}

public Cursor At(int npos) {
return new Cursor(global, mo, nstate, xact, npos);
return new Cursor(global, mo, nstate, xact, npos, captures);
}

public Cursor StripCaps() {
return new Cursor(global, save_klass, from, pos, null);
}

// TODO: cache generated lists
Expand Down
5 changes: 4 additions & 1 deletion src/CgOp.pm
Expand Up @@ -210,6 +210,7 @@ use warnings;
sub cursor_backing { rawcall($_[0], 'GetBacking:m,String') }
sub cursor_dows { rawcall($_[0], 'SimpleWS') }
sub cursor_item { rawcall($_[0], 'GetKey', $_[1]) }
sub rxstripcaps { rawcall($_[0], 'StripCaps:m,Cursor') }

sub bget { getfield('v', $_[0]) }
sub bset { setfield('v', $_[0], $_[1]) }
Expand Down Expand Up @@ -288,7 +289,8 @@ use warnings;
sub popcut { rxcall('PopCutGroup') }

sub rxinit {
setfield('rx', callframe(), rawnew('clr:RxFrame', $_[0], $_[1]))
setfield('rx', callframe(), rawnew('clr:RxFrame', $_[0], $_[1],
bool($_[2]), bool($_[3])))
}
sub rxpushcapture {
my $c = shift;
Expand All @@ -305,6 +307,7 @@ use warnings;
sub rxincquant { rxcall('IncQuant') }
sub rxsetclass { rxcall('SetClass', $_[0]) }
sub rxsetpos { rxcall('SetPos', $_[0]) }
sub rxsetcapsfrom { rxcall('SetCapturesFrom:m,Void', $_[0]) }
sub rxgetpos { rxcall('GetPos:m,Int32') }
sub rxcommitgroup { rxcall('CommitGroup', $_[0]) }

Expand Down
46 changes: 31 additions & 15 deletions src/Niecza/Actions.pm
Expand Up @@ -307,6 +307,27 @@ sub quote__S_Slash_Slash { my ($cl, $M) = @_;
rxop => $rxop)));
}

sub encapsulate_regex { my ($cl, $M, $rxop, %args) = @_;
my @lift = $rxop->oplift;
my ($nrxop, $mb) = Optimizer::RxSimple::run($rxop);
unshift @lift, Op::Bind->new(readonly => 1,
lhs => Op::Lexical->new(name => '$*GOAL', declaring => 1),
rhs => Op::StringLiteral->new(text => $args{goal}))
if exists $args{goal};
my $subop = Op::SubDef->new(
var => $cl->gensym,
body => Body->new(
transparent => 1,
class => 'Regex',
type => 'regex',
signature => Sig->simple->for_regex,
do => Op::RegexBody->new(canback => $mb, pre => \@lift,
passcut => $args{passcut}, passcap => $args{passcap},
rxop => $nrxop)));
return RxOp::Subrule->new(regex => $subop, passcap => $args{passcap},
_passcapzyg => $nrxop);
}

sub regex_block { my ($cl, $M) = @_;
if (@{ $M->{quotepair} }) {
$M->sorry('Regex adverbs NYI');
Expand Down Expand Up @@ -655,21 +676,20 @@ sub metachar__S_var { my ($cl, $M) = @_;
return;
}

$M->sorry("Explicit regex bindings NYI");
$M->{_ast} = RxOp::Sequence->new;
$M->{_ast} = $cl->rxcapturize($M, $cid, $a);
return;
}
$M->{_ast} = RxOp::VarString->new(value =>
$cl->do_variable_reference($M, $M->{variable}{_ast}));
}

sub rxcapturize { my ($cl, $name, $rxop) = @_;
sub rxcapturize { my ($cl, $M, $name, $rxop) = @_;
if (!$rxop->isa('RxOp::Subrule')) {
# <before>, etc. Not yet really handled XXX
return $rxop;
# $<foo>=[...]
$rxop = $cl->encapsulate_regex($M, $rxop, passcut => 1, passcap => 1);
}

my @extra = map { $_ => $rxop->$_ } qw/zyg arglist name/;
my @extra = map { $_ => $rxop->$_ } qw/zyg arglist method regex passcap _passcapzyg/;

# $<foo>=(...)
if (@{ $rxop->captures } == 1 && !defined($rxop->captures->[0])) {
Expand All @@ -691,7 +711,7 @@ sub do_cclass { my ($cl, $M) = @_;
RxOp::CClassElem->new(cc => CClass::internal($1)) :
$_->{quibble} ?
RxOp::CClassElem->new(cc => $_->{quibble}{_ast}) :
RxOp::Subrule->new(captures => [], name => $_->{name}->Str);
RxOp::Subrule->new(captures => [], method => $_->{name}->Str);

if ($sign) {
$rxop = $rxop ? RxOp::SeqAlt->new(zyg => [ $exp, $rxop ]) : $exp;
Expand All @@ -713,16 +733,12 @@ sub decapturize { my ($cl, $M) = @_;
if (!$M->{assertion}{_ast}->isa('RxOp::Subrule')) {
return $M->{assertion}{_ast};
}
RxOp::Subrule->new(captures => [],
zyg => $M->{assertion}{_ast}->zyg,
arglist => $M->{assertion}{_ast}->arglist,
name => $M->{assertion}{_ast}->name);
RxOp::Subrule->new(%{ $M->{assertion}{_ast} }, captures => []);
}

sub cclass_elem {}

sub assertion {}
# This needs to be deconstructed by :method, so it needs a regular structure
sub assertion__S_name { my ($cl, $M) = @_;
my $name = $cl->unqual_longname($M->{longname},
"Qualified method calls NYI");
Expand All @@ -731,13 +747,13 @@ sub assertion__S_name { my ($cl, $M) = @_;
} else {
if ($M->{nibbler}[0]) {
my $args = [$M->{nibbler}[0]{_ast}];
$M->{_ast} = RxOp::Subrule->new(zyg => $args, name => $name);
$M->{_ast} = RxOp::Subrule->new(zyg => $args, method => $name);
} else {
my $args = ($M->{arglist}[0] ? $M->{arglist}[0]{_ast} : []);
$M->{_ast} = RxOp::Subrule->new(arglist => $args, name => $name);
$M->{_ast} = RxOp::Subrule->new(arglist => $args, method => $name);
}
}
$M->{_ast} = $cl->rxcapturize($name, $M->{_ast});
$M->{_ast} = $cl->rxcapturize($M, $name, $M->{_ast});
}

sub assertion__S_method { my ($cl, $M) = @_;
Expand Down
16 changes: 11 additions & 5 deletions src/Op.pm
Expand Up @@ -1005,6 +1005,8 @@ use CgOp;

has rxop => (isa => 'RxOp', is => 'ro', required => 1);
has name => (isa => 'Str', is => 'ro', default => '');
has passcap => (isa => 'Bool', is => 'ro', default => 0);
has passcut => (isa => 'Bool', is => 'ro', default => 0);
has sym => (isa => 'Maybe[Str]', is => 'ro');
has pre => (isa => 'ArrayRef[Op]', is => 'ro', default => sub { [] });
has canback => (isa => 'Bool', is => 'ro', default => 1);
Expand All @@ -1017,17 +1019,21 @@ use CgOp;
local $::symtext = $self->sym;
my @mcaps;
local $::in_quant = 0;
my $u = $self->rxop->used_caps;
for (keys %$u) {
push @mcaps, $_ if $u->{$_} >= 2;
if (!$self->passcap) {
my $u = $self->rxop->used_caps;
for (keys %$u) {
push @mcaps, $_ if $u->{$_} >= 2;
}
}
my @pre = map { CgOp::sink($_->code($body)) } @{ $self->pre };

CgOp::prog(
@pre,
CgOp::rxinit(CgOp::clr_string($self->name),
CgOp::cast('cursor', CgOp::fetch(CgOp::scopedlex('')))),
CgOp::rxpushcapture(CgOp::null('cursor'), @mcaps),
CgOp::cast('cursor', CgOp::fetch(CgOp::scopedlex(''))),
$self->passcap, $self->passcut),
($self->passcap ? () :
CgOp::rxpushcapture(CgOp::null('cursor'), @mcaps)),
$self->rxop->code($body),
($self->canback ? CgOp::rxend() : CgOp::rxfinalend()),
CgOp::label('backtrack'),
Expand Down
42 changes: 30 additions & 12 deletions src/RxOp.pm
Expand Up @@ -459,26 +459,37 @@ use CgOp;
use Moose;
extends 'RxOp';

has name => (isa => 'Str', is => 'ro', required => 1);
has method => (isa => 'Maybe[Str]', is => 'ro');
has regex => (isa => 'Maybe[Op]', is => 'ro');
has passcap => (isa => 'Bool', is => 'ro', default => 0);
has _passcapzyg => (isa => 'Maybe[RxOp]', is => 'ro');
has captures => (isa => 'ArrayRef[Maybe[Str]]', is => 'ro', default => sub { [] });
has arglist => (isa => 'Maybe[ArrayRef[Op]]', is => 'ro');
has selfcut => (isa => 'Bool', is => 'ro', default => 0);

sub opzyg { ($_[0]->regex ? ($_[0]->regex) : ()), @{ $_[0]->arglist // [] } }

sub used_caps {
my ($self) = @_;
+{ map { $_ => $::in_quant ? 2 : 1 } @{ $self->captures } };
my $h = { map { $_ => $::in_quant ? 2 : 1 } @{ $self->captures } };
if ($self->passcap) {
my $h2 = $self->_passcapzyg->used_caps;
for (keys %$h2) { $h->{$_} += $h2->{$_} }
}
$h
}

sub true {
my ($self) = @_;
# all not quite right in the capturey case
if ($self->name eq 'sym') {
return unless $self->method;
if ($self->method eq 'sym') {
return RxOp::String->new(text => $::symtext);
}
if ($self->name eq 'before') {
if ($self->method eq 'before') {
return RxOp::Before->new(zyg => $self->zyg);
}
if ($self->name eq 'after') {
if ($self->method eq 'after') {
return RxOp::After->new(zyg => $self->zyg);
}
}
Expand All @@ -492,11 +503,18 @@ use CgOp;
return $true->code($body);
}

my $callf = CgOp::methodcall(CgOp::newscalar(
CgOp::rxcall("MakeCursor")), $self->name);
my @pushcapf = (@{ $self->captures } == 0) ? () : (
CgOp::rxpushcapture(CgOp::letvar("kv"),
@{ $self->captures }));
my $callf = $self->regex ?
CgOp::subcall(CgOp::fetch($self->regex->cgop($body)),
CgOp::newscalar(CgOp::rxcall("MakeCursor"))) :
CgOp::methodcall(CgOp::newscalar(
CgOp::rxcall("MakeCursor")), $self->method);
my @pushcapf = (@{ $self->captures } == 0) ? () : ($self->passcap ?
(CgOp::rxsetcapsfrom(CgOp::cast("cursor",
CgOp::letvar("k"))),
CgOp::rxpushcapture(CgOp::newscalar(CgOp::rxstripcaps(CgOp::cast("cursor", CgOp::letvar("k")))),
@{ $self->captures })) :
(CgOp::rxpushcapture(CgOp::letvar("kv"),
@{ $self->captures })));
my $updatef = CgOp::prog(
CgOp::ncgoto('backtrack', CgOp::obj_is_defined(CgOp::letvar("k"))),
@pushcapf,
Expand Down Expand Up @@ -534,7 +552,7 @@ use CgOp;
if (my $true = $self->true) {
return $true->lad;
}
[ 'Method', $self->name ];
[ 'Method', $self->method ];
}

__PACKAGE__->meta->make_immutable;
Expand All @@ -549,7 +567,7 @@ use CgOp;

sub code {
my ($self, $body) = @_;
RxOp::Subrule->new(name => 'ws',
RxOp::Subrule->new(method => 'ws',
selfcut => $self->selfcut)->code($body);
}

Expand Down
10 changes: 10 additions & 0 deletions test2.pl
Expand Up @@ -83,6 +83,16 @@
my $m = "" ~~ / $<foo> = { 2 + 2 } $<bar> = {"x"} $<bar> = {"y"} /;
is $m<foo>, 4, "value aliasing works (sing)";
is $m<bar>, "x y", "value aliasing works (plur)";

#$m = "fo" ~~ / (.) (.) /;
#is $m[0], "f", "numbered captures work";
#is $m[1], "o", "capture auto-numbering works";

$m = "def" ~~ /<a=.alpha> $<moo> = [ <b=.alpha> <c=.alpha> ]/;
is $m<a>, "d", "aliasing works";
is $m<c>, "f", "aliased [] transparent to captures";
is $m<moo>, "ef", "aliased [] captures string";
ok !$m<moo><b>, "no spurious nested captures";
}

# {
Expand Down
1 change: 1 addition & 0 deletions v6/TODO
Expand Up @@ -24,6 +24,7 @@ Highwater stuff
Audit EXPR, termish, nibble
Change STD to use $<foo> = { 1 }
self in regexes
Non-string parameterized role arguments
$<sym>
temp $*FOO
token { $param-role-var }
Expand Down

0 comments on commit 4c83f15

Please sign in to comment.