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 |
