• No se han encontrado resultados

Compiladores e Intérpretes APUNTES PROF. DR. FRANCISCO J. TORRES-ROJAS. Gabriel Vargas Rodríguez

N/A
N/A
Protected

Academic year: 2021

Share "Compiladores e Intérpretes APUNTES PROF. DR. FRANCISCO J. TORRES-ROJAS. Gabriel Vargas Rodríguez"

Copied!
61
0
0

Texto completo

(1)

PROF. DR. FRANCISCO J. TORRES-ROJAS Gabriel Vargas Rodríguez 2018103129

(2)

Estructura de un compilador (continuación) Estructuras de datos de un compilador

Análisis léxico

Conceptos generales Lenguajes formales

(3)

ESTRUCTURA DE UN

COMPILADOR

(4)

CONTINUACIÓN ESTRUCTURA

FUENTE TOKENS

SCANNER

PARSER

ANÁLISIS SEMÁNTICO GENERACIÓN IR REPRESENTACIÓN INTERMEDIA TABLA DE SÍMBOLOS ÁRBOL DECORADO ÁRBOL SINTÁCTICO

La clase pasada nos habíamos quedado en la generación del código intermedio

(5)

EJEMPLO DE CÓDIGO INTERMEDIO

A = B * C + D * E

* C B * E D = + A Pasos:

-

Comenzando por las hojas, se

agrupan los nodos por operación

-

Creamos “variables” temporales con nombres arbitrarios para agrupar

cada operación

-

Se van agrupando los nodos hasta llegar a la raíz del árbol

-

Recordatorio: la asignación también es una operación

(6)

EJEMPLO DE CÓDIGO INTERMEDIO

A = B * C + D * E

* C B * E D = + A

T1 = B * C

T2 = D * E

T3 = T1 + T2

T4 = A = T3

T1

T2

T3

T4

(7)

ENTRADA Y SALIDA DE GENERACIÓN I.R.

GENERACIÓN

I.R.

REPRESENTACIÓN INTERMEDIA TABLA DE SÍMBOLOS ÁRBOL DECORADO

(8)

SCANNER

PARSER

ANALIZADOR

SEMÁNTICO

OPTIMIZADOR DE

FUENTE

FRONT END

OPTIMIZADOR DE

CÓDIGO

GENERADOR DE

CÓDIGO

BACK END FUENTE OBJETO REPRESENTACIÓN INTERMEDIA Pasamos ahora al generador de código

(9)

Es parte del backend del compilador

Recibe código intermedio y lo convierte en código real

Se puede producir ensamblador (y luego ensamblar el producto) o lenguaje máquina Hay que conocer representación de datos (cantidad de bytes, restricciones de

colocación)

Hay que conocer y explotar las posibilidades de la arquitectura

En este punto no nos preocupamos mucho por generar código bueno y eficiente, solo ocupamos que el código funcione

(10)

EJEMPLO GENERACIÓN DE CÓDIGO

T1 = B * C

T2 = D * E

T3 = T1 + T2

T4 = A = T3

mov AX, C mul B mov T1, AX —————— mov AX, E mul D mov T2, Ax —————— mov AX, T2 add AX, T1 mov T3, AX —————— mov AX, T3 mov A, AX mov T4, AX

A = B * C + D * E

Recordatorio: en x86, la operación

mul espera que uno de los

operandos esté en el AX y deja el resultado de la operación en AX

Tip: se pueden tener plantillas de

código ensamblador pre hechas para cada operación

(11)

FUENTE TOKENS

SCANNER

PARSER

ANÁLISIS SEMÁNTICO GENERACIÓN IR GENERACIÓN CÓDIGO REPRESENTACIÓN INTERMEDIA TABLA DE SÍMBOLOS LENGUAJE MÁQUINA ÁRBOL DECORADO ÁRBOL SINTÁCTICO

(12)

SCANNER

PARSER

ANALIZADOR

SEMÁNTICO

OPTIMIZADOR DE

FUENTE

FRONT END

OPTIMIZADOR DE

CÓDIGO

GENERADOR DE

CÓDIGO

BACK END FUENTE OBJETO REPRESENTACIÓN INTERMEDIA Pasamos ahora al optimizador de código

(13)

Mejora el código producido por el generador

Busca instrucciones equivalentes pero más rápidas

