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