compiladores: análisis léxico - laboratorio de sistemaslsub.org/comp/slides/s02.lex.pdf · un...
TRANSCRIPT
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 1 of 90http://127.0.0.1:3999/s02.lex.slide#1
Compiladores: Análisis léxicoFrancisco J BallesterosLSUB, URJC
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 2 of 90http://127.0.0.1:3999/s02.lex.slide#1
Analizador léxico
Identificar tokens en la cadena de entrada
procesar los ficheros de entrada
generar la entrada para el parser
Ignorar comentarios
Mantener la idea de fichero-número de línea para mensajes de error
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 3 of 90http://127.0.0.1:3999/s02.lex.slide#1
Analizador léxico
Al definir el lenguaje
tendremos que definir una gramática para el mismo
Los elementos básicos de la gramática son los tokens
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 4 of 90http://127.0.0.1:3999/s02.lex.slide#1
Analizador léxico
Token
palabras reservadas
identificadores
números
signos de puntuación
El conjunto de tokens depende del lenguaje en cuestión
Usaremos palabras reservadas en el análisis sintáctico
Normalmente ignoramos el espacio en blanco
El valor de un token o lexema es el string para el mismo
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 5 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Para C, por ejemplo
LPAREN (RPAREN )IF ifIDENT mainSCOL ;PLUS +PLUSEQ +=...
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 6 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Para printf
FMT %DECARG dSTRARG sPCENT %%CHARS ...
Por ejemplo
%d, %%d
Nos da
FMT DECARG CHARS(", ") PCENT CHARS("d")
Entre paréntesis van los lexemas
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 7 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Otro ejemplo
x*2+3
Nos podría dar
VAR("x") MULT NUM(2) ADD NUM(3)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 8 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Otro ejemplo
pi*2+3
Nos podría dar
PI MULT NUM(2) ADD NUM(3)
Esta vez pi no es una variable, está reservado.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 9 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Otro ejemplo, expresiones regulares:
[ab]+.*\.c$
Podríamos tener los tokens
LBRA CHR('a') CHR('b') RBRA PLUS DOT STAR CHR('.') CHR('c') ETEXT
O tal vez
LBRA STR("ab") RBRA PLUS DOT STAR STR(".c") ETEXT
O tal vez
SET("ab") PLUS DOT STAR CHR('.') CHR('c') ETEXT
Todo depende de cómo hagamos el lenguaje
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 10 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Un token tiene
identificador único (ID, LBRA, RBRA, ...)
lexema o valor (3.5, main, ...)
Muchas veces fichero y número de línea (para errores)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 11 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens
Podemos meter la pata al definirlos
Por ej, en C++
Vector<Number>cin >> xVector<Vector<Number>>
Es el último?
>>
o?
> >
C++ no lo sabe y por eso no compila
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 12 of 90http://127.0.0.1:3999/s02.lex.slide#1
Una calculadora
Expresiones sencillas y no ambiguas tales como...
# esto es un comentario3 + 4( 5 * 3 ) + 434 / 5 / 72 * piabs ( 2 * pi )
Por ahora sólo pi y abs como predefinidos.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 13 of 90http://127.0.0.1:3999/s02.lex.slide#1
Una calculadora
Ya hay dudas:
-32 * -32 - 3
No hay cambio de signo.
- 2*1 // no válido.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 14 of 90http://127.0.0.1:3999/s02.lex.slide#1
Una calculadora: Tokens
NUMLPARENRPARENADDSUBMULDIVPIABS
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 15 of 90http://127.0.0.1:3999/s02.lex.slide#1
Una calculadora: Tokens
Valor de los tokens:
NUM -> valor como float con signo
Y el resto nada
LPARENRPARENADDSUBMULDIVPIABS
El comentario lo eliminamos y no es un token
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 16 of 90http://127.0.0.1:3999/s02.lex.slide#1
Ejemplo:
# esto es un comentario3 + 4( 5 * 3 ) + 434 / 5 / 72 * piabs ( 2 * pi )
nos da
NUM(3) ADD NUM(4)LPAREN NUM(5) MUL NUM(3) RPAREN ADD NUM(43)NUM(4) DIV NUM(5) DIV NUM(7)NUM(2) MUL PIABS LPAREN NUM(2) MUL PI RPAREN
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 17 of 90http://127.0.0.1:3999/s02.lex.slide#1
Un trozo de un lenguaje
Sentencias sencillas
{ print x; print y; print z; }x = "texto";if x == "texto" { ... }for x in "a" "b" "c" { print x; }
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 18 of 90http://127.0.0.1:3999/s02.lex.slide#1
Un trozo de un lenguaje
{ print x; print y; print z; }x = "texto";if x == "texto" { ... }for x in "a" "b" "c" { print x; }
Tokens:
LBRARBRASCOLEQEQEQPRINTFORIFNAMESTR
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 19 of 90http://127.0.0.1:3999/s02.lex.slide#1
Un trozo de un lenguaje
Valores de los tokens:
NAME -> xSTR -> "texto"
El resto ninguno
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 20 of 90http://127.0.0.1:3999/s02.lex.slide#1
Expresiones regulares
Sólo expresiones sencillas
abca|b|c.[0-9]([0-9]|[a-z])*
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 21 of 90http://127.0.0.1:3999/s02.lex.slide#1
Expresiones regulares
abca|b|c.[0-9]([0-9]|[a-z])*
Tokens
CHRORANYRANGELPARENRPARENSTAR
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 22 of 90http://127.0.0.1:3999/s02.lex.slide#1
Expresiones regulares
Valor de los tokens
CHR -> aRANGE -> 0-9
Y el resto ninguno
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 23 of 90http://127.0.0.1:3999/s02.lex.slide#1
Construcción de un scanner
Tenemos que pasar de texto a tokens
leyendo de izquierda a derecha
normalmente se permite mirar un char adelante
cada token corresponde a un string
hay que ver hasta dónde llega cada uno
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 24 of 90http://127.0.0.1:3999/s02.lex.slide#1
Construcción de un scanner
Podríamos describir cada token con una expresión regular
teniendo cuidado de evitar ambigüedad
probar en cada punto de la entrada cada expresión
devolver el token que encaja con la expresión
En esto se basa lex(1), pero es más fácil.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 25 of 90http://127.0.0.1:3999/s02.lex.slide#1
Lenguajes y alfabetos
Un lenguaje es un conjunto de strings (los válidos en el lenguaje)
Los strings son secuencias de símbolos de un alfabeto
No todos los strings pertenecen al lenguaje
A = {símbolos válidos en el lenguaje}
L(A) = {strings de A válidos}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 26 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens, lenguajes y alfabetos
Para tener un scanner podemos definir un lenguaje para los lexemas
lexema: "valor" de los tokens
Por ejemplo
NUM -?[0-9]+(\.[0-9]+) // 3 -4 -2.3LPAREN \( // (RPAREN \) // )ADD \+ // +SUB - // -MUL \* // *DIV / // /PI pi // piABS abs // abs
Nos da
(-?[0-9]+(\.[0-9]+))|\(|\)|\+|-|\*|/|pi|abs
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 27 of 90http://127.0.0.1:3999/s02.lex.slide#1
Tokens, lenguajes y alfabetos
En este lenguaje podemos reconocer las cadenas
sin depender del contexto en que están
empleando expresiones regulares
Es un lenguaje regular
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 28 of 90http://127.0.0.1:3999/s02.lex.slide#1
Lenguajes y autómatas
Un atómata finito es una máquina que acepta cadenas
Un lenguaje regular es reconocible por un atómata finito
Un lenguaje regular es describible con una expresión regular
Una expresión regular es implementable con un autómata finito
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 29 of 90http://127.0.0.1:3999/s02.lex.slide#1
Expresión regular
Definida recursivamente
Siendo x un char y a y b expresiones regulares:
L(x) = { x }, siendo x cualquier char salvo \, (, ), ., |, *, ?
L(\x) = { x }
L((a)) = L(a)
L(.) = { cualquier char }
L(ab) = { la de L(a) concatenado con lb de L(b) }
L(a|b) = L(a) U L(b)
L(a*) = { "" } U L(a) U L(aa) U L(aaa) U ...
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 30 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata finito
En este lenguaje podemos reconocer las cadenas utilizando un autómata finito
Para reconocerlo:
partimos de un estado inicial
en cada carácter de la entrada transitamos a otro estado
algunos de los estados son finales
si terminamos y no hay estado final, tenemos un error
Un error es una cadena no reconocida
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 31 of 90http://127.0.0.1:3999/s02.lex.slide#1
Lenguaje para tokens de calculadora
Ejemplo, el lenguaje que describe los tokens de
# esto es un comentario3 + 4.4( 5 * 3 ) + -434 / 5 / 72 * piabs ( 2 * pi )
que son
NUMLPARENRPARENADDSUBMULDIVPIABS
puesto que ignoramos comentarios y espacio y en blanco!
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 32 of 90http://127.0.0.1:3999/s02.lex.slide#1
Lenguaje para tokens de calculadora
Podríamos describirlo como la expresión regular
LTC = (-?[0-9]+(\.[0-9]+))|\(|\)|\+|-|\*|/|pi|abs
que reconoce entre otros...
3+4.4(5*3)+-43abspi
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 33 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Para definir un atómata finito
partimos de un estado inicial
para cada estado y símbolo en la entrada transitamos a otro estado
indicamos qué estados son finales
Si no está definida una transición, no reconocemos ese caso. El automáta es:
alfabeto de entrada
conjunto de estados (con inicial y finales)
conjunto de transiciones
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 34 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Por ejemplo, para
abs
Podríamos definir
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 35 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
O lo que es lo mismo
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 36 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Y para pi|abs
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 37 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Por ejemplo, para nuestros números
4 54
Podríamos definir
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 38 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Y con decimales...
4 54 43.23
Podríamos definir
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 39 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Y con decimales y signo opcional...
4 54 43.23 -32 -2.3
Podríamos definir
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 40 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
Todo junto
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 41 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
¿Está todo?
No
Nos falta (, ), +, -, * y /
Y tenemos problemas
-3
- 3
Hay algo de ambigüedad.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 42 of 90http://127.0.0.1:3999/s02.lex.slide#1
Ambigüedad
-3
- 3
Podemos decidir entre signo y resta mirando si sigue un dígito o no.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 43 of 90http://127.0.0.1:3999/s02.lex.slide#1
Ambigüedad
En general podemos
Utilizar la cadena más larga que encaja
Utilizar la primera de las subexpresiones si hay varias
En nuestro ejemplo en realidad no hay ambigüedad: hay no determinismo
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 44 of 90http://127.0.0.1:3999/s02.lex.slide#1
No determinismo
Necitamos un atómata finito no determinista
Hay dos transiciones válidas para -
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 45 of 90http://127.0.0.1:3999/s02.lex.slide#1
Atómata finito no determinista
Podemos tener transiciones en la cadena vacía para el signo
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 46 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómata para LTC
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 47 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scan de nombres
En lugar de utilizar estados para reconocer todos los nombres
Podemos reconocer un nombre en general
Y buscar el nombre en una tabla para ver si está reservado
Esto se hace si hay muchas palabras reservadas (keywords)
O si son varias pero son largas
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 48 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas
Finitos Deterministas (AFD, o DFA)
una única transición por estado y entrada
Finitos no deterministas (AFND, o NFA)
varias transiciones posibles
transiciones con la cadena vacía (se puede transitar o no)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 49 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
x
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 50 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
re1 re2
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 51 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
re1 | re2
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 52 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
re1 ?
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 53 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
re1 *
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 54 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
c(a|b)*
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 55 of 90http://127.0.0.1:3999/s02.lex.slide#1
Autómatas para expresiones regulares
NFA para
c(a|b)*
Y podemos simplificarlo
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 56 of 90http://127.0.0.1:3999/s02.lex.slide#1
Construir un DFA desde un NFA
Es fácil pero tedioso:
los estados del DFA son los conjuntos de estados alcanzados en el NFA
empezar en el estado inicial del NFA
para cada posible transición NFA: transitar al estado del DFA para el cjto de estadosNFA alcanzado
si tenemos un estado final del NFA, el estado es final.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 57 of 90http://127.0.0.1:3999/s02.lex.slide#1
Implementar un DFA
Podemos utilizar una tabla
Columnas para los estados
Filas para la entradas
Nuevos estados como valores
La función de estado toma una entrada y devuelve el nuevo estado
Hasta que la entrada se acepta
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 58 of 90http://127.0.0.1:3999/s02.lex.slide#1
¿Dónde estábamos?
Queríamos un scanner para la calculadora
Y para eso hicimos un NFA para el lenguaje de sus tokens
(que a su vez son tokens de otro lenguaje!)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 59 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 60 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Podemos implementar directamente el autómata
Usando lex(1) y dándole las expresiones regulares
o mejor
escribiendo en Go el código para el autómata
si hay muchos nombres usaríamos una tabla.
va a quedar pequeño y rápido
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 61 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Primero la entrada...
var text = `3 + (4.3 * abs(-1 * pi))`
func main() { fmt.Printf("scanning %s\n", text) txt := NewStrText(text) for { r, err := txt.Get() if err != nil { fmt.Printf("got err %s\n", err) break } fmt.Printf("got %c\n", r) }} Run
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 62 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
¿Qué es la entrada para nosotros?
type Text interface { Get() (rune, error) Unget() error}
Utilizaremos Unget para look-ahead
De hecho, go tiene un interface (io.RuneScanner) definido para esto.
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 63 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Tokens
type TokId inttype Tok struct { Id TokId Num float64}
// token id valuesconst ( None TokId = iota Num Lparen Rparen Add Sub Mul Div Pi Abs)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 64 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lexer
type Lexer interface { // return next token Scan() (Tok, error) // Look ahead one token Peek() (Tok, error)}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 65 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex
type lex struct { in Text saved Tok}
func NewLex(t Text) Lexer { return &lex{in: t}}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 66 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex
func (l *lex) Peek() (Tok, error) { tok, err := l.Scan() l.saved = tok return tok, err}
func (l *lex) Scan() (Tok, error) { if l.saved.Id != None { x := l.saved l.saved = Tok{} return x, nil } if err := l.skipBlanks(); err != nil { return Tok{}, err } return l.nextTok()}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 67 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex
func (l *lex) skipBlanks() error { for { c, err := l.in.Get() if err != nil { return err } if c != ' ' && c != '\t' && c != '\n' { l.in.Unget() return nil } }}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 68 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch { case c == '+': return Tok{Id:Add}, nil case c == '-': return Tok{Id:Sub}, nil case c == '*': return Tok{Id:Mul}, nil case c == '/': return Tok{Id:Div}, nil case c >= '0' && c <= '9': l.in.Unget() return l.scanNum() case c == 'p': l.in.Unget() return l.scanPi() case c == 'a': l.in.Unget() return l.scanAbs() } return Tok{}, fmt.Errorf("wrong input at char %c", c)}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 69 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex, números
func (l *lex) scanNum() (Tok, error) { n, err := l.scanInt() if err != nil { return Tok{}, err } c, err := l.in.Get() if err != nil { return Tok{Id: Num, Num: n}, nil } if c != '.' { l.in.Unget() return Tok{Id: Num, Num: n}, nil } dec, err := l.scanDec() if err != nil { return Tok{}, err } return Tok{Id: Num, Num: n+dec}, nil}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 70 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex, números
func (l *lex) scanInt() (float64, error) { r := 0.0 some := false for { c, err := l.in.Get() if some && err == io.EOF { return r, nil } if err != nil { return r, err } if c <= '0' || c >= '9' { l.in.Unget() return r, nil } r *= 10 r += float64(int(c) - int('0')) some = true }}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 71 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Lex, números
func (l *lex) scanDec() (float64, error) { r := 0.0 d := 1.0 some := false for { c, err := l.in.Get() if some && err == io.EOF { return r, nil } if err != nil { return r, err } if c <= '0' || c >= '9' { l.in.Unget() return r, nil } n := int(c) - int('0') r += float64(n) / d d *= 10.0 some = true }}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 72 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora
Listo
Ojo a bug en SUB!
func main() { text := `3 + (41.32 * abs(-1 * pi))` fmt.Printf("scanning %s\n", text) txt := NewStrText(text) l := NewLex(txt) for { t, err := l.Scan() if err != nil { fmt.Printf("got err %s\n", err) break } fmt.Printf("got tok %s\tnum %v\n", t.Id, t.Num) }} Run
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 73 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora: fixed
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch { case c == '+': return Tok{Id:Add}, nil case c == '-': n, _ := l.in.Get() l.in.Unget() if n >= '0' && n <= '9' { t, err := l.scanNum() t.Num *= -1 return t, err } return Tok{Id:Sub}, nil case c == '*': return Tok{Id:Mul}, nil case c == '/': return Tok{Id:Div}, nil case c == '(': return Tok{Id:Lparen}, nil case c == ')': return Tok{Id:Rparen}, nil case c >= '0' && c <= '9': l.in.Unget() return l.scanNum() case c == 'p': l.in.Unget()
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 74 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para la calculadora: fixed
Y ahora
func main() { text := `3 - (41.32 * abs(-1 * pi))` fmt.Printf("scanning %s\n", text) txt := NewStrText(text) l := NewLex(txt) for { t, err := l.Scan() if err != nil { fmt.Printf("got err %s\n", err) break } fmt.Printf("got tok %s\tnum %v\n", t.Id, t.Num) }} Run
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 75 of 90http://127.0.0.1:3999/s02.lex.slide#1
Comentarios
func (l *lex) skipBlanks() error { for { c, err := l.in.Get() if err != nil { return err } if c == '#' { for c != '\n' { if c, err = l.in.Get(); err != nil { return err } } } if c != ' ' && c != '\t' && c != '\n' { l.in.Unget() return nil } }}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 76 of 90http://127.0.0.1:3999/s02.lex.slide#1
Comentarios
func main() { text := `# comentario3 - (41.32 * abs(-1 * pi))`
fmt.Printf("scanning %s\n", text) txt := NewStrText(text) l := NewLex(txt) for { t, err := l.Scan() if err != nil { fmt.Printf("got err %s\n", err) break } fmt.Printf("got tok %s\tnum %v\n", t.Id, t.Num) }} Run
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 77 of 90http://127.0.0.1:3999/s02.lex.slide#1
Comentarios
La parte delicada es reconocerlos
sin que sea ambiguo si es otro token.
a / b
vs
a // b
Se hace que el autómata se coma todo desde el token de principio de comentario hasta el de fin de comentario
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 78 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
# comentario{ print x; print y; print z; }x = "texto";if x == "texto" { ... }for x in "a" "b" "c" { print x; }
Esta vez mantendremos nombre de fichero y número de línea
Y guardaremos el lexema
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 79 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Tokens
type TokId inttype Tok struct { Id TokId Val string Ln int}
// token id valuesconst ( None TokId = iota Str Lbra Rbra Eq Cmp Id Scol Print If For In)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 80 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Nuevo lex
type lex struct { in Text saved Tok ln int val []rune}
func NewLex(t Text) Lexer { return &lex{in: t, ln: 1}}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 81 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
SkipBlanks cuenta líneas ahora
func (l *lex) skipBlanks() error { for { c, err := l.in.Get() if err != nil { return err } if c == '#' { for c != '\n' { if c, err = l.in.Get(); err != nil { return err } } if c == '\n' { l.ln++ } } if c != ' ' && c != '\t' && c != '\n' { l.in.Unget() return nil } if c == '\n' { l.ln++ } }}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 82 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Scan y peek como antes
func (l *lex) Peek() (Tok, error) { tok, err := l.Scan() l.saved = tok return tok, err}
func (l *lex) Scan() (Tok, error) { if l.saved.Id != None { x := l.saved l.saved = Tok{} return x, nil } if err := l.skipBlanks(); err != nil { return Tok{}, err } return l.nextTok()}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 83 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
NextTok es nuestro scanner utilizando got para acumular caracteres que nos gustan y tambien gotTok para terminar con el token actual
func (l *lex) got(r rune) { l.val = append(l.val, r)}
func (l *lex) gotTok(id TokId) Tok { t := Tok{ Id: id, Val: string(l.val), Ln: l.ln, } l.val = nil return t}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 84 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
La parte fácil
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch { case c == '{': l.got('{'); return l.gotTok(Lbra), nil case c == '}': l.got('}'); return l.gotTok(Rbra), nil case c == ';': l.got(';'); return l.gotTok(Scol), nil
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 85 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Los strings...
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch {
case c == '"': for { c, err := l.in.Get() if err != nil { return Tok{}, err } if c == '"' { return l.gotTok(Str), nil } l.got(c) } //str
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 86 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Asignación y comparación
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch {
case c == '=': l.got('=') n, _ := l.in.Get() if n == '=' { l.got('=') return l.gotTok(Cmp), nil } l.in.Unget() return l.gotTok(Eq), nil
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 87 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Identificadores y keywords
Vamos a usar una tabla de keywords
var keywords = map[string]TokId { "print": Print, "if": If, "for": For, "in": In,}
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 88 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Identificadores y keywords
func (l *lex) nextTok() (Tok, error) { c, err := l.in.Get() if err != nil { return Tok{}, err } switch {
case unicode.IsLetter(c): l.got(c) for { c, err := l.in.Get() if err != nil { return Tok{}, err } if !unicode.IsLetter(c) && !unicode.IsNumber(c) { l.in.Unget() t := l.gotTok(Id) if id, ok := keywords[t.Val]; ok { t.Id = id } return t, nil } l.got(c)
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 89 of 90http://127.0.0.1:3999/s02.lex.slide#1
Scanner para sentencias sencillas
Y listo:
var keywords = map[string]TokId { "print": Print, "if": If, "for": For, "in": In,}var text = `{ print x; print y; print z; } x = "texto";if x == "texto" { print xxx; }for x in "a" "b" "c" { print x; }`func main() {
fmt.Printf("scanning %s\n", text) txt := NewStrText(text) l := NewLex(txt) for { t, err := l.Scan() if err != nil { fmt.Printf("got err %s\n", err) break } fmt.Printf("ln %d tok %s\t '%v'\n", t.Ln, t.Id, t.Val) }} Run
1/25/16, 2:47 PMCompiladores: Análisis léxico - (c)2014 LSUB
Page 90 of 90http://127.0.0.1:3999/s02.lex.slide#1
Questions?
Francisco J BallesterosLSUB, URJChttp://lsub.org (http://lsub.org)