Cambiar a modos de direccionamiento más apropiados (ej: es más rápido usar registros que memoria)

Quita código redundante

Elimina código innecesario

Se puede optimizar por espacio o por tiempo

Siempre se puede optimizar más (nunca se van a quedar sin trabajo los que hacen compiladores)

(14)

EJEMPLO OPTIMIZADOR

T1 = B * C

T2 = D * E

T3 = T1 + T2

T4 = A = T3

mov AX, C mul B mov T1, AX —————— mov AX, E mul D mov T2, Ax —————— mov AX, T2 add AX, T1 mov T3, AX —————— mov AX, T3 mov A, AX mov T4, AX

A = B * C + D * E

mov AX, C mul B mov DX, AX mov AX, D mul E add AX, DX mov A, AX Generación código Generación I.R. Optimización

(15)

SCANNER

PARSER

ANALIZADOR

SEMÁNTICO

OPTIMIZADOR DE

FUENTE

FRONT END

OPTIMIZADOR DE

CÓDIGO

GENERADOR DE

CÓDIGO

BACK END FUENTE OBJETO REPRESENTACIÓN INTERMEDIA

¡Listo! Pero… ¿en realidad es así?

(16)

FUENTE TOKENS

SCANNER

PARSER

ANÁLISIS SEMÁNTICO GENERACIÓN IR GENERACIÓN CÓDIGO REPRESENTACIÓN INTERMEDIA TABLA DE SÍMBOLOS LENGUAJE MÁQUINA ÁRBOL DECORADO ÁRBOL SINTÁCTICO LENGUAJE MÁQUINA MEJORADO OPTIMIZACIÓN

Esto es una abstracción útil para comprender qué

pasa, pero en realidad el compilador no es

(17)

ESTRUCTURAS DE DATOS

DE UN COMPILADOR

(18)

Estructura con dos campos (usualmente):

Código de token: número que indica a qué categoría de token pertenece

BEGIN

INTLITERAL RPAREN

Hilera específica dentro del fuente (llamado lexema) valor particular

nombre de la variable… Los regresa el scanner

TOKEN

(19)

Lo crea el parser

Luego, es decorado por el analizador semántico Árbol creado en memoria dinámica

En el compilador de micro, nunca se crea una

estructura “árbol”. Este está representado en las funciones entre ellas (system goal, program,

statement_list…)

Hay una variable que señala a la raíz

Los campos son llenados por el parser y el analizador semántico

Los campos podrían tener punteros a otras estructuras (ej: a la tabla de símbolos, tabla de constantes…)

A veces (como en el compilador de Micro), no existe

(20)

Contiene la información asociada a todos los identificadores (de variables, de funciones, de

constantes… cualquier cosa que tenga un nombre) Casi todas las partes del compilador interactúan

con la tabla de símbolos (scanner, parser, analizador semántico…)

Con los profilers, se ha determinado que es la

estructura de datos más usada por un compilador Dado que se usa tanto, es buena idea

implementarla con hash

Puede ser formada por varias tablas, no solo una

(21)

Contiene todas las constantes (numéricas e hileras)

Como contiene valores constantes, esta tabla no cambia Está en memoria

Sus campos pueden ser apuntados por la tabla de símbolos

Ayuda a reducir el tamaño final del código (tal vez no más rápido, pero sí más pequeño)

Una de las banderas típicas del gcc es para indicar si se quiere optimizar por tiempo o tamaño

(22)

Se puede representar de muchísimas formas: Arreglo de punteros a hileras

Archivo de texto

Lista enlazada de hileras…

El tipo de representación depende de lo que quiera hacer con él

El proceso de optimización puede necesitar reorganizar el código intermedio, entonces

necesita poder borrar, reorganizar, reemplazar… una lista enlazada es más apropiado que un archivo para hacer esto, por ejemplo

(23)

Muy utilizado por los compiladores

Vida de un archivo temporal: Crea archivo,

graba cosas, lo cierra, lo lee después, lo cierra y lo borra

Los archivos temporales viven solo por unos cuantos segundos

A veces el sistema operativo ayuda al

compilador, no llegando a guardar los archivos en disco

Muchos problemas complicados son fáciles de resolver con archivos temporales

ARCHIVOS TEMPORALES

Sistema operativo

Archivo Temporal

