But before we start…

Marpa::R2 is a parser module by Jeffrey Kegler.
Grammar: Backus-Naur form
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
      Parses (or generates) mathematical expressions of arbitrary complexity.
Grammar: Backus-Naur form
| 
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
 |   | 
| 7 + 2 * 3 | 
Grammar: Backus-Naur form
| 
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
 |   | 
| 7 + 2 * 3 | 
Grammar: Backus-Naur form
| 
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
 |   | 
| 7 + 2 * 3 | 
Grammar: Backus-Naur form
| 
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
 |   | 
| 7 + 2 * 3 | 
Grammar: Backus-Naur form
| 
Expression ::= Term
             | Expression + Term
             | Expression - Term
Term       ::= Factor
             | Factor * Factor
             | Factor / Factor
Factor     ::= number
             | ( Expression )
             | + Factor
             | - Factor
 |   | 
| 7 + 2 * 3 | 
#!/usr/bin/perl use warnings; use strict; use Marpa::R2; my $rules = << '__G__'; lexeme default = latm => 1 :default ::= action => ::first :start ::= Expression Expression ::= Term | Expression (plus) Term action => add | Expression (minus) Term action => subtract Term ::= Factor | Factor (asterisk) Factor action => multiply | Factor (slash) Factor action => divide Factor ::= number | (lbrace) Expression (rbrace) | (plus) Factor | (minus) Factor action => negate number ~ [0-9.]+ plus ~ '+' minus ~ '-' asterisk ~ '*' slash ~ '/' lbrace ~ '(' rbrace ~ ')' whitespace ~ [ \t]+ :discard ~ whitespace __G__ sub multiply { $_[1] * $_[2] } sub divide { $_[1] / $_[2] } sub add { $_[1] + $_[2] } sub subtract { $_[1] - $_[2] } sub negate { - $_[1] } my $input = shift; my $grammar = 'Marpa::R2::Scanless::G'->new({ source => \$rules }); my $value = $grammar->parse(\$input, { semantics_package => 'main' }); print $$value;
Expression ::= Term
             | Expression (plus) Term        action => add
             | Expression (minus) Term       action => subtract
Term       ::= Factor
             | Factor (asterisk) Factor      action => multiply
             | Factor (slash) Factor         action => divide
Factor     ::= number
             | (lbrace) Expression (rbrace)
             | (plus) Factor
             | (minus) Factor                action => negate
number   ~ [0-9.]+
plus     ~ '+'
minus    ~ '-'
asterisk ~ '*'
slash    ~ '/'
lbrace   ~ '('
rbrace   ~ ')'
    
Expression ::= (lbrace) Expression (rbrace)                          assoc => group
             | number
             | (minus) Expression                action => negate
            || Expression (asterisk) Expression  action => multiply
             | Expression (slash) Expression     action => divide
            || Expression (plus) Expression      action => add
             | Expression (minus) Expression     action => subtract
number   ~ [0-9.]+
plus     ~ '+'
minus    ~ '-'
asterisk ~ '*'
slash    ~ '/'
lbrace   ~ '('
rbrace   ~ ')'
      No need for Factor and Term.
    
Expression ::= (lbrace) Expression (rbrace)                          assoc => group
             | number
             | (minus) Expression                action => negate
            || Expression (asterisk) Expression  action => multiply
             | Expression (slash) Expression     action => divide
            || Expression (plus) Expression      action => add
             | Expression (minus) Expression     action => subtract
number   ~ [0-9.]+
plus     ~ '+'
minus    ~ '-'
asterisk ~ '*'
slash    ~ '/'
lbrace   ~ '('
rbrace   ~ ')'
    Expression ::= (lbrace) Expression (rbrace)                          assoc => group
             | number
             | (minus) Expression                action => negate
            || Expression (caret) Expression     action => power     assoc => right
            || Expression (asterisk) Expression  action => multiply
             | Expression (slash) Expression     action => divide
            || Expression (plus) Expression      action => add
             | Expression (minus) Expression     action => subtract
number   ~ [0-9.]+
plus     ~ '+'
minus    ~ '-'
asterisk ~ '*'
slash    ~ '/'
caret    ~ '^'
lbrace   ~ '('
rbrace   ~ ')'
      | Right associative: 4^3^2 = 4^(3^2) | Missing action: sub power { $_[1] ** $_[2] } | 
