NAME
Parse::Eyapp::defaultactionsintro - Introduction to Default Actions and Grammar Reuse
Default Actions
Default actions
When no action is specified both yapp
and eyapp
implicitly insert the semantic action { $_[1] }
. In Parse::Eyapp
you can modify such behavior using the %defaultaction { Perl code }
directive. The { Perl code }
clause that follows the %defaultaction
directive is executed when reducing by any production for which no explicit action was specified.
Translator from Infix to Postfix
See an example that translates an infix expression like a=b*-3
into a postfix expression like a b 3 NEG * =
:
# File Postfix.eyp (See the examples/ directory)
%right '='
%left '-' '+'
%left '*' '/'
%left NEG
%defaultaction { return "$left $right $op"; }
%%
line: $exp { print "$exp\n" }
;
exp: $NUM { $NUM }
| $VAR { $VAR }
| VAR.left '='.op exp.right
| exp.left '+'.op exp.right
| exp.left '-'.op exp.right
| exp.left '*'.op exp.right
| exp.left '/'.op exp.right
| '-' $exp %prec NEG { "$exp NEG" }
| '(' $exp ')' { $exp }
;
%%
# Support subroutines as in the Synopsis example
...
The file containing the Eyapp
program must be compiled with eyapp
:
nereida:~/src/perl/YappWithDefaultAction/examples> eyapp Postfix.eyp
Next, you have to write a client program:
nereida:~/src/perl/YappWithDefaultAction/examples> cat -n usepostfix.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use Postfix;
4
5 my $parser = new Postfix();
6 $parser->Run;
Now we can run the client program:
nereida:~/src/perl/YappWithDefaultAction/examples> usepostfix.pl
Write an expression: -(2*a-b*-3)
2 a * b 3 NEG * - NEG
Default Actions, %name
and YYName
In eyapp
each production rule has a name. The name of a rule can be explicitly given by the programmer using the %name
directive. For example, in the piece of code that follows the name ASSIGN
is given to the rule exp: VAR '=' exp
.
When no explicit name is given the rule has an implicit name. The implicit name of a rule is shaped by concatenating the name of the syntactic variable on its left, an underscore and the ordinal number of the production rule Lhs_#
as it appears in the .output
file. Avoid giving names matching such pattern to production rules. The patterns /${lhs}_\d+$/
where ${lhs}
is the name of the syntactic variable are reserved for internal use by eyapp
.
pl@nereida:~/LEyapp/examples$ cat -n Lhs.eyp
1 # Lhs.eyp
2
3 %right '='
4 %left '-' '+'
5 %left '*' '/'
6 %left NEG
7
8 %defaultaction {
9 my $self = shift;
10 my $name = $self->YYName();
11 bless { children => [ grep {ref($_)} @_] }, $name;
12 }
13
14 %%
15 input:
16 /* empty */
17 { [] }
18 | input line
19 {
20 push @{$_[1]}, $_[2] if defined($_[2]);
21 $_[1]
22 }
23 ;
24
25 line: '\n' { }
26 | exp '\n' { $_[1] }
27 ;
28
29 exp:
30 NUM { $_[1] }
31 | VAR { $_[1] }
32 | %name ASSIGN
33 VAR '=' exp
34 | %name PLUS
35 exp '+' exp
36 | %name MINUS
37 exp '-' exp
38 | %name TIMES
39 exp '*' exp
40 | %name DIV
41 exp '/' exp
42 | %name UMINUS
43 '-' exp %prec NEG
44 | '(' exp ')' { $_[2] }
45 ;
Inside a semantic action the name of the current rule can be recovered using the method YYName
of the parser object.
The default action (lines 8-12) computes as attribute of the left hand side a reference to an object blessed in the name of the rule. The object has an attribute children
which is a reference to the list of children of the node. The call to grep
11 bless { children => [ grep {ref($_)} @_] }, $name;
excludes children that aren't references. Notice that the lexical analyzer only returns references for the NUM
and VAR
terminals:
59 sub _Lexer {
60 my($parser)=shift;
61
62 for ($parser->YYData->{INPUT}) {
63 s/^[ \t]+//;
64 return('',undef) unless $_;
65 s/^([0-9]+(?:\.[0-9]+)?)//
66 and return('NUM', bless { attr => $1}, 'NUM');
67 s/^([A-Za-z][A-Za-z0-9_]*)//
68 and return('VAR',bless {attr => $1}, 'VAR');
69 s/^(.)//s
70 and return($1, $1);
71 }
72 return('',undef);
73 }
follows the client program:
pl@nereida:~/LEyapp/examples$ cat -n uselhs.pl
1 #!/usr/bin/perl -w
2 use Lhs;
3 use Data::Dumper;
4
5 $parser = new Lhs();
6 my $tree = $parser->Run;
7 $Data::Dumper::Indent = 1;
8 if (defined($tree)) { print Dumper($tree); }
9 else { print "Cadena no válida\n"; }
When executed with input a=(2+3)*b
the parser produces the following tree:
ASSIGN(TIMES(PLUS(NUM[2],NUM[3]), VAR[b]))
See the result of an execution:
pl@nereida:~/LEyapp/examples$ uselhs.pl
a=(2+3)*b
$VAR1 = [
bless( {
'children' => [
bless( { 'attr' => 'a' }, 'VAR' ),
bless( {
'children' => [
bless( {
'children' => [
bless( { 'attr' => '2' }, 'NUM' ),
bless( { 'attr' => '3' }, 'NUM' )
]
}, 'PLUS' ),
bless( { 'attr' => 'b' }, 'VAR' )
]
}, 'TIMES' )
]
}, 'ASSIGN' )
];
The name of a production rule can be changed at execution time. See the following example:
29 exp:
30 NUM { $_[1] }
31 | VAR { $_[1] }
32 | %name ASSIGN
33 VAR '=' exp
34 | %name PLUS
35 exp '+' exp
36 | %name MINUS
37 exp '-' exp
38 {
39 my $self = shift;
40 $self->YYName('SUBSTRACT'); # rename it
41 $self->YYBuildAST(@_); # build the node
42 }
43 | %name TIMES
44 exp '*' exp
45 | %name DIV
46 exp '/' exp
47 | %name UMINUS
48 '-' exp %prec NEG
49 | '(' exp ')' { $_[2] }
50 ;
When the client program is executed we can see the presence of the SUBSTRACT
nodes:
pl@nereida:~/LEyapp/examples$ useyynamedynamic.pl
2-b
$VAR1 = [
bless( {
'children' => [
bless( {
'attr' => '2'
}, 'NUM' ),
bless( {
'attr' => 'b'
}, 'VAR' )
]
}, 'SUBSTRACT' )
];
Grammar Reuse
An Action Method for each Production
Default actions provide a way to write reusable grammars. Here is one solution:
pl@europa:~/LEyapp/examples/recycle$ cat -n Noactions.eyp
1 %left '+'
2 %left '*'
3
4 %defaultaction {
5 my $self = shift;
6
7 my $class = $self->YYName;
8
9 $class->action(@_);
10 }
11
12 %%
13 exp: %name NUM
14 NUM
15 | %name PLUS
16 exp '+' exp
17 | %name TIMES
18 exp '*' exp
19 | '(' exp ')'
20 { $_[2] }
21 ;
22
23 %%
24
25 sub _Error {
26 my($token)=$_[0]->YYCurval;
27 my($what)= $token ? "input: '$token'" : "end of input";
28 my @expected = $_[0]->YYExpect();
29
30 local $" = ', ';
31 die "Syntax error near $what. Expected one of these tokens: @expected\n";
32 }
33
34
35 my $x = '';
36
37 sub _Lexer {
38 my($parser)=shift;
39
40 for ($x) {
41 s/^\s+//;
42 $_ eq '' and return('',undef);
43
44 s/^([0-9]+(?:\.[0-9]+)?)// and return('NUM',$1);
45 s/^([A-Za-z][A-Za-z0-9_]*)// and return('VAR',$1);
46 s/^(.)//s and return($1,$1);
47 }
48 }
49
50 sub Run {
51 my($self)=shift;
52 $x = shift;
53 my $debug = shift;
54
55 $self->YYParse(
56 yylex => \&_Lexer,
57 yyerror => \&_Error,
58 yydebug => $debug,
59 );
60 }
This grammar can be reused by a program that implements a calculator:
pl@europa:~/LEyapp/examples/recycle$ cat -n calcu.pl
1 #!/usr/bin/perl
2 use warnings;
3 use Noactions;
4
5 sub NUM::action {
6 return $_[1];
7 }
8
9 sub PLUS::action {
10 $_[1]+$_[3];
11 }
12
13 sub TIMES::action {
14 $_[1]*$_[3];
15 }
16
17 my $parser = Noactions->new();
18 print "Write an expression: ";
19 my $x;
20 {
21 local $/ = undef;
22 $x = <>;
23 }
24 my $t = $parser->Run($x);
25
26 print "$t\n";
But it also can be used by a program that translates infix to postfix expressions:
pl@europa:~/LEyapp/examples/recycle$ cat -n postf.pl
1 #!/usr/bin/perl
2 use warnings;
3 use Noactions;
4
5 sub NUM::action {
6 return $_[1];
7 }
8
9 sub PLUS::action {
10 "$_[1] $_[3] +";
11 }
12
13 sub TIMES::action {
14 "$_[1] $_[3] *";
15 }
16
17 my $parser = Noactions->new();
18 print "Write an expression: ";
19 my $x;
20 {
21 local $/ = undef;
22 $x = <>;
23 }
24 my $t = $parser->Run($x);
25
26 print "$t\n";
Reusing Grammars Using Inheritance
An laternative method is to use inheritance. The grammar inherits the action methods from the client:
pl@europa:~/LEyapp/examples/recycle$ cat -n NoacInh.eyp
1 %left '+'
2 %left '*'
3
4 %defaultaction {
5 my $self = shift;
6
7 my $action = $self->YYName;
8
9 $self->$action(@_);
10 }
11
12 %%
13 exp: %name NUM
14 NUM
15 | %name PLUS
16 exp '+' exp
17 | %name TIMES
18 exp '*' exp
19 | '(' exp ')'
20 { $_[2] }
21 ;
22
23 %%
24
25 sub _Error {
26 my($token)=$_[0]->YYCurval;
27 my($what)= $token ? "input: '$token'" : "end of input";
28 my @expected = $_[0]->YYExpect();
29
30 local $" = ', ';
31 die "Syntax error near $what. Expected one of these tokens: @expected\n";
32 }
33
34
35 my $x = '';
36
37 sub _Lexer {
38 my($parser)=shift;
39
40 for ($x) {
41 s/^\s+//;
42 $_ eq '' and return('',undef);
43
44 s/^([0-9]+(?:\.[0-9]+)?)// and return('NUM',$1);
45 s/^([A-Za-z][A-Za-z0-9_]*)// and return('VAR',$1);
46 s/^(.)//s and return($1,$1);
47 }
48 }
49
50 sub Run {
51 my($self)=shift;
52 $x = shift;
53 my $debug = shift;
54
55 $self->YYParse(
56 yylex => \&_Lexer,
57 yyerror => \&_Error,
58 yydebug => $debug,
59 );
60 }
This programs icalcu.pl
injects the actions into the parser package, implementing this way a calculator:
pl@europa:~/LEyapp/examples/recycle$ cat -n icalcu.pl
1 #!/usr/bin/perl
2 use warnings;
3 use NoacInh;
4
5 package Actions;
6 push @NoacInh::ISA, 'Actions';
7
8 sub NUM {
9 return $_[1];
10 }
11
12 sub PLUS {
13 $_[1]+$_[3];
14 }
15
16 sub TIMES {
17 $_[1]*$_[3];
18 }
19
20 package main;
21
22 my $parser = NoacInh->new();
23 print "Write an expression: ";
24 my $x;
25 {
26 local $/ = undef;
27 $x = <>;
28 }
29 my $t = $parser->Run($x);
30
31 print "$t\n";
In the same way this program implements the translator:
pl@europa:~/LEyapp/examples/recycle$ cat -n ipostf.pl
1 #!/usr/bin/perl
2 use warnings;
3 use NoacInh;
4
5 package Actions;
6 push @NoacInh::ISA, 'Actions';
7
8 sub NUM {
9 return $_[1];
10 }
11
12 sub PLUS {
13 "$_[1] $_[3] +";
14 }
15
16 sub TIMES {
17 "$_[1] $_[3] *";
18 }
19
20 package main;
21
22 my $parser = NoacInh->new();
23 print "Write an expression: ";
24 my $x;
25 {
26 local $/ = undef;
27 $x = <>;
28 }
29 my $t = $parser->Run($x);
30
31 print "$t\n";
SEE ALSO
Parse::Eyapp, Parse::Eyapp::eyapplanguageref, Parse::Eyapp::debugingtut, Parse::Eyapp::translationschemestut, Parse::Eyapp::Driver, Parse::Eyapp::Node, Parse::Eyapp::YATW, Parse::Eyapp::Treeregexp, Parse::Eyapp::Scope, Parse::Eyapp::Base,
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/languageintro.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/debuggingtut.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/eyapplanguageref.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/Treeregexp.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/Node.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/YATW.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/Eyapp.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/Base.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/translationschemestut.pdf
The pdf file in http://nereida.deioc.ull.es/~pl/perlexamples/MatchingTrees.pdf
The tutorial Parsing Strings and Trees with
Parse::Eyapp
(An Introduction to Compiler Construction in seven pages) in http://nereida.deioc.ull.es/~pl/eyapsimple/perldoc eyapp,
perldoc treereg,
perldoc vgg,
The Syntax Highlight file for vim at http://www.vim.org/scripts/script.php?script_id=2453 and http://nereida.deioc.ull.es/~vim/
Analisis Lexico y Sintactico, (Notes for a course in compiler construction) by Casiano Rodriguez-Leon. Available at http://nereida.deioc.ull.es/~pl/perlexamples/ Is the more complete and reliable source for Parse::Eyapp. However is in Spanish.
Man pages of yacc(1),
Man pages of bison(1),
ocamlyacc tutorial at http://plus.kaist.ac.kr/~shoh/ocaml/ocamllex-ocamlyacc/ocamlyacc-tutorial/ocamlyacc-tutorial.html
REFERENCES
The classic Dragon's book Compilers: Principles, Techniques, and Tools by Alfred V. Aho, Ravi Sethi and Jeffrey D. Ullman (Addison-Wesley 1986)
AUTHOR
Casiano Rodriguez-Leon (casiano@ull.es)
ACKNOWLEDGMENTS
This work has been supported by CEE (FEDER) and the Spanish Ministry of Educacion y Ciencia through Plan Nacional I+D+I number TIN2005-08818-C04-04 (ULL::OPLINK project http://www.oplink.ull.es/). Support from Gobierno de Canarias was through GC02210601 (Grupos Consolidados). The University of La Laguna has also supported my work in many ways and for many years.
A large percentage of code is verbatim taken from Parse::Yapp 1.05. The author of Parse::Yapp is Francois Desarmenien.
I wish to thank Francois Desarmenien for his Parse::Yapp module, to my students at La Laguna and to the Perl Community. Special thanks to my family and Larry Wall.
LICENCE AND COPYRIGHT
Copyright (c) 2006-2008 Casiano Rodriguez-Leon (casiano@ull.es). All rights reserved.
Parse::Yapp copyright is of Francois Desarmenien, all rights reserved. 1998-2001
These modules are free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 176:
Non-ASCII character seen before =encoding in 'válida\n";'. Assuming UTF-8