Compiladores: Yacc - (c)2014 LSUBlsub.org/comp/slides/s07.yacc.pdf · Compiladores: Yacc - (c)2014...

Post on 22-Aug-2020

2 views 0 download

Transcript of Compiladores: Yacc - (c)2014 LSUBlsub.org/comp/slides/s07.yacc.pdf · Compiladores: Yacc - (c)2014...

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)