

%%.
Por defecto, el símbolo de arranque es el primero que aparece, en este caso
list. En bison es posible hacer que otro variable lo sea
utilizando la declaración %start:
nereida:~/src/precedencia/hoc1> cat -n hoc1.y
1 %{
2 /* File: /home/pl/src/precedencia/hoc1/hoc1.y */
3 #define YYSTYPE double
4 #include <stdio.h>
5 %}
6 %token NUMBER
7 %left '+' '-'
8 %left '*' '/'
9 %%
10 list
11 :
12 | list '\n'
13 | list expr { printf("%.8g\n",$2);}
14 ;
15
16 expr
17 : NUMBER { $$ = $1;}
18 | expr '+' expr {$$ = $1 + $3;}
19 | expr '-' expr {$$ = $1 - $3;}
20 | expr '*' expr {$$ = $1 * $3;}
21 | expr '/' expr {$$ = $1 / $3;}
22 ;
23
24 %%
25
26 extern FILE * yyin;
27
28 main(int argc, char **argv) {
29 if (argc > 1) yyin = fopen(argv[1],"r");
30 yydebug = 1;
31 yyparse();
32 }
33
34 yyerror(char *s) {
35 printf("%s\n",s);
36 }
int.
El fichero yyin (líneas 26 y 29) es definido en el fichero conteniendo el analizador léxico lex.yy.c . Refiere al fichero de entrada conteniendo el texto a analizar.
Al poner la variable yydebug a 1 activamos el modo depuración. Para que la depuración se haga efectiva es necesario definir además la macro YYDEBUG .
El analizador sintáctico proveido por yacc se llama yyparse
(línea 31). Por defecto su declaración es int yyparse ()
nereida:~/src/precedencia/hoc1> cat -n hoc1.l
1 %{
2 #include "y.tab.h"
3 extern YYSTYPE yylval;
4 %}
5 number [0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?
6 %%
7 {number} { yylval = atof(yytext); return NUMBER; }
8 .|\n { return yytext[0];}
9 %%
10 int yywrap() { return 1; }
Al compilar el program yacc con la opción -d
se produce además del fichero y.tab.c conteniendo el
analizador sintáctico un fichero adicional de cabecera y.tab.h
conteniendo
las definiciones de los terminales:
nereida:~/src/precedencia/hoc1> yacc -d -v hoc1.y
nereida:~/src/precedencia/hoc1> ls -lt | head -4
total 200
-rw-rw---- 1 pl users 2857 2007-01-18 10:26 y.output
-rw-rw---- 1 pl users 35936 2007-01-18 10:26 y.tab.c
-rw-rw---- 1 pl users 1638 2007-01-18 10:26 y.tab.h
nereida:~/src/precedencia/hoc1> sed -ne '27,48p' y.tab.h | cat -n
1 #ifndef YYTOKENTYPE
2 # define YYTOKENTYPE
3 /* Put the tokens into the symbol table, so that GDB and other debuggers
4 know about them. */
5 enum yytokentype {
6 NUMBER = 258
7 };
8 #endif
9 /* Tokens. */
10 #define NUMBER 258
.. .........................................................
15 #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
16 typedef int YYSTYPE;
17 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
18 # define YYSTYPE_IS_DECLARED 1
19 # define YYSTYPE_IS_TRIVIAL 1
20 #endif
21
22 extern YYSTYPE yylval;
La variable yylval (líneas 3 y 7 del listado 11.1.2)
es declarada por el
analizador sintáctico y usada por el analizador
léxico. El analizador léxico deja en la misma el valor semántico
asociado con el token actual.
Para compilar todo el proyecto usaremos el siguiente fichero Makefile:
> cat Makefile
hoc1: y.tab.c lex.yy.c
gcc -DYYDEBUG=1 -g -o hoc1 y.tab.c lex.yy.c
y.tab.c y.tab.h: hoc1.y
yacc -d -v hoc1.y
lex.yy.c: hoc1.l y.tab.h
flex -l hoc1.l
clean:
- rm -f y.tab.c lex.yy.c *.o core hoc1
yydebug = 1; justo antes de la llamada a
yyparse() y ejecutamos el programa resultante:
$ hoc1 yydebug: state 0, reducing by rule 1 (list :) yydebug: after reduction, shifting from state 0 to state 1 2.5+3.5+1Introducimos la expresión
2.5+3.5+1. Antes que incluso ocurra la entrada, el algoritmo LR reduce por la regla
yydebug: state 1, reading 257 (NUMBER)
yydebug: state 1, shifting to state 2
yydebug: state 2, reducing by rule 4 (expr : NUMBER)
yydebug: after reduction, shifting from state 1 to state 4
yydebug: state 4, reading 43 ('+')
yydebug: state 4, shifting to state 5
yydebug: state 5, reading 257 (NUMBER)
yydebug: state 5, shifting to state 2
yydebug: state 2, reducing by rule 4 (expr : NUMBER)
yydebug: after reduction, shifting from state 5 to state 6
yydebug: state 6, reducing by rule 5 (expr : expr '+' expr)
Observe como la declaración de la asociatividad a izquierdas %left '+' se traduce en la reducción por la regla 5.
yydebug: after reduction, shifting from state 1 to state 4
yydebug: state 4, reading 43 ('+')
yydebug: state 4, shifting to state 5
yydebug: state 5, reading 257 (NUMBER)
yydebug: state 5, shifting to state 2
yydebug: state 2, reducing by rule 4 (expr : NUMBER)
yydebug: after reduction, shifting from state 5 to state 6
yydebug: state 6, reducing by rule 5 (expr : expr '+' expr)
yydebug: after reduction, shifting from state 1 to state 4
yydebug: state 4, reading 10 ('\n')
yydebug: state 4, reducing by rule 3 (list : list expr)
7
La reducción por la regla
printf("%.8g\n",$2); asociado con la regla y la salida
del valor 7 que constituye el atributo de yydebug: after reduction, shifting from state 0 to state 1 yydebug: state 1, shifting to state 3 yydebug: state 3, reducing by rule 2 (list : list '\n') yydebug: after reduction, shifting from state 0 to state 1 yydebug: state 1, reading 0 (end-of-file) $En Unix la combinación de teclas CTRL-D nos permite generar el final de fichero.

