# this should be a compile phase warning my $x = 0; my $x = 1; my $x = 2; #### # this should be a compile phase error my $x = 0; $x=; #### # this should be a run phase warning warn "Test Warning 1"; #### # this should be a run phase error my $x = 0; $x = 711/0; #### #!perl # Ensure various coding errors are caught use 5.010_000; use strict; use warnings; use Test::More tests => 42; use lib 'lib'; use lib 't/lib'; use Marpa::Test; use Carp; use English qw( -no_match_vars ); BEGIN { use_ok( 'Parse::Marpa' ); } my @features = qw( preamble lex_preamble e_op_action default_action lexer null_action unstringify_grammar unstringify_recce ); my @tests = ( 'compile phase warning', 'compile phase fatal', 'run phase warning', 'run phase error', 'run phase die', ); my %good_code = ( 'e op action' => 'my $error =', 'e number action' => 'my $error =', 'default action' => 'my $error =', ); my %test_code; my %expected; for my $test (@tests) { $test_code{$test} = '1;'; for my $feature (@features) { $expected{$test}{$feature} = q{}; } } my $getting_headers = 1; my @headers; my $data = q{}; LINE: while (my $line = ) { if ($getting_headers) { next LINE if $line =~ m/ \A \s* \Z/xms; if ($line =~ s/ \A [|] \s+ //xms) { chomp $line; push(@headers, $line); next LINE; } else { $getting_headers = 0; $data = q{}; } } # getting data if ($line =~ /\A__END__\Z/xms) { HEADER: while (my $header = pop @headers) { if ($header =~ s/\A expected \s //xms) { my ($feature, $test) = ($header =~ m/\A ([^\s]*) \s+ (.*) \Z/xms); croak("expected result given for unknown test, feature: $test, $feature") unless defined $expected{$test}{$feature}; $expected{$test}{$feature} = $data; next HEADER; } if ($header =~ s/\A good \s code \s //xms) { chomp $header; $good_code{$header} = $data; next HEADER; } if ($header =~ s/\A bad \s code \s //xms) { chomp $header; croak("test code given for unknown test: $header") unless defined $test_code{$header}; $test_code{$header} = $data; next HEADER; } croak("Bad header: $header"); } # HEADER $getting_headers = 1; $data = q{}; } # if $line $data .= $line; } sub canonical { my $template = shift; my $where = shift; my $long_where = shift; $long_where //= $where; $template =~ s/ \b package \s Parse [:][:] Marpa [:][:] [EP] _ [0-9a-fA-F]+ [;] $ /package Parse::Marpa::;/xms; $template =~ s/ \s* at \s [^\s]* code_diag[.]t \s line \s \d+\Z//xms; $template =~ s/[<]WHERE[>]/$where/xmsg; $template =~ s/[<]LONG_WHERE[>]/$long_where/xmsg; $template =~ s/ \s [<]DATA[>] \s line \s \d+ / line /xmsg; $template =~ s/ \s at \s [(] eval \s \d+ [)] \s line \s / at (eval ) line /xmsg; return $template; } sub run_test { my $args = shift; my $E_Op_action = $good_code{e_op_action}; my $E_Number_action = $good_code{e_number_action}; my $preamble = q{1}; my $lex_preamble = q{1}; my $default_action = $good_code{default_action}; my $text_lexer = 'lex_q_quote'; my $null_action = q{ '[null]' }; my $default_null_value = q{[default null]}; while (my ($arg, $value) = each %{$args}) { given(lc $arg) { when ('e_op_action') { $E_Op_action = $value } when ('e_number_action') { $E_Number_action = $value } when ('default_action') { $default_action = $value } when ('lex_preamble') { $lex_preamble = $value } when ('preamble') { $preamble = $value } when ('lexer') { $text_lexer = $value } when ('null_action') { $null_action = $value } when ('unstringify_grammar') { return Parse::Marpa::Grammar::unstringify(\$value) } when ('unstringify_recce') { return Parse::Marpa::Recognizer::unstringify(\$value) } default { croak("unknown argument to run_test: $arg"); } } } my $grammar = new Parse::Marpa::Grammar({ start => 'S', rules => [ [ 'S', [qw/E trailer optional_trailer1 optional_trailer2/], ], [ 'E', [qw/E Op E/], $E_Op_action, ], [ 'E', [qw/Number/], $E_Number_action, ], [ 'optional_trailer1', [qw/trailer/], ], [ 'optional_trailer1', [], ], [ 'optional_trailer2', [], $null_action ], [ 'trailer', [qw/Text/], ], ], terminals => [ [ 'Number' => { regex => qr/\d+/xms } ], [ 'Op' => { regex => qr/[-+*]/xms } ], [ 'Text' => { action => $text_lexer } ], ], default_action => $default_action, preamble => $preamble, lex_preamble => $lex_preamble, default_lex_prefix => '\s*', default_null_value => $default_null_value, }); my $recce = new Parse::Marpa::Recognizer({grammar => $grammar}); my $fail_offset = $recce->text( '2 - 0 * 3 + 1 q{trailer}' ); if ( $fail_offset >= 0 ) { croak("Parse failed at offset $fail_offset"); } $recce->end_input(); my $expected = '((((2-0)*3)+1)==7; q{trailer};[default null];[null])'; my $evaler = new Parse::Marpa::Evaluator( { recce => $recce } ); my $value = $evaler->value(); Marpa::Test::is(${$value}, $expected, 'Ambiguous Equation Value'); return 1; } # sub run_test run_test({}); my %where = ( preamble => 'evaluating preamble', lex_preamble => 'evaluating lex preamble', e_op_action => 'compiling action', default_action => 'compiling action', null_action => 'evaluating null value', lexer => 'compiling lexer', unstringify_grammar => 'unstringifying grammar', unstringify_recce => 'unstringifying recognizer', ); my %long_where = ( preamble => 'evaluating preamble', lex_preamble => 'evaluating lex preamble', e_op_action => 'compiling action for 1: E -> E Op E', default_action => 'compiling action for 3: optional_trailer1 -> trailer', null_action => 'evaluating null value for optional_trailer2', lexer => 'compiling lexer for Text', unstringify_grammar => 'unstringifying grammar', unstringify_recce => 'unstringifying recognizer', ); for my $test (@tests) { for my $feature (@features) { my $test_name = "$test in $feature"; if (eval { run_test({ $feature => $test_code{$test}, }); }) { fail("$test_name did not fail -- that shouldn't happen"); } else { my $eval_error = $EVAL_ERROR; my $where = $where{$feature}; my $long_where = $long_where{$feature}; Marpa::Test::is( canonical($eval_error, $where, $long_where), canonical($expected{$test}{$feature}, $where, $long_where), $test_name ); } } } # Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: __DATA__ | bad code compile phase warning # this should be a compile phase warning my $x = 0; my $x = 1; my $x = 2; $x++; 1; __END__ | expected preamble compile phase warning | expected lex_preamble compile phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 2: # this should be a compile phase warning 3: my $x = 0; *4: my $x = 1; *5: my $x = 2; 6: $x++; 7: 1; ====== Warning #0 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 4, line 1. ====== Warning #1 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 5, line 1. ====== __END__ | expected unstringify_grammar compile phase warning | expected unstringify_recce compile phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 1: # this should be a compile phase warning 2: my $x = 0; *3: my $x = 1; *4: my $x = 2; 5: $x++; 6: 1; ====== Warning #0 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 3, line 1. ====== Warning #1 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 4, line 1. ====== __END__ | expected e_op_action compile phase warning | expected default_action compile phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 3: # this should be a compile phase warning 4: my $x = 0; *5: my $x = 1; *6: my $x = 2; 7: $x++; 8: 1; 9: } ====== Warning #0 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 5, line 1. ====== Warning #1 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 6, line 1. ====== __END__ | expected null_action compile phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 3: # this should be a compile phase warning 4: my $x = 0; *5: my $x = 1; *6: my $x = 2; 7: $x++; 8: 1; ====== Warning #0 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 5, line 1. ====== Warning #1 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 6, line 1. ====== __END__ | expected lexer compile phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 5: # this should be a compile phase warning 6: my $x = 0; *7: my $x = 1; *8: my $x = 2; 9: $x++; 10: 1; 11: ; ====== Warning #0 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 7, line 1. ====== Warning #1 in : "my" variable $x masks earlier declaration in same scope at (eval ) line 8, line 1. ====== __END__ | bad code compile phase fatal # this should be a compile phase error my $x = 0; $x=; $x++; 1; __END__ | expected preamble compile phase fatal | expected lex_preamble compile phase fatal Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: # this should be a compile phase error 3: my $x = 0; 4: $x=; 5: $x++; 6: 1; ====== Error in : syntax error at (eval ) line 4, at EOF ====== __END__ | expected unstringify_grammar compile phase fatal | expected unstringify_recce compile phase fatal Fatal problem(s) in Fatal Error Problem code begins: 1: # this should be a compile phase error 2: my $x = 0; 3: $x=; 4: $x++; 5: 1; ====== Error in : syntax error at (eval ) line 3, at EOF ====== __END__ | expected e_op_action compile phase fatal | expected default_action compile phase fatal Fatal problem(s) in Fatal Error Problem code begins: 1: sub { 2: package Parse::Marpa::; 3: # this should be a compile phase error 4: my $x = 0; 5: $x=; 6: $x++; 7: 1; ====== Error in : syntax error at (eval ) line 5, at EOF ====== __END__ | expected null_action compile phase fatal Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: @_=(); 3: # this should be a compile phase error 4: my $x = 0; 5: $x=; 6: $x++; 7: 1; ====== Error in : syntax error at (eval ) line 5, at EOF ====== __END__ | expected lexer compile phase fatal Fatal problem(s) in Fatal Error Problem code begins: 1: sub { 2: my $STRING = shift; 3: my $START = shift; 4: package Parse::Marpa::; 5: # this should be a compile phase error 6: my $x = 0; 7: $x=; ====== Error in : syntax error at (eval ) line 7, at EOF ====== __END__ | bad code run phase warning # this should be a run phase warning my $x = 0; warn "Test Warning 1"; warn "Test Warning 2"; $x++; 1; __END__ | expected preamble run phase warning | expected lex_preamble run phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 2: # this should be a run phase warning 3: my $x = 0; *4: warn "Test Warning 1"; *5: warn "Test Warning 2"; 6: $x++; 7: 1; ====== Warning #0 in : Test Warning 1 at (eval ) line 4, line . ====== Warning #1 in : Test Warning 2 at (eval ) line 5, line . ====== __END__ | expected unstringify_grammar run phase warning | expected unstringify_recce run phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 1: # this should be a run phase warning 2: my $x = 0; *3: warn "Test Warning 1"; *4: warn "Test Warning 2"; 5: $x++; 6: 1; ====== Warning #0 in : Test Warning 1 at (eval ) line 3, line . ====== Warning #1 in : Test Warning 2 at (eval ) line 4, line . ====== __END__ | expected e_op_action run phase warning Fatal problem(s) in computing value for rule: 1: E -> E Op E 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 3: # this should be a run phase warning 4: my $x = 0; *5: warn "Test Warning 1"; *6: warn "Test Warning 2"; 7: $x++; 8: 1; 9: } ====== Warning #0 in computing value: Test Warning 1 at (eval ) line 5, line . ====== Warning #1 in computing value: Test Warning 2 at (eval ) line 6, line . ====== __END__ | expected default_action run phase warning Fatal problem(s) in computing value for rule: 6: trailer -> Text 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 3: # this should be a run phase warning 4: my $x = 0; *5: warn "Test Warning 1"; *6: warn "Test Warning 2"; 7: $x++; 8: 1; 9: } ====== Warning #0 in computing value: Test Warning 1 at (eval ) line 5, line . ====== Warning #1 in computing value: Test Warning 2 at (eval ) line 6, line . ====== __END__ | expected null_action run phase warning Fatal problem(s) in 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 3: # this should be a run phase warning 4: my $x = 0; *5: warn "Test Warning 1"; *6: warn "Test Warning 2"; 7: $x++; 8: 1; ====== Warning #0 in : Test Warning 1 at (eval ) line 5, line . ====== Warning #1 in : Test Warning 2 at (eval ) line 6, line . ====== __END__ | expected lexer run phase warning Fatal problem(s) in user supplied lexer for Text at 1 2 Warning(s) Warning(s) treated as fatal problem Last warning occurred in this code: 5: # this should be a run phase warning 6: my $x = 0; *7: warn "Test Warning 1"; *8: warn "Test Warning 2"; 9: $x++; 10: 1; 11: ; ====== Warning #0 in user supplied lexer: Test Warning 1 at (eval ) line 7, line . ====== Warning #1 in user supplied lexer: Test Warning 2 at (eval ) line 8, line . ====== __END__ | bad code run phase error # this should be a run phase error my $x = 0; $x = 711/0; $x++; 1; __END__ | expected preamble run phase error | expected lex_preamble run phase error Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: # this should be a run phase error 3: my $x = 0; 4: $x = 711/0; 5: $x++; 6: 1; ====== Error in : Illegal division by zero at (eval ) line 4, line . ====== __END__ | expected unstringify_grammar run phase error | expected unstringify_recce run phase error Fatal problem(s) in Fatal Error Problem code begins: 1: # this should be a run phase error 2: my $x = 0; 3: $x = 711/0; 4: $x++; 5: 1; ====== Error in : Illegal division by zero at (eval ) line 3, line . ====== __END__ | expected e_op_action run phase error Fatal problem(s) in computing value for rule: 1: E -> E Op E Fatal Error Problem code begins: 1: sub { 2: package Parse::Marpa::; 3: # this should be a run phase error 4: my $x = 0; 5: $x = 711/0; 6: $x++; 7: 1; ====== Error in computing value: Illegal division by zero at (eval ) line 5, line . ====== __END__ | expected default_action run phase error Fatal problem(s) in computing value for rule: 6: trailer -> Text Fatal Error Problem code begins: 1: sub { 2: package Parse::Marpa::; 3: # this should be a run phase error 4: my $x = 0; 5: $x = 711/0; 6: $x++; 7: 1; ====== Error in computing value: Illegal division by zero at (eval ) line 5, line . ====== __END__ | expected null_action run phase error Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: @_=(); 3: # this should be a run phase error 4: my $x = 0; 5: $x = 711/0; 6: $x++; 7: 1; ====== Error in : Illegal division by zero at (eval ) line 5, line . ====== __END__ | expected lexer run phase error Fatal problem(s) in user supplied lexer for Text at 1 Fatal Error Problem code begins: 1: sub { 2: my $STRING = shift; 3: my $START = shift; 4: package Parse::Marpa::; 5: # this should be a run phase error 6: my $x = 0; 7: $x = 711/0; ====== Error in user supplied lexer: Illegal division by zero at (eval ) line 7, line . ====== __END__ | bad code run phase die # this is a call to die() my $x = 0; die('test call to die'); $x++; 1; __END__ | expected preamble run phase die | expected lex_preamble run phase die Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: # this is a call to die() 3: my $x = 0; 4: die('test call to die'); 5: $x++; 6: 1; ====== Error in : test call to die at (eval ) line 4, line . ====== __END__ | expected unstringify_grammar run phase die | expected unstringify_recce run phase die Fatal problem(s) in Fatal Error Problem code begins: 1: # this is a call to die() 2: my $x = 0; 3: die('test call to die'); 4: $x++; 5: 1; ====== Error in : test call to die at (eval ) line 3, line . ====== __END__ | expected e_op_action run phase die Fatal problem(s) in computing value for rule: 1: E -> E Op E Fatal Error Problem code begins: 1: sub { 2: package Parse::Marpa::; 3: # this is a call to die() 4: my $x = 0; 5: die('test call to die'); 6: $x++; 7: 1; ====== Error in computing value: test call to die at (eval ) line 5, line . ====== __END__ | expected default_action run phase die Fatal problem(s) in computing value for rule: 6: trailer -> Text Fatal Error Problem code begins: 1: sub { 2: package Parse::Marpa::; 3: # this is a call to die() 4: my $x = 0; 5: die('test call to die'); 6: $x++; 7: 1; ====== Error in computing value: test call to die at (eval ) line 5, line . ====== __END__ | expected null_action run phase die Fatal problem(s) in Fatal Error Problem code begins: 1: package Parse::Marpa::; 2: @_=(); 3: # this is a call to die() 4: my $x = 0; 5: die('test call to die'); 6: $x++; 7: 1; ====== Error in : test call to die at (eval ) line 5, line . ====== __END__ | expected lexer run phase die Fatal problem(s) in user supplied lexer for Text at 1 Fatal Error Problem code begins: 1: sub { 2: my $STRING = shift; 3: my $START = shift; 4: package Parse::Marpa::; 5: # this is a call to die() 6: my $x = 0; 7: die('test call to die'); ====== Error in user supplied lexer: test call to die at (eval ) line 7, line . ====== __END__ | good code e_op_action my ($right_string, $right_value) = ($_[2] =~ /^(.*)==(.*)$/); my ($left_string, $left_value) = ($_[0] =~ /^(.*)==(.*)$/); my $op = $_[1]; my $value; if ($op eq '+') { $value = $left_value + $right_value; } elsif ($op eq '*') { $value = $left_value * $right_value; } elsif ($op eq '-') { $value = $left_value - $right_value; } else { croak("Unknown op: $op"); } '(' . $left_string . $op . $right_string . ')==' . $value; __END__ | good code e_number_action my $v0 = pop @_; $v0 . q{==} . $v0; __END__ | good code default_action my $v_count = scalar @_; return q{} if $v_count <= 0; return $_[0] if $v_count == 1; '(' . join(q{;}, (map { $_ // 'undef' } @_)) . ')'; __END__