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.pmObserve 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)