The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
use strict;
#use Test::More qw(no_plan);
use Test::More tests => 3;
use_ok qw(Parse::Eyapp) or exit;
use_ok qw(Parse::Eyapp::Treeregexp) or exit;

my $grammar = q{
  %right  '='
  %left   '-' '+'
  %left   '*' '/'
  %left   NEG

  %{
    use Parse::Eyapp::Treeregexp;
    use Data::Dumper;
    $Data::Dumper::Indent = 1;
    $Data::Dumper::Deepcopy = 1;
    $Data::Dumper::Deparse = 1;
  %}

  %metatree

  %defaultaction { 
    if (@_==4) { # binary operations: 4 = lhs, left, operand, right
      $lhs->{t} = "$_[1]->{t} $_[3]->{t} $_[2]->{attr}";
      return  
    }
    die "Fatal Error. Unexpected input\n".Dumper(@_);
  }

  %%
  line: %name PROG
         exp <%name EXP + ';'> 
           { @{$lhs->{t}} = map { $_->{t}} ($lhs->child(0)->Children()); }
           
  ;

  exp:      %name NUM     NUM         { $lhs->{t} = $_[1]->{attr}; }    
          | %name VAR     VAR         { $lhs->{t} = $_[1]->{attr}; } 
          | %name ASSIGN  VAR '=' exp { $lhs->{t} = "$_[1]->{attr} $_[3]->{t} =" }
          | %name PLUS    exp '+' exp         
          | %name MINUS   exp '-' exp        
          | %name TIMES   exp '*' exp       
          | %name DIV     exp '/' exp      
          | %name UMINUS  '-' exp %prec NEG { $_[0]->{t} = "$_[2]->{t} NEG" }
          |               '(' exp ')' %begin { $_[2] } /* skip parenthesis */     
  ;

  %%

  sub _Error {
      my($token)=$_[0]->YYCurval;

      my($what)= $token ? "input: '$token'" : "end of input";
      die "Syntax error near $what.\n";
  }

  my $x; # Used for input

  sub _Lexer {
      my($parser)=shift;

      $x =~ s/^\s+//;
      return('',undef) if $x eq '';


      $x =~ s/^([0-9]+(?:\.[0-9]+)?)//   and return('NUM',$1);
      $x =~ s/^([A-Za-z][A-Za-z0-9_]*)// and return('VAR',$1);
      $x =~ s/^(.)//s                    and return($1,$1);
  }

  sub Run {
      my($self)=shift;

      $x = 'a=-2; b=2/a*-3';
      my $tree = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error,
        #yydebug => 0xFF
      );

      my $transform = Parse::Eyapp::Treeregexp->new( STRING => q{

        delete_code : CODE => { $delete_code->delete() }

        {
          sub not_semantic {
            my $self = shift;
            return  1 if $self->{token} eq $self->{attr};
            return 0;
          }
        }

        delete_tokens : TERMINAL and { not_semantic($TERMINAL) } => { $delete_tokens->delete() }

        delete = delete_code delete_tokens;

        uminus: UMINUS(., NUM($x), .) => { $x->{attr} = -$x->{attr}; $_[0] = $NUM }

        constantfold: /TIMES|PLUS|DIV|MINUS/:bin(NUM($z), ., NUM($y)) 
           => { 
          $z->{attr} = eval  "$z->{attr} $W->{attr} $y->{attr}";
          $_[0] = $NUM[0]; 
        }

        commutative_add: PLUS($x, ., $y, .)  
          => { my $t = $x; $_[0]->child(0, $y); $_[0]->child(2, $t)}

        comasocfold: TIMES(DIV(NUM($x), ., $b), ., NUM($y)) 
           => { 
          $x->{attr} = $x->{attr} * $y->{attr};
          $_[0] = $DIV; 
        }

        zero_times: TIMES(NUM($x), ., .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
        times_zero: TIMES(., ., NUM($x)) and { $x->{attr} == 0 } => { $_[0] = $NUM }

        algebraic_transformations = constantfold zero_times times_zero comasocfold;

      }, 
      #OUTPUTFILE => 'main.pm',
      SEVERITY => 0,
      NUMBERS => 0,
      );

      # Create the transformer
      $transform->generate();
      # Get the AST

      our ($uminus);
      $uminus->s($tree);

      our (@algebraic_transformations);
      $tree->s(@algebraic_transformations);
      #print Dumper($tree);

      #our ($commutative_add);
      #$commutative_add->s($tree);
      #print Dumper($tree);

      $tree->translation_scheme();
      
      our (@delete);
      $tree->s(@delete);
      #print Dumper($tree);

      my $expected_tree = bless( { 'children' => [
        bless( { 'children' => [
            bless( { 'children' => [
                bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' ),
                bless( { 'children' => [
                    bless( { 'children' => [], 'attr' => -2, 'token' => 'NUM' }, 'TERMINAL' )
                  ],
                  't' => -2
                }, 'NUM' )
              ],
              't' => 'a -2 ='
            }, 'ASSIGN' ),
            bless( { 'children' => [
                bless( { 'children' => [], 'attr' => 'b', 'token' => 'VAR' }, 'TERMINAL' ),
                bless( { 'children' => [
                    bless( { 'children' => [ 
                        bless( { 'children' => [], 'attr' => -6, 'token' => 'NUM' }, 'TERMINAL' )
                      ],
                      't' => -6
                    }, 'NUM' ),
                    bless( { 'children' => [
                        bless( { 'children' => [], 'attr' => 'a', 'token' => 'VAR' }, 'TERMINAL' )
                      ],
                      't' => 'a'
                    }, 'VAR' )
                  ],
                  't' => '-6 a /'
                }, 'DIV' )
              ],
              't' => 'b -6 a / ='
            }, 'ASSIGN' )
          ]
        }, 'EXP' )
      ],
      't' => [ 'a -2 =', 'b -6 a / =' ]
    }, 'PROG' );
    is_deeply($tree, $expected_tree, "index bug");
  } #  sub Run
}; # grammar

my $p = Parse::Eyapp->new_grammar(
  input=>$grammar,
  classname=>'main',
  firstline => 9,
  #outputfile => 'main.pm'
);

die $p->Warnings."\nSolve Ambiguities. See file main.output\n"  if $p->Warnings;
my $parser = main->new();
$parser->Run();