Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 1 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Compiladores: YaccFrancisco J BallesterosLSUB, URJC
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 2 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Yacc
Es un compilador de compiladores
yet another compiler compiler
Genera parsers LALR para gramáticas adecuadas
Disponible en casi todos los sistemas
En C, en Limbo, en Go, etc.
Ver Yacc: A parser generator Stephen C. Johnson y Ravi Sethi
Ver tambien
Yacc: Yet Another Compiler-Compiler (http://dinosaur.compilertools.net/yacc/)
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 3 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Yacc en Go
Basta ejecutar:
term% go tool yaccusage: yacc [-o output] [-v parsetable] inputterm%
O, haciendo un script...
term% gaccusage: yacc [-o output] [-v parsetable] inputterm%
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 4 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Yacc
El fuente para yacc es un fichero xxx.y con formato
/* declaraciones para yacc */%{ ... declaraciones en Go ...%}... mas declaraciones para yacc ...%%expr : expr '+' expr | expr '-' expr
term : id | '(' expr ')'
... mas reglas en BNF ...
%%
... codigo en Go ...
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 5 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Yacc
...declaraciones...%%...reglas BNF de la gramática...%%...código para el compilador...
Las declaraciones definen tokens, etc.
Las que van entre %{ y %} son declaraciones en Go
La gramática son reglas en BNF (formateadas a gusto del autor)
El código se escribe en Go.
En el yacc para C se genera C y se usa C, no Go.
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 6 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Yacc
A partir del fich.y yacc genera un fichero fuente en Go
Como Go permite definir items después de usarlos, el orden da un poco igual.
Pero no ocurre así en C.
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 7 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Declaraciones
Normalmente incluyen declaraciones iniciales para el fuente...
/* Evaluador de expresiones simples */
%{
// +build ignore
package main
import ( "io" "os" "strconv" "fmt" "unicode" "bufio")
%}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 8 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Declaraciones
y definiciones de tokens, lexemas y atributos
/* tipo de datos para atributos de simbolos de la gramatica */%union { num float64 name string}
/* tokens */%token '('%token ')'%token '\n'%token <num> NUM PI /* su valor es float64 */%token <name> FN /* su valor es string */
/* tokens para operadores, de menor a mayor precedencia * y con asociatividad indicada (left, right, unary) */%left '+' '-'%left '*' '/'
%%
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 9 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Tokens
Un token es un int
Si definimos
%token '('
El (identificador del) token vale '('
Si definimos
%token NUM
Yacc crea una constante NUM para el id del token.
Si definimos
%token <num> NUM
Decimos que el lexema vale yySymVal.num, esto es, float64
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 10 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Tokens
%union { num float64 name string}
%token '('%token ')'%token '\n'%token <num> NUM PI%token <name> FN
¿Más claro ahora?
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 11 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Tokens
Cuando los tokens son operadores y queremos definir su precedencia podemos utilizar
%left '+'
para un operador binario asociado a la izquierda
%right ARROW
para un operador binario asociado a la derecha
%unary UMINUS
para un operador unario
Su precedencia es menor si se declaran antes
%left '+' '-'%left '*' '/'
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 12 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Atributos y símbolos
Cada símbolo tiene un valor definido en la unión
%union { num float64 name string}
Según la declaración de token o la declaración de tipo para el no-terminal que hagamos:
%type <num> expr call
indica que los no-terminales expr y call son yySymVal.num
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 13 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Símbolo inicial
La definición
%start prog
indica que prog es el símbolo inicial de la gramática
Si no la indicamos, es el primero en la gramática
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 14 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Gramática
La gramática se define en BNF.
Para
A ::= B | C | <empty>
podemos escribir
A : B | C | /* empty */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 15 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Ejemplo de gramática
lines : line | lines line ;
line : expr '\n' | '\n' ;
expr : expr '+' expr | expr '-' expr | expr '*' expr | expr '/' expr | '(' expr ')' | NUM | PI | FN '(' expr ')' ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 16 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Ejemplo de gramática
Mejor que no sea recursiva por la derecha!
lines : line | lines line ;
line : expr '\n' | '\n' ;
La entrada tiene en este caso líneas con una expresión por línea (o líneas vacías).
Nuestro lex tendrá que darnos el token '\n' en este ejemplo
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 17 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Ejemplo de gramática
expr : expr '+' expr | expr '-' expr | expr '*' expr | expr '/' expr | '(' expr ')' | NUM | PI | FN '(' expr ')' ;
La gramática para expresiones es ambigua, pero las declaraciones de precedencia lo resuelven.
%left '+' '-'%left '*' '/'
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 18 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Acciones
Para, por ejemplo, pasar de notación infija a postfija:
%%lines : line | lines line ;
line : expr '\n' { fmt.Printf("\n") } | '\n' ;
expr : expr '+' expr { fmt.Printf(" +") } | expr '-' expr { fmt.Printf(" -") } | expr '*' expr { fmt.Printf(" *") } | expr '/' expr { fmt.Printf(" /") } | '(' expr ')' { } | NUM { fmt.Printf(" %v", $1) } | PI { fmt.Printf(" pi") } | FN '(' expr ')' { fmt.Printf(" %s\n", $1) } ;%%
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 19 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Acciones
Y con esta gramática:
term% gacc -p Expr expr.yterm% go run y.go3 + 4 * pi 3 4 pi * +2 2
La opción -p Expr hace que se use ExprSymVal en lugar de yySymVal
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 20 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Código
Tras el último %% escribimos normalmente el programa principal y a menudo el código del scanner.
Por ejemplo, este es el que hemos utilizado en el traductor a postfija:
func main() { debuglex = false txt := &bufsrc{in: bufio.NewReader(os.Stdin)} l := NewLex(txt, "stdin")
ExprParse(l) os.Exit(nerrors);}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 21 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Código
La llamada
yyParse(l)
ejecuta el parser
El argumento ha de implementar
type yyLex interface { Lex(lval *yySymType) int Error(e string)}
y es el scanner.
Con el flag -p Expr usamos ExprParse, ExprLex y ExprSymType
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 22 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func main() { debuglex = false txt := &bufsrc{in: bufio.NewReader(os.Stdin)} l := NewLex(txt, "stdin")
ExprParse(l) os.Exit(nerrors);}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 23 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
type Text interface { Get() (rune, error) Unget() error}
type bufsrc struct { in io.RuneScanner}
func (s *bufsrc) Get() (rune, error) { r, _, err := s.in.ReadRune() return r, err}
func (s *bufsrc) Unget() error { return s.in.UnreadRune()}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 24 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
type builtin struct { tok int num float64}var builtins = map[string]builtin{ "pi": builtin{tok: PI, num: 3.1415926}, "abs": builtin{tok: FN},}
var file stringvar line intvar debuglex boolvar nerrors int
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 25 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
type ExprLex interface { Lex(lval *ExprSymType) int Error(e string)}
type lex struct { in Text val []rune}
func NewLex(t Text, fname string) *lex { file = fname line = 1 return &lex{in: t}}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 26 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func (l *lex) got(r rune) { l.val = append(l.val, r)}
func (l *lex) getval() string { return string(l.val)}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 27 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func (l *lex) skipBlanks() error { for { c, err := l.in.Get() if err != nil { return err } if c == '#' { for c != '\n' { if c, err = l.in.Get(); err != nil { return err } } if c == '\n' { line++ } } if c == '\n' { line++ } if c != ' ' && c != '\t' { l.in.Unget() return nil } }}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 28 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func Errorf(s string, v ...interface{}) { fmt.Printf("%s:%d: ", file, line) fmt.Printf(s, v...) fmt.Printf("\n")
nerrors++ if nerrors > 5 { fmt.Printf("too many errors\n") os.Exit(1) }}
func (l *lex) Error(s string) { Errorf("%s near '%s'", s, l.getval())}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 29 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func (l *lex) Lex(lval *ExprSymType) (tid int) { if debuglex { defer func() { fmt.Printf("tok %s\n", tname(tid, lval)) }() } l.val = nil if err := l.skipBlanks(); err != nil { if err != io.EOF { Errorf("%s", err) } return 0 } c, err := l.in.Get() if err != nil { Errorf("%s", err) return 0 } l.got(c) switch {
...
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 30 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func (l *lex) Lex(lval *ExprSymType) (tid int) {
...
switch { case c == '\n' || c == '+' || c == '*' || c == '/' || c == '(' || c == ')': lval.name = l.getval() return int(c) case c == '-': n, _ := l.in.Get() l.in.Unget() if n < '0' || n > '9' { return '-' } fallthrough
...
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 31 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
case c >= '0' && c <= '9': for { c, err := l.in.Get() if err != nil { Errorf("%s", err) return 0 } if c != '-' && c != 'e' && c != '+' && c != '.' && !unicode.IsNumber(c) { l.in.Unget() break } l.got(c) } // id,kw lval.name = l.getval() n, err := strconv.ParseFloat(lval.name, 64) if err != nil { Errorf("%s", err) return 0 } lval.num = n return NUM
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 32 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
case unicode.IsLetter(c): for { c, err := l.in.Get() if err != nil { Errorf("%s", err) return 0 } if !unicode.IsLetter(c) && !unicode.IsNumber(c) { l.in.Unget() break } l.got(c) } // id,kw lval.name = l.getval() b, ok := builtins[lval.name] if !ok { Errorf("unknown name '%s'", lval.name) return 0 } lval.num = b.num return b.tok } Errorf("wrong input at char %c", c) return 0}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 33 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Lex para yacc
func main() { debuglex = false txt := &bufsrc{in: bufio.NewReader(os.Stdin)} l := NewLex(txt, "stdin")
ExprParse(l) os.Exit(nerrors);}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 34 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Normalmente se usa una global nerrors
Y la usaremos para abortar tras varios errores
El parser llama al método Error del scanner en errores sintácticos:
func (l *lex) Error(s string) { Errorf("%s near '%s'", s, l.getval())}
Y luego hace lo que puede por recuperarse...
En C llama a yyerror y yacc mantiene yynerrs.
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 35 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
La salida es
El fuente en Go para el parser
Un fichero y.output con información del parser
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 36 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
Para
%token num%left '+'%left '*'
%%
expr : expr '+' expr | expr '*' expr | num
%%
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 37 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
Tenemos un y.output como sigue
state 0 $accept: .expr $end
num shift 2 . error
expr goto 1
state 1 $accept: expr.$end expr: expr.+ expr expr: expr.* expr
$end accept + shift 3 * shift 4 . error
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 38 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
state 2 expr: num. (3)
. reduce 3 (src line 8)
state 3 expr: expr +.expr
num shift 2 . error
expr goto 5
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 39 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
state 4 expr: expr *.expr
num shift 2 . error
expr goto 6
state 5 expr: expr.+ expr expr: expr + expr. (1) expr: expr.* expr
* shift 4 . reduce 1 (src line 8)
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 40 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Salida de Yacc
state 6 expr: expr.+ expr expr: expr.* expr expr: expr * expr. (2)
. reduce 2 (src line 8)
6 terminals, 2 nonterminals4 grammar rules, 7/2000 states0 shift/reduce, 0 reduce/reduce conflicts reported51 working sets usedmemory: parser 2/300000 extra closures6 shift entries, 1 exceptions3 goto entries0 entries saved by goto defaultOptimizer space used: output 9/300009 table entries, 2 zeromaximum spread: 6, maximum offset: 6
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 41 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Conflictos
Shift/reduce
A veces los buscamos (se hará un shift)
Reduce/reduce
La hemos liado (normalmente)
Pero se usa la primera encontrada en el estado
Hay que ver y.output
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 42 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Atributos
Cada símbolo tiene un valor
definido por la declaración dada a Yacc
En una regla de la forma
A: B C { ....} X Y
Podemos utilizar en la acción
$$: Valor de A
$1: Valor de B
$2: Valor de C
$4: Valor de X
$5: Valor de Y
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 43 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Calcular expresiones
Si incluimos las definiciones
%union { num float64 name string}
%token '('%token ')'%token '\n'%token <num> NUM PI%token <name> FN
%left '+' '-'%left '*' '/'
%type <num> expr
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 44 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Calcular expresiones
lines : line | lines line ;
line : expr '\n' { fmt.Printf("%v\n", $1) } | '\n' ;
expr : expr '+' expr { $$ = $1 + $3 } | expr '-' expr { $$ = $1 - $3 } | expr '*' expr { $$ = $1 * $3 } | expr '/' expr { $$ = $1 / $3 } | '(' expr ')' { $$ = $2 } | NUM | PI /* by default: {$$ = $1} */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 45 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Calcular expresiones
term% gacc -p Expr expr2.yterm% go run y.go1 + 2 * pi7.2831852
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 46 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Atributos
Como definimos los valores a partir de los hijos
los denominamos atributos sintetizados
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 47 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Acciones y gramática
La introducción de acciones equivale a introducir nuevos símbolos
Y modifica la gramática
A: B {...} C
No es lo mismo que
A: B C {...}
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 48 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Acciones y gramática
A: B {...} C
Equivale a
A: B TEMP CTEMP: /* empty */ { ... }
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 49 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Acciones y gramática
A: B { $$ = 1 } C { $$ = $2 + 2 }
Hace que el valor de A sea 3.
A: B TEMP C { $$ = $2 + 2 }TEMP: /* empty */ { $$ = 1 }
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 50 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Debug
Podemos darle un valor a la global yyDebug
O ExprDebug si usamos el flag de yacc -p Expr
term% go run y.go3 + 253 +reduce 10 in: state-6stdin:2: syntax error near ''state-10saw
error recovery pops state 10error recovery pops state 3error recovery pops state 0exit status 1
Esto es para su valor 2
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 51 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Debug
ExprDebug = 3
nos da
term% go run y.go3 + 3lex U+E002 NUMreduce 10 in: state-6lex U+002B +lex U+E002 NUMreduce 10 in: state-6lex U+000A
reduce 5 in: state-15reduce 3 in: state-96reduce 1 in: state-2lex U+0000 tok-1
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 52 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Tomemos de nuevo
lines : line | lines line ;
line : expr '\n' { fmt.Printf("%v\n", $1) } | '\n' ;
expr : expr '+' expr { $$ = $1 + $3 } | expr '-' expr { $$ = $1 - $3 } | expr '*' expr { $$ = $1 * $3 } | expr '/' expr { $$ = $1 / $3 } | '(' expr ')' { $$ = $2 } | NUM | PI /* by default: {$$ = $1} */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 53 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Y con esta entrada...
term% gacc -p Expr expr3.yterm% go run y.go3 + 2 *stdin:1: syntax error near '\n'exit status 1term%
en la primera línea con error dejamos de compilar!
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 54 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Pero podemos modificar la gramática usando:
lines : line | lines line ;
line : expr '\n' { fmt.Printf("%v\n", $1) } | error \n | '\n' ;
Y ahora tenemos:
term% go run y.go3 + 2 *stdin:1: syntax error near '\n'2 + 24
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 55 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
El símbolo error
está predefinido en la gramática
equivale a un error sintáctico
yacc puede reducir el error a error si tiene problemas
Con la producción hemos
dado la línea por zanjada
y la gramática sigue evaluando las líneas siguientes
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 56 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Veámoslo con yyDebug=2 (o ExprDebug)
term% go run y.go3 +reduce 11 in: state-7stdin:1: syntax error near '\n'state-11saw
error recovery pops state 11error recovery pops state 3reduce 4 in: state-15reduce 1 in: state-2
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 57 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
stdin:1: syntax error near '\n'state-11saw
error recovery pops state 11
Y de y.output:
state 11 expr: expr +.expr
( shift 6 NUM shift 7 PI shift 8 . error
expr goto 17
No había expresión tras el +
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 58 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Y sigue con...
error recovery pops state 3
Y de y.output:
state 3 line: expr.\n expr: expr.+ expr expr: expr.- expr expr: expr.* expr expr: expr./ expr
\n shift 10 + shift 11 - shift 12 * shift 13 / shift 14 . error
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 59 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Y sigue con...
reduce 4 in: state-15
Y de y.output:
state 15 line: error \n. (4)
. reduce 4 (src line 48)
El error ha podido reducirse por la nueva producción, y el análisis sigue...
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 60 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Veamos con esta otra...
lines : line | lines line ;
line : expr '\n' { if nerrors == 0 { fmt.Printf("%v\n", $1) } } | error '\n' { Errorf("wrong expression") } | '\n' ;
expr : expr '+' expr { $$ = $1 + $3 } | expr '+' error { Errorf("operand expected after '+'") } | expr '-' expr { $$ = $1 - $3 } | expr '*' expr { $$ = $1 * $3 } | expr '/' expr { $$ = $1 / $3 } | '(' expr ')' { $$ = $2 } | NUM | PI /* by default: {$$ = $1} */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 61 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Ahora tenemos...
term% go run y.go3 +stdin:1: syntax error near '\n'stdin:1: operand expected after '+'
Con yacc es muy difícil dar buenos errores.
aumentamos la gramática con errores sintácticos
Y nos sincronizamos en algún signo de puntuación
Pero habrá errores en cascada
Al final hay que evitar producir salida si hay errores
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 62 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Cuándo acaba un error y empieza otro?
Otro error:
term% go run y.go3 + +stdin:1: syntax error near '+'stdin:1: operand expected after '+'stdin:2: operand expected after '+'
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 63 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Cuándo acaba un error y empieza otro?
Ahora con...
lines : line | lines line ;
line : expr '\n' { if nerrors == 0 { fmt.Printf("%v\n", $1) } } | error '\n' { Errorf("wrong expression"); Errflag = 0 } | '\n' ;
expr : expr '+' expr { $$ = $1 + $3 } | expr '+' error { Errorf("operand expected after '+'"); Errflag = 0 } | expr '-' expr { $$ = $1 - $3 } | expr '*' expr { $$ = $1 * $3 } | expr '/' expr { $$ = $1 / $3 } | '(' expr ')' { $$ = $2 } | NUM | PI /* by default: {$$ = $1} */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 64 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Cuándo acaba un error y empieza otro?
Errflag = 0
le dice a Yacc que considere el error por zanjado.
En lugar de recuperarse llamará de nuevo a yyError
En este caso es peor usarlo, otras veces no
term% go run y.go3 + +stdin:1: syntax error near '+'stdin:1: operand expected after '+'stdin:2: syntax error near '\n'stdin:2: operand expected after '+'too many errors
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 65 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
A veces no son sintácticos...
expr : expr '+' expr { $$ = $1 + $3 } | expr '-' expr { $$ = $1 - $3 } | expr '*' expr { $$ = $1 * $3 } | expr '/' expr { if $3 == 0.0 { Errorf("divide by 0") $$ = 0 } else { $$ = $1 / $3 } } | '(' expr ')' { $$ = $2 } | NUM | PI /* by default: {$$ = $1} */ ;
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 66 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Errores
Con lo que tenemos...
term% go run y.go3 / 0stdin:0: divide by 0
1/25/16, 2:49 PMCompiladores: Yacc - (c)2014 LSUB
Page 67 of 67http://127.0.0.1:3999/s07.yacc.slide#1
Questions?
Francisco J BallesterosLSUB, URJChttp://lsub.org (http://lsub.org)