package Perl6::Rule::Parser;
use strict;
use warnings;
use parent qw{Regexp::Parser};
my ($nest_eval, $nest_logical);
$nest_eval = qr[ (?> [^\\{}]+ | \\. | { (??{ $nest_eval }) } )* ]x;
$nest_logical = qr[ (?> [^\\{}]+ | \\. | { (??{ $nest_logical }) } )* ]x;
our $non_nest_brack = qr{
\{ [^\}]* \} |
\< [^\>]* \> |
\( [^\)]* \) |
\[ [^\]]* \]
}x;
sub nextchar {
my ($self) = @_;
${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc;
}
sub match_scalar { }
sub match_array { }
sub match_hash { }
sub init {
my ($self) = @_;
$self->add_handler('atom' => sub {
my ($S) = @_;
$S->nextchar;
${&Rx} =~ m{ \G (.) }xgcs or return;
my $c = $1;
push @{ $S->{next} }, qw< atom >;
return $S->$c if $S->can($c);
return $S->object(exact => $c);
});
### BACKSLASHED THINGS
$self->add_handler('\\' => sub {
my ($S, $cc) = @_;
my $c = '\\';
if (${&Rx} =~ m{ \G (.) }xgcs) {
$c .= (my $n = $1);
return $S->$c($cc) if $S->can($c);
--&RxPOS;
$S->warn($S->RPe_BADESC, $c = $n, "") if $n =~ /[a-zA-Z]/;
return $S->object(exact => $n, $c);
}
$S->error($S->RPe_ESLASH);
});
$self->add_handler('\b' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\b", '\b') if $cc;
return $S->object(bound => bound => '\b');
});
$self->add_handler('\B' => sub {
my ($S, $cc) = @_;
$S->warn($S->RPe_BADESC, "B", " in character class") if $cc;
return $S->force_object(anyof_char => 'B') if $cc;
return $S->object(bound => nbound => '\B');
});
$self->add_handler('\c' => sub {
my ($S, $cc) = @_;
if (${&Rx} =~ m{ \G ($non_nest_brack) }xgc) {
my $n = substr($1, 1, -1);
my @names = split /;/, $n;
if ($cc) {
$S->error(0, "\\c[A;B] in character class") if @names > 1;
return $S->force_object(anyof_char => $S->nchar($n), "\\c[$n]");
}
return $S->object(exact => join("", $S->nchar(@names)), "\\c[$n]");
}
$S->error(0, "Missing right %s on \\%s", 'brace', "c$1") if ${&Rx} =~ m{ \G ([\[(\{<]) }xgc;
$S->error(0, "Missing brackets on \\%s", 'c');
});
$self->add_handler('\C' => sub {
my ($S, $cc) = @_;
if (${&Rx} =~ m{ \G ($non_nest_brack) }xgc) {
my $n = substr($1, 1, -1);
my @names = split /;/, $n;
if ($cc) {
$S->error(0, "\\C[A;B] in character class") if @names > 1;
return $S->force_object(anyof_char_comp => $S->nchar($n), "\\C[$n]");
}
return $S->object(exact_comp => join("", $S->nchar(@names)), "\\C[$n]");
}
$S->error(0, "Missing right %s on \\%s", 'brace', "C$1") if ${&Rx} =~ m{ \G ([\[(\{<]) }xgc;
$S->error(0, "Missing brackets on \\%s", 'C');
});
$self->add_handler('\d' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(digit => 0)) if $cc;
return $S->object(digit => 0);
});
$self->add_handler('\D' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(digit => 1)) if $cc;
return $S->object(digit => 1);
});
$self->add_handler('\e' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\e", '\e') if $cc;
return $S->object(exact => "\e", '\e');
});
$self->add_handler('\E' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char_comp => "\e", '\E') if $cc;
return $S->object(exact_comp => "\e", '\E');
});
$self->add_handler('\f' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\f", '\f') if $cc;
return $S->object(exact => "\f", '\f');
});
$self->add_handler('\F' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char_comp => "\f", '\F') if $cc;
return $S->object(exact_comp => "\f", '\F');
});
$self->add_handler('\h' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(horiz => 0)) if $cc;
return $S->object(horiz => 0);
});
$self->add_handler('\H' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(horiz => 1)) if $cc;
return $S->object(horiz => 1);
});
$self->add_handler('\n' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\n", '\n') if $cc;
return $S->object(exact => "\n", '\n');
});
$self->add_handler('\N' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char_comp => "\n", '\N') if $cc;
return $S->object(exact_comp => "\n", '\N');
});
$self->add_handler('\r' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\r", '\r') if $cc;
return $S->object(exact => "\r", '\r');
});
$self->add_handler('\R' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char_comp => "\r", '\R') if $cc;
return $S->object(exact_comp => "\r", '\R');
});
$self->add_handler('\s' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(space => 0)) if $cc;
return $S->object(space => 0);
});
$self->add_handler('\S' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(space => 1)) if $cc;
return $S->object(space => 1);
});
$self->add_handler('\t' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char => "\t", '\t') if $cc;
return $S->object(exact => "\t", '\t');
});
$self->add_handler('\T' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_char_comp => "\t", '\T') if $cc;
return $S->object(exact_comp => "\t", '\T');
});
$self->add_handler('\v' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(vert => 0)) if $cc;
return $S->object(vert => 0);
});
$self->add_handler('\V' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(vert => 1)) if $cc;
return $S->object(vert => 1);
});
$self->add_handler('\w' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(alnum => 0)) if $cc;
return $S->object(alnum => 0);
});
$self->add_handler('\W' => sub {
my ($S, $cc) = @_;
return $S->force_object(anyof_class => $S->force_object(alnum => 1)) if $cc;
return $S->object(alnum => 1);
});
$self->add_handler('\x' => sub {
my ($S, $cc) = @_;
if (${&Rx} =~ m{ \G ($non_nest_brack) }xgc) {
my $h = substr($1, 1, -1);
my @hex = split /;/, $h;
$S->warn(0, "Illegal hexadecimal digit '%s' ignored", $1) if $h =~ /([^a-fA-F0-9;])/;
if ($cc) {
$S->error(0, "\\x[A;B] in character class") if @hex > 1;
return $S->force_object(anyof_char => chr(hex $h), "\\x[$h]");
}
return $S->object(exact => join("", map chr(hex), @hex), "\\x[$h]");
}
$S->error(0, "Missing right %s on \\%s", 'brace', "x$1") if ${&Rx} =~ m{ \G ([\{\[(<]) }xgc;
if (${&Rx} =~ m{ \G ( [a-fA-F0-9]+ ) }xgc) {
return $S->force_object(anyof_char => chr(hex $1), "\\x$1") if $cc;
return $S->object(exact => chr(hex $1), "\\x$1");
}
$S->warn(0, "Illegal hexadecimal digit '%s' ignored", substr(${&Rx}, &RxPOS, 1));
return $S->force_object(anyof_char => "\0", "\\x[0]") if $cc;
return $S->object(exact => "\0", "\\x[0]");
});
$self->add_handler('\X' => sub {
my ($S, $cc) = @_;
if (${&Rx} =~ m{ \G ($non_nest_brack) }xgc) {
my $h = substr($1, 1, -1);
my @hex = split /;/, $h;
$S->warn(0, "Illegal hexadecimal digit '%s' ignored", $1) if $h =~ /([^a-fA-F0-9;])/;
if ($cc) {
$S->error(0, "\\X[A;B] in character class") if @hex > 1;
return $S->force_object(anyof_char_comp => chr(hex $h), "\\X[$h]");
}
return $S->object(exact_comp => join("", map chr(hex), @hex), "\\X[$h]");
}
$S->error(0, "Missing right %s on \\%s", 'brace', "X$1") if ${&Rx} =~ m{ \G ([\[(\{<]) }xgc;
if (${&Rx} =~ m{ \G ( [a-fA-F0-9]+ ) }xgc) {
return $S->force_object(anyof_char_comp => chr(hex $1), "\\X$1") if $cc;
return $S->object(exact_comp => chr(hex $1), "\\X$1");
}
$S->warn(0, "Illegal hexadecimal digit '%s' ignored", substr(${&Rx}, &RxPOS, 1));
return $S->force_object(anyof_char_comp => "\0", "\\X[0]") if $cc;
return $S->object(exact_comp => "\0", "\\X[0]");
});
$self->add_handler('\0' => sub {
my ($S, $cc) = @_;
if (${&Rx} =~ m{ \G ($non_nest_brack) }xgc) {
my $o = substr($1, 1, -1);
my @oct = split /;/, $o;
$S->warn(0, "Illegal octal digit '%s' ignored", $1) if $o =~ /([^0-7;])/;
if ($cc) {
$S->error(0, "\\0[A;B] in character class") if @oct > 1;
return $S->force_object(anyof_char => chr(oct $o), "\\0[$o]");
}
return $S->object(exact => join("", map chr(oct), @oct), "\\0[$o]");
}
$S->error(0, "Missing right %s on \\%s", 'brace', "0$1") if ${&Rx} =~ m{ \G ([\[(\{<]) }xgc;
if (${&Rx} =~ m{ \G ( [0-7]+ ) }xgc) {
return $S->force_object(anyof_char => chr(oct $1), "\\0$1") if $cc;
return $S->object(exact => chr(oct $1), "\\0$1");
}
$S->warn(0, "Illegal octal digit '%s' ignored", substr(${&Rx}, &RxPOS, 1));
return $S->force_object(anyof_char => "\0", "\\0[0]") if $cc;
return $S->object(exact => "\0", "\\0[0]");
});
### ':'
$self->add_flag('i' => sub { 0x01 });
$self->add_handler(':' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G \: }xgc) {
my $n = '::';
return $S->$n;
}
if (${&Rx} =~ m{ \G ([a-z]+) }xgc) {
}
});
$self->add_handler('::' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G \: }xgc) {
my $n = ':::';
return $S->$n;
}
});
$self->add_handler(':::' => sub {
my ($S) = @_;
});
### '#'
# I don't think I need to handle this because the
# nextchar() method skips whitespace and comments
### '$'
$self->add_handler('$' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G \$ }xgc) {
my $n = '$$';
return $S->$n;
}
if ($S->match_scalar) {
}
return $S->object(eol => eos => '$');
});
$self->add_handler('$$' => sub {
my ($S) = @_;
if ($S->match_scalar) {
}
return $S->object(eol => eol => '$$');
});
### '@'
### '%'
### '^'
$self->add_handler('^' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G \^ }xgc) {
my $n = '^^';
return $S->$n;
}
return $S->object(bol => bos => '^');
});
$self->add_handler('^^' => sub {
my ($S) = @_;
return $S->object(bol => bol => '^^');
});
### '&'
### '*'
$self->add_handler('*' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< minmod >;
return $S->object(quant => 0, '');
});
### '+'
$self->add_handler('+' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< minmod >;
return $S->object(quant => 1, '');
});
### '?'
$self->add_handler('?' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< minmod >;
return $S->object(quant => 0, 1);
});
### '('
$self->add_handler('(' => sub {
my ($S) = @_;
$S->nextchar;
push @{ $S->{next} }, qw< c) atom >;
&SIZE_ONLY ? ++$S->{maxpar} : ++$S->{nparen};
push @{ $S->{flags} }, &Rf;
return $S->object(open => $S->{nparen});
});
### ')'
### '{'
### '}'
### '['
### ']'
### '<'
### '>'
### '.'
$self->add_handler('.' => sub {
my ($S) = @_;
return $S->object(reg_any => sany => '.');
});
### '|'
$self->add_handler('|' => sub {
my ($S) = @_;
return $S->object(branch =>);
});
return;
# control character
$self->add_handler('\c' => sub {
my ($S, $cc) = @_;
${&Rx} =~ m{ \G (.?) }xgc;
my $c = $1;
return $S->force_object(anyof_char => chr(64 ^ ord $c), "\\c$c") if $cc;
return $S->object(exact => chr(64 ^ ord $c), "\\c$c");
});
# nprop (not a unicode property)
$self->add_handler('\P' => sub {
my ($S, $cc) = @_;
$S->error($self->RPe_EMPTYB, 'P') if ${&Rx} !~ m{ \G (.) }xgcs;
my $name = $1;
if ($name eq '{') {
$S->error($self->RPe_RBRACE, 'P') if ${&Rx} !~ m{ \G ([^\}]*) \} }xgc;
$name = $1;
}
return $S->force_object(anyof_class => $S->force_object(prop => $name, 1)) if $cc;
return $S->object(prop => $name, 1);
});
# prop (a unicode property)
$self->add_handler('\p' => sub {
my ($S, $cc) = @_;
$S->error($self->RPe_EMPTYB, 'p') if ${&Rx} !~ m{ \G (.) }xgcs;
my $name = $1;
if ($name eq '{') {
$S->error($self->RPe_RBRACE, 'p') if ${&Rx} !~ m{ \G ([^\}]*) \} }xgc;
$name = $1;
}
return $S->force_object(anyof_class => $S->force_object(prop => $name, 0)) if $cc;
return $S->object(prop => $name, 0);
});
# clump (a unicode clump)
$self->add_handler('\X' => sub {
my ($S, $cc) = @_;
$S->warn($S->RPe_BADESC, 'X', ' in character class') if $cc;
return $S->force_object(anyof_char => 'X') if $cc;
return $S->object(clump => '\X');
});
# hex character
$self->add_handler('\x' => sub {
my ($S, $cc) = @_;
${&Rx} =~ m{ \G ( \{ | .{0,2} ) }sxgc;
my $brace = 0;
my $num = $1;
if ($num eq '{') {
$S->error($self->RPe_RBRACE, 'x') if ${&Rx} !~ m{ \G ( [^\}]* ) \} }xgc;
$num = $1;
$brace = 1;
}
else {
my $good = ($num =~ s/^([a-fA-F0-9]*)// and $1);
&RxPOS -= length $num;
$num = $good;
}
my $rep = $brace ? "\\x{$num}" : sprintf("\\x%02s", $num);
return $S->force_object(anyof_char => chr hex $num, $rep) if $cc;
return $S->object(exact => chr hex $num, $rep);
});
# alpha POSIX class
$self->add_handler('POSIX_alpha' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => alpha => $neg, \$how);
});
# alnum POSIX class
$self->add_handler('POSIX_alnum' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => alnum => $neg, \$how);
});
# ascii POSIX class
$self->add_handler('POSIX_ascii' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => ascii => $neg, \$how);
});
# cntrl POSIX class
$self->add_handler('POSIX_cntrl' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => cntrl => $neg, \$how);
});
# digit POSIX class
$self->add_handler('POSIX_digit' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => digit => $neg, \$how);
});
# graph POSIX class
$self->add_handler('POSIX_graph' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => graph => $neg, \$how);
});
# lower POSIX class
$self->add_handler('POSIX_lower' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => lower => $neg, \$how);
});
# print POSIX class
$self->add_handler('POSIX_print' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => print => $neg, \$how);
});
# punct POSIX class
$self->add_handler('POSIX_punct' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => punct => $neg, \$how);
});
# space POSIX class
$self->add_handler('POSIX_space' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => space => $neg, \$how);
});
# upper POSIX class
$self->add_handler('POSIX_upper' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => upper => $neg, \$how);
});
# word POSIX class
$self->add_handler('POSIX_word' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => word => $neg, \$how);
});
# xdigit POSIX class
$self->add_handler('POSIX_xdigit' => sub {
my ($S, $neg, $how) = @_;
return $S->force_object(anyof_class => xdigit => $neg, \$how);
});
$self->add_handler('{' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G (\d+) (,?) (\d*) \} }xgc) {
my ($min, $range, $max) = ($1, $2, $3);
$max = $min unless $range;
push @{ $S->{next} }, qw< minmod >;
$S->error($self->RPe_BCURLY) if length($max) and $min > $max;
return $S->object(quant => $min, $max);
}
return $S->object(exact => '{');
});
$self->add_handler('minmod' => sub {
my ($S) = @_;
$S->nextchar;
return $S->object(minmod =>) if ${&Rx} =~ m{ \G \? }xgc;
return;
});
# alternation branch
# opening parenthesis (maybe capturing paren)
# any character
# backslash
# start of char class (and possible negation)
$self->add_handler('[' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< cce] cc cc] >;
my $neg = ${&Rx} =~ m{ \G \^ }xgc;
my $pos = &RxPOS;
if (${&Rx} =~ m{ \G ([:.=]) .*? \1 ] }xgc) {
$S->warn($S->RPe_OUTPOS, $1, $1);
&RxPOS = $pos;
}
return $S->object(anyof => $neg);
});
# char class ] at beginning
$self->add_handler('cc]' => sub {
my ($S) = @_;
return unless ${&Rx} =~ m{ \G ] }xgc;
return $S->object(anyof_char => "]");
});
# start of char class range (or maybe just char)
$self->add_handler('cc' => sub {
my ($S) = @_;
return if ${&Rx} =~ m{ \G (?= ] | \z ) }xgc;
push @{ $S->{next} }, qw< cc >;
my ($lhs, $rhs, $before_range);
my $ret = \$lhs;
{
if (${&Rx} =~ m{ \G ( \\ ) }xgcs) {
my $c = $1;
$$ret = $S->$c(1);
}
elsif (${&Rx} =~ m{ \G \[ ([.=:]) (\^?) (.*?) \1 \] }xgcs) {
my ($how, $neg, $name) = ($1, $2, $3);
my $posix = "POSIX_$name";
if ($S->can($posix)) { $$ret = $S->$posix($neg, $how) }
else { $S->error($S->RPe_BADPOS, "$how$neg$name$how") }
}
elsif (${&Rx} =~ m{ \G (.) }xgcs) {
$$ret = $S->force_object(anyof_char => $1);
}
if ($ret == \$lhs) {
if (${&Rx} =~ m{ \G (?= - ) }xgc) {
if ($lhs->visual =~ /^(?:\[\[:.=]|\\[dDsSwWpP])/) {
$S->warn($S->RPe_FRANGE, $lhs->visual, "");
$ret = $lhs;
last;
}
$before_range = &RxPOS++;
$ret = \$rhs;
redo;
}
$ret = $lhs;
}
elsif ($ret == \$rhs) {
if ($rhs->visual =~ /^(?:\[[:.=]|\\[dDsSwWpP])/) {
$S->warn($S->RPe_FRANGE, $lhs->visual, $rhs->visual);
&RxPOS = $before_range;
$ret = $lhs;
}
elsif ($lhs->visual gt $rhs->visual) {
$S->error($S->RPe_IRANGE, $lhs->visual, $rhs->visual);
}
else {
$ret = $S->object(anyof_range => $lhs, $rhs);
}
}
}
return if &SIZE_ONLY;
return $ret;
});
# end of char class
$self->add_handler('cce]' => sub {
my ($S) = @_;
$S->error($S->RPe_LBRACK) if ${&Rx} !~ m{ \G ] }xgc;
return $S->object(anyof_close => "]");
});
# closing paren coming from 'atom'
$self->add_handler(')' => sub {
my ($S) = @_;
pop @{ $S->{next} };
&RxPOS--;
return;
});
# closing paren coming from an opening paren
$self->add_handler('c)' => sub {
my ($S) = @_;
$S->error($S->RPe_LPAREN) if ${&Rx} !~ m{ \G \) }xgc;
pop @{ $S->{flags} };
return $S->object(close =>);
});
# some kind of assertion...
$self->add_handler('(?' => sub {
my ($S) = @_;
my $c = '(?';
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
&RxPOS--;
}
else {
$S->error($S->RPe_SEQINC);
}
# flag assertion or non-capturing group
${&Rx} =~ m{ \G ([a-zA-Z]*) (-? [a-zA-Z]*) }xgc;
my ($on, $off) = ($1, $2);
my ($r_on, $r_off) = ("", "");
my ($f_on, $f_off) = (0,0);
&RxPOS -= length($on.$off);
my $old = &RxPOS;
for (split //, $on) {
&RxPOS++;
if (my $f = $S->can("FLAG_$_")) {
my $v = $S->$f(1) and $r_on .= $_;
$f_on |= $v;
next;
}
my $bad = substr ${&Rx}, $old;
$S->error($S->RPe_NOTREC, &RxPOS - $old, $bad);
}
&RxPOS++ if $off =~ s/^-//;
for (split //, $off) {
&RxPOS++;
if (my $f = $S->can("FLAG_$_")) {
my $v = $S->$f(0) and $r_off .= $_;
$f_off |= $v;
next;
}
my $bad = substr ${&Rx}, $old;
$S->error($S->RPe_NOTREC, &RxPOS - $old, $bad);
}
if (${&Rx} =~ m{ \G ([:)]) }xgc) {
my $type = $1 eq ':' ? 'group' : 'flags';
if ($type eq 'group') {
push @{ $S->{flags} }, &Rf;
push @{ $S->{next} }, qw< c) atom >;
}
&Rf |= $f_on;
&Rf &= ~$f_off;
return $S->object($type => $r_on, $r_off);
}
&RxPOS++;
my $l = length($on.$off) + 2;
$S->error($S->RPe_NOTREC, $l, substr(${&Rx}, $old));
});
# comment
$self->add_handler('(?#' => sub {
my ($S) = @_;
${&Rx} =~ m{ \G [^)]* }xgc;
$S->error($S->RPe_NOTERM) unless ${&Rx} =~ m{ \G \) }xgc;
return;
});
# not implemented (?$...)
$self->add_handler('(?$' => sub {
my ($S) = @_;
$S->error($S->RPe_NOTREC, 1, substr(${&Rx}, &RxPOS - 1));
});
# not implemented (?@...)
$self->add_handler('(?@' => sub {
my ($S) = @_;
$S->error($S->RPe_NOTREC, 1, substr(${&Rx}, &RxPOS - 1));
});
# look-ahead
$self->add_handler('(?=' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< c) atom >;
push @{ $S->{flags} }, &Rf;
return $S->object(ifmatch => 1);
});
# look-ahead (neg)
$self->add_handler('(?!' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< c) atom >;
push @{ $S->{flags} }, &Rf;
return $S->object(unlessm => 1);
});
# look-behind prefix
$self->add_handler('(?<' => sub {
my ($S) = @_;
my $c = '(?<';
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
}
$S->error($S->RPe_NOTREC, 2, substr(${&Rx}, &RxPOS - 2));
});
# look-behind
$self->add_handler('(?<=' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< c) atom >;
push @{ $S->{flags} }, &Rf;
return $S->object(ifmatch => -1);
});
# look-behind (neg)
$self->add_handler('(?<!' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< c) atom >;
push @{ $S->{flags} }, &Rf;
return $S->object(unlessm => -1);
});
# suspend
$self->add_handler('(?>' => sub {
my ($S) = @_;
push @{ $S->{next} }, qw< c) atom >;
push @{ $S->{flags} }, &Rf;
return $S->object(suspend =>);
});
# eval
$self->add_handler('(?{' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G ($nest_eval) \} \) }xgc) {
push @{ $S->{flags} }, &Rf;
return $S->object(eval => $1);
}
$S->error($S->RPe_NOTBAL);
});
# logical prefix
$self->add_handler('(??' => sub {
my ($S) = @_;
my $c = '(??';
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
}
$S->error($S->RPe_NOTREC, 2, substr(${&Rx}, &RxPOS - 2));
});
# logical
$self->add_handler('(??{' => sub {
my ($S) = @_;
if (${&Rx} =~ m{ \G ($nest_logical) \} \) }xgc) {
push @{ $S->{flags} }, &Rf;
return $S->object(logical => $1);
}
$S->error($S->RPe_NOTBAL);
});
# logical prefix
$self->add_handler('(?p' => sub {
my ($S) = @_;
my $c = '(?p';
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
}
$S->error($S->RPe_NOTREC, 2, substr(${&Rx}, &RxPOS - 2));
});
# logical
$self->add_handler('(?p{' => sub {
my ($S) = @_;
$S->warn($S->RPe_LOGDEP);
my $c = "(??{";
return $S->$c;
});
$self->add_handler('(?(' => sub {
my ($S) = @_;
my $c = '(?(';
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
&RxPOS--;
}
push @{ $S->{next} }, qw< ifthen( >;
push @{ $S->{flags} }, &Rf;
return $S->object(ifthen =>);
});
# (?(...)t|f) condition
$self->add_handler('ifthen(' => sub {
my ($S) = @_;
my $c = 'ifthen(';
push @{ $S->{next} }, qw< c) atom >;
if (${&Rx} =~ m{ \G (.) }xgcs) {
my $n = "$c$1";
return $S->$n if $S->can($n);
&RxPOS--;
}
if (${&Rx} =~ m{ \G ( [1-9]\d* ) }xgc) {
my $n = $1;
$S->error($S->RPe_SWNREC) if ${&Rx} !~ m{ \G \) }xgc;
push @{ $S->{next} }, qw< ifthen|2 ifthen| ifthen_atom >;
return $S->object(groupp => $n);
}
$S->error($S->RPe_SWUNKN, &RxCUR);
});
# atom inside an ifthen
$self->add_handler('ifthen_atom' => sub {
my ($S) = @_;
$S->nextchar;
${&Rx} =~ m{ \G ([^|]) }xgcs or return;
my $c = $1;
push @{ $S->{next} }, qw< ifthen_atom >;
return $S->$c if $S->can($c);
return $S->object(exact => $c);
});
# alternation branch inside ifthen
$self->add_handler('ifthen|' => sub {
my ($S) = @_;
return if ${&Rx} !~ m{ \G \| }xgc;
push @{ $S->{next} }, qw< ifthen_atom >;
return $S->object(branch =>);
});
# illegal 2nd alternation branch inside ifthen
$self->add_handler('ifthen|2' => sub {
my ($S) = @_;
return if ${&Rx} !~ m{ \G \| }xgc;
$S->error($S->RPe_SWBRAN);
});
$self->add_handler('ifthen(?' => sub {
my ($S) = @_;
my $c = '(?';
push @{ $S->{next} }, qw< ifthen|2 ifthen| ifthen_atom >;
if (${&Rx} =~ m{ \G ( (?: <? [!=] | \{ ) ) }xgc) {
my $n = "$c$1";
return $S->$n if $S->can($n);
&RxPOS -= length $1;
}
$S->error($S->RPe_SEQINC);
});
}
1;
__END__