diff --git a/src5/lib/Perlito5/Grammar/CORE.pm b/src5/lib/Perlito5/Grammar/CORE.pm index d5eaee29d..4d51d1d16 100644 --- a/src5/lib/Perlito5/Grammar/CORE.pm +++ b/src5/lib/Perlito5/Grammar/CORE.pm @@ -232,7 +232,7 @@ token term_next_last_redo { }; token unary_op { - 'shift' | 'pop' + 'shift' | 'pop' | 'scalar' }; token term_unary { <.Perlito5::Grammar::Space::opt_ws> @@ -240,10 +240,15 @@ token term_unary { '(' ')' { my $args = Perlito5::Match::flat($MATCH->{paren_parse}); + my $op = Perlito5::Match::flat($MATCH->{unary_op}); + + Perlito5::Compiler::error "Not enough arguments for $op" + if $op eq 'scalar' && $args eq '*undef*'; + $MATCH->{capture} = [ 'term', Perlito5::AST::Apply->new( - code => Perlito5::Match::flat($MATCH->{unary_op}), - arguments => $args eq '*undef*' ? [] : [$args], + code => $op, + arguments => expand_list($args), namespace => '', bareword => 0, ) @@ -253,9 +258,14 @@ token term_unary { { my $args = Perlito5::Match::flat($MATCH->{argument_parse}); + my $op = Perlito5::Match::flat($MATCH->{unary_op}); + + Perlito5::Compiler::error "Not enough arguments for $op" + if $op eq 'scalar' && $args eq '*undef*'; + $MATCH->{capture} = [ 'term', Perlito5::AST::Apply->new( - code => Perlito5::Match::flat($MATCH->{unary_op}), + code => $op, arguments => $args eq '*undef*' ? [] : [$args], namespace => '', bareword => $args eq '*undef*' ? 1 : 0, @@ -318,20 +328,21 @@ sub term_core { return; }; -Perlito5::Grammar::Precedence::add_term( 'my' => \&term_declarator ); -Perlito5::Grammar::Precedence::add_term( 'our' => \&term_declarator ); -Perlito5::Grammar::Precedence::add_term( 'eval' => \&term_eval ); -Perlito5::Grammar::Precedence::add_term( 'state' => \&term_declarator ); -Perlito5::Grammar::Precedence::add_term( 'local' => \&term_local ); +Perlito5::Grammar::Precedence::add_term( 'my' => \&term_declarator ); +Perlito5::Grammar::Precedence::add_term( 'our' => \&term_declarator ); +Perlito5::Grammar::Precedence::add_term( 'eval' => \&term_eval ); +Perlito5::Grammar::Precedence::add_term( 'state' => \&term_declarator ); +Perlito5::Grammar::Precedence::add_term( 'local' => \&term_local ); Perlito5::Grammar::Precedence::add_term( 'return' => \&term_return ); -Perlito5::Grammar::Precedence::add_term( 'pos' => \&term_pos ); -Perlito5::Grammar::Precedence::add_term( 'chomp' => \&term_operator_with_paren ); -Perlito5::Grammar::Precedence::add_term( 'chop' => \&term_operator_with_paren ); -Perlito5::Grammar::Precedence::add_term( 'next' => \&term_next_last_redo ); -Perlito5::Grammar::Precedence::add_term( 'last' => \&term_next_last_redo ); -Perlito5::Grammar::Precedence::add_term( 'redo' => \&term_next_last_redo ); -Perlito5::Grammar::Precedence::add_term( 'shift' => \&term_unary ); -Perlito5::Grammar::Precedence::add_term( 'pop' => \&term_unary ); +Perlito5::Grammar::Precedence::add_term( 'pos' => \&term_pos ); +Perlito5::Grammar::Precedence::add_term( 'chomp' => \&term_operator_with_paren ); +Perlito5::Grammar::Precedence::add_term( 'chop' => \&term_operator_with_paren ); +Perlito5::Grammar::Precedence::add_term( 'next' => \&term_next_last_redo ); +Perlito5::Grammar::Precedence::add_term( 'last' => \&term_next_last_redo ); +Perlito5::Grammar::Precedence::add_term( 'redo' => \&term_next_last_redo ); +Perlito5::Grammar::Precedence::add_term( 'shift' => \&term_unary ); +Perlito5::Grammar::Precedence::add_term( 'pop' => \&term_unary ); +Perlito5::Grammar::Precedence::add_term( 'scalar' => \&term_unary ); Perlito5::Grammar::Precedence::add_term( $_ => \&term_file_test ) for qw( -r -w -x -o -R -W -X -O -e -z -s -f -d -l -p -S -b -c -t -u -g -k -T -B -M -A -C ); diff --git a/t/test.pl b/t/test.pl index 23c95da33..c96ac4fe4 100755 --- a/t/test.pl +++ b/t/test.pl @@ -1127,78 +1127,78 @@ ($$;$) _ok( !$diag, _where(), $name ); } -#### -#### # Purposefully avoiding a closure. -#### sub __capture { -#### push @::__capture, join "", @_; -#### } -#### -#### sub capture_warnings { -#### my $code = shift; -#### -#### local @::__capture; -#### local $SIG {__WARN__} = \&__capture; -#### &$code; -#### return @::__capture; -#### } -#### -#### # This will generate a variable number of tests. -#### # Use done_testing() instead of a fixed plan. -#### sub warnings_like { -#### my ($code, $expect, $name) = @_; -#### local $Level = $Level + 1; -#### -#### my @w = capture_warnings($code); -#### -#### cmp_ok(scalar @w, '==', scalar @$expect, $name); -#### foreach my $e (@$expect) { -#### if (ref $e) { -#### like(shift @w, $e, $name); -#### } else { -#### is(shift @w, $e, $name); -#### } -#### } -#### if (@w) { -#### diag("Saw these additional warnings:"); -#### diag($_) foreach @w; -#### } -#### } -#### -#### sub _fail_excess_warnings { -#### my($expect, $got, $name) = @_; -#### local $Level = $Level + 1; -#### # This will fail, and produce diagnostics -#### is($expect, scalar @$got, $name); -#### diag("Saw these warnings:"); -#### diag($_) foreach @$got; -#### } -#### -#### sub warning_is { -#### my ($code, $expect, $name) = @_; -#### die sprintf "Expect must be a string or undef, not a %s reference", ref $expect -#### if ref $expect; -#### local $Level = $Level + 1; -#### my @w = capture_warnings($code); -#### if (@w > 1) { -#### _fail_excess_warnings(0 + defined $expect, \@w, $name); -#### } else { -#### is($w[0], $expect, $name); -#### } -#### } -#### -#### sub warning_like { -#### my ($code, $expect, $name) = @_; -#### die sprintf "Expect must be a regexp object" -#### unless ref $expect eq 'Regexp'; -#### local $Level = $Level + 1; -#### my @w = capture_warnings($code); -#### if (@w > 1) { -#### _fail_excess_warnings(0 + defined $expect, \@w, $name); -#### } else { -#### like($w[0], $expect, $name); -#### } -#### } -#### + +# Purposefully avoiding a closure. +sub __capture { + push @::__capture, join "", @_; +} + +sub capture_warnings { + my $code = shift; + + local @::__capture; + local $SIG {__WARN__} = \&__capture; + &$code; + return @::__capture; +} + +# This will generate a variable number of tests. +# Use done_testing() instead of a fixed plan. +sub warnings_like { + my ($code, $expect, $name) = @_; + local $Level = $Level + 1; + + my @w = capture_warnings($code); + + cmp_ok(scalar @w, '==', scalar @$expect, $name); + foreach my $e (@$expect) { + if (ref $e) { + like(shift @w, $e, $name); + } else { + is(shift @w, $e, $name); + } + } + if (@w) { + diag("Saw these additional warnings:"); + diag($_) foreach @w; + } +} + +sub _fail_excess_warnings { + my($expect, $got, $name) = @_; + local $Level = $Level + 1; + # This will fail, and produce diagnostics + is($expect, scalar @$got, $name); + diag("Saw these warnings:"); + diag($_) foreach @$got; +} + +sub warning_is { + my ($code, $expect, $name) = @_; + die sprintf "Expect must be a string or undef, not a %s reference", ref $expect + if ref $expect; + local $Level = $Level + 1; + my @w = capture_warnings($code); + if (@w > 1) { + _fail_excess_warnings(0 + defined $expect, \@w, $name); + } else { + is($w[0], $expect, $name); + } +} + +sub warning_like { + my ($code, $expect, $name) = @_; + die sprintf "Expect must be a regexp object" + unless ref $expect eq 'Regexp'; + local $Level = $Level + 1; + my @w = capture_warnings($code); + if (@w > 1) { + _fail_excess_warnings(0 + defined $expect, \@w, $name); + } else { + like($w[0], $expect, $name); + } +} + #### # Set a watchdog to timeout the entire test file #### # NOTE: If the test file uses 'threads', then call the watchdog() function #### # _AFTER_ the 'threads' module is loaded.