public
Created

Gist for blog post announcing Marpa's Scanless interface

  • Download Gist
gistfile1.pl
Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#!/usr/bin/perl
# Copyright 2012 Jeffrey Kegler
# This file is part of Marpa::R2. Marpa::R2 is free software: you can
# redistribute it and/or modify it under the terms of the GNU Lesser
# General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# Marpa::R2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser
# General Public License along with Marpa::R2. If not, see
# http://www.gnu.org/licenses/.
 
# A example of the Scannerless interface, for a blog post
 
use 5.010;
use strict;
use warnings;
use English qw( -no_match_vars );
use Marpa::R2 2.038000;
 
my $grammar = Marpa::R2::Scanless::G->new(
{
action_object => 'My_Actions',
default_action => 'do_first_arg',
source => \(<<'END_OF_SOURCE'),
:start ::= Script
Script ::= Expression+ separator => comma action => do_script
comma ~ [,]
Expression ::=
Number
| '(' Expression ')' action => do_parens assoc => group
|| Expression '**' Expression action => do_pow assoc => right
|| Expression '*' Expression action => do_multiply
| Expression '/' Expression action => do_divide
|| Expression '+' Expression action => do_add
| Expression '-' Expression action => do_subtract
Number ~ [\d]+
 
:discard ~ whitespace
whitespace ~ [\s]+
# allow comments
:discard ~ <hash comment>
<hash comment> ~ <terminated hash comment> | <unterminated
final hash comment>
<terminated hash comment> ~ '#' <hash comment body> <vertical space char>
<unterminated final hash comment> ~ '#' <hash comment body>
<hash comment body> ~ <hash comment char>*
<vertical space char> ~ [\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
<hash comment char> ~ [^\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
END_OF_SOURCE
}
);
 
sub my_parser {
my ( $grammar, $p_input_string ) = @_;
 
my $recce = Marpa::R2::Scanless::R->new( { grammar => $grammar } );
my $self = bless { grammar => $grammar }, 'My_Actions';
$self->{recce} = $recce;
local $My_Actions::SELF = $self;
 
if ( not defined eval { $recce->read($p_input_string); 1 }
)
{
## Add last expression found, and rethrow
my $eval_error = $EVAL_ERROR;
chomp $eval_error;
die $self->show_last_expression(), "\n", $eval_error, "\n";
} ## end if ( not defined eval { $event_count = $recce->read...})
 
my $value_ref = $recce->value();
if ( not defined $value_ref ) {
die $self->show_last_expression(), "\n",
"No parse was found, after reading the entire input\n";
}
 
return ${$value_ref};
 
} ## end sub my_parser
 
my @tests = (
[ '42*2+7/3, 42*(2+7)/3, 2**7-3, 2**(7-3)' =>
qr/\A 86[.]3\d+ \s+ 126 \s+ 125 \s+ 16\z/xms
],
[ '42*3+7, 42 * 3 + 7, 42 * 3+7' => qr/ \s* 133 \s+ 133 \s+ 133 \s* /xms
],
[ '15329 + 42 * 290 * 711, 42*3+7, 3*3+4* 4' =>
qr/ \s* 8675309 \s+ 133 \s+ 25 \s* /xms
],
);
 
for my $test (@tests) {
my ( $input, $output_re ) = @{$test};
my $value = my_parser( $grammar, \$input );
$value =~ $output_re
or die "Problem parsing $input\n",
" Value is $value\n",
" It was expected to, but did not match $output_re";
} ## end for my $test (@tests)
 
package My_Actions;
 
our $SELF;
sub new { return $SELF }
 
sub do_parens { shift; return $_[1] }
sub do_add { shift; return $_[0] + $_[2] }
sub do_subtract { shift; return $_[0] - $_[2] }
sub do_multiply { shift; return $_[0] * $_[2] }
sub do_divide { shift; return $_[0] / $_[2] }
sub do_pow { shift; return $_[0]**$_[2] }
sub do_first_arg { shift; return shift; }
sub do_script { shift; return join q{ }, @_ }
 
sub show_last_expression {
my ($self) = @_;
my $recce = $self->{recce};
my ( $start, $end ) = $recce->last_completed_range('Expression');
return 'No expression was successfully parsed' if not defined $start;
my $last_expression = $recce->range_to_string( $start, $end );
return "Last expression successfully parsed was: $last_expression";
} ## end sub show_last_expression
 
# vim: expandtab shiftwidth=4:

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.