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);

Reply via email to