Subsecciones

Ejemplo de Transformaciones Árbol: Parse::Eyapp::TreeRegexp

Instalación

[~/jison/jison-aSb(master)]$ sudo cpan Parse::Eyapp

Donde

La gramática: Expresiones

my $grammar = q{
  %lexer {
      m{\G\s+}gc;
      m{\G([0-9]+(?:\.[0-9]+)?)}gc and return('NUM',$1);
      m{\G([A-Za-z][A-Za-z0-9_]*)}gc and return('VAR',$1);
      m{\G(.)}gcs and return($1,$1);
  }

  %right  '='     # Lowest precedence
  %left   '-' '+' # + and - have more precedence than = Disambiguate a-b-c as (a-b)-c
  %left   '*' '/' # * and / have more precedence than + Disambiguate a/b/c as (a/b)/c
  %left   NEG     # Disambiguate -a-b as (-a)-b and not as -(a-b)
  %tree           # Let us build an abstract syntax tree ...

  %%
  line: 
      exp <%name EXPRESSION_LIST + ';'>  
        { $_[1] } /* list of expressions separated by ';' */
  ;

  /* The %name directive defines the name of the
     class to which the node being built belongs */
  exp:
      %name NUM  
      NUM            
    | %name VAR   
      VAR         
    | %name ASSIGN 
      VAR '=' exp 
    | %name PLUS 
      exp '+' exp    
    | %name MINUS 
      exp '-' exp 
    | %name TIMES  
      exp '*' exp 
    | %name DIV     
      exp '/' exp 
    | %name UMINUS 
      '-' exp %prec NEG 
    | '(' exp ')'  
        { $_[2] }  /* Let us simplify a bit the tree */
  ;

  %%
}; # end grammar

Ejecución

