The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
%token NUM VAR
%right  '='
%left   '-' '+'
%left   '*' '/'
%left   NEG
%right  '^'

%{
use base q{Math::Tail};
my %s; # symbol table
our $VERSION = '1.1';
%}

%lexer {
  my $beginline = $self->tokenline();

  m{\G[ \t]*(\#.*)?}gc;

  m{\G([0-9]+(?:\.[0-9]+)?)}gc   and return ('NUM', [$1, $beginline]);
  m{\G([A-Za-z][A-Za-z0-9_]*)}gc and return ('VAR', [$1, $beginline]);
  m{\G\n}gc                      and do { 
                                      $self->tokenline(1); 
                                      return ("\n", ["\n", $beginline]) 
                                 };
  m{\G(.)}gc                     and return ($1,    [$1, $beginline]);

  return('',undef);
}

%%
start: 
    input { \%s }
;

input: line * 
;

line:       
  '\n'         { undef }
  | exp '\n'   { print "$_[1]\n" if defined($_[1]); $_[1] }
  | exp ''     { print "$_[1]\n" if defined($_[1]); $_[1] }
  | error  '\n'
    {
      $_[0]->YYErrok; # error recovery
      undef
    }

;

exp:
    $NUM            { $NUM->[0] }
  | $VAR                   
     { 
       my $id = $VAR->[0];
       my $val = $s{$id};
       $_[0]->semantic_error("Accesing undefined variable $id at line $VAR->[1].\n") 
       unless defined($val);
       return $val;
     }
  | $VAR '=' $exp   { $s{$VAR->[0]} = $exp }
  | exp.x '+' exp.y { $x + $y }
  | exp.x '-' exp.y { $x - $y }
  | exp.x '*' exp.y { $x * $y }
  | exp.x '/'.barr exp.y         
    {
       return($x/$y) if $y;
       $_[0]->semantic_error("Illegal division by zero at line $barr->[1].\n");
       undef
    }
  | '-' $exp %prec NEG  { -$exp }
  | exp.x '^' exp.y     { $x ** $y }
  | '(' $exp ')'        { $exp }
;

%%