(24)
(25)
(26)

Léxico = vocabulario

Se le conoce como scanner

Toma el fuente y lo va descomponiendo en categorías léxicas mínimas: tokens

El scanner busca el símbolo más grande que puede reportar

Los tokens del lenguaje natural son las palabras

Es lo primero que se aprende cuando se estudia un idioma

(27)

EJEMPLO DE ANÁLISIS LÉXICO

void bubbleSort(int arr[], int n)

{

int i, j, tmp;

for (i = 0; i < n-1; i++)

for (j = 0; j < n-i-1; j++)

if (arr[j] > arr[j+1]) {

tmp = arr[j];

arr[j] = arr[i];

arr[i] = tmp;

}

}

Palabras reservadas IDs Operadores Constantes Puntuación

(28)

VERSIÓN ABSTRACTA: MENTIRA

Qué cuento más mentira

FUENTE TOKENS

(29)

Lo más usual en los compiladores

Todo el proceso dirigido por el parser

Invoca al scanner cada vez que necesita un token (parser es cliente del scanner)

Token getToken(void);

Invoca a las rutinas semánticas cuando corresponde

El trabajo del parser termina cuando se generó la representación intermedia o incluso el lenguaje máquina

(30)

TRADUCCIÓN DIRIGIDA POR SINTAXIS

El proceso no es tan lineal como

parecía en la versión abstracta

PARSER

SCANNER

SEMÁNTICAS

RUTINAS

Producto

GetToken()

(31)

Regresa el siguiente token

Un token representa a un conjunto de hileras equivalentes sintácticamente (diferentes léxica y semánticamente)

Podría ser:

Un solo elemento: LPAREN

Una cantidad pequeña de elementos: int, float, char… Conjunto potencialmente infinito: números, strings

GetToken()

PARSER

SCANNER

(32)

Estructura del compilador con dos campos: Tipo de token

LPAREN

INTLITERAL ID

Puntero al lexema (hilera particular) “12.34”

“foo”

NULL (a veces no se necesita especificar un lexema, como en el caso de LPAREN o COMMA, pues el lexema es el mismo siempre)

Un token representa una categoría léxica

TOKEN

Categoría

Puntero al lexema

(33)

Token: categoría léxica, nombre del conjunto

Lexema: hilera particular en el programa que corresponde al token reportado por el scanner

Un token contiene muchos lexemas

Un lexema puede corresponder solo a un token

La estructura Token tiene dos campos, el nombre de la categoría (token) y el lexema particular

(34)

El programa completo es una hilera en memoria

Scanner mantiene una posición actual conforme va leyendo Se salta “espacios” (blancos, salto de línea, tabs, ….)

Avanza hasta reconocer un token completo (símbolo más grande que pueda reportar) Regresa token

GetToken() EN EJECUCIÓN

f o o = 4 * 2 salta tab reconoce el símbolo más grande posible

termina cuando reconoce el fin del token

Guarda posición actual ID

Puntero a “foo”

(35)

Generalmente, no hay un token que represente espacio en blanco (hay excepciones como Python)

Hay que ignorarlos (en algún momento) Para Fortran, Backus propuso eliminarlos todos antes de compilar

(36)

DO: se usa para hacer ciclos sobre una serie de instrucciones

Altera una variable dado un valor

inicial, un valor final y un incremento, sumando el incremento al valor

inicial (en cada iteración) hasta llegar al valor final

EJEMPLO DO FORTRAN

Fortran: j = 0 DO 10 i = 1,10,1 j = j + i 10 CONTINUE C: int j = 0;

for (int i=1;i≤10;i++) {

j = j + i; }

(37)

El compilador elimina previamente los espacios en blanco

Debe avanzar hasta encontrar un token

No puede parar solo en DO, porque el los nombres de variables pueden contener números

Como no sabe si se trata de DO o de DO10i, el

scanner continúa hasta eliminar ambigüedad y toma DO10i

El scanner no se da cuenta que está lidiando con un DO hasta que encuentra la primera coma

Debe regresarse 6 tokens (perdió tiempo)

SCANNER Y DO DE FORTRAN

D O 1 0 i = 1 , 1 0 , 1

(38)

Los espacios no significan nada en el código, pero son buenos separadores: son

nuestros amigos

No es buena idea eliminar los espacios antes de

compilar

