Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Handle genericity of NFAs for regexes in roles.
Make it so that if you have a regex that mentions a variable that is
a role parameter, then a generic version of the NFA will be saved. As
the regex is incoprorated into the concrete role, this generic NFA is
turned into a concrete one with the value of the role parameter being
substituted in.	Thus it participates fully in LTM.
  • Loading branch information
jnthn committed Oct 21, 2012
1 parent f8df485 commit 8aa7fe1
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
10 changes: 10 additions & 0 deletions src/NQP/Actions.pm
Expand Up @@ -1188,6 +1188,16 @@ class NQP::Actions is HLL::Actions {
# Add the actual method.
$*W.pkg_add_method($*PACKAGE, 'add_method', $name, $code);
}

# If this appears in a role, its NFA may depend on generic args.
# If it does, we store the generic version of it.
if $*PKGDECL eq 'role' {
my $gen_nfa := QRegex::NFA.new();
$gen_nfa.addnode($<p6regex>.ast, :vars_as_generic);
if $gen_nfa.generic {
$code.SET_GENERIC_NFA($gen_nfa);
}
}

# In sink context, we don't need the Regex::Regex object.
$past := QAST::Op.new(
Expand Down
58 changes: 57 additions & 1 deletion src/QRegex/NFA.nqp
Expand Up @@ -12,9 +12,11 @@ class QRegex::NFA {
our $EDGE_SUBRULE := 8;
our $EDGE_CODEPOINT_I := 9;
our $EDGE_CODEPOINT_I_NEG := 10;
our $EDGE_GENERIC_VAR := 11;

has $!states;
has $!edges;
has int $!generic;

method new() {
my $new := self.bless(:states(nqp::list()), :edges(nqp::list()));
Expand All @@ -41,7 +43,7 @@ class QRegex::NFA {

method states() { $!states }

method addnode($node) {
method addnode($node, :$*vars_as_generic) {
self.regex_nfa($node, 1, 0);
self;
}
Expand Down Expand Up @@ -159,6 +161,12 @@ class QRegex::NFA {
self.fate($node, $end, $to);
}
}
elsif $*vars_as_generic && $subtype eq 'method' &&
$node[0][0] ~~ QAST::SVal && $node[0][0].value eq '!INTERPOLATE' &&
$node[0][1] ~~ QAST::Var && $node[0][1].scope eq 'lexical' {
$!generic := 1;
self.addedge($from, $to, $EDGE_GENERIC_VAR, $node[0][1].name);
}
else {
$subtype eq 'capture' && $node[1]
?? self.regex_nfa($node[1], $from, $to)
Expand Down Expand Up @@ -413,6 +421,54 @@ class QRegex::NFA {
method run_alt(str $target, int $offset, $bstack, $cstack) {
pir::nqp_nfa_run_alternation__vPSIPP($!states, $target, $offset, $bstack, $cstack)
}

method generic() {
$!generic
}

method instantiate_generic($env) {
# Create a copy.
my $copy := nqp::create(self);
my @copied_states;
for $!states -> @values {
nqp::push(@copied_states, nqp::clone(@values));
}
nqp::bindattr($copy, QRegex::NFA, '$!states', @copied_states);
nqp::bindattr($copy, QRegex::NFA, '$!edges', $!edges);

# Work out what we need to do to instantiate it by replacing any
# generic edges.
my int $from := 0;
for $!states -> @values {
my @output_values;
my int $i := 0;
my int $n := nqp::elems(@values);
while $i < $n {
my $act := @values[$i];
my $arg := @values[$i + 1];
my $to := @values[$i + 2];
if $act == $EDGE_GENERIC_VAR {
if nqp::existskey($env, $arg) {
$copy.literal(
QAST::Regex.new( :rxtype('literal'), nqp::atkey($env, $arg) ),
$from, $to);
@values[$i] := $EDGE_EPSILON;
@values[$i + 1] := 0;
@values[$i + 2] := 0;
}
else {
@values[$i] := $EDGE_FATE;
@values[$i + 1] := 0;
@values[$i + 2] := 0;
}
}
$i := $i + 3;
}
$from++;
}

$copy
}

method __dump($dumper, $label) {
my $subindent := $dumper.'newIndent'();
Expand Down
16 changes: 16 additions & 0 deletions src/core/NQPRoutine.pm
Expand Up @@ -65,6 +65,7 @@ my knowhow NQPRegex {
has $!caps;
has $!nfa;
has %!alt_nfas;
has $!generic_nfa;
method SET_CAPS($caps) {
$!caps := $caps;
}
Expand All @@ -75,6 +76,9 @@ my knowhow NQPRegex {
nqp::ifnull(%!alt_nfas, %!alt_nfas := {});
%!alt_nfas{$name} := $nfa;
}
method SET_GENERIC_NFA($nfa) {
$!generic_nfa := $nfa;
}
method CAPS() {
$!caps
}
Expand All @@ -101,6 +105,18 @@ my knowhow NQPRegex {

$der
}
method instantiate_generic($env) {
if nqp::isnull($!generic_nfa) || !nqp::can($!generic_nfa, 'instantiate_generic') {
self
}
else {
my $ins := self.clone();
nqp::bindattr($ins, NQPRegex, '$!nfa',
$!generic_nfa.instantiate_generic($env).save());
nqp::bindattr($ins, NQPRegex, '$!generic_nfa', nqp::null());
$ins
}
}
method !set_name($name) {
pir::assign__0Ps($!do, $name);
}
Expand Down
4 changes: 3 additions & 1 deletion src/how/NQPParametricRoleHOW.pm
Expand Up @@ -133,7 +133,9 @@ knowhow NQPParametricRoleHOW {
# Capture methods in the correct lexical context.
for %!methods {
my $name := $_.key;
my $meth := $_.value.clone();
my $meth := nqp::can($_.value, 'instantiate_generic')
?? $_.value.instantiate_generic($pad)
!! $_.value.clone();
if nqp::substr($name, 0, 12) eq '!!LATENAME!!' {
$name := nqp::atkey($pad, nqp::substr($name, 12));
$meth.'!set_name'($name);
Expand Down

0 comments on commit 8aa7fe1

Please sign in to comment.