I need to build a simple algebraic parser and evaluator to work with
dynamically generated Verilog code and parametric bus assignments, and
since Damian's class notes contain such a parser, I started there (code
below, original is part of an infix to RPN converter).
Basically, it goes a few simple steps beyond Damian's code:
1) Creating an "equation" variable = expr
2) Allowing multiple equations
3) Instead of translating to RPN, it actually goes and evaluates
the expression.
4) As variables are resolved, they are locally stored in a hash to
be used in further calculations.
It works great, except I need to clear %variables between calls to
$parse->eqns. I realize that in this instance that:
$list = ();
works, but it doesn't feel right. Something cleaner would be nicer.
I'd also prefer not to use a global in the main package instead of the
private namespace variable.
Isn't there a more encapsulated way to either clear the private
variable or have a different solution?
Thanks,
Colin
#!/bin/perl -w
use strict;
use Parse::RecDescent;
use Data::Dumper;
use Switch qw(Perl6);
#$RD_TRACE = 1;
sub Parse::RecDescent::calc {
my ($self,$result,@list) = @_;
return $result unless @list;
my ($op, $val);
while ( ($op,$val) = splice(@list,0,2) ) {
given($op) {
when '+' { $result += $val }
when '-' { $result -= $val }
when '*' { $result *= $val }
when '/' { $result /= $val }
when '%' { $result %= $val }
when '**' { $result **= $val }
when '&&' { $result &&= $val }
when '||' { $result ||= $val }
}
}
return $result;
}
sub Parse::RecDescent::spew {
print join ":", @_;
print "\n";
}
my $grammar =
q{
{ my %variables = (); }
eqns : eqn(s) { \%variables }
eqn : variable "=" expr { $variables{$item[1]} = $item[3]; }
operation : <leftop:
<matchrule: $arg[0] >
/($arg[1])/
<matchrule: $arg[0] >
>
{ $thisparser->calc(@{ $item[1] }) }
expr : operation[ "conj", '\|\|' ]
conj : operation[ "addn", '\&\&' ]
addn : operation[ "mult", '[+-]' ]
mult : operation[ "expo", '[*\/\%]' ]
expo : operation[ "unary", '\*\*' ]
unary : "(" expr ")" { $item[2] }
| literal { $item[1] }
| var { $item[1] }
var : /[A-Za-z]\w*/ { $variables{ $item[1] } }
variable : /\w+/ { $item[1] }
literal : /\d+(\.\d+)?/ { $item[1] }
};
my $string = <<EOS;
a=1
b=2
c=b+3 d=4+a
EOS
my $parse = Parse::RecDescent->new($grammar)
or die "bad grammar\n";
my $list = $parse->eqns($string);
print Dumper($list);
my $list2 = $parse->eqns("e=d+2");
print Dumper($list2);