(39)

La teoría de scanners ya es muy conocida (estudiada principalmente en los 50)

Hay mucha teoría y experiencia acumulada respecto a análisis léxico

Casi todo se puede automatizar

lex, flex: generadores de scanners (yacc y bison generan parsers)

Generar un scanner es relativamente fácil

Siempre hay que agregar un poco de código a mano

(40)

La función principal del scanner es detectar tokens

Avanzar en la entrada hasta reconocer el token más grande posible

Para hacer esto, nos apoyamos en algunas de las siguientes técnicas:

lenguajes formales teoría de autómatas

autómatas determinísticos de estados finitos

(41)
(42)

Definición

Habilidad humana para adquirir y usar complejos sistemas de comunicación

El estudio de los lenguajes corresponde a la lingüística

Conjunto de sonidos o señales que manifiestan lo que se piensa o se siente: expresan algo

Tipos

Lenguajes naturales (español, inglés, alemán…)

Lenguajes de computadora (C, Java, Ensamblador…) Lenguaje matemático

Lenguajes formales (este es el que nos interesa ahorita)

(43)

Concepto primitivo (no lo definimos, sino que lo aceptamos así como es)

“Representación” de un concepto, idea o entidad, con un significado convencional

No hay nada intrínseco en el símbolo, sino que decidimos qué iban a significar

No necesariamente es un solo carácter (o es lo mismo que un carácter)

Denotados como las primeras letras del alfabeto en itálica (a, b, c)

(44)

Definición: conjunto finito y no vacío de símbolos Recordatorio: los conjuntos no tienen orden y no tienen elementos repetidos

Usualmente denotado como ∑ Ejemplos

∑ = {a, b, c, …, x, y, z}

∑ = {0,1,2,3,4,5,6,7,8,9} ∑ = {0, 1}

∑ = {A,T,C,G}

∑ = {Ala, Arg, Asn, …, Trp, Tyr, Val} -> aminoácidos

(45)

Secuencia de longitud arbitraria formada con símbolos tomados de un alfabeto ∑ Diferencia entre conjunto y secuencia:

la secuencia puede tener símbolos repetidos la secuencia tiene orden

Denotadas como w, x, y, z (generalmente)

También se les conoce como “palabras” o “frases”

(46)

Sea ∑ = {0,1} 1010101 1

000

1010100101111100101

EJEMPLOS HILERA SOBRE ∑

Sea ∑ = {A,T,C,G} ACATGA

GGGC

(47)

Sea w una hilera sobre ∑

|w| = longitud de la hilera w = cantidad de símbolos de w Ejemplos

|CACTAGACTACAG| = 13 |01| = 2

|GUAUAUAUAUAUAUA| = 15

La longitud puede ser 0 -> hilera vacía

Hilera vacía se representa como Ɛ (forma más tradicional) o λ | Ɛ | = 0

(48)

Sea w una hilera sobre ∑

Un prefijo de w es una hilera formada tomando (en el mismo orden) los primeros k símbolos (de la izquierda) de w

0 ≤ k ≤ |w|

si k < |w|, tenemos un prefijo propio

si k = |w|, tenemos un prefijo no propio

Como k puede ser 0, Ɛ es prefijo de cualquier hilera (prefijo vacío)

(49)

Sea ∑ = {A,T,C,G} y w = ACTCGCTAAGC una hilera sobre ∑ Los siguientes son prefijos de w

ACTCG A

ACTCGCTAAGC <- prefijo no propio Ɛ

(50)

Sea w una hilera sobre ∑

Un sufijo de w es una hilera formada tomando (en el mismo orden) los últimos k símbolos (de la derecha) de w

0 ≤ k ≤ |w|

si k < |w|, tenemos un sufijo propio

si k = |w|, tenemos un sufijo no propio

Como k puede ser 0, Ɛ es sufijo de cualquier hilera (sufijo vacío)

(51)

Sea ∑ = {A,T,C,G} y w = ACTCGCTAAGC una hilera sobre ∑ Los siguientes son sufijos de w

TAAGC C

ACTCGCTAAGC <- sufijo no propio Ɛ

(52)

Una subhilera de w es una hilera formada tomando (en el mismo orden) k símbolos consecutivos de w a partir de una posición j

0 ≤ k,j ≤ |w| 0 ≤ k + j ≤ |w|

