APUNTES Programacion de Sistemas (modificandolos)

Download Report

Transcript APUNTES Programacion de Sistemas (modificandolos)

Temario.
Unidad 1. Repaso de sintaxis.
1.1 Gramática de un lenguaje.
1.2 Descenso recursivo.
Unidad 2. Semántico.
2.1 Forma interna del programa fuente.
•
Notación polaca.
•
Cuádruplos.
2.2 Rutinas semánticas.
•
Expresiones aritméticas.
•
Estatutos condicionales.
Unidad 3. Generación de código.
3.1 Cuádruplos.
3.2 Notación polaca.
•
Expresiones aritméticas.
•
Expresiones condicionales.
3.3 Optimización de código.
Unidad 4. Administración de la memoria principal.
4.1 Paginación.
4.2 Segmentación.
4.3 Paginación-Segmentación.
Gramática.
Conjunto de reglas que determinan un un lenguaje.
Sintáxis.
Verifica que la secuencia de tokens sea válida para un lenguaje.
Tipos de gramática.
1. Irrestricta.
x  y en donde x tiene por lo menos un elemento no terminal.
2. Contexto sensitivo o sensitiva al contexto.
x y
x <= y (en cuanto al número de elementos de y)
y > 0 (no permite producciones con el elemento vacio)
3. Libre de contexto.
x y
x =1
y es un elemento no terminal.
y >= 0
Tipos de gramática libre de contexto.
•
Lineal izquierdo.
•
Lineal derecho
•
Recursiva izquierdo
•
Recursiva derecho
•
Recursiva central
Lineal izquierdo.
Es la derivación en donde tan solo el no terminal de más a la izquierda de cualquier
forma de frase se sustituye a cada paso.
<S>
<S>  <Y> x
<Y>  <Z> y
<Z>  <W> z
<Z>
y
<Z>  
<W>  w
x
<Y>
z
<W>
w
wzyx
Lineal derecha.
Es la derivación en donde tan solo el no terminal de más a la derecha de cualquier
forma de frase se sustituye a cada paso.
<S>  x <Y>
<S>
<Y>  y <Z>
x
<Y>
<Z>  z <W>
y
<Z>  
<Z>
z
<W>  w
<W>
xyzw
w
Recursiva izquierda.
Es la derivación en donde tan solo el no terminal de más a la izquierda se sustituye
a cada paso recursivamente es decir a si misma.
<S>
<S>  <S> a | <S> d
<S>  <S> b
<S>
<S>  c
a
a
<S>
<S>  <S>
<S>
c
b
cbaa
Recursivo derecho.
Es la derivación en donde tan solo el no terminal de más a la derecha se sustituye
a cada paso recursivamente es decir a si misma.
<X>  <S>
<X>
<S>  a <S> | d <S>
<S>
<S>  b <S>
a
<X>  c
<S>
a
<S>
<S>
b
aabc
c
Recursiva central.
Es la derivación en donde tan solo el no terminal del centro se sustituye a cada
paso recursivamente es decir a si misma.
<X>  <S>
<X>
<S>  a <S> b
<S>
<S>  c <S> d
a
<S>  e
c
acedb
<S>
b
<S>
d
e
Ejemplos:
Libre de contexto.
<S>  if <X> then <Y>
x y
<X>  a <op> b
|x|=1
<op>  > | <
| Y | >= 0
<Y>  x <opA> y
<opA>  =
Puede tener vacio
Contexto sensitivo.
<S>  <X> <Y>
x≤y
<X>  a <Z>
y ≠ ε no permite vacio
a<Z>  b <W>
<Y>  d
<W>  c
Irrestricta.
<X>  <A> <B>
Sin restricciones.
<A>  a
<B>  b
Derivación.
Consiste en sustituir los elementos no terminales por sus producciones.
Ejemplo:
<S>  IF <X> then <Y>
Se sustituye de izquierda a derecha
<S>  IF a <OP> b then <Y>
<S>  IF a<b then <Y>
<S>  IF a<b then x <OPA> y
<S>  IF a<b then x=y
El resultado es solo con terminales
Componentes de la gramática.
G={P, S, V, V’} o bien G={P, S, T, N}
En donde:
P=producciones.
S=producción inicial
V=los elementos terminales
V’=los elementos no terminales
De el ejemplo anterior........
P={S, X, Y, op, opA}
S={S}
V={a, if, b, then, x, y, =, >, >}
V’={S, X, Y, op, opA}
¿Cómo se representa una gramática gráficamente?
Diagrama de sintáxis.
Dirección
Terminales
No terminales
Patrones más comunes.
a) Secuencia de símbolos.
<S>
a
<S>  a <X> b
X
b
b) Alternativa de símbolos.
<TIPOS>
real
char
int
<TIPOS>  real | char | int
c) Repetición de símbolos.
<X>
a
<X>  a <X>
<X>  a
Ejemplos:
<PROGRAM>
PARAM
fun
id
proc
id
;
PARAM
TIPO
<PROGRAM>  fun id <X> ; <TIPO>
<X>  <PARAM> | 
<PROGRAM>  prac id <X>
<PARAM>
var
(
id
X
;
Y
<PARAM>  ( <X> <Y> : <TIPO> )
<X>  var | 
<Y>  id <Z>
<Z>  ; id <Z>
<Z>  
<TIPO>  char | int | real
Validar la siguientes cadenas:
(var id;id:real)
<PARAM>  (<X> <Y> : <TIPO>)
<PARAM>  (var <Y> : <TIPO>)
<PARAM>  (var id <Z> : <TIPO>)
<PARAM>  (var id ; id <Z> : <TIPO>)
<PARAM>  (var id ; id : <TIPO>)
<PARAM>  (var id ; id : real)
:
TIPO
)
fun id (id : char); real
<PROGRAM>  fun id <X> ; <TIPO>
<PROGRAM>  fun id <PARAM> ; <TIPO>
<PROGRAM>  fun id (<X> <Y> : <TIPO>) ; <TIPO>
<PROGRAM>  fun id (<Y> : <TIPO>) ; <TIPO>
<PROGRAM>  fun id ( id <Z> : <TIPO>) ; <TIPO>
<PROGRAM>  fun id ( id : <TIPO>) ; <TIPO>
<PROGRAM>  fun id ( id : char ) ; real
Análisis Sintáctico Descendente.
•Análisis Sintáctico por descenso recursivo.
•Análisis Sintáctico recursivo. (predictivo).
Análisis Sintáctico Ascendente.
•Análisis sintáctico LR-Simple.
•Análisis sintáctico LR-Canónico.
•Análisis sintáctico LALR (lookahead-LR)
Análisis sintáctico
Buffer de
entrada
a
+
b
$
Y
+
Programa Sintáctico
Salida de
producciones
Z
X
$
<X> --> <Y>+<Z>
<Z> --> b
<Y> --> a
PILA
Tabla de análisis sintáctico predictivo
(Matriz predictiva)
Un analizador sintáctico esta guiado por tablas, tiene un buffer de entrada, una pila,
y una tabla de análisis sintáctico y también una salida.
El buffer de entrada contiene la cadena que se va a analizar seguida de un símbolo
de pesos ($), un símbolo utilizado como delimitador derecho para indicar el fin de la
cadena.
La pila contiene una secuencia de símbolos gramaticales con un símbolo de pesos
en la parte inferior que indica la base de la pila, al principio la pila contiene el
símbolo inicial de la gramática encima del signo de pesos. La tabla de análisis
sintáctico es una matriz bidimensional de la forma M [A,a] en donde A es un no
terminal y donde “a” es el símbolo terminal o bien el signo de $, se controla el
analizador sintáctico mediante un programa que se comporta como sigue:
Sea X el símbolo superior de la pila y “a” el símbolo en curso de la entrada: estos
dos símbolos determinan la acción del analizador y tienen las siguientes acciones:
1)Si x = a = $ El string es válido.
2)Si x = a ≠ $
Se saca x de la pila
Se mueve el apuntador al siguiente símbolo o analizador en curso.
3)Si x es un no terminal el programa consulta la entrada de M [X,a] de la tabla de la
matriz de análisis sintáctico.
Esta entrada será o una producción de x de la gramática o una entrada de error. Si,
por ejemplo, M [X,a] es igual a x que produce uvw osea M [X,a] =
{X→<y>+<z>} el analizador sintáctico sustituye la x de la cima de la pila por
<z>+<y> quedando en la parte de encima de la “<y>” como salida, se sabe que el
analizador sintáctico solo imprime la producción utilizada; ahí se podría utilizar
cualquier otro código. Si M [X,a] = error ; el analizador sintáctico llama a una rutina
de recuperación de error e indica el tipo de error que a ocurrido.
Ventajas:
1.
Programación medianamente corta.
2.
Medianamente fácil de programar.
Desventajas.
1.
Un mantenimiento no sencillo.
Restricciones.
1.
Solo para gramáticas libres de contexto.
2.
Se tiene que eliminar la recursividad izquierda y factorizar si es necesario.
(No debe haber 2 elementos en una sola de las casillas de la matriz
predictiva)
Ejemplo:
1) <E>  <T> <E’>
2) <E’>  + <T> <E’>
3) <E’>  
4) <T>  <F> <T’>
5) <T’>  * <F> <T’>
6) <T’>  
7) <F>  id
8) <F>  (<E>)
Validar la siguiente cadena:
Id + id
A continuación se dará una corrida a la gramática para saber si la cadena es válida
PILA
ENTRADA
SALIDA
$<E>
Id+id$
1) <E>  <T><E’>
$<E’><T>
Id+id$
4) <T>  <F> <T’>
$<E’><T’><F>
Id+id$
7) <F>  id
$<E’><T’> id
Id + id $
6) <T’>  
2) <E’>  + <T><E’>
$<E’>ℇ
$<E’><T>+
+ id $
4) <T>  <F> <T’>
$<E’><T’><F>
Id $
7) <F>  id
$<E’><T’>id
Id $
$<E’><T’>
$
6) <T’>  
$<E’>ℇ
$
3) <E’>  
$
$
Cadena válida
Primeros y siguientes.
Se facilita la construcción de una analizador sintáctico predictivo con 2 funciones
asociadas a una gramática (G).
Estas funciones P y S permiten rellenar siempre que sea posible las entradas de
una tabla de análisis sintáctico predictivo para una gramática.
También se puede utilizar los conjuntos de componentes léxicos devueltos por la
función S como componentes léxicos de sincronización durante la recuperación de
errores.
Si α es una cadena de símbolos gramaticales se considera primeros de α como el
conjunto de terminales que inician las cadenas derivadas de α.
Si α → ε entonces el vacío también está en primeros de α.
Se define siguientes de A para el no terminal de A, como el conjunto de terminales
de A que pueden aparecer inmediatamente a la derecha de A en alguna forma de
frase, es decir, el conjunto de terminales de A tal que halla una derivación de la
forma S → αAaβ para algún α y β.
Primeros.
Primero (α) es el conjunto de símbolos terminales que inician cualquier derivación
de α.
Cálculo:
a)
Si X es un símbolo terminal, entonces primeros (α) = X
b)
Si X es un símbolo no terminal, entonces para cada producción del tipo
x → β1, β2........ βn
1)
Incluir primeros de (βi) el primeros de (X)
2)
De i=1 hasta n-1
Si ε está incluido en primeros de (βi) incluir en primeros de (βi+1).
3)
Tomando como base el punto anterior, si vacío está incluido en
primeros de (βi) hasta primeros de (βn) incluir vacio en primeros de (X).
Ejemplo:
<S>  <E> a
<E>  op
<E>  
P<S> = {P<E>} = {op, }
P<E> = {op, }
Siguientes.
X  YZ
1)
2)
Si X es la primera producción de la gramática se incluye $ en siguientes de X.
Los siguientes de Y son:
a) Si Z es un terminal se incluyen los primeros (Z) en siguientes (Y) a excepción del
vacío.
b)
Si Z es un no terminal se incluyen los primeros (Z) en siguientes de Y a
excepción del vacío.
3)
Los siguientes de Z son, si Z es el último término de la producción, se incluyen
los siguientes de esa producción en siguientes de Z.
No se incluye el vacío en vez de esto se incluyen los siguientes de la producción que
genera el vacío.
Ejemplo:
1) <E>  <T> <E’>
2) <E’>  + <T> <E’>
3) <E’>  
4) <T>  <F> <T’>
5) <T’>  * <F> <T’>
6) <T’>  
7) <F>  id
8) <F>  (<E>)
Primeros.
P(<E>) = {P(<T>)} = {id, ( }
P(<E’>) = {+, }
P(<T>) = {P(<F>)} = {id, ( }
P(<T’>) = { *, }
P(<F>) = {id, ( }
Siguientes.
S(< E>) = { $, )}
S(<E’>) = {S(<E>), S(<E’>) } = { $, ) }
S(<T>) = {P(<E’>)} = { +, S(<E’>)} = { +, $, ) }
S(<T’>) = {S(<T>), S(<T’>)} = { +, $, ) }
S(<F>) = {P(<T’>)} = { *, S(<T’>) } = { *, +, $, ) }
Construcción de la matriz predictiva.
1.
Para cada producción A   realizar lo siguiente:
a) Para cada símbolo terminal en primero de () agregar A   en M[A, ]
b) Si  esta contenido en primero (), entonces para cada símbolo terminal
en sig (A), agregar M   en [A,$]
2. Cada espacio de la matriz indefinido hace error.
A continuación se muestra un ejemplo de cómo construir la matriz predictiva, se utilizó
la gramática anterior para obtener los first y los follows.
V
$
id
(
)
+
*
<E>
error
1
1
error
error
error
<E’>
3
error
error
3
2
error
<T>
error
4
4
error
error
error
<T’>
6
error
error
6
6
5
<F>
error
7
8
error
error
error
V’
PILA
ENTRADA
SALIDA
(producciones)
$<E>
Id*id+id$
1
$<E’><T>
Id*id+id$
4
$<E’><T’><F>
Id*id+id$
7
$<E’><T’>id
Id *id+id$
5
$<E’><T’><F>*
*id+id$
7
$<E’><T’>id
id+id$
6
$<E’>
2
$<E’><T>+
+id$
4
$<E’><T’><F>
Id$
7
$<E’><T’>id
Id$
6
$<E’>
$
3
$
$
Cadena válida
Ejercicio:
Realizar primeros, siguientes, matriz predictiva y validar las siguientes cadenas:
bmdm, fd, fdm
1) <A>  <B> d <A>
2) <A>  m
3) <B>  <C> <D>
4) <C>  b <A>
5) <C>  
checar gramatica
6) <D>  f
7) <D>  g
Primeros.
P(<A>) = {P(<B>), m, } = {b, m, }
P(<B>) = {P(<C>)} = {b, }
P(<C>) = {b, }
P(<D>) = {f, g}
Siguientes.
S(<A>) = {S(<A>), S(<C>)} = {f, g, $}
S(<B>) = {d}
S(<C>) = {P(<D>)} = {f, g}
S(<D>) = {S(<B>)} = {d}
T
b
m
d
f
G
$
1
1
error
N
<A>
1
2
error
<B>
3
error
error
3
3
error
<C>
4
error
error
5
5
error
<D>
error
error
error
6
7
error
bmdm
PILA
ENTRADA
SALIDA
$<A>
bmdm$
1
$<A>d<B>
bmdm$
3
$<A>d<D><C>
bmdm$
4
$<A>d<D><A>b
bmdm$
2
$<A>d<D>m
mdm$
error
fd
PILA
ENTRADA
SALIDA
$<A>
fd$
1
$<A>d<B>
fd$
3
$<A>d<D><C>
fd$
5
$<A>d<D>
fd$
6
$<A>df
fd$
$ <A>
$
Cadena no válida
fdm
PILA
ENTRADA
SALIDA
$<A>
fdm$
1
$<A>d<B>
fdm$
3
$<A>d<D><C>
fdm$
5
$<A>d<D>
fdm$
6
$<A>df
m$
2
$m
m$
Cadena válida
1) <S>  if <COND> then <OPERACION> else <ASIG> end
2) <COND>  id <OR> id
3) <OR>  >
4) <OR>  <
5) <OPERACION>  id <OA> id
6) <OA>  +
7) <OA>  8) <OA>  *
9) <ASIG>  id <A> id
10) <A>  :=
Para la gramática anterior validar la siguiente cadena: if id < id
Primeros.
P(<S>) = { if }
P(<COND>) = { id }
P(<OR>) = { >, < }
P(<OPERACION>) = { id }
P(<OA>) = { +, -, * }
P(<ASIG>) = { id }
P(<A>) = { := }
Siguientes.
S(<S>) = { $ }
S(<COND>) = { then }
S(<OR>) = { id }
S(<OPERACION>) = { else }
S(<OA>) = { id }
S(<ASIG>) = { end }
S(<A>) = { id }
If
the
n
else
end
id
>
<
+
-
*
:=
$
1
1
error
error
error
error
error
error
error
error
error
error
error
2
error
error
error
error
2
error
error
error
error
error
error
error
3
error
error
error
error
error
3
4
error
error
error
error
error
4
error
error
error
error
5
error
error
error
error
error
error
error
5
error
error
error
error
error
error
error
6
7
8
error
error
6
error
error
error
error
9
error
error
error
error
error
error
error
7
error
error
error
error
error
error
error
error
error
error
10
error
PILA
ENTRADA
SALIDA
$<S>
If id < id then $
1
$end<ASIG>else<OPERACION>then<COND>if
id < id then $
2
$end<ASIG>else<OPERACION>then id<OR> id
< id then $
4
$end<ASIG>else<OPERACION>then id
$
error
Técnicas de análisis ascendentes para analizadores sintácticos.
•
LR- Simple
•
LR- Canónico
•
LALR
Constituyen un árbol de derivación para un string de entrada, iniciando por las
y avanzando a la raíz.
Ventajas.
1.
Se detectan errores tan pronto como se van obteniendo las entradas.
2.
No importan la recursividad de las gramáticas ni deben estar factorizadas.
Desventajas.
1.
Cuesta más trabajo implementar la técnica.
Método LR-Simple.
Entrada
0
$
$
Programa
Analizador
LR
SALIDA
PILA
TABLA
DE
ACCIONES
TABLA
DE SALTOS
En este método la pila almacena símbolos terminales y no terminales en el tope de
la pila siempre se debe encontrar un estado (que va a ser el estado actual del
analizador). Cada espacio resume la información contenida en la pila.
La combinación del símbolo de estado en el tope se la pila y el símbolo de estado
En el tope de la pila y el símbolo de entrada actual se utiliza para indexar la tabla
de acciones a la tabla de saltos, inicialmente la pila contiene el estado cero.
Agregar apuntes pag 90 libro de
fundamentos de compiladores
Tabla de acciones.
Es un arreglo bidimensional con un arreglo para cada posible estado y una
columna para cada símbolo terminal de la gramática y para el signo de pesos.
Cada casilla de la tabla contienen las siguientes acciones.
a)
ACC. Aceptación.
b)
ERR. Error.
c)
SN. Desplazamiento en introducción del estado no terminal de la pila.
d)
RN. LA reducción utilizando la producción no terminal.
Tabla de saltos.
Es un arreglo bidimensional con un renglón para cada posible estado y una
columna para cada símbolo no terminal de la gramática, cada casilla contiene
un estado.
TABLA DE ACCIONES
TERMINALES
TABLA DE SALTOS
NO TERMINALES
id
+
/
*
$
x
y
z
w
0
error
error
error
error
ACC
1
error
error
error
1
error
error
error
error
error
error
2
error
error
2
error
error
error
error
error
3
error
error
4
3
error
error
S1
error
error
error
5
error
error
4
R1
error
error
error
S2
error
error
error
error
5
R3
error
error
R2
error
error
error
error
error
etc
error
error
error
error
error
error
error
error
error
Ejemplo Método LR.
0) <E’> . <E>
1) <E> .<E>+<T>
2) <E> .<T>
3) <T> .<T>* <F>
4) <T> . <F>
5) <F> .id
6) <F> .(<E>)
Primeros.
P(<E’>) = {P(<E>) } = { id, ( }
P(<E>) = {P(<E>), P(<T>)} = { id, ( }
P(<T>) = {P(<T>), P(<F>)} = { id, ( }
P(<F>) = { id, ( }
Siguientes.
S(<E’>) = { $ }
S(<E>) = {S(<E’>), +, ) } = { $,+, ) }
S(<T>) = {S(<E>), *} = { $,+, ), * }
S(<F>) = { $,+, ), * }
0
<E’>  .<E>
<E>  .<E> + <T>
<E>
<E>
<E>  .<T>
<T>
<T>  .<T> * <F>
<T>
<T>  .<F>
<F>
id
<F>  .id
<F>  .(<E>)
(
1
1
2
2
3
S4
S5
1
<E’>  <E>.
<E>  <E>. + <T>
2
<E>  <T>.
<T>  <T>. * <F>
3
<T>  <F>.
4
<F>  id.
5
$
ACC
Sig E’
+
S6
+, $, )
R2
Sig E
*
S7
$, +, ), *
$, +, ), *
Sig F
<F>  (.<E>)
<E>
<E>  .<E> + <T>
<E>
<E>  .<T>
<T>
<T>  .<T> * <F>
<T>
<T>  .<F>
<F>
id
<F>  .id
<F>  .(<E>)
(
R4
R5
8
8
2
2
3
S4
S5
6
7
9
<T>  .<T> * <F>
T
9
<T>  .<F>
F
3
<F>  .id
id
<F>  .(<E>)
(
<T>  <T> * .<F>
<F>  .id
<F>  .(<E>)
8
T
<E>  <E> + .<T>
<F>  (<E>.)
<E> <E> .+ <T>
S4
S5
F
id
10
S4
(
S5
)
S11
+
S6
$, +, )
9
<E> <E> + <T>.
<T> <T> .* <F>
10
<T>  <T> * <F>.
Sig. E
*
R1
S7
$, +, ), *
R3
Sig. T
11
<F>  (<E>).
$, +, ), *
R6
Sig. F
TABLA DE ACCIONES
TERMINALES
TABLA DE SALTOS
NO TERMINALES
+
)
(
id
*
$
E
T
F
0
error
error
S5
S4
error
error
1
2
3
1
S6
error
error
error
error
ACC
error
error
error
2
R2
R2
error
error
S7
R2
error
error
error
3
R4
R4
error
error
R4
R4
error
error
error
4
R5
R5
error
error
R5
R5
error
error
error
5
error
error
S5
S4
error
error
8
2
3
6
error
error
S5
S4
error
error
error
9
3
7
error
error
S5
S4
error
error
error
error
10
8
S6
11
error
error
error
error
error
error
error
9
R1
R1
error
error
error
R1
error
error
error
10
R3
R3
error
error
R3
R3
error
error
error
11
R6
R6
error
error
R6
R6
error
error
error
Verificar si la siguiente cadena es válida: id + id$
PILA
ENTRADA
SALIDA
$0
id + id$
S4
$0id4
+ id$
R5
F  id
$0F3
+ id$
R4
T F
$0T2
+ id$
R2
E T
$0E1
+ id$
S6
desplaza
$0E1+6
Id$
S4
desplaza
$0E2+6id4
$
R5
F  id
$0E1+6F3
$
R4
T F
$0E1+6T9
$
R1
E E+T
$0E1
$
ACC
Eliminación de la recursividad por la izquierda.
A  Aα | B
Ejemplo:
1)
Aquellas que no contengan al elemento recursivo se agrega una
producción nueva (A’) al lado derecho.
A
2)

