You are on page 1of 61

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 + . 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
...

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 + 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 ) .

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")
}
} Run

http://127.0.0.1:3999/s04.topdown.slide#1 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")
}
} Run

http://127.0.0.1:3999/s04.topdown.slide#1 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 { $$ = 3.14 }


| num { $$ = valor(num)}
| abs '(' EXPR ')' { $$ = abs($3)}
| '(' EXPR ')' { $$ = $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