:start     ::= Expression
Expression ::= Term
             | Expression (plus) Term        action => add
             | Expression (minus) Term       action => subtract
Term       ::= Exp
             | Term (asterisk) Exp           action => multiply
             | Term (slash) Exp              action => divide
Exp        ::= Factor (caret) Exp            action => power
             | Factor
Factor     ::= number
             | (lbrace) Expression (rbrace)
             | (plus) Factor
             | (minus) Factor                action => negate
number     ~ [0-9.]+
plus       ~ '+'
minus      ~ '-'
asterisk   ~ '*'
slash      ~ '/'
lbrace     ~ '('
rbrace     ~ ')'
caret      ~ '^'
    number ~ [.0-9]+
0..0

number     ~ sign_maybe digit_many e
           | sign_maybe digit_any '.' digit_many e_maybe
           | sign_maybe digit_many e_maybe
           | sign_maybe non_zero digit_any
empty      ~
sign_maybe ~ [+-] | empty
digit      ~ [0-9]
non_zero   ~ [1-9]
digit_any  ~ digit*
digit_many ~ digit+
e          ~ [Ee] sign_maybe digit_many
e_maybe    ~ e | empty
      
        Understands the following: -12E+12, .12e5…
      
a = 3 * 7; b = a * 2; print b + 1
:start ::= Program Program ::= Statement semicolon Program action => none | Statement action => none Statement ::= Assign action => none | Output action => none Assign ::= Var (eq) Expression action => store Output ::= (print) List action => show List ::= Expression (comma) List action => concat | Expression Expression ::= (lbrace) Expression (rbrace) assoc => group | number | (minus) Expression action => negate | Var action => interpolate || Expression (caret) Expression action => power assoc => right || Expression (asterisk) Expression action => multiply | Expression (slash) Expression action => divide || Expression (plus) Expression action => add | Expression (minus) Expression action => subtract Var ::= varname varname ~ alpha | alpha alnum alpha ~ [a-zA-Z] alnum ~ [a-zA-Z0-9]+ semicolon ~ ';' eq ~ '=' comma ~ ',' print ~ 'print'
my %vars; sub none {} sub show { say $_[1] } sub concat { $_[1] . $_[2] } sub store { $vars{ $_[1] } = $_[2] } sub interpolate { $vars{ $_[1] } // die "Unknown variable $_[1]" }
r = 3 ^ 3; print "The result is: ", r
:start     ::= Program
Program    ::= Statement semicolon Program       action => none
             | Statement                         action => none
Statement  ::= Assign                            action => none
             | Output                            action => none
Assign     ::= Var (eq) Expression               action => store
Output     ::= (print) List                      action => show
List       ::= Expression (comma) List           action => concat
             | Expression
             | String (comma) List               action => concat
             | String
Expression ::= (lbrace) Expression (rbrace)                          assoc => group
             | number
             | (minus) Expression                action => negate
             | Var                               action => interpolate
            || Expression (caret) Expression     action => power     assoc => right
            || Expression (asterisk) Expression  action => multiply
             | Expression (slash) Expression     action => divide
            || Expression (plus) Expression      action => add
             | Expression (minus) Expression     action => subtract
String     ::= (quote) string (quote)
Var        ::= varname
semicolon  ~ ';'
eq         ~ '='
comma      ~ ','
print      ~ 'print'
string     ~ [^"]*
quote      ~ '"'
    a=1 b=2 c=a+b print c

my $grammar = 'Marpa::R2::Scanless::G'->new({ source => \$rules }); my $value = $grammar->parse(\$input, { semantics_package => 'main' }); print $$value if defined $$value;
my $grammar = 'Marpa::R2::Scanless::G'->new({source => \$rules}); my $recce = 'Marpa::R2::Scanless::R'->new({grammar => $grammar, semantics_package => 'main', rejection => 'event'}); my $last_pos = -1; for ( $recce->read(\$input); $recce->pos < length $input; $recce->resume ) { if (grep 'semicolon' eq $_, @{ $recce->terminals_expected }) { $recce->lexeme_read('semicolon', $recce->pos, 0); $last_pos = $recce->pos; warn 'Semicolon inserted at ', $last_pos; } else { die "No lexeme found at ", $recce->pos; } } my $value = $recce->value; print $$value if defined $$value;
Me using Marpa:
| Perl & Koha, Helsinki | 2023 | 