BA’
Aquellas producciones que si contengan al elemento recursivo se pasa el
elemento recursivo como la producción nueva al lado derecho y se
agrega una producción de la misma que produce ε.
A’  α A’
1) A  BA’
A’  ε
2) A’  αA’
3) A’  ε
Ejemplos:
Gramatica original.
Eliminación de la recursividad.
S  Aa | b
S  Aa | b
A  Ac | Sd | ε
A  bd A’
A ε
A’  cA’
A’  adA’
A’  ε
Gramatica original.
Eliminación de la recursividad.
1)
E E+T
1) E  T E’
2)
E T
2) E’  + T E’
3)
T T*F
3) E’  ε
4)
T F
4) T  F T’
5)
F  id
5) T’  * F T’
6)
F  (E)
6) T’  ε
7) F  id
El código intermedio forma un lenguaje de bajo nivel, sin llegar al nivel más
primitivo.
Tipos de código intermedio.
CONVERTIDA
•Notación polaca.
A:=B+C*D
•Triplos.
A:=B+C*D
•Cuádruplos.
•Código P.
A:=B+C*D
A:=B+C*D
ABCD*+:=
oper
op1
op2
*
C
D
+
B
T1
:=
A
R2
oper
op1
op2
res
*
C
D
T1
+
B
T1
T2
:=
A
T2
Cargar B
Cargar C
Cargar D
Multiplicar , Suma
almacena A
Triplos.
Este tipo de código utiliza instrucciones en un formato de 3 campos.
Oper
Op1
Op2
En donde:
Oper = Código de operación.
Op1 = Operando 1
Op2 = Operando 2
Ejemplo:
A+B
3
<S>
1
<OP1>
OP1
id
2
+
cte
-
TRIPLO
B
A
+
Pila de
operandos
Pila de
operadores
+
A
B
Acciones.
1
Insertar en la pila de operandos.
PUSH_pilaop(pos_act) = id ó cte
pos_act+1
2
Insertar en la pila de operadores.
PUSH_pilaoper(pos_act) = + ó –
pos_act+1
3
Genera triplo de la siguiente forma.
Op2 = tope pila_op
 sacar
