

yacc de Berkeley que permite producir
código para Perl:
> byacc -V byacc: Berkeley yacc version 1.8.2 (C or perl)Se trata por tanto de un generador de analizadores LALR. Es bastante compatible con
AT&T yacc. Puedes encontrar
una versión en formato tar.gz en nuestro servidor
http://nereida.deioc.ull.es/~pl/pyacc-pack.tgz
o también desde
http://www.perl.com/CPAN/src/misc/.
El formato de llamada es:
byacc [ -CPcdlrtv ] [ -b file_prefix ] [ -p symbol_prefix ] filename
Las opciones C o c permiten generar código C. Usando -P
se genera código Perl. Las opciones d y v funcionan como es usual en
yacc. Con t se incorpora código para la depuración de la gramática.
Si se especifica l el código del usuario no es insertado.
La opción r permite generar ficheros separados para el código y las tablas. No la use
con Perl.
Fichero conteniendo la gramática:
%{
%}
%token INT EOL
%token LEFT_PAR RIGHT_PAR
%left PLUS MINUS
%left MULT DIV
%%
start: |
start input
;
input: expr EOL { print $1 . "\n"; }
| EOL
;
expr: INT { $p->mydebug("INT -> Expr!"); $$ = $1; }
| expr PLUS expr { $p->mydebug("PLUS -> Expr!"); $$ = $1 + $3; }
| expr MINUS expr { $p->mydebug("MINUS -> Expr!"); $$ = $1 - $3; }
| expr MULT expr { $p->mydebug("MULT -> Expr!"); $$ = $1 * $3; }
| expr DIV expr { $p->mydebug("DIV -> Expr!"); $$ = $1 / $3; }
| LEFT_PAR expr RIGHT_PAR { $p->mydebug("PARENS -> Expr!"); $$ = $2; }
;
%%
sub yyerror {
my ($msg, $s) = @_;
my ($package, $filename, $line) = caller;
die "$msg at <DATA> \n$package\n$filename\n$line\n";
}
sub mydebug {
my $p = shift;
my $msg = shift;
if ($p->{'yydebug'})
{
print "$msg\n";
}
}
La compilación con byacc del fichero calc.y
conteniendo la descripción de la gramática produce el módulo
Perl conteniendo el analizador.
> ls -l
total 12
-rw-r----- 1 pl casiano 47 Dec 29 2002 Makefile
-rw-r----- 1 pl casiano 823 Dec 29 2002 calc.y
-rwxr-x--x 1 pl casiano 627 Nov 10 15:37 tokenizer.pl
> cat Makefile
MyParser.pm: calc.y
byacc -d -P MyParser $<
> make
byacc -d -P MyParser calc.y
> ls -ltr
total 28
-rw-r----- 1 pl casiano 823 Dec 29 2002 calc.y
-rw-r----- 1 pl casiano 47 Dec 29 2002 Makefile
-rwxr-x--x 1 pl casiano 627 Nov 10 15:37 tokenizer.pl
-rw-rw---- 1 pl users 95 Nov 16 12:49 y.tab.ph
-rw-rw---- 1 pl users 9790 Nov 16 12:49 MyParser.pm
Observe que la opción -P es la que permite producir código Perl.
Anteriormente se usaba la opción -p. Esto se hizo para mantener la
compatibilidad con otras versiones de yacc en las que la opción -p
se usa para cambiar el prefijo por defecto (yy). Ese es el significado actual
de la opción -p en perl-byacc.
El fichero y.tab.ph generado contiene las definiciones de los tokens:
cat y.tab.ph $INT=257; $EOL=258; $LEFT_PAR=259; $RIGHT_PAR=260; $PLUS=261; $MINUS=262; $MULT=263; $DIV=264;El programa
tokenizer.pl contiene la llamada al analizador
y la definición del analizador léxico:
> cat tokenizer.pl
#!/usr/local/bin/perl5.8.0
require 5.004;
use strict;
use Parse::YYLex;
use MyParser;
print STDERR "Version $Parse::ALex::VERSION\n";
my (@tokens) = ((LEFT_PAR => '\(',
RIGHT_PAR => '\)',
MINUS => '-',
PLUS => '\+',
MULT => '\*',
DIV => '/',
INT => '[1-9][0-9]*',
EOL => '\n',
ERROR => '.*'),
sub { die "!can\'t analyze: \"$_[1]\"\n!"; });
my $lexer = Parse::YYLex->new(@tokens);
sub yyerror
{
die "There was an error:" . join("\n", @_). "\n";
}
my $debug = 0;
my $parser = new MyParser($lexer->getyylex(), \&MyParser::yyerror , $debug);
$lexer->from(\*STDIN);
$parser->yyparse(\*STDIN);
El módulo Parse::YYLex contiene una versión de Parse::Lex
que ha sido adaptada para funcionar con byacc. Todas las versiones de yacc
esperan que el analizador léxico devuelva un token numérico, mientras que
Parse::Lex devuelbe un objeto de la clase token.
Veamos un ejemplo de ejecución:
> tokenizer.pl Version 2.15 yydebug: state 0, reducing by rule 1 (start :) yydebug: after reduction, shifting from state 0 to state 1 3*(5-9) yydebug: state 1, reading 257 (INT) yydebug: state 1, shifting to state 2 yydebug: state 2, reducing by rule 5 (expr : INT) INT -> Expr! yydebug: after reduction, shifting from state 1 to state 6 yydebug: state 6, reading 263 (MULT) yydebug: state 6, shifting to state 11 yydebug: state 11, reading 259 (LEFT_PAR) yydebug: state 11, shifting to state 4 yydebug: state 4, reading 257 (INT) yydebug: state 4, shifting to state 2 yydebug: state 2, reducing by rule 5 (expr : INT) INT -> Expr! yydebug: after reduction, shifting from state 4 to state 7 yydebug: state 7, reading 262 (MINUS) yydebug: state 7, shifting to state 10 yydebug: state 10, reading 257 (INT) yydebug: state 10, shifting to state 2 yydebug: state 2, reducing by rule 5 (expr : INT) INT -> Expr! yydebug: after reduction, shifting from state 10 to state 15 yydebug: state 15, reading 260 (RIGHT_PAR) yydebug: state 15, reducing by rule 7 (expr : expr MINUS expr) MINUS -> Expr! yydebug: after reduction, shifting from state 4 to state 7 yydebug: state 7, shifting to state 13 yydebug: state 13, reducing by rule 10 (expr : LEFT_PAR expr RIGHT_PAR) PARENS -> Expr! yydebug: after reduction, shifting from state 11 to state 16 yydebug: state 16, reducing by rule 8 (expr : expr MULT expr) MULT -> Expr! yydebug: after reduction, shifting from state 1 to state 6 yydebug: state 6, reading 258 (EOL) yydebug: state 6, shifting to state 8 yydebug: state 8, reducing by rule 3 (input : expr EOL) -12 yydebug: after reduction, shifting from state 1 to state 5 yydebug: state 5, reducing by rule 2 (start : start input) yydebug: after reduction, shifting from state 0 to state 1 yydebug: state 1, reading 0 (end-of-file)

