compiladores: análisis léxico - laboratorio de sistemaslsub.org/comp/slides/s02.lex.pdf · un...

Post on 29-Sep-2018

214 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

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)

top related