[~/jison/jison-aSb(master)]$ sudo cpan Parse::Eyapp
[~/src/perl/parse-eyapp/examples/MatchingTrees]$ pwd -P /Users/casiano/local/src/perl/parse-eyapp/examples/MatchingTrees
s)
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
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]))
[~/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 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);
}
s)
Casiano Rodríguez León