61
1/25/16, 2:48 PM Compiladores: Análisis sintáctico descendente recursivo - (c)2014 LSUB Page 1 of 61 http://127.0.0.1:3999/s04.topdown.slide#1 Compiladores: Análisis sintáctico descendente recursivo Francisco J Ballesteros LSUB, URJC

Compiladores: Análisis sintáctico descendente recursivo - …lsub.org/comp/slides/s04.topdown.pdf · Analizador sintáctico En adelante lo llamaremos parser Dados los tokens de

  • Upload
    dolien

  • View
    223

  • Download
    0

Embed Size (px)

Citation preview

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)