Compiladores: Análisis sintáctico descendente recursivo - (c

Anuncio
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Compiladores: Análisis sintáctico
descendente recursivo
Francisco J Ballesteros
LSUB, URJC
http://127.0.0.1:3999/s04.topdown.slide#1
Page 1 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 2 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 3 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente recursivo
Tomemos esta gramática
EXPR ::= FACT '+' EXPR |
FACT
FACT ::= ELEM '*' FACT |
ELEM
ELEM ::= pi | num | abs '(' EXPR ')' | '(' EXPR ')'
http://127.0.0.1:3999/s04.topdown.slide#1
Page 4 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente recursivo
3 + 5 * ( pi + 1 )
http://127.0.0.1:3999/s04.topdown.slide#1
Page 5 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente recursivo
3 + 5 * ( pi + 1 )
Lo construimos top-down, left-right
Los terminales aparecen en el mismo orden
http://127.0.0.1:3999/s04.topdown.slide#1
Page 6 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 7 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 8 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 9 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 10 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente recursivo
3 + . 5 * ( pi + 1 )
y llegamos a
->EXPR
->FACT + EXPR
->ELEM + EXPR
->3 + . FACT
http://127.0.0.1:3999/s04.topdown.slide#1
Page 11 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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)
http://127.0.0.1:3999/s04.topdown.slide#1
Page 12 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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.
http://127.0.0.1:3999/s04.topdown.slide#1
Page 13 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 14 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 15 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 16 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Factorización
Podemos factorizar:
EXPR ::= FACT '+' EXPR
EXPR ::= FACT
Si hay duda de si 3 requiere la primera o la segunda
Aplazamos la decisión
EXPR ::= TERM ADD
ADD ::= + TERM ADD | <empty>
Ahora 3 es TERM siempre
http://127.0.0.1:3999/s04.topdown.slide#1
Page 17 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Factorización
Podemos factorizar:
IFTHEN ::= if EXPR then STMT else STMT |
if EXPR then STMT
en
IFTHEN ::= if EXPR then STMT ELSE
ELSE ::= 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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 18 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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 RESTO
RESTO ::= FACT RESTO | <empty>
http://127.0.0.1:3999/s04.topdown.slide#1
Page 19 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Gramática para parser descendente predictivo
Con recursión izquierda eliminada
Factorizada
Que sepamos predecir la producción dado el siguiente token
http://127.0.0.1:3999/s04.topdown.slide#1
Page 20 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente predictivo
Ya factorizada...
EXPR ::= TERM ADD
ADD ::= + TERM ADD | <empty>
TERM ::= FACT MUL
MUL ::= * 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.
http://127.0.0.1:3999/s04.topdown.slide#1
Page 21 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente predictivo
3 + 5 * ( pi + 1 )
nos da
-> TERM ADD
-> FACT MUL ADD
-> pi MUL ADD
backtrack
-> num MUL ADD
-> 3 MUL ADD
http://127.0.0.1:3999/s04.topdown.slide#1
Page 22 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 23 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente predictivo
3 + . 5 * ( pi + 1 )
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
...
+
+
+
+
+
+
+
+
+
+
+
+
+
.
.
.
5
5
5
5
5
5
5
5
5
5
TERM ADD
FACT MUL ADD
num MUL ADD
. MUL ADD
. * FACT MUL ADD
* . FACT MUL ADD
* . ( EXPR ) MUL ADD
* ( . EXPR ) MUL ADD
* ( . TERM ADD ) MUL ADD
* ( . FACT MUL ADD ) MUL ADD
* ( pi . MUL ADD ) MUL ADD
* ( pi <empty> . ADD ) MUL ADD
* ( pi . ADD ) MUL ADD
http://127.0.0.1:3999/s04.topdown.slide#1
Page 24 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Parser descendente predictivo
3 + 5 * ( pi . + 1 )
...
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
-> 3
+
+
+
+
+
+
+
+
+
+
+
+
5
5
5
5
5
5
5
5
5
5
5
5
*
*
*
*
*
*
*
*
*
*
*
*
(
(
(
(
(
(
(
(
(
(
(
(
pi
pi
pi
pi
pi
pi
pi
pi
pi
pi
pi
pi
.
+
+
+
+
+
+
+
+
+
+
+
ADD ) MUL ADD
. TERM ADD ) MUL ADD
. FACT MUL ADD ) MUL ADD
. 1 MUL ADD ) MUL ADD
1 . <empty> ADD ) MUL ADD
1 . ADD ) MUL ADD
1 . <empty> ) MUL ADD
1 ) . MUL ADD
1 ) . <empty> ADD
1 ) . ADD
1 ) . <empty>
1 ) .
http://127.0.0.1:3999/s04.topdown.slide#1
Page 25 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 26 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Implementación descendente recursiva
Empecemos por definir el parser
type Parse struct {
l Lexer
}
func NewParser(l Lexer) *Parse {
return &Parse{l: l}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 27 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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()
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 28 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 29 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 30 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Implementación descendente recursiva
TERM ::= FACT MUL
func (p *Parse) Term() error {
if err := p.Fact(); err != nil {
return err
}
return p.Mul()
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 31 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 32 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
// ...
http://127.0.0.1:3999/s04.topdown.slide#1
Page 33 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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")
}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 34 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 35 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 36 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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")
}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Run
Page 37 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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)
}
...
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 38 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 39 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 40 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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-}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 41 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 42 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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()
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 43 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 44 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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()
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 45 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 46 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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
// ...
http://127.0.0.1:3999/s04.topdown.slide#1
Page 47 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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")
}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 48 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Á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")
}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Run
Page 49 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 50 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
http://127.0.0.1:3999/s04.topdown.slide#1
Page 51 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Atributos
3 * 2 + abs(5)
http://127.0.0.1:3999/s04.topdown.slide#1
Page 52 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
num
abs '(' EXPR ')'
'(' EXPR ')'
{
{
{
{
$$
$$
$$
$$
=
=
=
=
3.14 }
valor(num)}
abs($3)}
$2}
Si ejecutamos estas acciones tras cada producción, hecho!
http://127.0.0.1:3999/s04.topdown.slide#1
Page 53 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
// ...
http://127.0.0.1:3999/s04.topdown.slide#1
Page 54 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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")
}
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 55 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 56 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 57 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 58 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
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
}
http://127.0.0.1:3999/s04.topdown.slide#1
Page 59 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Atributos en parser predictivo
Y ya lo tenemos...
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
Es nuestro primer compilador completo.
http://127.0.0.1:3999/s04.topdown.slide#1
Page 60 of 61
Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB
1/25/16, 2:48 PM
Questions?
Francisco J Ballesteros
LSUB, URJC
http://lsub.org (http://lsub.org)
http://127.0.0.1:3999/s04.topdown.slide#1
Page 61 of 61
Descargar