Op1 = tope pila_op
 sacar
Oper = tope pila_oper  sacar
Comparación de los diversos modos expuestos.
En cuanto a la cantidad de memoria que requiere para su almacenamiento
podríamos ordenarlos de mayor a menor.
•
Notación polaca.
•
Código P.
•
Triplos.
•
Cuádruplos.
En cuanto a la velocidad de su ejecución de menor a mayor.
•
Cuádruplos.
•
Triplos y código P.
•
Notación polaca.
Si lo que se requiere es convertir el código intermedio a código objeto,
ordenado de menor a mayor grado de dificultad .
•
Cuádruplos.
•
Triplos.
•
Código P.
•
Notación polaca.
Las ventajas de los modelos que vamos a estudiar para generar el código
intermedio es que su implementación podrá ser modular.
Ejercicio.
A:=B-C+D
<S>
1
id
2
:=
<OP2>
4
OP2
3
<OP1>
1
OP1
id
+
cte
-
TEMP2
D
TEMP
C
-
B
A
Pila de
operandores
Oper
Op1
Op2
TEMP=
-
B
C
TEMP2 =
+
TEMP
D
TRIPLO =
:=
A
TEMP2
+
:=
Pila de
operandos
Acciones.
1
Insertar en pila_operandos.
PUSH pila_op(pos_act) = id
pos_act + 1
2
Insertar en pila de operadores.
PUSH pila_oper(pos_act) = := ó + ó –
pos_act+1
3
Mientras el tope de la pila_operadores sea igual a un + ó un – entonces
generar triplo temporal.
Op2 = tope pila_op
 sacar
