Skip to content

Commit

Permalink
Fix and improve ** regex quantifier
Browse files Browse the repository at this point in the history
Fixes RT#130911: https://rt.perl.org/Ticket/Display.html?id=130911
Fixes RT#130127: https://rt.perl.org/Ticket/Display.html?id=130127
Fixes RT#130125: https://rt.perl.org/Ticket/Display.html?id=130125
Fixes RT#130124: https://rt.perl.org/Ticket/Display.html?id=130124

- Negative values are treated as zero. The only thing I'm unsure of
    here is that /{2..1}/ throws about empty Range, while {-1..-2}
    would match zero characters, since it's effectively {0..0}
- Throw typed exceptions; don't return Failures (they get exploded
    somewhere in the regex engine, causing poor errors)
- Add X::Syntax::Regex::QuantifierValue exception. The X::Syntax
    isn't a perfect classification, but all the other Regex errors
    are under this umbrella, so this felt righter than creating an
    exception outside of X::Syntax::Regex
- Dies with "cannot unbox to native integer" on large Int values,
    which is a bit unhelpful, but this behaviour existed without this patch
  • Loading branch information
zoffixznet committed Oct 1, 2017
1 parent 80d6b42 commit 681d6be
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
5 changes: 4 additions & 1 deletion src/Perl6/Actions.nqp
Expand Up @@ -9798,7 +9798,10 @@ class Perl6::RegexActions is QRegex::P6Regex::Actions does STDActions {
if $<upto> eq '^' {
$max--;
}
$/.panic("Empty range") if $min > $max;

$/.typed_panic(
'X::Syntax::Regex::QuantifierValue', :empty-range
) if $min > $max;
}
$qast := QAST::Regex.new( :rxtype<quant>, :min($min), :max($max), :node($/) );
}
Expand Down
19 changes: 19 additions & 0 deletions src/core/Exception.pm
Expand Up @@ -1696,6 +1696,25 @@ my class X::Syntax::Regex::SpacesInBareRange does X::Syntax {
method message { 'Spaces not allowed in bare range.' }
}

my class X::Syntax::Regex::QuantifierValue does X::Syntax {
has $.inf;
has $.non-numeric;
has $.non-numeric-range;
has $.empty-range;
method message {
$!inf
&& 'Minimum quantity to match for quantifier cannot be Inf.'
~ ' Did you mean to use + or * quantifiers instead of **?'
|| $!non-numeric-range
&& 'Cannot use Range with non-Numeric or NaN end points as quantifier'
|| $!non-numeric
&& 'Cannot non-Numeric or NaN value as quantifier'

This comment has been minimized.

Copy link
@MasterDuke17

MasterDuke17 Oct 1, 2017

Contributor

Looks like you're missing a 'use' here.

This comment has been minimized.

Copy link
@zoffixznet

zoffixznet Oct 1, 2017

Author Contributor

Thanks. Fixed in f8a74eabae

|| $!empty-range
&& 'Cannot use empty Range as quantifier'
|| 'Invalid quantifier value'
}
}

my class X::Syntax::Regex::SolitaryQuantifier does X::Syntax {
method message { 'Quantifier quantifies nothing' }
}
Expand Down
56 changes: 45 additions & 11 deletions src/core/Match.pm
Expand Up @@ -412,17 +412,51 @@ my class Match is Capture is Cool does NQPMatchRole {
}

method DYNQUANT_LIMITS($mm) {
nqp::istype($mm,Range)
?? $mm.min == Inf
?? die 'Range minimum in quantifier (**) cannot be +Inf'
!! $mm.max == -Inf
?? die 'Range maximum in quantifier (**) cannot be -Inf'
!! nqp::list_i(
$mm.min < 0 ?? 0 !! $mm.min.Int,
$mm.max == Inf ?? -1 !! $mm.max.Int)
!! $mm == -Inf || $mm == Inf
?? Failure.new('Fixed quantifier cannot be infinite')
!! nqp::list_i($mm.Int, $mm.Int)
# Treat non-Range values as range with that value on both end points
# Throw for non-Numeric or NaN Ranges, or if minimum limit is +Inf
# If starting end point is less than 0, treat is as 0
nqp::if(
nqp::istype($mm,Range),
nqp::if(
nqp::isfalse(nqp::istype((my $min := $mm.min),Numeric))
|| nqp::isfalse(nqp::istype((my $max := $mm.max),Numeric))
|| $min.isNaN || $max.isNaN,
X::Syntax::Regex::QuantifierValue.new(:non-numeric-range).throw,
nqp::if(
$min == Inf,
X::Syntax::Regex::QuantifierValue.new(:inf).throw,
nqp::stmts(
nqp::if(
nqp::islt_i(
($min := nqp::add_i($min == -Inf ?? -1 !! $min.Int,
$mm.excludes-min)),
0),
$min := 0),
nqp::if(
$max == Inf,
nqp::list_i($min,-1),
nqp::stmts(
nqp::if(
$max == -Inf || nqp::islt_i(
($max := nqp::sub_i($max.Int,$mm.excludes-max)),0),
$max := 0),
nqp::if(
nqp::islt_i($max, $min),
X::Syntax::Regex::QuantifierValue.new(:empty-range).throw,
nqp::list_i($min,$max))))))),
nqp::if(
nqp::istype((my $v := $mm.Int), Failure),
nqp::if(
nqp::istype($mm,Numeric) && nqp::isfalse($mm.isNaN),
nqp::if(
$mm == Inf,
X::Syntax::Regex::QuantifierValue.new(:inf).throw,
nqp::list_i(0,0)), # if we got here, $mm is -Inf, treat as zero
X::Syntax::Regex::QuantifierValue.new(:non-numeric).throw),
nqp::if(
nqp::islt_i($v,0),
nqp::list_i(0,0),
nqp::list_i($v,$v))))
}

method OTHERGRAMMAR($grammar, $name, |) {
Expand Down

0 comments on commit 681d6be

Please sign in to comment.