Si k + j < |w| y j > 0, tenemos una subhilera propia

Como k puede ser 0, Ɛ es subhilera de cualquier hilera (subhilera vacía)

(53)

Sea ∑ = {A,T,C,G} y w = ACTCGCTAAGC una hilera sobre ∑ Las siguientes son subhileras de w

TGCG

GC <- también es un sufijo, pero es una subhilera no propia ACTCGCTAAGC <- subhilera no propia

Ɛ

(54)

Sean v y w dos hileras sobre ∑

Si v = a1a2a3a4…an y w = b1b2b3b4…bk entonces:

La concatenación de v y w es la hilera: a1a2a3a4…

anb1b2b3b4…bk

Esto se denota como vw

La concatenación consiste en escribir una hilera justo después de la otra

(55)

Sea ∑ = {01} Sean

x = 01 y = 10

z = 10101

EJEMPLOS CONCATENACIÓN DE HILERAS

Tenemos que xy = 0110

yz = 1010101

(56)

Asociativa

xyz = (xy)z = x(yz)

No es conmutativa (generalmente)

wv ≠ vw

Las longitudes se suman |vw| = |v| + |w|

Elemento neutro = Ɛ

vƐ = Ɛv = v

(57)

Sea v una hilera sobre ∑

La potencia n-esima de v es la hilera resultado de concatenar n copias de v Se denota como vn, con n = cantidad de veces que se concatena

Casos especiales

v1 = v

v0 = Ɛ <- porque no concatenamos v ni una sola vez

(58)

Sea v = ATCGA

v1 = ATCGA

v2 = ATCGAATCGA

v10 = ATCGAATCGAATCGAATCGAATCGAATCGAATCGAATCGAATCGAATCGA

v0 = Ɛ

(59)

Sea ∑ un alfabeto, entonces ∑k es el conjunto de todas las hileras sobre ∑ tales que tengan una longitud de k Sea ∑ = {0,1} ∑3 = {000, 001, 010, 011 100, 101, 110, 111} ∑2 = {00,01,10,11} ∑1 = {0,1} ∑0 = {Ɛ}

∑ ≠ ∑1 porque ∑ es un conjunto de símbolos y ∑1 es un conjunto de hileras

(60)

Sea ∑ un alfabeto, el conjunto ∑* se define recursivamente como:

1. 1. Ɛ ∈ ∑*

2. Si w ∈ ∑* y a ∈ ∑*, entonces wa ∈ ∑*

3. w ∈ ∑* solo si puede ser construida desde Ɛ usando el paso 2

repetidamente Equivalentemente

Σ* =

k i=0

Σ

i

CONJUNTO ∑*

(61)

Todas las posibles hileras que se pueden hacer con el alfabeto ∑

Es un conjunto infinito, pero ninguno de sus miembros (hileras) tiene longitud infinita

Las hileras pueden ser arbitrariamente largas, pero no infinitas

Referencias

Documento similar

Super Árbol Verde Limón : Prácticas del Lenguaje 1 / Analía Klinger ; Begoña Medina ; Daiana Zanik.. Práctica

Árbol completo es un árbol cuyos nodos corresponden a los nodos numerados (la numeración se realiza desde la raíz hacia las hojas y, en cada nivel, de izquierda a derecha) de 1 a

Adquirir los conocimientos necesarios para la generación solvente e independiente de código en lenguaje Kotlin y JavaScript con un plan formativo eminentemente práctico para

Porque antiguamente se había establecido que hubiese quienes públicamente interpretaran el derecho, a cuales se dio por el César el de derecho de responder, y se les

&#34;No pasó nada&#34;, dijo el jardinero, recogiendo la bolsa y lo que se había caído de ella y tirando todo dentro.. Lo miré y un grito estrangulado escapó de

 Durante la 2ª sesión, cada alumno dispondrá de una cartulina o papel de tamaño DIN A3 con la silueta de un gran árbol lleno de ramas (los profesores de 1er ciclo de Primaria

Para los detalles concretos, tanto sobre el manuscrito como sobre las fuentes del texto, remito a F erraces r odríguez , Arsenio, «El glosario Agrestia et siluestria

27 Para el análisis del valor que agrega la generación de energía en Chile, no se encontró información respecto del PIB en función de la generación o distribución