Op1 = tope pila_op
 sacar
Oper = tope pila_oper  sacar
TEMP = OP, OP1, OP2  TEMP= OP1, OPER, OP2
Insertar en pila de operandos
p_operandos(pos_act) = TEMP
pos_act + 1
4
Generar triplo
Op2 = tope pila_op  sacar de la pila
Op1 = tope pila_op  sacar de la pila
Oper = tope pila_oper  sacar de la pila
Realizar el siguiente ejercicio tomando en cuenta los diagramas de sintaxis y las
acciones del ejercicio anterior.
X:=A+B+C*D
TEMP2
D
TEMP1
C
+
TEMP
+
A
B
TEMP1
+
TEMP
C
+
TEMP1
D
:=
X
TEMP2
TEMP
+
B
TEMP2
+
A
X
Pila de
operadores
:=
Pila de
operandos
TRIPLO
Generación de cuádruplos para operaciones aritméticas.
Cuádruplos.
Estructura de tipo registro que tiene 4 campos que son : llamados: operador,
operando1, operando2, resultado.
En donde:
Operador: código interno para la operación.
Operando1 y operando 2: valor o variables que intervienen en la operación.
Resultado: registro donde se guarda el resultado.
Nota: Op1 y Op2 normalmente son apuntadores a una tabla de símbolos y su valor
puede ser nulo.
Ejemplo:
C:=1
Oper
Op1
:=
C
Op2
Res
1
nulo
Ejemplo:
(A*B) + (C*D)
4
<OPER2>
<S>
EST
2
OPER1
*
4
<OPER1>
/
OPER2
2
1
<EST>
id
+
5
(
OPER1
3
)
R2
D
*
C
Oper
Op1
Op2
R
*
A
B
R1
*
C
D
R2
+
R1
R2
R3
R1
B
Acciones.
Insertar en la pila de operandos.
PUSH Pila_operandos (pos_act) = id
pos_act+1
2
Insertar en la pila de operadores.
PUSH Pila_operadores (pos_act) = + ó – ó * ó /
pos_act+1
3
Insertar en pila de operadores.
PUSH Pila_operadores(pos_act) = marca fondo falso
pos_act+1
4
Mientras que el tope de la pila sea = + ó – ó * ó /
entonces :
Generar cuádruplo
Op2 = tope Pila_operandos  sacar de la pila
Op1 = tope Pila_operandos  sacar de la pila
Operador = tope Pila_operadores  sacar de la pila
Resultado = Resultado de operaciones Rn + 1
Resul = Op1 Oper Op2
*
A
Pila_op
1
+
Pila_oper
e Insertar en la pila de operandos
PUSH Pila_operandos (pos_act) = Rn + 1
Pos_act + 1
5
Sacar Pila_operadores la marca de fondo falso.
POP -Pila_operadores(pos_act) = M.F.F
Ejemplo:
W:=A + B
<S>
3
<OP1>
1
id
2
4
OP2
:=
OP1
2
+
<OP2>
-
1
id
Cte_num
Cuádruplos
R1
B
A
+
W
:=
Pila operandos
Pila operadores
+
A
:=
W
B
R1
R1
Acciones.
1
Insertar en la pila de operandos.
PUSH Pila_operandos (pos_act) = id
pos_act + 1
2
Insertar en pila de operadores.
PUSH Pila_operadores (pos_act) = + ó – ó :=
pos_act + 1
3
Mientras el tope de la pila de operadores sea igual a + ó –
Generar cuádruplo.
Op2 = Tope Pila_operandos  sacar
Op1 = Tope Pila_operandos  sacar
Oper = Tope Pila_operadores  sacar
Res = Resultado Obt = Rn + 1
Res = Op1 Oper Op2
Rn + 1
e Insertar en la pila de operandos
PUSH pila_operandos (pos_act) = Rn + 1
pos_act + 1
4
Generar cuádruplo (Asignación).
Op2 = nulo
Op1 = Tope pila_operandos  sacar de la pila
Oper = Tope pila_operadores  sacar de la pila
Res = Tope pila_operandos  sacar de la pila
Ejemplo:
W:= A / B +C
<S>
3
<OP1>
1
id
2
4
OP2
:=
OP1
2
+
-
3
<OP2>
EST
<EST>
1
/
id
*
Constante
C
B
+
A
/
W
:=
Pila de
operandos
Pila de
operadores
Acciones.
1
Insertar en la pila de operandos
PUSH pila_operandos (pos_act) = id
pos_act + 1
/
A
B
R1
+
R1
C
R2
:=
R2
W
2
Inserta en la pila de operadores.
PUSH pila_operadores (pos_act) = + ó – ó / ó * ó =
3
Mientras que el tope de la pila de operadores se igual a + ó – ó / ó * ó =
Generar cuádruplo.
Op2 = Tope Pila_operandos  sacar
Op1 = Tope Pila_operandos  sacar
Oper = Tope Pila_operadores  sacar
Res = Resultado Obt = Rn + 1
Res = Op1 Oper Op2
* Inseratar en la pila de operandos
PUSH pila_operandos (pos_act) = Rn + 1
pos_act + 1
4.
Generar cuádruplo (Asignación)
Op2 = nulo
Op1 = Tope pila_operandos sacar de la pila
Oper = Tope pila_operadores  sacar de la pila
Res = Tope pila_operandos  sacar de la pila
Generación de código para estatutos.
Se requiere una nueva pila; una pila de saltos además nuevas instrucciones
para el código como son:
SF
Salto en falso.
SV
Salto verdadero.
SI
Salto incondicional.
Nota: Normalmente al generar un salto, aun no sabemos la dirección a la cual
será el salto entonces dejamos la dirección pendiente por rellenar utilizando.
Rellenar(dirección a rellenar, valor con que se rellenará)
Además utilizaremos un contador el cual contendrá la dirección del siguiente
cuádruplo a analizar.
Estatuto IF – THEN – ELSE
5
<X>
6
if
7
8
then
E
<E>
<OP1>
OP1
else
S
2
2
<
S
OP1
*
cte
>
/
<S>
1
id
2
:=
4
OP2
10
endif
3
<OP2>
1
id
9
Acciones.
1
Insertar en la pila de operandos.
PUSH pila_operandos(pos_actual) = id
pos_act + 1
2
Insertar en pila de operadores.
PUSH pila_oper(pos_actual) = + ó – ó :=
pos_act + 1
3
Mientras el tope de la pila de operadores sea igual a + ó – ó *
entonces
Generar cuádruplo
Op2 = tope pila_operandos ⇒ sacar
Op1 = tope pila_operandos ⇒ sacar
Oper = tope pila_operadores ⇒ sacar
Res = result = Rn + 1
Insertar en la pila de operandos PUSH pila_operandos (pos_act) = Rn
pos_act + 1
4
Generar cuádruplo de asignación.
Res = Tope pila_operandos
Op1 = Tope pila_operandos
Op2 = Nulo
Oper = Tope pila_operadores
5
Insertar en pila de operadores (pos_act) = Marca fondo falso
6
Generar cuádruplo
Op2 = tope pila_operandos  sacar
Op1 = tope pila_operandos  sacar
Oper = tope pila_operadores  sacar
Res = result = Rn + 1
Insertar Rn en pila de operandos.
7
Salto en falso con lo que esta en el tope pila_operandos y gurdar la dirección
de cuádruplos en pila_saltos.
8
Salto incondicional (SI_ _)
Rellenar (TOPE_saltos, cont + 1) e inseratr la dirección de SI en P.saltos.
9.
Rellenar (TOPE-saltos, cont + 1)
10.
Sacar marca de fondo falso de pila_operadores.
Ejemplo:
If A>B then
A:=B*C
Else
A:=C*D
endif
Pila
operandos
A
Pila
operadores
Pila de
saltos
B
>
2
R1
:=
A
*
B
:=
C
R2
A
C
*
5
#
oper
op1
op2
Res
1
>
A
B
R1
2
SF
R1
3
*
B
C
R2
4
:=
A
-
R2
5
SI
6
*
C
D
R3
7
:=
A
-
R3
6
8
D
R2
Estatuto REPEAT
5
<X>
6
S
Repeat
Until
<S>
E
3
<OP2>
1
2
id
:=
4
OP1
OP2
*
<E>
/
OP1
<OP1>
2
>=
1
id
<=
cte
>
<
Acciones.
1, 2, 3, 4. Son igual a las anteriores del if.
5.
PUSH pila_saltos (la dirección del siguiente cuádruplo a analizar) = cont
6
Generar cuádruplo y SF de la sig forma:
Op2 = Tope pila_operandos
Op1 = Tope pila_operandos
Oper = Tope pila_operadores
Rn
= Rn + 1 e insertar en pila_op
Generar un SF con lo que está en el tope pila_operandos y rellenar la dirección de
este salto con tope pila_saltos y sacar.
Ejemplo:
X=1
Y=2
Z=3
Repeat
X:=Y + Z
Z:=Y + 1
Until
Z ≥ 10
Y=0
X
1
Pila de
operandos
X
Y
Z
R1
Z
Y
Pila de
operadores
:=
=
=
:=
+
:=
Pila de saltos
Y
2
Z
3
1
R2
+
Z
10
>=
4
#
oper
op1
op2
Rn
1
:=
X
-
1
2
:=
Y
-
2
3
:=
Z
-
3
4
+
Y
Z
R1
5
:=
X
-
R1
6
+
Y
1
R2
7
:=
Z
-
R2
8
>=
Z
10
R3
9
SF
R3
4
10
:=
Y
0
R3
Estatuto FOR.
<FOR>
1
FOR
2
id
3
:=
4
EXP
5
TO
6
EXP
7
do
8
S
9
endfor
Acciones.
1
Insertar en la pila de operandos el siguiente id.
2
Insertar el id en pila_operandos
3
Insertar el := en la pila de operadores.
4
Insertar EXP y generar cuádruplo(s) de la sig forma:
Res = Top_pila de operandos  sacar de la pila
Op1 = Top_pila de operandos  sacar de la pila
Op2 = Nulo
Oper = Top_pila de operadores  sacar de la pila
5
Insertar en la pila de saltos (el siguiente cuádruplo a analizar ) . Cont + 1
6
Insertar la EXP (id) p_oper y generar cuádruplo de la siguiente forma:
Op2 = tope pila_operandos. (POP)
Op1 = tope pila_operandos. (POP)
Oper = >
Res = Rn + 1 e insertar Rn + 1 en la pila de operandos.
7
Hacer un salto verdadero con tope pila_operandos i dejar la dirección
pendiente por rellenar, e insertar pila de saltos su dirección.
8
Generar cuádruplos.
9
Hacer un SI con TOP – 1 (pila_saltos) y rellenar.
Rellenar (top_pila_saltos, cont + 1)
Ejemplo:
For i:= 0 to 10 do
A:=A+1
i:=i+1
endfor
#
oper
op1
1
=
i
2
>
i
3
SV
R1
4
+
A
5
:=
R2
6
+
I
7
=
R3
8
SI
op2
Res
0
10
R1
9
1
R2
A
1
R3
i
2
9
Pila de
operandos
i
i
0
Pila de
operadores
:=
+
+
Pila de
saltos
2
10
R1
A
>  SV
3
<  SF
1
A
R2
i
1
Estatuto CASE
<CASE>
1
case
EXP
2
of
OPC
3
:
EST
else
5
EST
4
endcase
Acciones.
1
Insertar EXP en la pila de operandos.
2
Insertar OPC en la pila de operandos y hacer un cuadruplo de la siguiente
forma:
Op2 = Top_pila de operandos  sacar de pila
Op1 = Top_pila operandos  utilizar y no sacar de la pila
Oper = =
Res = Rn + 1 e insertar en pila_operandos.
Hacer un SF con Top_pila_operandos y dejar dirección pendiente por rellenar
e insertar la dirección del SF en la pila de saltos.
3
Generar cuádruplos de resultantes.
Hacer un salto incondicional y dejar la dirección pendiente por rellenar e
insertar la dirección del SI en la pila_saltos.
Rellenar(top – 1 pila_saltos, cont + 1) y Rellenar (top – 1 ,cont).
4
Generar cuádruplos.
6.
Rellenar (top_pila_saltos, cont + 1)
5
Sacar lo que está en la pila de operandos.
6
Ejemplo:
Case A of
1: read(B);
2: A:=A+B
Else
write(A);
A
#
oper
op1
op2
Res
1
=
A
1
R1
2
SF
R1
3
Read
X
4
SI
9
5
=
A
6
SF
R2
7
+
A
8
:=
9
SI
10
Write
A
5
2
R2
10
B
NULO
R3
B
A
A
R2
2
9
Write
R3
X
R3
R1
11
1
A
A
read
Pila
operandos
Pila
operadores
6
+
4
:=
2
Pila saltos
Estatuto WHILE
<WHILE>
5
6
while
E
7
8
do
<E>
S
<OP1>
OP1
enddo
1
id
2
3
<OP2>
OP1
2
>
cte
<
*
/
<S>
1
id
2
:=
4
OP2
Acciones.
1, 2, 3, 4. Igual
5.
PUSH pila_saltos(el siguiente cuádruplo a analizar) = cont + 1
6.
Generar cuádruplo e insertar Rn en la pila.
7.
Generar un salto en falso con tope de pila de operandos dejar la dirección
pendiente por rellenar e insertar la dirección en la pila de saltos.
8.
Generar un salto incondicional con lo que está en el (top-1 pila_saltos) y
rellenar (pila_saltos, cont+1)
Ejemplo:
While X<5 do
X:=Y+Z
Enddo
x:=0
#
oper
op1
op2
Res
R2
1
<
X
5
R1
Z
2
SF
R1
6
Y
3
+
Y
R2
X
4
:=
R2
5
SI
6
:=
Z
X
1
X
0
2
1
Pila de
saltos
+
R1
5
:=
X
<
Pila de
operandos
Pila de
operadores
Para incluir los tipos de las variables en la tabla de símbolos podemos efectuar las
siguientes acciones.
1
<V>
id
2
:
TIPO
;
Acciones.
1
PUSH pila_operandos (dirección de la variable).
2
Poner el tipo a todas las variables que se metieron en la pila de operandos
y sacarlas de la pila.
1,3
Real
Id1 = 1, 1
1,2
Real
1,1
entero
Id2 = 1, 2
1
1
2
entero
real
3
4
real
Id3 = 1, 3
Reglas semánticas.
Utilizaremos como referencia las reglas semánticas de las expresiones en Pascal,
para esto construiremos una tabla donde:
E = entero
R = real
C = caracter
S = string
B = booleano
x = error semántico
Op1
Op2
*, +, -
/
div
mod
relac
and
or
E
E
E
R
E
B
X
E
R
R
R
X
B
X
R
E
R
R
X
B
X
R
R
R
R
X
B
X
C
C
X
X
X
B
X
C
S
X
X
X
B
X
S
C
X
X
X
B
X
S
S
X
X
X
B
X
B
B
X
X
X
B
B
En esta tabla se omitieron un conjunto de combinaciones que con cualquier
operación produce error, como por ejemplo ENTERO con STRING.
Ya que un traductor es un autómata (con funcionamiento automático), es conveniente
que el análisis semántico también se automatice. Las claves para automatizar el
análisis semántico son:
•Utilizar acciones para verificación semántica.
•Escoger una estructura de datos que permita accesarla directamente, encontrar el
resultado de una operación y descubrir si ésta es o no válida.
De hecho estas claves están vigentes para todo el proceso de traducción, en cuanto
a utilizar acciones y estructuras de datos automáticas.
Para el análisis semántico no se recomienda usar la tabla mostrada anteriormente,
debido a que su acceso no es automático y le faltan muchas combinaciones.
Acciones para verificación semántica.
Para mostrar un ejemplo de cómo diseñar las acciones de verificación semántica,
utilizaremos un subconjunto de expresiones aritméticas. Estas acciones se deberán
de añadir a las acciones de generación de código, y no modificar nada de lo ya visto
en generación de código.
Para realizar la verificación semántica se requerirá de una pila de tipos.
4
<E>
5
<T>
T
F
+
*
2
3
1
<T>
id
(
E
6
Acciones.
1.
PUSH pila_tipos (tipo de la variable).
2,3.
No llevan acción semántica.
)
7
4.
Si tipos del TOP y TOP – 1 de la pila de tipos son permitidos en la operación
a generar ENTONCES:
POP pila_tipos; POP pila_tipos
PUSH pila_tipos (resultado de la operación)
SI NO
Marcar error semántico, y aplicar acción correctiva que podría ser:
POP pila_tipos; POP pila_tipos
PUSH pila_tipos (posible resultado de la operación)
5.
Igual a 4
6.
No llevan acción semántica.