El trozo de código:
\begin{verbatim}
$parser->input(\"2*-3+b*0;--2\n");       # Set the input
my $t = $parser->YYParse;
da lugar a este árbol:
[~/src/perl/parse-eyapp/examples/MatchingTrees]$ ./synopsis.pl 
Syntax Tree:
EXPRESSION_LIST(
  PLUS(
    TIMES(
      NUM( TERMINAL[2]),
      UMINUS( NUM( TERMINAL[3])) # UMINUS
    ) # TIMES,
    TIMES( VAR( TERMINAL[b]), NUM( TERMINAL[0])) # TIMES
  ) # PLUS,
  UMINUS(
    UMINUS( NUM( TERMINAL[2])) # UMINUS
  ) # UMINUS
) # EXPRESSION_LIST
Al aplicar las transformaciones:
# Let us transform the tree. Define the tree-regular expressions ..
my $p = Parse::Eyapp::Treeregexp->new( STRING => q{
    { #  Example of support code
      my %Op = (PLUS=>'+', MINUS => '-', TIMES=>'*', DIV => '/');
    }
    constantfold: /TIMES|PLUS|DIV|MINUS/:bin(NUM($x), NUM($y)) 
      => { 
        my $op = $Op{ref($bin)};
        $x->{attr} = eval  "$x->{attr} $op $y->{attr}";
        $_[0] = $NUM[0]; 
      }
    uminus: UMINUS(NUM($x)) => { $x->{attr} = -$x->{attr}; $_[0] = $NUM }
    zero_times_whatever: TIMES(NUM($x), .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
    whatever_times_zero: TIMES(., NUM($x)) and { $x->{attr} == 0 } => { $_[0] = $NUM }
  },
  OUTPUTFILE=> 'main.pm'
);
$p->generate(); # Create the tranformations

$t->s($uminus); # Transform UMINUS nodes
$t->s(@all);    # constant folding and mult. by zero
Obtenemos el árbol:
Syntax Tree after transformations:
EXPRESSION_LIST(NUM(TERMINAL[-6]),NUM(TERMINAL[2]))

synopsis.pl

[~/src/perl/parse-eyapp/examples/MatchingTrees]$ cat synopsis.pl 
#!/usr/bin/perl -w
use strict;
use Parse::Eyapp;
use Parse::Eyapp::Treeregexp;

sub TERMINAL::info {
  $_[0]{attr}
}

my $grammar = q{
  %lexer {
      m{\G\s+}gc;
      m{\G([0-9]+(?:\.[0-9]+)?)}gc and return('NUM',$1);
      m{\G([A-Za-z][A-Za-z0-9_]*)}gc and return('VAR',$1);
      m{\G(.)}gcs and return($1,$1);
  }

  %right  '='     # Lowest precedence
  %left   '-' '+' # + and - have more precedence than = Disambiguate a-b-c as (a-b)-c
  %left   '*' '/' # * and / have more precedence than + Disambiguate a/b/c as (a/b)/c
  %left   NEG     # Disambiguate -a-b as (-a)-b and not as -(a-b)
  %tree           # Let us build an abstract syntax tree ...

  %%
  line: 
      exp <%name EXPRESSION_LIST + ';'>  
        { $_[1] } /* list of expressions separated by ';' */
  ;

  /* The %name directive defines the name of the
     class to which the node being built belongs */
  exp:
      %name NUM  
      NUM            
    | %name VAR   
      VAR         
    | %name ASSIGN 
      VAR '=' exp 
    | %name PLUS 
      exp '+' exp    
    | %name MINUS 
      exp '-' exp 
    | %name TIMES  
      exp '*' exp 
    | %name DIV     
      exp '/' exp 
    | %name UMINUS 
      '-' exp %prec NEG 
    | '(' exp ')'  
        { $_[2] }  /* Let us simplify a bit the tree */
  ;

  %%
}; # end grammar

our (@all, $uminus);

Parse::Eyapp->new_grammar( # Create the parser package/class
  input=>$grammar,    
  classname=>'Calc', # The name of the package containing the parser
); 
my $parser = Calc->new();                # Create a parser
$parser->input(\"2*-3+b*0;--2\n");       # Set the input
my $t = $parser->YYParse;                # Parse it!
local $Parse::Eyapp::Node::INDENT=2;
print "Syntax Tree:",$t->str;

# Let us transform the tree. Define the tree-regular expressions ..
my $p = Parse::Eyapp::Treeregexp->new( STRING => q{
    { #  Example of support code
      my %Op = (PLUS=>'+', MINUS => '-', TIMES=>'*', DIV => '/');
    }
    constantfold: /TIMES|PLUS|DIV|MINUS/:bin(NUM($x), NUM($y)) 
      => { 
        my $op = $Op{ref($bin)};
        $x->{attr} = eval  "$x->{attr} $op $y->{attr}";
        $_[0] = $NUM[0]; 
      }
    uminus: UMINUS(NUM($x)) => { $x->{attr} = -$x->{attr}; $_[0] = $NUM }
    zero_times_whatever: TIMES(NUM($x), .) and { $x->{attr} == 0 } => { $_[0] = $NUM }
    whatever_times_zero: TIMES(., NUM($x)) and { $x->{attr} == 0 } => { $_[0] = $NUM }
  },
  OUTPUTFILE=> 'main.pm'
);
$p->generate(); # Create the tranformations

$t->s($uminus); # Transform UMINUS nodes
$t->s(@all);    # constant folding and mult. by zero

local $Parse::Eyapp::Node::INDENT=0;
print "\nSyntax Tree after transformations:\n",$t->str,"\n";

El método s

El código de s está en lib/Parse/Eyapp/Node.pm:

sub s {
  my @patterns = @_[1..$#_];

  # Make them Parse::Eyapp:YATW objects if they are CODE references
  @patterns = map { ref($_) eq 'CODE'? 
                      Parse::Eyapp::YATW->new(
                        PATTERN => $_,
                        #PATTERN_ARGS => [],
                      )
                      :
                      $_
                  } 
                  @patterns;
  my $changes; 
  do { 
    $changes = 0;
    foreach (@patterns) {
      $_->{CHANGES} = 0;
      $_->s($_[0]);
      $changes += $_->{CHANGES};
    }
  } while ($changes);
}

Véase

Casiano Rodríguez León
2016-03-27