Compiladores: Análisis sintáctico descendente recursivo -...
Transcript of Compiladores: Análisis sintáctico descendente recursivo -...
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 1 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Compiladores: Análisis sintácticodescendente recursivoFrancisco J BallesterosLSUB, URJC
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 2 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Analizador sintáctico
En adelante lo llamaremos parser
Dados los tokens de la entrada
Construir el árbol sintáctico
Utilizando la gramática del lenguaje
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 3 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
Vamos a programar funciones
Siguiendo la gramática
Reconociendo desde la raiz del árbol hacia abajo
Posiblemente haciendo back-tracking si fallamos
Podríamos construir el árbol de parsing
O podríamos utilizar la pila de ejecución implícitamente
Dicha pila recorre el árbol en profundidad
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 4 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
Tomemos esta gramática
EXPR ::= FACT '+' EXPR | FACT
FACT ::= ELEM '*' FACT | ELEM
ELEM ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 5 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + 5 * ( pi + 1 )
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 6 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + 5 * ( pi + 1 )
Lo construimos top-down, left-right
Los terminales aparecen en el mismo orden
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 7 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + 5 * ( pi + 1 )
Empecemos a evaluar siguiendo el orden de la gramática
->EXPR->FACT + EXPR->ELEM '*' FACT->pi '*' FACT
Como encontramos un 3, backtrack
->EXPR->FACT + EXPR->ELEM '*' FACT->num '*' FACT
Ahora si
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 8 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
(el . indica por donde vamos)
3 . + 5 * ( pi + 1 )
->EXPR->FACT + EXPR->ELEM '*' FACT-> 3 . '*' FACT
Encontramos un + y no *: backtrack
->EXPR->FACT + EXPR->ELEM + EXPR->3 . + EXPR
Ahora si
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 9 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 . + 5 * ( pi + 1 )
->EXPR->FACT + EXPR->ELEM + EXPR->3 . + EXPR
Otro paso
3 + . 5 * ( pi + 1 )
->EXPR->FACT + EXPR->ELEM + EXPR->3 + . EXPR
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 10 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + . 5 * ( pi + 1 )
->EXPR->FACT + EXPR->ELEM + EXPR->3 + . EXPR
otro
->EXPR->FACT + EXPR->ELEM + EXPR->3 + . FACT + EXPR
que hará otro backtrack
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 11 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + . 5 * ( pi + 1 )
y llegamos a
->EXPR->FACT + EXPR->ELEM + EXPR->3 + . FACT
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 12 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
3 + 5 * ( pi + 1 ) .
y así llegamos al final a...
->EXPR->FACT + EXPR->ELEM + EXPR->3 + . ELEM * FACT->3 + 5 * FACT->3 + 5 * ELEM->3 + 5 * ( EXPR )->3 + 5 * ( FACT + EXPR )->3 + 5 * ( ELEM + EXPR )->3 + 5 * ( pi + EXPR )->3 + 5 * ( pi + FACT )->3 + 5 * ( pi + ELEM )->3 + 5 * ( pi + 1 )
Como se acaba la entrada, la aceptamos. (Si no hubiera alternativas, rechazamos la entrada)
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 13 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente recursivo
Problemas para la implementación
Una vez hemos "ejecutado con éxito" una producción
hemos consumido la entrada para esa producción
no podemos hacer backtrack
aunque podemos utilizar un token de look ahead.
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 14 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
Podemos escribir una gramática para un parser predictivo:
desdendente recursivo
que no necesite backtracking
La gramática es de tipo LL(1):
derivación left-most
entrada left-to-right
con 1 token de look-ahead
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 15 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
Gramática LL(1)
mirando el siguiente símbolo de la entrada
debería ser obvio que producción aplicar
sin error posible
Luego no nos valen gramáticas recursivas por la izquierda
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 16 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
El problema con
EXPR ::= FACT '+' EXPR | FACT
FACT ::= ELEM '*' FACT | ELEM
ELEM ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
Es si vemos 3 en
3 + 5 * ( pi + 1 ) .
No sabemos si usar FACT o FACT+EXPR No nos sirve esta gramática para este parser
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 17 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Factorización
Podemos factorizar:
EXPR ::= FACT '+' EXPREXPR ::= FACT
Si hay duda de si 3 requiere la primera o la segunda
Aplazamos la decisión
EXPR ::= TERM ADDADD ::= + TERM ADD | <empty>
Ahora 3 es TERM siempre
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 18 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Factorización
Podemos factorizar:
IFTHEN ::= if EXPR then STMT else STMT | if EXPR then STMT
en
IFTHEN ::= if EXPR then STMT ELSEELSE ::= else STMT | <empty>
Ahora if indica siempre la primera producción IFTHEN
Se busca el prefijo más largo y el resto a otra producción
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 19 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Eliminación de recursividad izquierda
Si la gramática es recursiva por la izquierda Un parser descendente se mete en un bucle infinito
MUL ::= MUL FACT | FACT
Podemos hacerla recursiva derecha cambiando estas producciones por
MUL ::= FACT RESTORESTO ::= FACT RESTO | <empty>
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 20 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Gramática para parser descendente predictivo
Con recursión izquierda eliminada
Factorizada
Que sepamos predecir la producción dado el siguiente token
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 21 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
Ya factorizada...
EXPR ::= TERM ADDADD ::= + TERM ADD | <empty>TERM ::= FACT MULMUL ::= * FACT MUL | <empty>FACT ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
Cuando veamos 3 en
3 + 5 * ( pi + 1 )
Seguro que 3 es TERM, FACT, num
Intentamos trabajar con un token cada vez, sin alternativas.
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 22 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
3 + 5 * ( pi + 1 )
nos da
-> TERM ADD-> FACT MUL ADD-> pi MUL ADD
backtrack
-> num MUL ADD-> 3 MUL ADD
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 23 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
3 . + 5 * ( pi + 1 )
nos da
-> 3 . MUL ADD-> 3 . * FACT MUL ADD
backtrack
-> 3 . <empty> ADD-> 3 . ADD-> 3 . + TERM ADD
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 24 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
3 + . 5 * ( pi + 1 )
-> 3 + . TERM ADD-> 3 + . FACT MUL ADD-> 3 + . num MUL ADD-> 3 + 5 . MUL ADD-> 3 + 5 . * FACT MUL ADD-> 3 + 5 * . FACT MUL ADD-> 3 + 5 * . ( EXPR ) MUL ADD-> 3 + 5 * ( . EXPR ) MUL ADD-> 3 + 5 * ( . TERM ADD ) MUL ADD-> 3 + 5 * ( . FACT MUL ADD ) MUL ADD-> 3 + 5 * ( pi . MUL ADD ) MUL ADD-> 3 + 5 * ( pi <empty> . ADD ) MUL ADD-> 3 + 5 * ( pi . ADD ) MUL ADD...
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 25 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
3 + 5 * ( pi . + 1 )
...-> 3 + 5 * ( pi . ADD ) MUL ADD-> 3 + 5 * ( pi + . TERM ADD ) MUL ADD-> 3 + 5 * ( pi + . FACT MUL ADD ) MUL ADD-> 3 + 5 * ( pi + . 1 MUL ADD ) MUL ADD-> 3 + 5 * ( pi + 1 . <empty> ADD ) MUL ADD-> 3 + 5 * ( pi + 1 . ADD ) MUL ADD-> 3 + 5 * ( pi + 1 . <empty> ) MUL ADD-> 3 + 5 * ( pi + 1 ) . MUL ADD-> 3 + 5 * ( pi + 1 ) . <empty> ADD-> 3 + 5 * ( pi + 1 ) . ADD-> 3 + 5 * ( pi + 1 ) . <empty>-> 3 + 5 * ( pi + 1 ) .
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 26 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Parser descendente predictivo
En este caso ha quedado un poco complicado
Pero es muy útil
si el lenguaje es muy fácil (ej. printf)
si es fácil escribir una gramática LL(1)
si con un look-ahead sabemos la producción a usar
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 27 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Empecemos por definir el parser
type Parse struct { l Lexer}
func NewParser(l Lexer) *Parse { return &Parse{l: l}}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 28 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Y ahora producción a producción, empezando por...
EXPR ::= TERM ADD
func (p *Parse) Expr() error { if err := p.Term(); err != nil { return err } return p.Add()}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 29 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
ADD ::= + TERM ADD | <empty>
Ahora es útil ver si tenemos el token que queremos o no.
func (p *Parse) match(id TokId) (Tok, error, bool) { t, err := p.l.Peek() if err == io.EOF { return Tok{}, nil, false } if err != nil { return Tok{}, err, false } if t.Id != id { return t, nil, false } t, err = p.l.Scan() return t, err, true // matches}
Devolvemos error sólo en errores. EOF y mismatch devuelven nil como error y false en match
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 30 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
ADD ::= + TERM ADD | <empty>
func (p *Parse) Add() error { if _, err, found := p.match(Add); err != nil || !found { return err } if err := p.Term(); err != nil { return err } err := p.Add() if err == io.EOF { err = nil } return err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 31 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
TERM ::= FACT MUL
func (p *Parse) Term() error { if err := p.Fact(); err != nil { return err } return p.Mul()}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 32 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
MUL ::= * FACT MUL | <empty>
func (p *Parse) Mul() error { if _, err, found := p.match(Mul); err != nil || !found { return err } if err := p.Fact(); err != nil { return err } err := p.Mul() if err == io.EOF { err = nil } return err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 33 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
FACT ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
func (p *Parse) Fact() error { tok, err := p.l.Peek() if err != nil { return err } switch tok.Id { case Pi: p.l.Scan() return nil case Num: p.l.Scan() return nil // ...
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 34 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
FACT ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
// ... case Abs: p.l.Scan() fallthrough case Lparen: p.l.Scan() if err := p.Expr(); err != nil { return err } if _, _, found := p.match(Rparen); !found { return fmt.Errorf("')' expected") } return nil default: return fmt.Errorf("syntax error") }}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 35 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Y estamos listos!
func (p *Parse) Parse() error { if err := p.Expr(); err != nil { return err } if _, err := p.l.Scan(); err != io.EOF { return fmt.Errorf("syntax error") } return nil}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 36 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Pero antes vamos a ayudarnos a depurar. Hacemos que el scanner nos imprima los tokens aceptados si activamos su depuración.
var debuglex bool
func (l *lex) Scan() (Tok, error) { t, err := l.scan() if debuglex { fmt.Printf("scan %s\n", t.Id) } return t, err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 37 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Ahora sí
func main() { text := `3 + (41.32 * abs(1 * pi))`
fmt.Printf("parsing %s\n", text) l := NewLex(NewStrText(text)) debuglex = true p := NewParser(l) if err := p.Parse(); err != nil { fmt.Printf("failed: %s\n", err) } else { fmt.Printf("success\n") }} Run
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 38 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva
Depuración
También podemos poner un print de depuración
a la entrada de funciones del parser
al final de funciones del parser (esta quizá no)
func parseMul(l Lexer) (err error) { if debugParse { fmt.Printf("MUL\n") defer fmt.Printf("MUL end %v\n", err) } ...}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 39 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Implementación descendente recursiva y recursión
Ahora es fácil entender que si la gramática es recursiva por la izquierda nunca terminamos.
MUL ::= MUL * FACT | <empty>
Al programar Parse.Mul llamaríamos a Parse.Mul directamente.
Hemos creado una rama infinita en el árbol de parsing
Pero sólo por evaluar top-down
Si fuese bottom-up, la recursión derecha sería el problema
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 40 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
Lo que hemos hecho es construir un árbol de parsing
Implícitamente, utilizando la recursión
Podríamos haberlo creado si queríamos
Vamos a imprimirlo ahora para que lo veamos
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 41 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
Como puede ser útil para depuración, incluimos esto:
type Parse struct { l Lexer Debug bool lvl int}
func (p *Parse) trz(tag string) { if p.Debug { s := strings.Repeat(" ", p.lvl) fmt.Printf("%s%s\n", s, tag) } p.lvl++}
func (p *Parse) untrz() { p.lvl--}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 42 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
Basta
incrementar Parse.lvl al entrar a una producción
decrementarlo al salir
imprimir los no terminales que hemos encontrado
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 43 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
func (p *Parse) Expr() (err error) { p.trz("expr") defer p.untrz() if err := p.Term(); err != nil { return err } return p.Add()}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 44 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
func (p *Parse) Add() (err error) { if _, err, found := p.match(Add); err != nil || !found { return err } p.trz("add") defer p.untrz() if err := p.Term(); err != nil { return err } err = p.Add() if err == io.EOF { err = nil } return err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 45 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
func (p *Parse) Term() (err error) { p.trz("term") defer p.untrz() if err := p.Fact(); err != nil { return err } return p.Mul()}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 46 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
func (p *Parse) Mul() (err error) { if _, err, found := p.match(Mul); err != nil || !found { return err } p.trz("mul") defer p.untrz() if err := p.Fact(); err != nil { return err } err = p.Mul() if err == io.EOF { err = nil } return err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 47 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
func (p *Parse) Fact() (err error) { tok, err := p.l.Peek() if err != nil { return err } switch tok.Id { case Pi, Num: p.l.Scan() p.trz(tok.Id.String()) defer p.untrz() return nil // ...
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 48 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
// ... case Abs: p.l.Scan() fallthrough case Lparen: p.trz(tok.Id.String()) defer p.untrz() p.l.Scan() if err := p.Expr(); err != nil { return err } if _, _, found := p.match(Rparen); !found { return fmt.Errorf("')' expected") } return nil default: p.trz(tok.Id.String()) defer p.untrz() return fmt.Errorf("syntax error") }}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 49 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Árbol de parsing de parser predictivo
Y listo...
func main() { text := `3 + 2 * abs(5)` // `3 + (41.32 * abs(1 * pi))`
fmt.Printf("parsing %s\n", text) l := NewLex(NewStrText(text)) p := NewParser(l) p.Debug = true if err := p.Parse(); err != nil { fmt.Printf("failed: %s\n", err) } else { fmt.Printf("success\n") }} Run
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 50 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Acciones
En realidad lo que acabamos de hacer es
modificar la gramática para incluir acciones
las acciones están mezcladas con el reconocimiento
cada una hace lo que queremos con el nodo del árbol
para eso hemos hecho el parser
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 51 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Evaluar expresiones con parser predictivo
Vamos ahora a asociarle un valor a cada nodo del árbol
cada nodo tendrá como atributo el valor de su subexpresión
los calcularemos al vuelo, sin almacenar el árbol
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 52 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos
3 * 2 + abs(5)
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 53 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos: Gramática extendida con acciones
Por ejemplo, usando $$ para el valor de la izqda y $1, $2, etc. para los valores de símbolos a la derecha...
EXPR ::= TERM ADD {$$ = $1 + $2}
ADD ::= + TERM ADD {$$ = $2 + $3} | <empty> {$$ = 0}
TERM ::= FACT MUL {if $2 == 0 then $$ = $1 else $$ = $1 * $2}
MUL ::= * FACT MUL {if $3 == 0 then $$ = $2 else $$ = $2 * $3} | <empty> {$$ = 0}
FACT ::= pi { $$ = 3.14 } | num { $$ = valor(num)} | abs '(' EXPR ')' { $$ = abs($3)} | '(' EXPR ')' { $$ = $2}
Si ejecutamos estas acciones tras cada producción, hecho!
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 54 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
Empezando por abajo,
func (p *Parse) Fact() (v float64, err error) { tok, err := p.l.Peek() if err != nil { return 0, err } switch tok.Id { case Pi: p.l.Scan() p.trz(tok.Id.String()) defer p.untrz() return 3.14159926, nil case Num: p.l.Scan() p.trz(tok.Id.String()) defer p.untrz() return tok.Num, nil // ...
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 55 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
// ... case Abs: p.l.Scan() fallthrough case Lparen: p.trz(tok.Id.String()) defer p.untrz() p.l.Scan() v, err := p.Expr() if err != nil { return 0, err } if _, _, found := p.match(Rparen); !found { return 0, fmt.Errorf("')' expected") } if tok.Id == Abs && v < 0 { v = -v } return v, nil default: p.trz(tok.Id.String()) defer p.untrz() return 0, fmt.Errorf("syntax error") }}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 56 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
func (p *Parse) Mul() (v float64, err error) { if _, err, found := p.match(Mul); err != nil || !found { return 1, err } p.trz("mul") defer p.untrz() v, err = p.Fact() if err != nil { return 0, err } nv, err := p.Mul() if err == nil { v *= nv } if err == io.EOF { err = nil } return v, err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 57 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
func (p *Parse) Term() (v float64, err error) { p.trz("term") defer p.untrz() v, err = p.Fact() if err != nil { return v, err } nv, err := p.Mul() if err == nil { v *= nv } return v, err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 58 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
func (p *Parse) Add() (v float64, err error) { if _, err, found := p.match(Add); err != nil || !found { return 0, err } p.trz("add") defer p.untrz() v, err = p.Term() if err != nil { return 0, err } nv, err := p.Add() if err == nil { v += nv } if err == io.EOF { err = nil } return v, err}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 59 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
func (p *Parse) Expr() (v float64, err error) { p.trz("expr") defer p.untrz() v, err = p.Term() if err != nil { return 0, err } nv, err := p.Add() if err == nil { v += nv } return v, err}
func (p *Parse) Parse() (v float64, err error) { v, err = p.Expr() if err != nil { return 0, err } if _, err := p.l.Scan(); err != io.EOF { return 0, fmt.Errorf("syntax error") } return v, nil}
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 60 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Atributos en parser predictivo
Y ya lo tenemos...
Es nuestro primer compilador completo.
func main() { text := `abs(-1) + (3 * 2) * 1 + 5 + pi`
fmt.Printf("evaluating %s\n", text) l := NewLex(NewStrText(text)) p := NewParser(l) //p.Debug = true v, err := p.Parse() if err != nil { fmt.Printf("failed: %s\n", err) } else { fmt.Printf("success: %v\n", v) }} Run
1/25/16, 2:48 PMCompiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
Page 61 of 61http://127.0.0.1:3999/s04.topdown.slide#1
Questions?
Francisco J BallesterosLSUB, URJChttp://lsub.org (http://lsub.org)