Sei sulla pagina 1di 97

Universidad de San Carlos de Guatemala

Facultad de Ingeniería
Escuela de Ingeniería en Ciencias y Sistemas
Organización de Lenguajes y Compiladores 2
Segundo semestre 2019
Catedráticos: Ing. Bayron López, Ing. Erick Navarro e Ing. Edgar Saban
Tutores académicos: Luis Lizama, Rainman Sián, Julio Arango.

DOSLang
Contenido
1 Objetivos .................................................................................................................. 7
1.1 Objetivo general ............................................................................................ 7
1.2 Objetivos específicos .................................................................................... 7
2 Descripción .............................................................................................................. 7
2.1 Descripción de la aplicación .......................................................................... 7
2.1.1 DosLang ...................................................................................................... 7
2.1.2 DosIDE ........................................................................................................ 7
2.1.3 Consola ....................................................................................................... 8
2.1.4 Debugger ..................................................................................................... 8
2.1.5 Optimizador ................................................................................................. 8
2.1.6 Reportes ...................................................................................................... 9
2.2 Flujo del Proyecto ....................................................................................... 11
2.2.1 Flujo general de la aplicación .................................................................... 11
2.2.2 Flujo específico de la aplicación ................................................................ 12
3 Generalidades del lenguaje DOSLang ................................................................... 13
3.1 Tipos de dato .............................................................................................. 13
3.1.1 Tipos primitivos.......................................................................................... 13
3.1.2 Tipos definidos por el usuario .................................................................... 14
3.1.3 Tipos de dato enumerados ........................................................................ 14
3.1.4 Tipos de subrango ..................................................................................... 15
3.2 Arreglos ....................................................................................................... 15
1
3.2.1 Arreglos Multidimensionales y Arreglos de arreglos .................................. 17
3.2.2 Acceso a los arreglos ................................................................................ 17
3.3 Registros ..................................................................................................... 18
3.4 Memoria Virtual ........................................................................................... 19
3.4.1 Sizeof......................................................................................................... 19
3.4.2 malloc ........................................................................................................ 19
3.4.3 free ............................................................................................................ 20
4 Definición léxica ..................................................................................................... 21
4.1 Finalización de línea ................................................................................... 21
4.2 Espacios en blanco ..................................................................................... 21
4.3 Comentarios ................................................................................................ 21
4.4 Sensibilidad a mayúsculas y minúsculas .................................................... 22
4.5 Identificadores ............................................................................................. 22
4.6 Palabras reservadas ................................................................................... 22
4.7 Literales ...................................................................................................... 23
4.8 Secuencias de escape ................................................................................ 23
5 Estructura del Lenguaje ......................................................................................... 25
5.1 Nombre del programa ................................................................................. 26
5.2 Sentencia uses............................................................................................ 26
5.3 Declaración de variables ............................................................................. 26
5.4 Declaración de constantes .......................................................................... 27
5.5 Expresiones ................................................................................................ 28
5.5.1 Operaciones Aritméticas............................................................................ 28
5.5.2 Operadores relacionales............................................................................ 32
5.5.3 Operaciones lógicas .................................................................................. 32
5.6 Funciones y Procedimientos ....................................................................... 34
5.6.1 Funciones .................................................................................................. 34
5.6.2 Procedimientos .......................................................................................... 36
5.6.3 Paso de Parámetros .................................................................................. 36
5.6.4 Procedimientos y Funciones anidadas ...................................................... 39
5.6.5 Funciones Propias del lenguaje ................................................................. 39

2
6 Sentencias ............................................................................................................. 42
6.1 Bloques ....................................................................................................... 42
6.2 Sentencia With ............................................................................................ 43
6.3 Asignación de variables .............................................................................. 44
6.4 Sentencias de transferencia ........................................................................ 45
6.4.1 Break ......................................................................................................... 45
6.4.2 Continue .................................................................................................... 46
6.4.3 Exit ............................................................................................................ 47
6.5 Sentencias de selección ............................................................................. 47
6.5.1 Sentencia If................................................................................................ 47
6.5.2 Switch ........................................................................................................ 47
6.6 Ciclos .......................................................................................................... 49
6.6.1 While.......................................................................................................... 49
6.7 Sentencias de entrada/salida ...................................................................... 51
6.8 Sentencia Write ........................................................................................... 51
7 Generación de código intermedio .......................................................................... 53
7.1 Temporales ................................................................................................. 54
7.2 Etiquetas ..................................................................................................... 54
7.3 Identificadores ............................................................................................. 54
7.4 Comentarios ................................................................................................ 55
7.5 Operadores aritméticos ............................................................................... 55
7.6 Saltos .......................................................................................................... 56
7.6.1 Saltos condicionales .................................................................................. 56
7.6.2 Salto incondicional ..................................................................................... 57
7.7 Asignación a temporales ............................................................................. 57
7.8 Declaración de métodos ............................................................................. 58
7.9 Llamadas a métodos ................................................................................... 58
7.10 Print ............................................................................................................. 59
7.11 Entrada o lectura de dato ............................................................................ 59
7.12 Entorno de ejecución .................................................................................. 60
7.12.1 Estructuras del entorno de ejecución ..................................................... 60

3
7.12.2 Acceso a estructuras del entorno de ejecución ...................................... 62
8 Optimización de código intermedio ........................................................................ 63
8.1 Eliminación de instrucciones redundantes de carga y almacenamiento. .... 63
8.2 Eliminación de código inalcanzable ............................................................ 64
8.3 Simplificación algebraica y reducción por fuerza ........................................ 65
9 Generación de código ensamblador ...................................................................... 68
9.1 Entrada de ASM .......................................................................................... 68
9.2 Salida de ASM ............................................................................................ 68
9.3 Registros de la CPU.................................................................................... 68
9.3.1 Registros de uso general ........................................................................... 68
9.3.2 Registros de segmento .............................................................................. 69
9.3.3 Registros punteros .................................................................................... 69
9.3.4 Registros especiales ................................................................................. 69
9.3.5 Bits del registro de banderas ..................................................................... 69
9.4 Estructura stack assembler ......................................................................... 70
9.5 Instrucciones de transferencia de datos ...................................................... 71
9.5.1 MOV .......................................................................................................... 71
9.5.2 PUSH......................................................................................................... 71
9.5.3 POP ........................................................................................................... 72
9.5.4 XCHG ........................................................................................................ 72
9.5.5 XLAT.......................................................................................................... 72
9.5.6 LEA ............................................................................................................ 72
9.5.7 LDS ........................................................................................................... 73
9.5.8 LES ............................................................................................................ 73
9.5.9 LAHF ......................................................................................................... 73
9.5.10 SAHF...................................................................................................... 73
9.5.11 PUSHF ................................................................................................... 74
9.5.12 POPF ..................................................................................................... 74
9.6 Instrucciones aritméticas ............................................................................. 74
9.6.1 ADD ........................................................................................................... 74
9.6.2 ADC ........................................................................................................... 74
9.6.3 SUB ........................................................................................................... 75
4
9.6.4 SBB ........................................................................................................... 75
9.6.5 CMP........................................................................................................... 75
9.6.6 INC ............................................................................................................ 75
9.6.7 DEC ........................................................................................................... 76
9.6.8 NEG ........................................................................................................... 76
9.6.9 DAA ........................................................................................................... 76
9.6.10 DAS ........................................................................................................ 76
9.6.11 MUL ........................................................................................................ 77
9.6.12 IMUL ....................................................................................................... 77
9.6.13 CBW ....................................................................................................... 77
9.7 Instrucciones lógicas ................................................................................... 77
9.7.1 AND ........................................................................................................... 77
9.7.2 TEST dest,src ............................................................................................ 78
9.7.3 OR ............................................................................................................. 78
9.7.4 XOR ........................................................................................................... 78
9.7.5 NOT ........................................................................................................... 79
9.8 Instrucciones de desplazamiento ................................................................ 79
9.8.1 SHL/SAL .................................................................................................... 79
9.8.2 SHR ........................................................................................................... 80
9.8.3 SAR ........................................................................................................... 80
9.8.4 ROL ........................................................................................................... 81
9.8.5 ROR........................................................................................................... 81
9.8.6 RCL ........................................................................................................... 82
9.8.7 RCR ........................................................................................................... 82
9.9 Instrucciones de transferencia de control .................................................... 83
9.9.1 JMP etiqueta.............................................................................................. 83
9.9.2 CALL id ...................................................................................................... 83
9.9.3 RET ........................................................................................................... 83
9.10 Saltos condicionales aritméticos ................................................................. 83
9.10.1 Aritmética signada (con números positivos, negativos y cero) ............... 83
9.10.2 Aritmética sin signo (con números positivos y cero)............................... 84
9.11 Saltos condicionales según el valor de los indicadores: ............................. 84
5
9.12 Saltos condicionales CX ............................................................................. 84
9.13 Interrupciones: ............................................................................................ 84
9.14 Operadores analíticos ................................................................................. 85
9.14.1 SEG........................................................................................................ 85
9.14.2 OFFSET ................................................................................................. 85
9.14.3 TYPE ...................................................................................................... 86
9.14.4 Definición de procedimientos ................................................................. 86
9.14.5 Definición de macros .............................................................................. 87
9.15 Operadores de macros ............................................................................... 88
9.16 Instrucciones para el ensamblador (Directivas) .......................................... 88
9.16.1 Definición de símbolos ........................................................................... 88
9.16.2 Definición de datos ................................................................................. 88
9.16.3 Definición de segmentos ........................................................................ 89
9.16.4 Segmentos simplificados ........................................................................ 90
10 Manejo de errores ............................................................................................... 92
10.1 Tipos de errores .......................................................................................... 92
10.2 Contenido mínimo de tabla de errores ........................................................ 92
10.3 Método de recuperación ............................................................................. 92
11 Apéndice A: Precedencia y asociatividad de operadores .................................... 93
12 Entregables y Restricciones ................................................................................ 94
12.1 Entregables ................................................................................................. 94
12.2 Restricciones............................................................................................... 94
12.3 Consideraciones ......................................................................................... 94
12.4 Requisitos mínimos ..................................................................................... 95
12.5 Entrega del proyecto ................................................................................... 97

6
1 Objetivos
1.1 Objetivo general
Comprender la fase de síntesis durante el proceso de compilación mediante un
lenguaje de programación estructurado
.

1.2 Objetivos específicos


• Realizar una representación de bajo nivel a partir de un lenguaje de alto nivel
• Generar cuádruplos como código intermedio
• Generar código ensamblador a partir de código intermedio generado
• Conocer el manejo de las estructuras para manejo de memoria en tiempo de
ejecución
• Traducir un lenguaje de alto nivel a su equivalente en formato de código
intermedio y posteriormente a ensamblador.

2 Descripción
DOSLang es un lenguaje de programación procedural e imperativo. La característica
principal de este lenguaje es la capacidad de combinar código ensamblador con
DOSLang. Está característica existe ya que el código ensamblador se ejecuta mucho
más rápido. Es bien sabido que programar enteramente en ensamblador no suele ser
una buena idea, pero tener la posibilidad ayuda a ir experimentando con el lenguaje
ensamblador y a la vez poder aumentar la velocidad de ejecución de un programa si
fuera necesario.

2.1 Descripción de la aplicación


Se requiere la implementación de un entorno de desarrollo web la cual servirá tanto para
el desarrollo de aplicaciones, así como también para la ejecución de la misma. La
aplicación contará con los siguientes componentes:

2.1.1 DosLang
DOSLang es un lenguaje de programación estructurado, procedural e imperativo. Esto
significa que todos los programas deben tener una estructura y orden definido.

2.1.2 DosIDE
DosIDE es un entorno de desarrollo. Este provee las herramientas para la escritura de
programas en DosLang así como también las herramientas para solicitar la
transformación del código fuente a código intermedio.

7
2.1.3 Consola
Este componente permitirá observar el resultado de ejecución de las aplicaciones
desarrolladas en el IDE

2.1.4 Debugger
Este componente permitirá validar la ejecución del código intermedio, permitirá la
ejecución paso a paso, así como también permitirá visualizar el manejo de las estructuras
para el manejo de memoria como el Heap y Stack en tiempo de ejecución para poder
validar su correcta implementación.

2.1.5 Optimizador
Este componente permitirá optimizar el código intermedio generado, utilizando
optimización de mirilla. Al ejecutar esta versión de código podrá ser ejecutado o
transformado a código ensamblador produciendo la misma salida lógica de la aplicación.

8
2.1.6 Reportes
DOSIDE permite generar reportes sobre el proceso de compilación del archivo de
entrada. Estos reportes nos permiten tener una idea más detallada sobre el
funcionamiento de nuestro compilador ya que nos presenta los errores, la tabla de
símbolos utilizada y las optimizaciones que generamos.

2.1.6.1 Reporte de errores


Mostrará todos los errores encontrados en el proceso de compilación de una manera
detallada y en formato de tabla. Se debe mostrar el tipo de error, su ubicación, así como
una breve descripción que indique que fue lo que sucedió.

9
2.1.6.2 Reporte de Tabla de Símbolos
Este reporte mostrará la tabla de símbolos después del proceso de compilación. Se
deberán demostrar todas las variables, funciones y procedimientos que fueron
declarados, así como su tipo y toda la información que el estudiante considere necesaria.
Se calificará la presentación del reporte

2.1.6.3 Reporte de Optimización


Este reporte mostrará todas las optimizaciones que se realizaron en el código de
cuádruplos generados. Este reporte mostrará como mínimo la expresión original, la
expresión optimizada, su ubicación en el código y el número de la regla de optimización
que se aplicó, las reglas de optimización se describen más adelante.

10
2.2 Flujo del Proyecto
A continuación, se describe el flujo general y especifico del proyecto, desde su petición
al cliente hasta la ejecución del código intermedio o la generación de código
ensamblador.

2.2.1 Flujo general de la aplicación


La solución propuesta consta de dos partes, un entorno de desarrollo desde el cual el
usuario podrá programar sus aplicaciones en el lenguaje de alto nivel DOSLang y un
servidor el cual se encargará de sintetizar el código de alto nivel en código intermedio,
en este caso cuádruplos, el cual le devolverá al IDE.
Una vez que el cliente tenga le representación de la aplicación en código intermedio el
usuario podrá ejecutar dicho código y ver los resultados en el entorno de desarrollo,
también podrá debuggear el mismo código con fines de entender el manejo de las
estructuras internas de manejo de memoria en tiempo de ejecución. El usuario podrá
sintetizar el código intermedio a código ensamblador y el código ensamblador generado
podrá ser después ejecutado en un emulador.

Para el desarrollo del entorno de desarrollo en línea se hará uso del lenguaje javascript
y para el procesamiento del lenguaje intermedio se hará uso de Nodejs y JISON. El
servidor se desarrollará utilizando java y se hará uso de Jlex y CUP para la
transformación del programa fuente a código de cuádruplos.
Nota: La comunicación entre el cliente y el servidor se hará a través de sockets.

11
2.2.2 Flujo específico de la aplicación
A continuación, se detalla el flujo principal de la aplicación a través de la cual el usuario
puede desarrollar sus aplicaciones
1. El programador crea su programa a través del entorno de desarrollo.
2. El programador solicita la compilación de su programa fuente.
3. La aplicación procesará el programa fuente y generará la representación en
código intermedio (cuádruplos) del programa fuente.
4. La aplicación proporcionará al programador el código intermedio generado o
notificará de posibles errores en el programa fuente
5. A partir del código intermedio el programador puede:
a. Ejecutar el código intermedio en el entorno de desarrollo en línea
b. Sintetizar el código intermedio en código ensamblador para
posteriormente evaluar su ejecución en un emulador
c. Debuggear el código intermedio para poder visualizar el manejo de
estructuras de memoria en tiempo de ejecución.
d. Optimizar el código intermedio utilizando el método de mirilla para luego
continuar con el flujo de síntesis (5)

12
3 Generalidades del lenguaje DOSLang
DOSLang es un lenguaje derivado de pascal, por lo tanto, está conformado por un
subconjunto de sus instrucciones que pretenden facilitar el aprendizaje
de programación a las personas, utilizando la programación estructurada y
estructuración de datos.

3.1 Tipos de dato


DOSLang es un lenguaje de programación de tipado estático, esto significa que la
comprobación de tipos se realiza durante la compilación y las variables no pueden
cambiar su tipo durante la ejecución.

3.1.1 Tipos primitivos


Se utilizarán los siguientes tipos de dato primitivos:

Tipo Definición Rango


Integer Acepta valores numéricos [-2147483648,
enteros 2.147.483.647]
Real Acepta valores numéricos de [-9223372036854775800,
punto flotante 9223372036854775800]
Char Acepta un único carácter [0,255] (ASCII)
Boolean Valores de verdadero y falso true, false
Word Acepta cadenas de caracteres [0, 256] caracteres
ascii
String Acepta cadenas de caracteres [0, 2.147.483.647]
caracteres ascii
Registros Agrupa valores de distintos Conjunto de tipos
tipos anteriores
NIL Representa la ausencia de -1
valor, será colocado en
registros, arreglos y cadenas
sin valor o reserva de espacio

3.1.1.1 Casteos implícitos


Además de la sobrecarga que posee en operador más (+) en las operaciones aritméticas,
DOSLang permite realizar casteos implícitos al momento de asignar valores a las
variables, tanto para variables definidas por el usuario como los parámetros de una
función o procedimiento. Se presenta la siguiente gráfica:

13
3.1.2 Tipos definidos por el usuario
Un tipo puede ser definid por un nombre, esto sirve para definir variaciones de un tipo.
La sintaxis de la declaración de un tipo es:

<Type_Declaration_List> ::= ‘type’ <Type_Declaration>


| ‘type’ <enum-identifier>
| ‘type’ <range-identifier>

<Type_Declaration> ::= <idList> ‘=’ id ’;’


| <idList> ‘=’ primitiveType ’;’

type
days, age = integer;
yes, true = boolean;
name, city = string;
fees, expenses = double;

3.1.3 Tipos de dato enumerados


Los tipos de datos enumerados son tipos de datos definidos por el usuario. Permiten que
los valores se especifiquen en una lista.

<enum-identifier> = id ‘=’‘(’ <idList> ‘)’

14
type
SUMMER = (April, May, June, July, September);
COLORS = (Red, Green, Blue, Yellow, Magenta, Cyan, Black, White);
TRANSPORT = (Bus, Train, Airplane, Ship);

3.1.4 Tipos de subrango


Los tipos de subrango permiten que una variable asuma valores que se encuentran
dentro de un cierto rango.

<subrange-identifier> ::= lower-limit ‘...’ upper-limit ‘;’

const
P = 18;
Q = 90;

type
Number = 1 ... 100;
Value = P ... Q;

Consideraciones:
• Únicamente aplica para tipos numéricos y caracteres

3.2 Arreglos
DOSLang proporciona una estructura de datos llamada arreglo que puede almacenar
una colección secuencial de tamaño fijo de elementos del mismo tipo.
En lugar de declarar variables individuales, como número1, número2, ... y número100,
declara una variable de matriz como números y usa números [1], números [2] y ...,

15
números [100] para representar variables individuales Se accede a un elemento
específico en una matriz mediante un índice.
Todas las matrices consisten en ubicaciones de memoria contiguas. La dirección más
baja corresponde al primer elemento y la dirección más alta al último elemento. Si se
desea una matriz de estilo C que comience desde el índice 0, solo necesita iniciar el
índice desde 0, en lugar de 1

Sintaxis

<TypeSpecifier> ::= ARRAY '[' <DimensionList> ']' OF <TypeSpecifier>

<DimensionList> ::= <Dimension>


| <Dimension> ',' <DimensionList>

<Dimension> ::= <constant> '..' <constant>


| id

Ejemplo

type
temperature = array [-10 .. 50] of real;
var
day_temp, night_temp: temperature;

type
ch_array = array[char] of 1..26;
var
alphabet: ch_array;
c: char;

begin
...
for c:= 'A' to 'Z' do
alphabet[c] := ord[m];
(* the ord() function returns the ordinal values *)

16
3.2.1 Arreglos Multidimensionales y Arreglos de arreglos

Los arreglos multidimensionales son estructuras de datos estáticas en la que tienen n


dimensiones para todo n>1 (n=1 se considera un vector). En DOSLang, los arreglos
multidimensionales y los arreglos de arreglos se trabajan de la misma forma, y existen
dos maneras de declararlos. Se presentan los siguientes ejemplos:

var
a: array [0..3, 0..3] of integer;
i, j : integer;

{ Es equivalente a… }

var
a: array [0..3] of array [0..3] of integer;
i, j : integer;

{ En ambos casos su acceso seria }

begin
for i:= 0 to 3 do
for j:= 0 to 3 do
a[i,j]:= i * j;
...
end;

3.2.2 Acceso a los arreglos


Se accede a un elemento de un arreglo mediante el nombre del arreglo indicando la
posición a la que se desea acceder. En el caso de los arreglos multidimensionales, se
coloca la posición de cada índice separado por una coma (,).

Sintaxis

<Variable> ::= id
| <Variable> '.' id
| <Variable> '^'
| <Variable> '[' <ExpressionList> ']'

<ExpressionList> ::= <Expression>


| <Expression> ',' <ExpressionList>

Ejemplo

program exArrays;
var
n: array [1..10] of integer; (* n is an array of 10 integers *)

17
i, j: integer;

begin
(* initialize elements of array n to 0 *)
for i := 1 to 10 do
n[ i ] := i + 100; (* set element at location i to i + 100 *)
(* output each array element's value *)

for j:= 1 to 10 do
writeln('Element[', j, '] = ', n[j] );
end.

Consideraciones:
• Para un arreglo de n dimensiones, si se hace un acceso sobre una dimensión m
donde m<n, al acceso retornara un arreglo de n-m dimensiones.

3.3 Registros
La principal limitación de un array es que todos los datos que contiene deben ser del
mismo tipo. Pero a veces nos interesa agrupar datos de distinta naturaleza, como pueden
ser el nombre y la edad de una persona, que serían del tipo string e integer,
respectivamente. En ese caso, podemos emplear los records o registros, que se definen
indicando el nombre y el tipo de cada dato individual (cada campo), y se accede a estos
campos indicando el nombre de la variable y el nombre del campo, separados por un
punto:

program Record2;
var
dato: record
nombre: string;
edad: integer;
end;

begin
dato1 : dato = malloc(sizeof(dato));
with dato1 do
begin
nombre := “Ignacio”;
edad := 23;
write(“El nombre es “, nombre );
write(“ y la edad “, edad);
end;
end.

18
En este caso tenemos un nuevo bloque en el cuerpo del programa, delimitado por el
"begin" y el "end" situados más a la derecha, y equivale a decir "en toda esta parte del
programa me estoy refiriendo a la variable dato". Así, podemos nombrar los campos que
queremos modificar o escribir, sin necesidad de repetir a qué variable pertenecen.

3.4 Memoria Virtual


DOSLang permite realizar una manipulación directa de la memoria virtual o como se
denomina en el entorno de ejecución el “Heap”. Para la manipulación de dicha memoria
el lenguaje contara con 3 funciones propias y únicamente serán aplicables para la
manipulación de registros.

3.4.1 Sizeof
Este método retornara un entero que indicara el tamaño de un registro. El tamaño de un
registro se calcula como la suma de cada uno de sus atributos. Si el registro tuviera como
atributos otro registro o un arreglo, también contaría como un espacio ya que únicamente
se almacenaría su apuntador.

Sintaxis

sizeof(registro : record)

Ejemplo

Estudiante: record
nombre: string;
edad: integer;
end;

cant := sizeof(Estudiante) (* cant toma el valor de 2 *)

----------------------------------------------------------------------------

Automovil: record
marca: string;
modelo: integer;
dueno: Estudiante;

cant := sizeof(Automovil) (* cant toma el valor de 3 *)

3.4.2 malloc
Este método recibirá como parámetro un entero, reservará en el “heap” dicha cantidad
de memoria y retornará un puntero hacia la posicion que se reservó. Esta operación
deberá de realizarse cada vez que se quiera instanciar un registro, en caso de no
19
reservarse memoria, el registro apuntará a nil y si se intenta utilizar se producirá un error
en tiempo de ejecución.

Sintaxis

malloc(cantidad : int)

Ejemplo

Type

Direccion
calle: String;
avenida: String;
zona: Integer

end;

Estudiante
nombre : String;
edad : Integer;
direccion : Direccion;
end;
Var
est1, est2 : Estudiante;

begin;

est1 = malloc(sizeof(Estudiante)); (* Se reserve memoria para est1 *)

est1.edad = 12; (* Se modifica el valor edad de est1*)


est2.edad = 2; (* Se produce un error porque no se reservo memoria
para est2*)

3.4.3 free
Este método recibirá como parámetro una variable de tipo registro y limpiará la memoria
en la que se alojaba dicha variable. El apuntador que se limpió nuevamente apuntará a
nil y si se quiere volver a utilizar se deberá emplear la sentencia malloc nuevamente. Si
se intenta limpiar un registro que ya apunta a nil, deberá de reportarse un error en tiempo
de ejecución.

Sintaxis

free(variable : registro)

Ejemplo

20
est1 = malloc(sizeof(Estudiante)); (* Se reserve memoria para est1 *)

est1.edad = 12; (* Se modifica el valor edad de est1*)

free(est1); (* Se limpia la memoria que estaba usando est1*)

est1.edad = 15; (* Se produce un error porque est1 esta apuntando a nil*)

4 Definición léxica
4.1 Finalización de línea
El compilador de DOSLang dividirá la entrada en líneas, estas líneas estarán divididas
mediante componentes léxicos que determinen la finalización de las mismas y son los
siguientes:
● Carácter salto de línea (LF ASCII)
● Carácter retorno de carro (CR ASCII)
● Carácter retorno de carro (CR ASCII) seguido de carácter salto de línea (LF ASCII)

4.2 Espacios en blanco


Los espacios en blancos definirán la división entre los componentes léxicos. Serán
considerados espacios en blanco los siguientes caracteres:
● Espacio (SP ASCCI)
● Tabulación horizontal (HT ASCII)
● Caracteres de finalización de línea

4.3 Comentarios
Un comentario está destinado a incrustar anotaciones legibles al programador en el
código fuente de un Programa
Existirán dos tipos de comentarios:
• Los comentarios de una línea que serán delimitados al inicio con el símbolo “(*” y
al final con el símbolo “*)”
• Los comentarios con múltiples líneas que empezarán con los símbolos “{” y
terminarán con los símbolos “}”.

(* este es un ejemplo de un lenguaje de una sola línea *)


{
este es un ejemplo de un lenguaje de múltiples líneas.

21
}

Ejemplo 1: ejemplo de comentarios


Fuente: Elaboración propia.

4.4 Sensibilidad a mayúsculas y minúsculas


DOSLang será un lenguaje case insensitive, por lo tanto, nuestro compilador no hará
distinción entre mayúsculas y minúsculas, tanto para palabras reservadas como para
identificadores

4.5 Identificadores
Los identificadores son símbolos léxicos que nombran entidades. Estas entidades
pueden ser variables, métodos o módulos. Un identificador es una secuencia de
caracteres alfabéticos [A-Z a-z] incluyendo el guion bajo [_] o dígitos [0-9] que
comienzan con un carácter alfabético o guion bajo.

A continuación, se muestran ejemplos de identificadores validos:

Identificador_valido_1
identificador1
sale_Compi
Ejemplo 2: identificadores validos
Fuente: Elaboración propia.

A continuación, se muestran ejemplos de identificadores no validos:

Valor.1
54var
Ejemplo 3: identificadores no válidos
Fuente: Elaboración propia.

4.6 Palabras reservadas


Las palabras reservadas son identificadores reservados predefinidos que tienen un
significado especial y no se pueden ser utilizados como identificadores en el programa

22
and array begin case const

div do downto else end

file for function goto if

in label mod nil not

of or packed procedure program

record repeat set then to

type until var while with


Tabla 1: lista de palabras reservadas
Fuente: Elaboración propia.

4.7 Literales
Representan el valor de un tipo primitivo
● Enteros: [0-9]+
● Real: [0-9]+(“.” [0-9]+)
● Caracter: “’” <Carácter ASCII> “’”
● Cadena/Word: “”” <Caracteres ASCII> “’”
○ Será el único literal que, si no se inicializa, su valor será nil.
● Booleanos: [true, false]
● Nulo: nil
○ Los literales de tipo char, int y real no podrán ser de tipo nil.

4.8 Secuencias de escape


Las secuencias de escape se utilizan para definir ciertos caracteres especiales dentro
de cadenas de texto. Las secuencias de escape disponibles son las siguientes:

Secuencia Significado
de escape

\' Comillas simple

\" Comillas doble

\? Signo de interrogación

\\ Barra invertida

23
\0 Carácter nulo

\a Pitido

\b Retorno

\f Nueva página

\n Nueva línea

\r Retorno de carro

\t Tabulación horizontal

\v Tabulación vertical

24
5 Estructura del Lenguaje

Un programa en DOSLang básicamente consisten en las siguientes partes


• Nombre del programa
• Sentencia Uses
• Declaraciones de tipos (Types)
• Declaraciones de constantes
• Declaraciones de variables
• Declaración de funciones y procedimientos
• Bloque del programa principal

Cada programa en DOSLang tiene una sección de encabezado, declaraciones y


ejecución específicamente en ese orden.

program { nombre del programa }


uses { otros programas a utilizar}
const { declaración de constantes globales }
var { declaración de variables globales }

function {declaración de funciones, si hay alguna}


{ variables locales }
begin
...
end;

procedure { declaración de procedimientos, si hay alguno}


{ variables locales }
begin
...
end;

begin { incio del programa principal}


...
end. { fin del programa principal }

25
5.1 Nombre del programa
Cada programa debe ser nombrado, y deben ser único dentro de un mismo directorio de
ejecución.

Program <Identificador>;

5.2 Sentencia uses


La cláusula de uses importa programas existentes en en un mismo directorio de
ejecucion

<statement> ::= “Uses” uses_list “;”

Sintaxis 1: sentencia uses


Fuente: Elaboración propia.

Consideraciones:
• Debe existir el programa, de lo contrario lanzar una excepción.
• Si existieran elementos repetidos, debe lanzar una excepción.

5.3 Declaración de variables


Una variable no es más que un nombre dado a un área de almacenamiento que nuestros
programas pueden manipular. Cada variable en DOSLang tiene un tipo y un identificador
asociado que no puede cambiar a lo largo de la ejecución.

Las variables declaradas en esta sección son globales.


<VariableDeclarations> ::= VAR <VariableDeclarationList>
|

<VariableDeclarationList> ::= <VariableDeclarationList> <VariableDec>


| <VariableDec>

<VariableDec> ::= <IdList> ':' <TypeSpecifier> ';'


| <IdList> ':' <TypeSpecifier> = <Expression>';'

Sintaxis 2: declaración de una variable local


Fuente: Elaboración propia.

26
var
age, weekdays : integer = 0;
choice, isready: boolean;
initials, grade: char;
name, surname : string;

Ejemplo 4: declaración de variables


Fuente: Elaboración propia.

Consideraciones:
● No puede declararse una variable con el mismo identificador de una variable
existente en el ámbito actual, de suceder debe reportarse un error.
● La inicialización es opcional y el tipo de la expresión debe ser el mismo que el
declarado, de lo contrario debe reportarse un error.
● Si existe un valor de inicialización este es el mismo para todas las variables en la
lista
● No pueden definirse nombre de variables con el mismo nombre que una función
o procedimiento

5.4 Declaración de constantes


Las constantes se definen con la palabra reserva const, a diferencia de las variables
estos no pueden cambiar de valor a lo largo de toda la ejecución del programa.

<ConstDeclarations> ::= CONST <ConstDeclarationList>


|

<ConstDeclarationList> ::= <ConstDeclarationList> <ConstDec>


| <ConstDec>

<ConstDec> ::= <IdList> ':' <TypeSpecifier> ';'


| <IdList> ':' <TypeSpecifier> = <Expression>';'

Sintaxis 3: declaración de una variable local


Fuente: Elaboración propia.

const
PI : real = 3.1416;

Ejemplo 5: declaración de variables


Fuente: Elaboración propia.

Consideraciones:
● No puede declararse más de una constante con el mismo identificador.
27
● La inicialización es requerida y el tipo de la expresión debe ser el mismo que el
declarado, de lo contrario debe reportarse un error.
● La expresión de valor inicial afecta a todas las constantes en la lista
● No pueden definirse nombre de constantes con el mismo nombre que una función
o procedimiento

5.5 Expresiones
DOSLang tendrá soporte a diferentes operaciones, todas estas serán descritas en
conjunto como operaciones, las expresiones son:

5.5.1 Operaciones Aritméticas


DOSLANG contara con un conjunto de operaciones aritméticas. Una operación
aritmética es la acción de un operador sobre los elementos de un conjunto. El operador
toma los elementos iniciales y los relaciona con otro elemento de un conjunto final que
puede ser de la misma naturaleza o no. Para la precedencia de operadores consultar
Apéndice A.

5.5.1.1Suma
Consiste en la adición de dos o más elementos para llegar a un resultado final donde
todo se incluye. El símbolo de la suma es el símbolo más (+) y se intercala entre los
elementos que se quiere sumar

Operandos Tipo resultante Ejemplos


int + real real 100 + 4.9 = 104.9
real + int 17.8 + ‘a’ = 114.8
real + char 2.5 + 2.5 = 5.0
char + real
real + real
int + char int 8 + ‘a’ = 105
char + int 10 + 15 = 25
int + int ‘a’ + ‘a’ = 194
char + char
word + int word “2” + 2 = “22”
int + word “2” + 4.05 = “24.05”
word + char “condición? ” + false =
char + word “condición? false”
word + real “2” + “4” = “24”
real + word
word + boolean
boolean + word
word + word
String + int String “2” + 2 = “22”
28
int + String “2” + 4.05 = “24.05”
String + char “condición? ” + false =
char + String “condición? false”
String + real “2” + “4” = “24”
real + String
String + boolean
boolean + String
String + word
Word + String
String + String
Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.1.2Resta
La resta es una operación aritmética básica a través de la cual se le disminuye al
minuendo la cantidad indicada en el sustraendo. El símbolo de la resta es el símbolo
menos (-) y se intercala entre los elementos que se quiere restar.

Operandos Tipo resultante Ejemplos


int – real 8 - 1.5 = 6.5
real - int 200.0 - ‘b’ = 102.0
real - char real 3.5 - 3.5 = 0
char - real
real - real

int - char 95 - ‘d’ = -5


char - int 50-10 = 40
int
int – int
char - char

Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.1.3Multiplicación
Consiste en calcular el resultado (producto) de sumar un mismo número (multiplicando)
tantas veces como indica otro número (multiplicador). La multiplicación se representa por
el símbolo (*) y se intercala entre los elementos que se quiere multiplicar.

29
Operandos Tipo Ejemplos
resultante
int * real 10.25 * 12.3 = 1550.72
real * int 20.1 * 5 = 80.4
real * char real 2.5 * ‘b’ = 240.0
char * real 3.5 * 8.5 = 29.75
real * real

int * char 2 * ‘a’ = 194


char * int ‘d’ * -3 = -300
int
int * int
char * char
Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.1.4 Potencia
La potenciación es una operación matemática entre dos términos denominados: base y
exponente. Se escribe y se lee normalmente como «a elevado a la n» . La potencia se
representa por el símbolo (^).

Operandos Tipo Ejemplos


resultante
int ^ int 2^5 = 32
char ^ int ‘a’^2 = 9409
int
int ^ char
char ^ char
Consideraciones:
● El primer operando es la base y el segundo es el exponente.
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.1.5 Modulo
Operación aritmética que retorna como resultado el residuo de una división. El módulo
se representa por el símbolo (%) y se intercala entre los elementos entre los que se
quiera obtener el módulo.

30
Operandos Tipo resultante Ejemplos
int % real 16 % 3.5 = 2.0
real % int ‘a’ % 94.5 = 2.5
real % char real 10.5 % 3.4 = 0.3
char % real
real % real

int % char 10 % 3 = 1
char % int int ‘b’ % 100 = 98
int % int
char % char

Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.1.6 División
La división es una operación matemática o aritmética que consiste en averiguar cuántas
veces un número (el divisor) está contenido en otro número (el dividendo). La división se
representa con el símbolo (/) y se intercala entre los elementos entre los que se quiera
obtener el resultado.

Operandos Tipo resultante Ejemplos


int / real 15 / 5.0 = 3.0
real / int 250.0/ ‘x’ = 2.083
real / char real
char / real
real / real

int / char 3 / 2 = 1
int
char / int ‘a’ / 8 = 12
(se descartan los
int / int
decimales)
char / char
Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.
● Si el divisor es 0, se tendrá que reportar un error en tiempo de ejecución, es decir,
no cuando se esté generando el formato de código intermedio.

31
5.5.2 Operadores relacionales
Una operación relacional es una operación que compara la igualdad, diferencia o
comportamiento entre dos valores. Este tipo de comparaciones siempre devuelve un
valor lógico según el resultado de la comparación (true | false). Una operación relacional
siempre tiene dos operandos y un único operador.

Operador relacional Operador Ejemplos de uso


relacional
int [>,<,>=,<=] real
15 < 10 = false
real [>,<,>=,<=] int
4.5 < 3 = false
real [>,<,>=,<=] char
50.58 >= ‘b’ = false
char [>,<,>=,<=]real
‘b’ <= 107 = true
int [>,<,>=,<=] char >,<,>=,<=
‘a’ <= 100 = true
char [>,<,>=,<=] int
200.25 > 52.2 = true
real [>,<,>=,<=]real
0 <= 0 = true
int [>,<,>=,<=]int
‘a’ < ‘a’ = false
char [>,<,>=,<=]char

int [=,<>]real
real [=,<>] int
real [=,<>] char
char [=,<>]real
50 = 50.0 = true
int [=,<>] char
5.5 <> 30 = true
char [=,<>] int
‘a’ = ‘a’ = true
real [=,<>]real
=,<> 1.2 <> 0.2 = false
int [=,<>] int
“123A” = “123a” = false
char [=,<>] char
true = true = true
String [=,<>] String
true <> true = false
String [=,<>] word
word [=,<>] String
word [=,<>] word
boolean [=,<>] boolean
Consideraciones:
● Cualquier otra combinación que no esté listada en la tabla deberá reportarse como
un error semántico.

5.5.3 Operaciones lógicas


Las operaciones lógicas son expresiones matemáticas cuyo resultado es un valor
booleano (true o false). Estas expresiones se utilizan principalmente en las estructuras
de control. Para la precedencia de operadores consultar Apéndice A.
32
Se presentan las operaciones lógicas que posee DOSLANG asi como sus tablas de
verdad:

AND
Multiplicación lógica o conjunción
Operando A Operando B AAND B
FALSE FALSE FALSE
FALSE TRUE FALSE
TRUE FALSE FALSE
TRUE TRUE TRUE
OR
Suma lógica o disyunción
Operando A Operando B A OR B
FALSE FALSE FALSE
FALSE TRUE TRUE
TRUE FALSE TRUE
TRUE TRUE TRUE
NAND
Compuerta Not And
Operando A Operando B A NAND B
FALSE FALSE TRUE
FALSE TRUE TRUE
TRUE FALSE TRUE
TRUE TRUE FALSE
NOR
Compuerta Not Or
Operando A Operando B A NOR B
FALSE FALSE TRUE
FALSE TRUE FALSE
TRUE FALSE FALSE
TRUE TRUE FALSE
NOT
Negación
Operando A NOT A
FALSE TRUE
TRUE FALSE
Consideraciones:
● Ambos operadores deberán ser de tipo booleano.
33
5.6 Funciones y Procedimientos
En DOSLang es una función es un subprograma que retorna siempre un valor, mientras
que un procedimiento es un subprograma que realiza un conjunto de acciones sin ningún
retorno.

5.6.1 Funciones
Una función es un grupo de declaraciones que juntas realizan una tarea. Cada programa
tiene al menos una función, que es el programa en sí, y todos los programas más triviales
pueden definir funciones adicionales. Una declaración de una función le dice al
compilador sobre el nombre, el tipo de retorno y los parámetros de una función. Una
definición de función proporciona el cuerpo real de la función.

En DOSLang, una función se define usando la palabra clave “function”. La forma general
de una definición de función es la siguiente

Sintaxis

<FunctionHeader> ::= FUNCTION id <Arguments> ':' <TypeSpecifier> ';'


[LOCAL VARS]
BEGIN
(* Cuerpo de la funcion*)
END;

<Arguments> ::= '(' <ArgumentList> ')'


|

<ArgumentList> ::= <Arg>


| <Arg> ';' <ArgumentList>

<Arg> ::= <IdList> ':' <TypeSpecifier>


| VAR <IdList> ':' <TypeSpecifier>

Ejemplo

program recursiveFibonacci;
var
i: integer;

function fibonacci(n: integer): integer;

34
begin
if n=1 then
fibonacci := 0;

else if n=2 then


fibonacci := 1;

else
fibonacci := fibonacci(n-1) + fibonacci(n-2);
end;

begin
for i:= 1 to 10 do

write(fibonacci (i), ' ');


end.

Consideraciones
• Si el tipo de retorno y el valor retornado no coinciden, se deberá de reportar un
error.
• Los parámetros pueden ser de cualquier tipo.
• Las funciones soportan todo tipo de llamadas recursivas
• Las funciones soportan sobrecarga de parámetros
• Si la función no define no define ningún argumento los paréntesis no son
requeridos

5.6.1.1Return
En DosLang no existe una palabra reservada para la sentencia return para la devolución
de resultados en una función. Para identificar el valor de retorno de una función se hace
una asignación a una variable predefinida con el mismo nombre de la función

Function Suma(a, b : Integer);


BEGIN
Suma := a + b; { valor de retorno}
END
> 120

Ejemplo 6: sentencia return


Fuente: Elaboración propia.

35
Consideraciones:
• Debe verificarse que el valor de la expresión sea del mismo tipo del que fue
declarado en dicha función.
• No puede declarase una variable local del mismo nombre que la función

5.6.2 Procedimientos
En DOSLang, un procedimiento se define utilizando la palabra clave de procedure. La
forma general de una definición de procedimiento es la siguiente:
Sintaxis

<ProcedureHeader> ::= PROCEDURE id <Arguments> ';'

Ejemplo

procedure findMin(x, y, z: integer; var m: integer);


(* Finds the minimum of the 3 values *)

begin
if x < y then
m := x
else
m := y;

if z <m then
m := z;
end; { end of procedure findMin }

Consideraciones
• Los parámetros pueden ser de cualquier tipo.

5.6.3 Paso de Parámetros


Si un subprograma (función o procedimiento) va a usar argumentos, debe declarar
variables que acepten los valores de los argumentos. Estas variables se denominan
parámetros formales del subprograma.
Los parámetros formales se comportan como otras variables locales dentro del
subprograma y se crean al ingresar al subprograma y se destruyen al salir. Hay dos
formas en que los argumentos se pueden pasar al subprograma

36
5.6.3.1Paso Por Valor
Este método copia el valor real de un argumento en el parámetro formal del subprograma.
En este caso, los cambios realizados en el parámetro dentro del subprograma no tienen
efecto en el argumento.
Por defecto, DOSLang utiliza el método de llamada por valor para pasar argumentos. En
general, esto significa que el código dentro de un subprograma no puede alterar los
argumentos utilizados para llamar al subprograma.

program exCallbyValue;
var
a, b : integer;
(*procedure definition *)
procedure swap(x, y: integer);

var
temp: integer;

begin
temp := x;
x:= y;
y := temp;
end;

begin
a := 100;
b := 200;
writeln('Before swap, value of a : ', a );
writeln('Before swap, value of b : ', b );

(* calling the procedure swap by value *)


swap(a, b);
writeln('After swap, value of a : ', a );
writeln('After swap, value of b : ', b );
end.

{*
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200

*}

37
5.6.3.2 Paso Por Referencia
Este método copia la dirección de un argumento en el parámetro formal. Dentro del
subprograma, la dirección se utiliza para acceder al argumento real utilizado en la
llamada. Esto significa que los cambios realizados en el parámetro afectan el argumento.

Para pasar los argumentos por referencia, DOSLang permite definir parámetros
variables. Esto se hace precediendo los parámetros formales por la palabra clave var.

program exCallbyRef;
var
a, b : integer;
(*procedure definition *)
procedure swap(var x, y: integer);

var
temp: integer;

begin
temp := x;
x:= y;
y := temp;
end;

begin
a := 100;
b := 200;
writeln('Before swap, value of a : ', a );
writeln('Before swap, value of b : ', b );

(* calling the procedure swap by value *)


swap(a, b);
writeln('After swap, value of a : ', a );
writeln('After swap, value of b : ', b );
end.

{*
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100

*}

38
5.6.4 Procedimientos y Funciones anidadas
Una función anidada (o procedimiento) que se encuentra definida dentro de otra función.
Las funciones contenidas dentro de otra función no son visibles desde afuera de la
función contenedora. Pueden definirse cualquier nivel de funciones anidada
Las funciones (y procedimientos) anidados tienen acceso a las variables locales
definidas en la función que los contiene.
Ejemplo de función anidada

function E(x: real): real;


function F(y: real): real;
begin
F := x + y
end;
begin
E := F(3) + F(4)
end;

Ejemplo de procedimientos anidados

PROCEDURE CallBack; { Outer level procedure }


BEGIN
END;
PROCEDURE CallingProcedure; { Outer level procedure }
VAR aVar: integer;

PROCEDURE NestedCallBack; { Nested procedure - can access "aVar" }


VAR
anotherVar: integer;
BEGIN {NestedCallBack }
aVar := 1;
anotherVar := 2;
END; {NestedCallBack }

BEGIN {CallingProcedure }
CallBack;
NestedCallBack;
END;

5.6.5 Funciones Propias del lenguaje


DOSLang posee un conjunto de funciones propias del lenguaje que permiten una
experiencia más agradable al usuario. Estas son:
39
5.6.5.1 CharAt
Recibe como parámetro una cadena y un entero que indica la posición del carácter que
se desea obtener. Devuelve el caracter indicado por parámetro

Sintaxis

charAt(cadena : string; posicion: int)

Ejemplo

caracter := charAt(“Cadena”,1) (* character toma el valor de “a” *)

5.6.5.2 Length
Recibe como parámetro una cadena y devuelve un entero con la longitud de la cadena.

Sintaxis

length(cadena : string)

Ejemplo

cant := length(“Cadena”) (* cant toma el valor de 6 *)

5.6.5.3 Replace
Recibe como parámetro dos cadenas. La primera indica la cadena que se quiere
modificar y la segunda indica que caracteres se quieren remplazar. Devuelve otra cadena
removiendo lo indicado en el segundo parámetro

Sintaxis

raplace(cadena, valores : string)

Ejemplo

cad := replace(“Cadena”, “Cad”) (* cad toma el valor de “ena” *)


cad := replace(“Cadena”, “cad”) (* cad toma el valor de “Cadena” *)

5.6.5.4 ToCharArray
Recibe como parámetro una cadena y devuelve un vector de tipo char con cada una de
las letras de la cadena.

Sintaxis

40
toCharArray(cadena: string)

Ejemplo

cad := toCharArray(“Cadena”) (* cad toma el valor de [C,a,d,e,n,a] *)

5.6.5.5 ToLowerCase
Recibe como parámetro una cadena y devuelve una segunda cadena que contiene la
palabra indicada en el parámetro con todas sus letras minúsculas.
Sintaxis

toLowerCase(cadena: string)

Ejemplo

cad := toLowerCase(“Cadena”) (* cad toma el valor de “cadena” *)

5.6.5.6 ToUpperCase
Recibe como parámetro una cadena y devuelve una segunda cadena que contiene la
palabra indicada en el parámetro con todas sus letras mayúsculas.
Sintaxis

toUpperCase(cadena: string)

Ejemplo

cad := toUpperCase(“Cadena”) (* cad toma el valor de “CADENA” *)

5.6.5.7 Equals
Recibe como parámetro dos cadenas y devuelve un booleano que indica si las cadenas
son iguales o no
Sintaxis

equals(cad1, cad2 : string)

Ejemplo

cad := equals(“Cadena”, “cadena”) (* cad toma el valor de false *)


cad := equals(“Mate”, “Mate”) (* cad toma el valor de true *)

41
5.6.5.8 Trunk
Recibe como parámetro un valor con punto flotante (real) y devuelve un entero sin los
valores decimales.
Sintaxis

trunk(val : real)

Ejemplo

val := trunk(5.8) (* val tome el valor de 5 *)

5.6.5.9 Round
Recibe como parámetro un valor con punto flotante (real) y devuelve un entero
redondeando el valor decimal según las siguientes reglas:
• Si el decimal es mayor o igual que 0.5, se aproxima al número superior
• Si el decimal es menor que 0.5, se aproxima al número inferior

Sintaxis

round(val : real)

Ejemplo

val := round(5.8) (* val tome el valor de 6 *)


val := round(5.5) (* val tome el valor de 6 *)
val := round(5.4) (* val tome el valor de 5 *)

6 Sentencias
Son los elementos básicos en los que se divide el código en un lenguaje de
programación. En DOSLang el signo que las separa es el punto y coma.

6.1 Bloques
Un bloque es un conjunto de sentencias que será delimitado por las palabras begin (o el
nombre de la sentencia) y end

Consideraciones
● Si un bloque contiene únicamente una sentencia pueden omitirse las palabras
reservadas begin y end.

42
● La palabra reservada “end.” (con punto al final) es exclusivo para el bloque
principal
● La palabra reservada “end;” (con punto y coma al final) sirva para delimitar un
bloque de otro totalmente independiente.
● La palabra reservada “end” (sin ningún símbolo de puntuación) sirve para
delimitar un subloque de una sentencia compuesta. Por ejemplo, el bloque de un
IF que contiene sentencias Else.

<CompoundStatement> ::= BEGIN <StatementList> END;

<StatementList> ::= <Statement>


| <Statement> ';' <StatementList>

<Statement> ::= <AssignmentStatement>


| <ProcedureCall>
| <ForStatement>
| <WhileStatement>
| <IfStatement>
| <CaseStatement>
| <RepeatStatement>
|
Sintaxis 4: bloque de sentencias
Fuente: Elaboración propia.

6.2 Sentencia With


Sirve para hacer referencia a los atributos de un Registro sin tener que especificar el
nombre de la variable. En cada bloque que se haga uso de la sentencia with, al momento
de encontrarse con una variable, se debe validar si esta variable es un atributo del
registro especificado, si es así, hacer uso de ella. En caso contrario se debe tratar como
una variable (local o global) normal. Se define con la siguiente sintaxis

With <nombre_variable> do <Statement>

Program withexample;
Type
Estudiante = Record
Edad : integer;
Nombre: Word;
End;
Var estudiante1, estudiante2 : Estudiante;

43
Begin
(* Ejemplo de asignación y lectura sin With *)
estudiante1.nombre := “Rainman”;
estudiante1.edad := 26;
Writeln(estudiante1.nombre, “ ”, estudiante1.edad);

(* Ejemplo de asignación y lectura con With *)


With estudiante2 do
{ dentro de las sentencias with no es necesario escribir el
nombre del registro para hacer uso de los atributos }
nombre := “Luis”; (* equivalente a estuidante2.nombre*)
edad := 23; (* equivalente a estuidante2.edad*)
Writeln(nombre, “ ”, edad)
End;
(* Sin la sentencia with si necesito escribir el nombre del registro*)
Writeln(estudiante2.nombre,“ ”, estudiante2.edad);

End.
> Rainman 26
> Luis 23
> Luis 23

Consideraciones
• Si existe una propiedad del registro y una variable (local o global) con el mismo
nombre, la propiedad del registro tiene mayor precedencia.

6.3 Asignación de variables


Una asignación de variable consiste en asignar un valor a una variable. A las variables
se les asigna un valor con dos puntos y el signo igual, seguido de una expresió. La forma
general de asignar un valor es

44
<AssignmentStatement> ::= <Variable> ':=' <Expression> ';'

<Variable> ::= id
| <Variable> '.' id
| <Variable> '(' <ExpressionList> ‘)’
| <Variable> '[' <ExpressionList> ']'
Sintaxis 5: asignación de variables
Fuente: Elaboración propia.

Nombre := “Luis”;

Cantidad := getCantidad();

Ejemplo 7: asignación de variables


Fuente: Elaboración propia.

Consideraciones:
● Si se intenta asignar un valor a una variable que no ha sido definida se debería de
reportar el error.
● Se debe verificar que el tipo que se está asignado sea el mismo con el que fue
definida la variable

6.4 Sentencias de transferencia


DOSLang soportará tres sentencias de salto o transferencia: break, continue y exit.
Estas sentencias transferirán el control a otras partes del programa.

6.4.1 Break
La sentencia break sirve para terminar la ejecución de un ciclo.

<break_def> := break ;

Sintaxis 6: sentencia break


Fuente: Elaboración propia.

VAR cuenta : Int = 0;


While cuenta < 5 DO
BEGIN
cuenta++;
if cuenta = 3 then
BEGIN
break;

45
END;
Writeln(cuenta);
END;
> 1
> 2

Ejemplo 8: sentencia break


Fuente: Elaboración propia.
Consideraciones:
• Deberá verificarse que la sentencia break aparezca dentro de un ciclo.
• La sentencia break únicamente detendrá la ejecución del bloque de sentencias
asociado a la sentencia cíclica que la contiene.

6.4.2 Continue
La sentencia continue se utilizará para transferir el control al principio del ciclo, es decir,
continuar con la siguiente iteración.

<continue_statement> ::= “continue” “;”

Sintaxis 7: sentencia continue


Fuente: Elaboración propia.

VAR cuenta : Int = 0;


While cuenta < 5 DO
BEGIN
cuenta++;
if cuenta == 3 THEN
BEGIN
Continue;
END;
Writeln(cuenta);
END;
> 1
> 2
> 4
> 5

Ejemplo 9: sentencia continue


Fuente: Elaboración propia.
46
Consideraciones:
• Deberá verificarse que la sentencia continue aparezca dentro de un ciclo y
afecte solamente las iteraciones de dicho ciclo.

6.4.3 Exit
La sentencia exit termina la ejecución del programa o subprograma actual y retorna
la ejecución al bloque que hizo la llamada. Si la palabra reservada exit se encuentra
en el programa principal el programa termina su ejecución.

Consideraciones:
• Solo aplica para el bloque principal y para el bloque principal de funciones y
procedures.

6.5 Sentencias de selección


6.5.1 Sentencia If
Esta sentencia de selección bifurcará el flujo de ejecución ya que proporciona control
sobre dos alternativas basadas en el valor lógico de una expresión (condición).

<IfStatement> ::= IF <Expression> THEN <Statement> [ELSE IF <statement>]*


ELSE <Statement>

Sintaxis 8: sentencia if
Fuente: Elaboración propia.

if color = red then

writeln('You have chosen a red car')

else

writeln('Please choose a color for your car');

Ejemplo 10: sentencia if


Fuente: Elaboración propia.
6.5.2 Switch
Esta sentencia de selección sirve para bifurcación múltiple, es decir, proporcionará
control sobre múltiples alternativas basadas en el valor de una expresión de control.
La sentencia switch compara con el valor seguido de cada case, y si coincide, se ejecuta
el bloque de sentencias asociadas a dicho case, y posteriormente finaliza su ejecución.

47
<CaseStatement> ::= CASE <Expression> OF <CaseList> [DEFAULT <statement>]?
END

<CaseList> ::= <Case>

| <Case> ';' <CaseList>

<Case> ::= <ConstantList> ':' <Statement>

<ConstantList> ::= <constant>

| <constant> ',' <ConstantList>

Sintaxis 9: sentencia switch


Fuente: Elaboración propia.

Ejemplo

program checkCase;

var

grade: char;

begin

grade := 'A';

case (grade) of

'A' : writeln(“Excellent!” );

'B', 'C': writeln(“Well done” );

'D' : writeln(“You passed” );

'F' : writeln(“Better try again” );

end;

writeln(“Your grade is “, grade );

end.

Ejemplo 11: sentencia switch


Fuente: Elaboración propia.
Consideraciones:
• Si 𝑖 es el caso actual y 𝑛 el número de casos, si la expresión del caso 𝑖 llegara a
coincidir con la expresión de control (para 𝑖 < 𝑛), se ejecuta el bloque i
correspondiente y la evaluación termina.
48
• Tanto la expresión de control como las expresiones asociadas a cada uno de los
casos deberá ser de tipo primitivo, es decir, char, integer, real, String,
Word o boolean.
• Si ninguno de los casos coincide con la expresión de control se ejecutará el bloque
de sentencias asociadas a default.

6.6 Ciclos
6.6.1 While
La sentencia cíclica mientras se utilizará para crear repeticiones de sentencias en el flujo
del programa.
El bloque de sentencias se ejecutará mientras la condición sea verdadera (0 𝑎 𝑛 𝑣𝑒𝑐𝑒𝑠),
de lo contrario el programa continuará con su flujo de ejecución normal.

<WhileStatement> ::= WHILE <Expression> DO <Statement>

Sintaxis 10: sentencia while


Fuente: Elaboración propia.

program whileLoop;
var
a: integer;

begin
a := 10;
while a < 20 do

begin
writeln(“value of a: “, a);
a := a + 1;
end;
end.

Ejemplo 12: sentencia while


Fuente: Elaboración propia.
6.6.1.1 Repeat
La sentencia cíclica repeat se utilizará para crear repeticiones de sentencias en el flujo
del programa.

49
El bloque de sentencias se ejecutará una vez y se seguirá ejecutando mientras la
condición sea verdadera (1 𝑎 𝑛 𝑣𝑒𝑐𝑒𝑠), de lo contrario el programa continuará con su flujo
de ejecución normal.

<RepeatStatement> ::= REPEAT <StatementList> UNTIL <Expression>

Sintaxis 11: sentencia do


Fuente: Elaboración propia.

program repeatUntilLoop;
var
a: integer;

begin
a := 10;
(* repeat until loop execution *)
repeat
writeln(“value of a: “, a);
a := a + 1
until a = 20;
end.

Ejemplo 13: sentencia do


Fuente: Elaboración propia.

Consideraciones:
• Este bloque no hace uso de las palabras reservadas begin y end

6.6.1.2 For
La sentencia cíclica para permitirá inicializar o establecer una variable como variable de
control, el ciclo tendrá una condición que se verificará en cada iteración, luego se deberá
definir una operación que actualice la variable de control cada vez que se ejecuta un ciclo
para luego verificar si la condición se cumple.

<ForStatement> ::= FOR id ':=' <Expression> TO <Expression> DO <Statement>


| FOR id ':=' <Expression> DOWNTO <Expression> DO
<Statement>

50
Sintaxis 12: sentencia for
Fuente: Elaboración propia.

program forLoop;

var

a: integer;

begin

for a := 10 to 20 do

begin

writeln(“value of a: “, a);

end;

end.

Ejemplo 14: sentencia for


Fuente: Elaboración propia.

Consideraciones:
• La variable de control se auto-incremente en uno al alcanzar el final del ciclo antes
de la siguiente iteración

6.7 Sentencias de entrada/salida


6.8 Sentencia Write
Instrucción que permitirá imprimir una expresión en la consola del editor en línea.

<statement> ::= “write” “(” <Expression_list> “)” “;”


| “writeln” “(” <Expression_list> “)” “;”

Sintaxis 13: sentencia write


Fuente: Elaboración propia.

write(“Hola ”);
writeln(“mundo”); //Agrega un salto de linea
write(69);

51
(*contatenación*)
Write(“hola”, “ “, “mundo”);

Ejemplo 15: sentencia writeln


Fuente: Elaboración propia.

Consideraciones:
• Si se utiliza la sentencia writeln se incluirá un salto de línea al final, de lo
contrario, si es una sentencia write no se incluirá un salto de línea al final
• Si existe más de un valor de expresión debe concatenar los valores
• Debe imprimirse la representación a String del valor que se esté pasando.
• En caso de que la expresión sea nil, deberá imprimirse la palabra nil.

6.8.1.1Lectura de datos de parte del usuario


En DOSLang existirá una función que interrumpirá el flujo normal del programa para pedir
un valor al usuario, a través de la consola del editor, el valor ingresado deberá ser
almacenado en la variable recibida como parámetro. Si el tipo de dato recibido es
diferente del dato esperado deberá de mostrar un mensaje de error y volverá a solicitarlo.

<read_console_statement> ::=
“read” “(” id “)”

Sintaxis 14: sentencia read


Fuente: Elaboración propia.

int Id_1;

String Id_2;

char Id_3;

boolean Id_4;

double Id_5;

read (Id_1);

read (Id_2);

read (Id_3);

read (Id_4);

read (Id_5);

52
Ejemplo 16: sentencia read

7 Generación de código intermedio


Cuando el compilador termine la fase de análisis de un programa escrito en DOSLang,
realizará una transformación a una representación intermedia equivalente al código de
alto nivel del lenguaje mencionado anteriormente. La representación intermedia para el
lenguaje será una representación intermedia de cuádruplos.
El flujo de ejecución de cuádruplos será una ejecución top-down, esto quiere decir que
el flujo comenzará desde la primera línea de instrucción y seguirá el flujo con las
siguientes líneas. El formato de cuádruplos es una representación reconocida por el
compilador, a diferencia del código 3 direcciones el formato de cuádruplos tiene 4
campos llamados: operador, argumento1, argumento2 y un campo que indicará el
espacio de memoria donde se almacenará el resultado. El campo operador contiene un
símbolo operador que estará entre los disponibles del código de cuádruplos.
Por ejemplo, la instrucción de tres direcciones x = y + z se representa colocando a “+” en
el campo operador, “y” en el campo argumento1, “z” en el campo argumento2 y “x” en el
campo de resultado.
Operador Argumento 1 Argumento 2 Resultado
+ Y Z X

Sintaxis
Operador, argumento_1, argumento_2, resultado

Consideraciones
• Cada campo debe estar delimitado por comas
• El campo operador como el del resultado no pueden venir vacíos.

53
• El campo de argumento 1 y 2 pueden venir vacíos dependiendo de la operación
que se realizará.
Ejemplo
+, 0, 5, t1
*, t1, t2, t3
=, 5, , t1

7.1 Temporales
Los temporales serán creados por el compilador en el proceso de generación de código
intermedio. Estos serán instanciados en el primer encuentro del interprete y deberán
empezar con la letra “t” seguida de un número.
t[0-9]+

A continuación, se muestran ejemplos de temporales validos:


t1
t152

7.2 Etiquetas
Etiquetas Las etiquetas serán creadas por el compilador en el proceso de generación de
código intermedio. Las etiquetas deberán empezar con la letra “L” seguida de un número.
L[0-9]+

A continuación, se muestran ejemplos de temporales validos:


L12
L25

7.3 Identificadores
Un identificador será utilizado para dar un nombre a variables, métodos o estructuras.

54
Un identificador es una secuencia de caracteres alfabéticos [A-Z a-z] incluyendo el guion
bajo [_] o dígitos [0-9] que comienzan con un carácter alfabético o guion bajo.

Ejemplos
este_es_un_identificador_valido_09
_este_tambien_09
Y_este_2018

7.4 Comentarios
Un comentario es un componente léxico del lenguaje que no es tomado en cuenta para
el análisis sintáctico de la entrada.
Existirán dos tipos de comentarios:
• Los comentarios de una línea que serán delimitados al inicio con los símbolos “//”
y al final con un carácter de finalización de línea
• Los comentarios con múltiples líneas que empezarán con los símbolos “/*” y
terminarán con los símbolos “*/”.

Ejemplo
//este es un ejemplo de un lenguaje de una sola línea
/*
este es un ejemplo de un lenguaje de múltiples líneas.
*/

7.5 Operadores aritméticos


Las operaciones aritméticas contarán con un argumento 1, argumento 2, campo para el
resultado y un operador perteneciente a la siguiente tabla:
Operación Símbolo Cuádruplos
Suma + +, t2, 6, t3
Resta - -, t2, 6, t3
Multiplicación * *, t2, 6, t3
División / /, t2, 6, t3
Modulo % %,5,1,t3

55
7.6 Saltos
Para definir el flujo que seguirá el intérprete se contará con bloques de código, estos
bloques están definidos por etiquetas y saltos, los saltos son aquellos que, definiéndole
una etiqueta y una posible condición, el flujo del código se desplaza hasta el bloque
contenido en luego de dicha etiqueta.
Cuádruplos contará con 2 tipos de saltos, los cuales son:
• Salto condicional: Contará con una condición para decidir si se realiza el salto o
no.
• Salto incondicional: Se realizará el salto siempre.

Ejemplo:
L1:
T1 = 5 * 2;
Jmp L2: // salto incondicional a L2
T2 = 5 + 1; // debido al salto esta instrucción no se ejecuta
L2: // luego de Jmp L2, el flujo continuo desde esta etiqueta

7.6.1 Saltos condicionales


Cuádruplos contará con una serie de saltos condicionales, donde contará con una
condición para ver si realiza la acción de salto o no, estas instrucciones contarán con un
argumento 1, argumento 2, etiqueta a la cual realizar el salto y un operador que definirá
el tipo de salto condicional a realizar, el operador debe estar en la siguiente tabla:
Operación Símbolo Equivalente Cuádruplos Alto nivel
Igual que Je == Je, t1, t2, L1 T1 == T2
Diferente que Jne != Jne, t1, t2, L1 T1 != T2
Mayor que Jg > Jg, t1, t2, L1 T1 > T2
Menor que Jl < Jl, t1, t2, L1 T1 < T2
Mayor o igual que Jge >= Jge, t1, t2, L1 T1 >= T2
Menor o igual que Jle <= Jle, t1, t2, L1 T1 <= T2

56
Ejemplo
L1:
*, t1, t2, t3
=, 5, , t1
jl, t1, t2, L2 // si T1 < T2 salta a L2
*, t1, t2, t3 // si no se cumple, continua el flujo
=, 5, , t1
L2: // si cumple, el flujo salta a esta etiqueta

7.6.2 Salto incondicional


Los saltos incondicionales contarán únicamente con resultado y operador, donde el
resultado contendrá la etiqueta destino y el operador estará definido por la instrucción
jmp. Este realizará el salto hacia una etiqueta que se le especifique.
Sintaxis
Jmp, , , etiqueta

Ejemplo
L1:
*, t1, t2, t3
Jmp, , , L2 // Salto a etiqueta L2
+, t1, 5, t3
L2: // El flujo continuo aquí

7.7 Asignación a temporales


Cuádruplos contará con una instrucción de asignación sin necesidad de realizar una
operación previa, esta instrucción contará únicamente con un operador igual, valor que
se le asignará y un temporal como campo de resultado.
Sintaxis
=, valor , argumento No usado, temporal

57
=, valor, , temporal

Ejemplo
=, 5, , t1 // Equivalente t1 = 5
+, 0, 5, t3
=, t3, , t1 // Equivalente t1 = t3

7.8 Declaración de métodos


Cuádruplos proveerá una definición de métodos, estos son bloques de código los cuales
se accederá a ellos únicamente con una llamada al método y al momento de continuar
con el flujo, top-down, serán ignorados.

Sintaxis
Begin, , , idMetodo
// Serie de instrucciones cuádruplos
End, , idMetodo

Ejemplo
begin, , , metodo1
+, 0, 5, t1
*, t1, t2, t3
=, 5, , t1
end, , metodo1

7.9 Llamadas a métodos


Como se mencionó anteriormente la forma de acceder a los métodos será por medio de
llamadas, estas llamadas realizarán un salto en el flujo hacia el bloque del método y al
finalizar todo el método el flujo continuará justo donde fue llamado.
Sintaxis
Call, , , idMetodo

58
Ejemplo
begin, , , metodo1
+, 0, 5, t1
=, 5, , t1
end, , metodo1
call, , , metodo1

7.10 Print
Esta instrucción será una función nativa de cuádruplos, su función principal es imprimir
en consola un valor definido según el formato del parámetro que se le asigne, la sintaxis
y parámetros permitidos son los siguientes:

Sintaxis
print( parámetro, valor );

Parámetro Acción
%c Imprime el valor carácter del identificador,
se basa según el código ascii.
%e Imprime únicamente el valor entero del
valor.
%d Imprime con punto decimal el valor.

Ejemplo
Print(%c, 65); >A
Print(%e, 65.4); > 65
Print(%d, 65.4); > 65.4

7.11 Entrada o lectura de dato


Esta es una función nativa de la representación intermedia de cuádruplos, esta nos
permitirá interrumpir el flujo normal de la ejecución para pedir un valor al usuario. Esta
función recibirá los parámetros de las posiciones en la pila descritos en la siguiente tabla.

59
Sintaxis
call, , , $_in_value

Posición en la pila Descripción


0 Retorno, no es usado
1 Referencia, dirección, donde guardará el valor
2 Referencia a la cadena o valor ingresado

7.12 Entorno de ejecución


Como ya se expuso en otras secciones de este documento, el proceso de compilación
genera el código intermedio y este se va a ejecutar, siendo indispensable para el flujo de
la aplicación, en el código intermedio NO existen cadenas, operaciones complejas,
llamadas a métodos con parámetros y muchas otras cosas que sí existen en los
lenguajes de alto nivel. Esto debido a que el código intermedio busca ser una
representación próxima al código máquina, por lo que todas las sentencias de las que se
compone se deben basar en las estructuras que componen el entorno de ejecución.
Típicamente se puede decir que los lenguajes de programación cuentan con dos
estructuras para realizar la ejecución de sus programas en bajo nivel, la pila (Stack) y el
montículo (Heap), en la siguiente sección se describen estas estructuras.

7.12.1 Estructuras del entorno de ejecución


Las estructuras del entorno de ejecución no son más que arreglos de bytes que emplean
ingeniosos mecanismos para emular las sentencias de alto nivel. Este proyecto consistirá
de dos estructuras indispensables para el manejo de la ejecución, siendo estas, Stack y
Heap, las cuales al ser empleadas en el código de tres direcciones determinará el flujo y
la memoria de la ejecución, estas estructuras de control serán representadas por arreglos
de números con punto flotante, esto con el objetivo de que se pueda tener un acceso
más rápido a los datos sin necesidad de leer por grupos de bytes y convertir esos bytes
al dato correspondiente, como normalmente se hace en otros lenguajes, por ejemplo, en
Java, un int ocupa 4 bytes, un boolean ocupa 1 solo byte, un double ocupa 8 bytes, un
char 2 bytes, etc.

7.12.1.1 El Stack y su puntero


El stack es la estructura que se utiliza en código intermedio para controlar las variables
locales, y la comunicación entre métodos (paso de parámetros y obtención de retornos
60
en llamadas a métodos). Se compone de ámbitos, que son secciones de memoria
reservados exclusivamente para cierto grupo de sentencias.

Cada llamada a método o función que se realiza en el código de alto nivel cuenta con un
espacio propio en memoria para comunicarse con otros métodos y administrar sus
variables locales. Esto se logra modificando el puntero del Stack, que en el proyecto se
identifica con la letra P, para ir moviendo el puntero de un ámbito a otro, cuidando de no
corromper ámbitos ajenos al que se está ejecutando. A continuación, se ilustra su
funcionamiento con un ejemplo.

El puntero se identifica con la letra “P” y este contendrá la dirección de memoria donde
comenzará el ámbito actual, su asignación se realizará exactamente igual que a como
los terminales.

Ejemplos
=, t1, , P // Equivalente a asignar P = t1
=, P, , t1 // Equivalente a asignar t1 = P
+, P, 5, P // Equivalente a asignar P = P + 5

Como se mencionó anteriormente con el puntero del stack se debe manejar el concepto
de ámbitos en cuádruplos, por lo cual la simulación de cambio de ámbito se debe realizar
por medio de operaciones sobre este puntero.
Ejemplo
+, P, 5, P
Call, , , metodo1
-, P, 5, P

61
7.12.1.2 El Heap y su puntero
El Heap (o en español, montículo) es la estructura de control del entorno de ejecución
encargada de guardar las referencias a variables globales o valores de cadenas y
arreglos.

El puntero se definirá con el identificador H y a diferencia de P (que aumenta y disminuye


según lo dicta el código intermedio), este únicamente aumenta y aumenta, su función es
brindar siempre la próxima posición libre de memoria.
Esta estructura también será la encargada de almacenar las cadenas de texto,
guardando únicamente en cada posición el ASCII de cada uno de los caracteres que
componen la cadena a guardar.

Ejemplos
+, H, 1, H // Equivalente a asignar H = H + 1
=, H, , t1 // Equivalente a t1 = H

7.12.2 Acceso a estructuras del entorno de ejecución


Para asignar un valor a una estructura del sistema es necesario colocar el identificador
del arreglo (Stack o Heap), la posición donde se desea colocar el valor y el valor a
asignar con la siguiente sintaxis:

Sintaxis de asignación
= , dirección, valor, estructura

Ejemplo
=, t1, 5, Stack // Equivalente a asignar 5 en la dirección t1 al Stack (Stack[t1] = 5)
=, H, 5, Heap // Equivalente a asignar 5 en la dirección del puntero H al Heap
(Stack[t1] = 5)

Para la obtención de un valor de cualquiera de las estructuras (Stack o Heap) se realizará


con la siguiente sintaxis:

Sintaxis de obtención

62
= , estructura, dirección, campo de memoria a guardar

Ejemplo
= , Stack, t1, t2 // Equivalente a asignar en t2 la posición t1 del Stack ( t2 = Stack[t1] )
= , Heap, t1, t2 // Equivalente a asignar en t2 la posición t1 del Heap ( t2 = Heap[t1] )

8 Optimización de código intermedio


DOSLang deberá tener la opción de poder optimizar el codigo intermedio. Para el
proceso de optimización se utilizará el método conocido como mirilla (Peephole).
El método de mirilla consiste en utilizar una ventana que se mueve a través del código
de cuádruplos, la cual se le conoce como mirilla, en donde se toman las instrucciones
dentro de la mirilla y se sustituyen en una secuencia equivalente que sea de menor
longitud y lo más rápido posible que el bloque original. El proceso de mirilla permite que
por cada optimización realizada con este método se puedan obtener mejores beneficios.
Los tipos de transformación para realizar la optimización por mirilla serán los siguientes:
• Eliminación de instrucciones redundantes de carga y almacenamiento.
• Eliminación de código inalcanzable.
• Simplificación algebraica y reducción por fuerza.
Cada uno de estos tipos de transformación utilizados para la optimización por mirilla,
tendrán asociadas reglas las cuales en total son 18, estas se detallan a continuación:

8.1 Eliminación de instrucciones redundantes de carga y


almacenamiento.
Regla 1
Si existe una asignación de valor de la forma a = b y posteriormente existe una asignación
de forma b = a, se eliminará la segunda asignación siempre que a no haya cambiado su
valor. Se deberá tener la seguridad de que no exista el cambio de valor y no existan
etiquetas entre las 2 asignaciones:
Tabla 2: regla 1 de optimización
Fuente: Elaboración propia.

Ejemplo Optimización

=,t2, , b
=,t2, ,b
= b, , t2

63
8.2 Eliminación de código inalcanzable
Consistirá en eliminar las instrucciones que nunca serán utilizadas. Por ejemplo,
instrucciones que estén luego de un salto incondicional, el cual direcciona el flujo de
ejecución a otra parte y nunca llegue a ejecutar las instrucciones posteriores al salto
incondicional. Las reglas aplicables son las siguientes:

Regla 2
Si existe un salto condicional de la forma Lx y exista una etiqueta Lx:, todo código
contenido entre el goto Lx y la etiqueta Lx, podrá ser eliminado siempre y cuando no
exista una etiqueta en dicho código.

Ejemplo Optimización

jmp L1
Jmp L1
<instrucciones>
L1:
L1:

Regla 3
Si existe un alto condicional inmediatamente después de sus etiquetas Lv:
<instrucciones> Lf: se podrá reducir el número de saltos negando la condición,
cambiando el salto condicional hacia la etiqueta falsa Lf: y eliminando el salto condicional
innecesario a goto Lf y quitando la etiqueta Lv:.

Ejemplo Optimización

jl, t1, t2, L1


jmp L2
jg, t1, t2, L2
L1:
<instrucciones>
<instrucciones>
L2:
L2:
<instrucciones>

Regla 4
Si se utilizan valores constantes dentro de las condiciones y el resultado de la condición
es una constante verdadera, se podrá transformar en un salto incondicional y eliminarse
el salto hacia la etiqueta falsa Lf.

Ejemplo Optimización

64
Je, 1, 1, L1
jmp L1;
jmp L2;

Regla 5
Si se utilizan valores constantes dentro de las condiciones y el resultado de la condición
es una constante falsa, se podrá transformar en un salto incondicional y eliminarse el
salto hacia la etiqueta verdadera Lv.

Ejemplo Optimización

Je, 1, 0, L1
jmp L2;
jmp L2;

8.3 Simplificación algebraica y reducción por fuerza


La optimización por mirilla podrá utilizar identidades algebraicas para eliminar las
instrucciones ineficientes.

Regla 6
Eliminación de las instrucciones que tenga la siguiente forma:

Ejemplo Optimización

+, x, 0, x

Regla 7
Eliminación de las instrucciones que tenga la siguiente forma:
Tabla 3: regla 9 de optimización
Fuente: Elaboración propia.

Ejemplo Optimización

-, x, 0, x

Regla 8
Eliminación de las instrucciones que tenga la siguiente forma:
Tabla 4: regla 10 de optimización
Fuente: Elaboración propia.

65
Ejemplo Optimización

*, x, 1, x

Regla 9
Eliminación de las instrucciones que tenga la siguiente forma:

Ejemplo Optimización

/, x, 1, x

Para las reglas 12, 13, 14, 15 es aplicable la eliminación de instrucciones con
operaciones de variable distinta a la variable de asignación y una constante, sin
modificación de la variable involucrada en la operación, por lo que la operación se
descartará y se convertirá en una asignación.

Regla 10

Ejemplo Optimización

+, y, 0, x =, y, , x

Regla 11

Ejemplo Optimización

-, y, 0, x =, y, , x

Regla 12

Ejemplo Optimización

*, y, 1, x =, y, , x

Regla 13

Ejemplo Optimización

/, y, 1, x =, y, , x

66
Se deberá realizar la eliminación de reducción por fuerza para sustituir por operaciones
de alto costo por expresiones equivalentes de menor costo.

Regla 14

Ejemplo Optimización

*, y, 2, x +, y, y, x

Regla 15

Ejemplo Optimización

*, 0, y, x =, 0, , x

Regla 16
.

Ejemplo Optimización

/, 0, y, x =, 0, , x

67
9 Generación de código ensamblador
La herramienta DOSLang contendrá un módulo externo que será una aplicación que
tendrá como tarea la traducción del código cuádruplos a código ensamblador.
Como el lenguaje ensamblador es un lenguaje de bajo nivel, se podrá evaluar el código
intermedio por medio de un emulador y garantizar que la salida de cuádruplos es
correcta.

DOSLang

9.1 Entrada de ASM


Como se mencionó anteriormente el código de entrada para la aplicación de escritorio
será cuádruplos generado por DOSLang. Esta entrada deberá enviarse de forma similar
a la arquitectura cliente servidor, pero con comunicación en una sola vía o unidireccional
y no como una simple entrada de texto plano.

9.2 Salida de ASM


La aplicación de escritorio traducirá el código de entrada formato de tres direcciones
generada por Colette en su equivalente para código Assembler 8086. Este código
generado deberá ser ejecutado en un emulador (se recomienda Dosbox para realizar
pruebas) y deberá imprimir la salida esperada como en cuádruplos.

9.3 Registros de la CPU


La CPU x86 tiene 14 registros internos y básicos. Algunos son realmente de 32 bits pero
por ahora se utilizará el modo real que es compatible con el procesador 8086 (igualmente
accesibles a la parte alta de éstos registros, inclusive en el modo real) son registros de
16 bits. Los registros son los siguientes:

9.3.1 Registros de uso general


Estos registros son de uso general por lo cual el usuario puede utilizarlos, asignarlos y
asignar, pero tambien tendrá una funcionalidad por aparte de algunas instrucciones
nativas de asm, las cuales se definirán cada una más adelante.
Además, cada registro está conformado por 2 partes, baja y alta, estos pueden ser
utilizados como el registro entero.

68
• AX, Registro Acumulador: Conformado por 2 registros internos (AL:AH), los
cuales tambien se podrán utilizar pero son de menor tamaño (8 bits c/u).
• BX, Registro base Conformado por 2 registros internos (BL:BH), los cuales
tambien se podrán utilizar, pero son de menor tamaño (8 bits c/u).
• CX, Registro contador Conformado por 2 registros internos (CL:CH), los cuales
tambien se podrán utilizar, pero son de menor tamaño (8 bits c/u).
• DX, Registro de datos Conformado por 2 registros internos (DL:DH), los cuales
tambien se podrán utilizar, pero son de menor tamaño (8 bits c/u).

9.3.2 Registros de segmento


Estos registros son de segmento de datos de la memoria de la máquina, el único
segmento que se puede utilizar para uso general es el ES, todos los demás segmentos
toman su valor de otras instrucciones que se definirán más adelante.
• DS: Registro del segmento de datos
• ES: Registro del segmento extra
• SS: Registro del segmento de pila
• CS: Registro del segmento de código

9.3.3 Registros punteros


Estos registros son utilizados comúnmente para el uso de los punteros de las estructuras
definidas en assembler, estos registros tambien pueden ser utilizados como uso general.
• BP: Registro de apuntadores base
• SI: Registro índice fuente
• DI: Registro índice destino

9.3.4 Registros especiales


Estos registros no pueden ser utilizados por uso general, tienen sus usos especiales en
ciertas instrucciones que serán definidas más adelante.
• SP: Registro apuntador de la pila
• IP: Registro apuntador de la siguiente instrucción
• F: Registro de banderas (8 bits)

9.3.5 Bits del registro de banderas


Una bandera en asm es un estado especifico de la máquina que puede estar encendido
o apagado, estos bits representan un estado en la maquina y no puede asignársele valor,
únicamente se pueden consultar y poder realizar alguna condición dentro de las
instrucciones.
A continuación, se enumeran los registros y el estado que representan:

69
Estado que
Bit de apagado Bit de encendido
representa
OverFlow
NV OV
(Desbordamiento)
DN UP
Direction
(Hacia atrás) (Hacia adelante)

Interrupts DI EI

PL NG
Sign
(Positivo, no signo) (Negativo, con signo)

Zero NZ ZR

Auxilary carry NA AC

PO PE
Parity
(Impar) (Paridad par)

Carry NC CY

9.4 Estructura stack assembler


Assembler cuenta con una estructura auxiliar para el almacenamiento de datos, esta
estructura es el stack que es un arreglo de registros de datos de tamaño de 16 bits, esta
estructura puede ser de uso general con instrucciones de push y pop, pero tambien la
utiliza assembler para definir el flujo de la ejecución. Sus implicaciones y usos se
describirán más adelante detalladamente.

70
9.5 Instrucciones de transferencia de datos
En assembler se le llama transferencia de datos a la asignación de datos a una dirección
de memoria, ya sea una posición definida de una estructura o un registro de uso común,
esta transferencia de datos no afecta ni se puede aplicar a registros de banderas. Las
trasferencias de datos que proporciona assembler son las siguientes.

9.5.1 MOV
Es la transferencia de datos básica donde copia el valor del operando fuente (src) a la
dirección de memoria o registro destino (dest).

Sintaxis
MOV src , dest

Aplicaciones
• MOV reg, {reg | mem | inmed}
• MOV mem,{reg | inmed}
• MOV {reg16 | mem16},{CS | DS | ES | SS}
• MOV {DS | ES | SS},{reg16 |mem16}

Consideraciones
• No se puede transferir de una posición de memoria a otra posición de memoria,
si fuente es una posición de memoria el destino deberá ser un registro.
• Se recomienda transferir datos de un registro a otro únicamente si estos tienen la
misma cantidad de memoria asignada, (16 bits con 16bits, 8 bits con 8 bits, etc.).

9.5.2 PUSH
Esta es una transferencia de datos que involucra la estructura stack de assembler, esta
instrucción recibe un valor o dirección fuente (src) de 16 bits y lo coloca en el tope del
stack.
Sintaxis
Push src

Aplicaciones
Push {reg16 | mem16 | CS | DS | ES | SS}

71
9.5.3 POP
Esta es una transferencia de datos que involucra la estructura stack de assembler, en
esta instrucción se retira y se obtiene el valor de 16 bits que contiene el tope del stack y
lo coloca en una dirección destino definida (dest).

Sintaxis
Pop dest

Aplicaciones
Pop {reg16 | mem16 | DS | ES | SS}

9.5.4 XCHG
Es una transferencia de datos doble, donde se intercambia el registro fuente con el
destino y el registro destino con el de fuente, siendo realmente un intercambio de valores.

Sintaxis
XCHG src, dest

Aplicaciones
XCHG reg, {reg | mem}

9.5.5 XLAT
Realiza una transferencia de datos de una tabla específica, donde recibe la referencia
de la tabla y hace la transferencia a AL la posición AL de la tabla.
Sintaxis
XLAT tabla // Equivalencia a mov AL, [tabla + AL]

9.5.6 LEA
Realiza una transferencia de datos donde recibiendo un destino (dest), se le coloca la
dirección del dato fuente(src), siendo esta dirección relativa al segmento de datos.

72
Sintaxis
LEA dest, src // reg contiene el puntero de src

9.5.7 LDS
Realiza una doble transferencia de datos, donde la primera es recibiendo un destino
(dest), se le coloca la dirección del dato fuente(src), siendo esta dirección relativa al
segmento de datos y la segunda es que se coloca el valor del dato fuente en el registro
DS.
Sintaxis
LDS dest, src // reg contiene el puntero de src
// DS contiene el valor de src

9.5.8 LES
Realiza una doble transferencia de datos, donde la primera es recibiendo un destino
(dest), se le coloca la dirección del dato fuente(src), siendo esta dirección relativa al
segmento de datos y la segunda es que se coloca el valor del dato fuente en el registro
ES.

Sintaxis
LES dest, src // reg contiene el puntero de src
// ES contiene el valor de src

9.5.9 LAHF
Realiza una transferencia de datos donde copia los últimos 8 bits del segmento, los
cuales son las banderas, al registro AH, el orden de las banderas serían las siguientes:
SF, ZF, ?, AF, ?, PF, ?, CF, siendo los signos de interrogación bits indefinidos

Sintaxis
LAHF // AH tiene los bits SF, ZF, ?, AF, ?, PF, ?, CF

9.5.10 SAHF
Realiza una transferencia de datos donde copia los últimos 8 bits del registro AH a los
últimos 8 bits del segmento, siendo estas las banderas.

73
Sintaxis
SAHF // SF:ZF:?:AF:?:PF:?:CF <- AH

9.5.11 PUSHF
Primero decrementa en 2 el puntero del stack y almacena los bits de las banderas en el
Stack de assembler, esperando ser recuperados por POPF

Sintaxis
PUSHF // SP <- SP - 2, [SP+1:SP] <- Flags.

9.5.12 POPF
Primero recupera los bits de las banderas en el Stack de assembler, almacenados por
PUSHF e incrementa en 2 el puntero del stack

Sintaxis
POPF // Flags <- [SP+1:SP], SP <- SP + 2

9.6 Instrucciones aritméticas


Assembler tiene un conjunto de varias instrucciones aritméticas, estas instrucciones
operan distintos registros y espacios de memoria y además afectan las banderas AF, CF,
OF, PF, SF, ZF para representar el estado de la operación o memoria, las instrucciones
que ofrece assembler son:

9.6.1 ADD
Realiza una operación de suma entre el operador destino(dest) y fuente(src), dejando el
valor resultante en el registro del destino.

Sintaxis
ADD dest, src

9.6.2 ADC
Realiza una operación de suma entre el operador destino(dest) y fuente(src), dejando el
valor resultante en el registro del destino, esta operación además suma la bandera CF,
la cual se encuentra activa (1) si existe acarreo, e inactiva (0) si no existe.

Sintaxis

74
ADC dest, src // Equivalente dest = dest + src + CF

9.6.3 SUB
Realiza una operación de resta entre el operador destino(dest) y fuente(src), dejando el
valor resultante en el registro del destino.
Sintaxis
SUB dest, src

9.6.4 SBB
Realiza una operación de resta entre el operador destino(dest) y fuente(src), dejando el
valor resultante en el registro del destino, esta operación además resta la bandera CF,
la cual se encuentra activa (1) si existe acarreo, e inactiva (0) si no existe.
Sintaxis
SBB dest, src // Equivalente dest = dest - src - CF

9.6.5 CMP
Realiza una operación de resta entre el operador destino(dest) y fuente(src), esta
instrucción, a diferencia de las restas anteriores, no guarda el valor en ningún registro y
únicamente sirve para alterar el estado de las banderas.

Sintaxis
CMP dest, src // Equivalente dest – src, alterando banderas AF, CF, OF, PF, SF, ZF

9.6.6 INC
Realiza una operación de suma entre el operador destino(dest) y un 1, dejando el valor
resultante en el registro del destino, siendo realmente un incremento del operador
destino, además no afecta la bandera CF.

Sintaxis
INC dest // Equivalente a dest = dest + 1

75
9.6.7 DEC
Realiza una operación de resta entre el operador destino(dest) y un 1, dejando el valor
resultante en el registro del destino, siendo realmente un decremento del operador
destino, además no afecta la bandera CF.

Sintaxis
DEC dest // Equivalente a dest = dest -1

9.6.8 NEG
Esta instrucción genera el complemento a 2 del registro y lo almacena en el mismo
operador destino.
Sintaxis
NEG dest // donde dest tiene que ser registro o memoria

9.6.9 DAA
Corrige el resultado de una suma de dos valores entre los registros de tipo BCD (decimal
codificado en binario (BCD)) empaquetados en el registro AL, esta instrucción debe estar
inmediatamente después de una instrucción ADD o ADC, alterando OF donde es
indefinido después de la operación.
Sintaxis
ADD dest, src | ADC dest, src
DAA

9.6.10 DAS
Corrige el resultado de una resta de dos valores entre los registros de tipo BCD
empaquetados en el registro AL, esta instrucción debe estar inmediatamente después
de una instrucción SUB o SBB, alterando OF donde es indefinido después de la
operación.
Sintaxis
SUB dest, src | SBB dest, src
DAS

76
9.6.11 MUL
Realiza una operación de multiplicación sin signo entre el operador destino(dest) y
fuente(src), dejando el valor resultante en el registro del destino.
Sintaxis
MUL dest, src

9.6.12 IMUL
Realiza una operación de multiplicación con signo entre el operador destino(dest) y
fuente(src), dejando el valor resultante en el registro del destino.
Sintaxis
IMUL dest, src

9.6.13 CBW
Extiende el signo de AL en AX, las banderas no se ven afectadas.

Sintaxis
CBW

9.7 Instrucciones lógicas


Assembler tiene un conjunto de varias instrucciones lógicas, estas instrucciones operan
distintos registros y espacios de memoria y además afectan las banderas AF, CF, OF,
PF, SF, ZF para representar el estado de la operación o memoria, las instrucciones que
ofrece assembler son:

9.7.1 AND
Esta instrucción lleva a cabo una operación and lógica entre dos operandos, guardando
el resultado en el operador destino (dest), la tabla de verdad de este operador es:
Fuente Destino Resultado
1 1 1
1 0 0
0 1 0
0 0 0

77
Sintaxis
AND dest, src

9.7.2 TEST dest,src


Esta instrucción lleva a cabo una operación and lógica entre dos operandos, solo que a
diferencia del and esta instrucción solo afecta a las banderas y no guardan el valor.

Sintaxis
TEST dest, src

9.7.3 OR
Esta instrucción lleva a cabo una operación or lógica entre dos operandos, guardando el
resultado en el operador destino (dest), la tabla de verdad de este operador es:
Fuente Destino Resultado
1 1 1
1 0 1
0 1 1
0 0 0

Sintaxis
OR dest, src

9.7.4 XOR
Esta instrucción lleva a cabo una operación de disyunción exclusiva entre dos operandos,
guardando el resultado en el operador destino (dest), la tabla de verdad de este operador
es:
Fuente Destino Resultado
1 1 0
1 0 1
0 1 1
0 0 0

78
Sintaxis
XOR dest, src

9.7.5 NOT
Esta instrucción genera el complemento a 1 del registro destino y lo almacena en el
mismo operador destino.

Sintaxis
NOT dest // donde dest tiene que ser registro o memoria

9.8 Instrucciones de desplazamiento


Assembler tiene un conjunto de instrucciones de desplazamiento, donde teniendo un
registro, se realiza un desplazamiento total de todos sus bits a alguna de las direcciones
especificadas, las instrucciones dadas por Assembler son:

9.8.1 SHL/SAL
Realiza un desplazamiento lógico (SHL) o aritmético (SAL) a la izquierda en todo el
registro, comportándose de la siguiente manera:

Sintaxis
SHL dest, count // Realiza count desplazamientos en dest
SAL dest, count // Realiza count desplazamientos en dest

79
9.8.2 SHR
Realiza un desplazamiento lógico a la derecha en todo el registro, comportándose de la
siguiente manera:

Sintaxis
SHR dest, count // Realiza count desplazamientos en dest

9.8.3 SAR
Realiza un desplazamiento aritmético a la derecha en todo el registro, comportándose
de la siguiente manera:

Sintaxis
SHR dest, count // Realiza count desplazamientos en dest

80
9.8.4 ROL
Realiza una rotación a la izquierda en todo el registro, comportándose de la siguiente
manera:

Sintaxis
ROL dest, count // Realiza count desplazamientos en dest

9.8.5 ROR
Realiza una rotación a la derecha en todo el registro, comportándose de la siguiente
manera:

Sintaxis
ROR dest, count // Realiza count rotaciones en dest

81
9.8.6 RCL
Realiza una rotación a la izquierda en todo el registro, incluyendo el CF, comportándose
de la siguiente manera:

Sintaxis
RCL dest, count // Realiza count rotaciones en dest

9.8.7 RCR
Realiza una rotación a la derecha en todo el registro, incluyendo el CF, comportándose
de la siguiente manera:

Sintaxis
RCR dest, count // Realiza count rotaciones en dest

82
9.9 Instrucciones de transferencia de control
Las instrucciones de transferencia de control son aquellas que saltan la ejecución del
código hacia una etiqueta predefinida o a un procedimiento, las instrucciones de
transferencia son:

9.9.1 JMP etiqueta


Realiza un salto incondicional de ejecución de código hacia la etiqueta especifica.

Sintaxis
JMP destino

9.9.2 CALL id
Realiza una transferencia de control que se dirige al procedimiento cuyo identificador es
especificado en la instrucción.

Sintaxis
Call proc1

9.9.3 RET
Esta es una transferencia de retorno que cuando se realizó una transferencia de control
dirigida a un procedimiento (call) puede retornar el control directamente a donde fue
llamado

Sintaxis
Ret

9.10 Saltos condicionales aritméticos


Estas son transferencias de control asignadas luego de realizar la instrucción de CMP,
ya que toma de base las banderas que esta instrucción altera.

9.10.1 Aritmética signada (con números positivos, negativos y cero)


• JL etiqueta/JNGE etiqueta: Saltar a etiqueta si es menor.
• JLE etiqueta/JNG etiqueta: Saltar a etiqueta si es menor o igual.
• JE etiqueta: Saltar a etiqueta si es igual.
• JNE etiqueta: Saltar a etiqueta si es distinto.
• JGE etiqueta/JNL etiqueta: Saltar a etiqueta si es mayor o igual.
• JG etiqueta/JNLE etiqueta: Saltar a etiqueta si es mayor.

83
9.10.2 Aritmética sin signo (con números positivos y cero)
• JB etiqueta/JNAE etiqueta: Saltar a etiqueta si es menor.
• JBE etiqueta/JNA etiqueta: Saltar a etiqueta si es menor o igual.
• JE etiqueta: Saltar a etiqueta si es igual.
• JNE etiqueta: Saltar a etiqueta si es distinto.
• JAE etiqueta/JNB etiqueta: Saltar a etiqueta si es mayor o igual.
• JA etiqueta/JNBE etiqueta: Saltar a etiqueta si es mayor.

9.11 Saltos condicionales según el valor de los indicadores:


• JC etiqueta: Saltar si hubo arrastre/préstamo (CF = 1).
• JNC etiqueta: Saltar si no hubo arrastre/préstamo (CF = 0).
• JZ etiqueta: Saltar si el resultado es cero (ZF = 1).
• JNZ etiqueta: Saltar si el resultado no es cero (ZF = 0).
• JS etiqueta: Saltar si el signo es negativo (SF = 1).
• JNS etiqueta: Saltar si el signo es positivo (SF = 0).
• JP/JPE etiqueta: Saltar si la paridad es par (PF = 1).
• JNP/JPO etiqueta: Saltar si la paridad es impar (PF = 0).

9.12 Saltos condicionales CX


Estas son transferencias condicionales las cuales toman de base el valor de CX como
contador, variando la cantidad de repeticiones según el valor de este registro, las
instrucciones de este tipo son:
• Loop etiqueta: Saltar si CX es diferente a 0, por cada iteración decrementa CX.
• LoopZ / LoopE etiqueta: Saltar si CX es diferente a 0 y ZF es igual a 1, por cada
iteración decrementa CX.
• LoopNZ / LoopNE etiqueta: Saltar si CX es diferente a 0 y ZF es igual a 0, por
cada iteración decrementa CX.
• JCXZ etiqueta: Saltar si CX es igual a 0.

9.13 Interrupciones:
El manejo de interrupciones es una técnica de ensamblador basado en un mecanismo
automático en el hardware del microcontrolador, donde el flujo del programa entra a una
subrutina de atención para ejecutarla y únicamente al finalizar toda la subrutina continua
con la ejecución del programa principal.

84
Las principales instrucciones de interrupciones que maneja ensamblador son las
siguientes:
• INT numero: Guarda los flags en la pila, hace TF=IF=0 y ejecuta la interrupción
con el número indicado.
• INTO: Interrupción condicional, ejecuta la interrupción 4 Si OF = 1.
• IRET: Realiza un retorno de interrupción y restaura los indicadores del stack.

9.14 Operadores analíticos


Descomponen los operadores o registros y devuelven su estado o propiedades de
memoria, las instrucciones analíticas que ofrece assembler son:

9.14.1 SEG
Instrucción que retorna el valor del segmento especificado.

Sintaxis
SEG memory-operand

Ejemplo
CMP DX, SEG A // Compara si DX es igual al segmento A

9.14.2 OFFSET
Retorna el valor del offset, el cual se refiere al desplazamiento que tuvo el registro desde
su segmento de datos, en pocas palabras retorna la dirección que tiene el registro.
85
Sintaxis
Offset memory-operand

Ejemplo
MOV DX, Offset AL // DX igual al puntero AL

9.14.3 TYPE
Retorna un valor que representa el tipo de operando, assembler cuenta con un índice
de tipos, siendo esta instrucción el retorno de dicho índice.
Índice Tipo
1 BYTE
2 WORD
4 DWORD
-1 NEAR
-2 FAR

Sintaxis
TYPE memory-operand

Ejemplo
MOV DX, TYPE Var // DX igual al índice del tipo Var

9.14.4 Definición de procedimientos


Los procedimientos son secciones de código que se pueden llamar para su ejecución
desde distintas partes del programa.

Sintaxis
Etiqueta PROC {NEAR|FAR}
// Lista Sentencias

86
ret
Etiqueta ENDP

Consideraciones
• Near/far depende si la operación implica un procedimiento cercano o lejano
• La instrucción ret regresa al segmento donde fue invocado el procedimiento.
• La llamada a un procedimiento se hace mediante call etiqueta.

9.14.5 Definición de macros


Los macros son secciones de codigo que se pueden llamar para su ejecución
desdedistintas partes del programa, a diferencia de los procedimientos, los macros
pueden recibir una lista de parámetros y el retorno es automático al finalizar el macro.

Sintaxis
Nombre_macro MACRO [parámetro [,parámetro...]]*
// Lista Sentencias
ENDM

Consideraciones
• Los parámetros son opcionales. Si existen, entonces también aparecerán en
algunas de las sentencias en la definición de la macro. Al invocar la macro
mediante:
• nombre_macro [argumento [,argumento..]] se ensamblarán las sentencias
indicadas en la macro teniendo en cuenta que cada lugar donde aparezca un
parámetro se reemplazará por el argumento correspondiente.
• PURGE nombre_macro [,nombre_macro...]: Borra las macros indicadas de la
memoria para poder utilizar este espacio para otros símbolos.
Ejemplo
SUMAR MACRO xx, yy, zz
MOV AX, xx
ADD AX, yy
MOV zz, AX
ENDM

87
9.15 Operadores de macros
Son operadores que se utilizan en las definiciones de macros. Hay cinco: &, <>, !, % y
;;.
• &parámetro: reemplaza el parámetro con el valor actual del argumento.
• <texto>: trata una serie de caracteres como una sola cadena. Se utiliza cuando
el texto incluye comas, espacios u otros símbolos especiales.
• !carácter: trata el carácter que sigue al operador ! como un carácter en vez de un
símbolo o separador.
• %texto: trata el texto que sigue a continuación del operador % como una
expresión. El ensamblador calcula el valor de la expresión y reemplaza el texto
por dicho valor.
• sentencia;; comentario: Permite definir comentarios que aparecerán en la
definición de la macro pero no cada vez que éste se invoque en el listado fuente
que genera el ensamblador.

9.16 Instrucciones para el ensamblador (Directivas)


9.16.1 Definición de símbolos
EQU: Define nombres simbólicos que representan valores u otros valores simbólicos.
Las dos formas son:
• nombre EQU expresión
• nuevo_nombre EQU viejo_nombre
Una vez definido un nombre mediante EQU, no se puede volver a definir.

9.16.2 Definición de datos


Ubica memoria para un ítem de datos y opcionalmente asocia un nombre simbólico con
esa dirección de memoria y/o genera el valor inicial para ese ítem.
[nombre] DB valor_inicial [, valor_inicial...]

Donde valor_inicial puede ser una cadena o una expresión numérica cuyo resultado
esté entre -255 y 255.
[nombre] DW valor_inicial [, valor_inicial...]

Donde valor_inicial puede ser una expresión numérica cuyo resultado esté entre -
65535 y 65535 o un operando de memoria en cuyo caso se almacenará el offset de
este.

88
[nombre] DD valor_inicial [, valor_inicial...]

Donde valor_inicial puede ser una constante cuyo valor esté entre -4294967295 y
4294967295, una expresión numérica cuyo valor absoluto no supere 65535, o bien un
operando de memoria en cuyo caso se almacenarán el offset y el segmento del mismo
(en ese orden).

Definición de valor inicial


• Si se desea que no haya valor inicial, deberá utilizarse el símbolo ?.
• Otra forma de expresar el valor inicial es:
DUP (valor_inicial [, valor_inicial...])
Donde cuenta es la cantidad de veces que debe repetirse lo que está entre
paréntesis.

9.16.3 Definición de segmentos


Organizan el programa para utilizar los segmentos de memoria del microprocesador
8088. Estos son SEGMENT, ENDS, DOSSEG, ASSUME, GROUP.

Sintaxis
nombre_segm SEGMENT [alineación][combinación]['clase']
// Lista sentencias
nombre_segm ENDS

Alineación: define el rango de direcciones de memoria para el cual puede elegirse el


inicio del segmento. Hay cinco posibles:
• BYTE: El segmento comienza en el siguiente byte.
• WORD: El segmento comienza en la siguiente dirección par.
• DWORD: Comienza en la siguiente dirección múltiplo de 4.
• PARA: Comienza en la siguiente dirección múltiplo de 16.
• PAGE: Comienza en la siguiente dirección múltiplo de 256.
Si no se indica la alineación ésta será PARA.
Combinación: define cómo combinar segmentos que tengan el mismo nombre. Hay
cinco posibles:

89
• PUBLIC: Concatena todos los segmentos que tienen el mismo nombre para
formar un sólo segmento. Todas las direcciones de datos e instrucciones se
representan la distancia entre el inicio del segmento y la dirección
correspondiente. La longitud del segmento formado será la suma de las
longitudes de los segmentos con el mismo nombre.
• STACK: Es similar a PUBLIC. La diferencia consiste que, al comenzar la
ejecución del programa, el registro SS apuntará a este segmento y SP se
inicializará con la longitud en bytes de este segmento.
• COMMON: Pone el inicio de todos los segmentos teniendo el mismo nombre en
la misma dirección de memoria. La longitud del segmento será la del segmento
más largo.
• MEMORY: Es igual a PUBLIC.
• AT dirección_de_segmento: Hace que todas las etiquetas y direcciones de
variables tengan el segmento especificado por la expresión contenida en
dirección_de_segmento. Este segmento no puede contener código o datos con
valores iniciales. Todos los símbolos que forman la expresión
dirección_de_segmento deben conocerse en el primer paso de ensamblado.
Si no se indica combinación, el segmento no se combinará con otros del mismo nombre
(combinación "privada").

9.16.4 Segmentos simplificados


Permite definir los segmentos sin necesidad de utilizar las directivas de segmentos que
aparecen más arriba.
• .MODEL modelo: Debe estar ubicada antes de otra directiva de segmento. El
modelo puede ser uno de los siguientes:
o TINY: Los datos y el código juntos ocupan menos de 64 KB por lo que
entran en el mismo segmento. Se utiliza para programas .COM. Algunos
ensambladores no soportan este modelo.
o SMALL: Los datos caben en un segmento de 64 KB y el código cabe en
otro segmento de 64 KB. Por lo tanto, todo el código y los datos se
pueden acceder como NEAR.
o MEDIUM: Los datos entran en un sólo segmento de 64 KB, pero el código
puede ser mayor de 64 KB. Por lo tanto, código es FAR, mientras que los
datos se acceden como NEAR.
o COMPACT: Todo el código entra en un segmento de 64 KB, pero los
datos no (pero no puede haber matrices de más de 64 KB). Por lo tanto,
código es NEAR, mientras que los datos se acceden como FAR.
o LARGE: Tanto el código como los datos pueden ocupar más de 64 KB
(pero no puede haber matrices de más de 64 KB), por lo que ambos se
acceden como FAR.
90
o HUGE: Tanto el código como los datos pueden ocupar más de 64 KB (y
las matrices también), por lo que ambos se acceden como FAR y los
punteros a los elementos de las matrices también son FAR.
• .STACK [size]: Define el segmento de pila de la longitud especificada.
• .CODE [name]: Define el segmento de código.
• .DATA: Define un segmento de datos NEAR con valores iniciales.

91
10 Manejo de errores
El compilador deberá ser capaz de detectar todos los errores que se encuentren durante
el proceso de compilación. Todos los errores se deberán de recolectar y se mostrara un
reporte de errores en el que, como mínimo, debe mostrarse el tipo de error, su ubicación
y una breve descripción de por qué se produjo.

10.1 Tipos de errores


Los tipos de errores se deberán de manejar son los siguientes:
• Errores léxicos.
• Errores sintácticos.
• Errores semánticos

10.2 Contenido mínimo de tabla de errores


La tabla de errores debe contener como mínimo la siguiente información:
• Línea: Número de línea donde se encuentra el error.
• Columna: Número de columna donde se encuentra el error.
• Tipo de error: Identifica el tipo de error encontrado. Este puede ser léxico,
sintáctico o semántico.
• Descripción del error: Dar una explicación concisa de por qué se generó el error.
Ejemplo

Tipo Descripción Fila Columna

Léxico El carácter “α” no pertenece al alfabeto del lenguaje 33 21

Sintáctico Se esperaba un ‘;’ 264 15

Semántico El método ‘caminar’ no tiene ninguna sobrecarga para 325 19


int,int

10.3 Método de recuperación

• Léxicos y sintácticos, modo pánico.


• Semánticos, a nivel de instrucción.

92
11 Apéndice A: Precedencia y asociatividad de
operadores
La precedencia de los operadores indica el orden en que se realizaran las distintas
operaciones del lenguaje. Cuando dos operadores tengan la misma precedencia, se
utilizará la asociatividad para decidir qué operación realizar primero.
A continuación, se presenta la precedencia de operadores lógicos, aritméticos y
relacionales.

NIVEL OPERADOR DESCRIPCION asociatividad


11 [] Acceso a elemento de arreglo Izquierda
. Acceso a miembro de un objeto
() paréntesis
10 - menos unario Derecha
! not
9 * / % multiplicativas Izquierda
8 + - aditivas Izquierda
+ concatenación de cadenas
7 < <= relacionales no
> >= asociativo
6 = Igualdad izquierda
<> Diferencia
5 AND And izquierda
4 NAND NOR NAND,NOR Izquierda
3 OR Or Izquierda
2 ? : ternario Derecha
1 := asignación derecha

93
12 Entregables y Restricciones
12.1 Entregables
● Código fuente
● Aplicación funcional
● Gramáticas
● Manual técnico
● Manual de usuario
Deberán entregarse todos los archivos necesarios para la ejecución de la aplicación, así
como el código fuente, la gramática y la documentación. La calificación del proyecto se
realizará con los archivos entregados en la fecha establecida. El usuario es completa y
únicamente responsable de verificar el contenido de los entregables. La calificación se
realizará sobre archivos ejecutables. Se proporcionarán archivos de entrada al momento
de calificación.

12.2 Restricciones
● La aplicación deberá ser desarrollada utilizando el lenguaje Java haciendo uso de
flex y cup para la generación de analizadores léxicos y sintácticos.
● El código intermedio deberá analizarse y ejecutarse con Jison y node js.
● El código ensamblador deberá únicamente generarse, el estudiante no debe
aplicar ninguna ejecución sobre este código.
● Copias de proyectos tendrán de manera automática una nota de 0 puntos y serán
reportados a la Escuela de Ingeniería en Ciencias y Sistemas los involucrados.
● Para la validación del código intermedio se correrá en un ambiente externo
proveído por los auxiliares para corroborar la correcta implementación.
● Para el flujo de la aplicación es necesario que el estudiante traiga instalado
DosBox para la ejecución de código ensamblador.

12.3 Consideraciones
• El auxiliar podrá realizar cambios al enunciado de forma oficial en un documento
de cambios, estos cambios solo podrán hacerse durante los primeros 15 días
después de la publicación de este.
• El estudiante deberá realizar lo que se exprese en este documento considerando
los cambios que estén plasmados en el documento de cambios.
• Durante la calificación se realizarán preguntas sobre el código para verificar la
autoría del mismo, de no responder correctamente la mayoría de las preguntas se
reportará la copia.

94
12.4 Requisitos mínimos
Los requerimientos mínimos del proyecto son funcionalidades del sistema que permitirán
un ciclo de ejecución básica, para tener derecho a calificación se deben cumplir con lo
siguiente:

2 Descripción
2.1 Descripción de la aplicación
2.1.2 DosIDE
2.1.3 Consola
2.1.4 Debugger
2.1.6 Reportes
3 Generalidades del lenguaje DOSLang
3.1 Tipos de dato
3.1.1 Tipos primitivos
3.1.2 Tipos definidos por el usuario
3.1.3 Tipos de dato enumerados
3.1.4 Tipos de subrango
3.2 Arreglos
3.2.1 Arreglos Multidimensionales y Arreglos de arreglos
3.2.2 Acceso a los arreglos
3.3 Registros
3.4 Memoria Virtual
3.4.1 Sizeof
3.4.2 malloc
4 Definición léxica
4.3 Comentarios
4.4 Sensibilidad a mayúsculas y minúsculas
4.5 Identificadores

95
4.6 Palabras reservadas
4.7 Literales
4.8 Secuencias de escape
5 Estructura del Lenguaje
5.1 Nombre del programa
5.2 Sentencia uses
5.3 Declaración de variables
5.4 Declaración de constantes
5.5 Expresiones
5.6 Funciones y Procedimientos
5.6.1 Funciones
5.6.2 Procedimientos
5.6.3 Paso de Parámetros
6 Sentencias
6.1 Bloques
6.3 Asignación de variables
6.4 Sentencias de transferencia
6.4.1 Break
6.4.3 Exit
6.5 Sentencias de selección
6.5.1 Sentencia If
6.6 Ciclos
6.8 Sentencia Write
7 Generación de código intermedio
7.1 Temporales
7.2 Etiquetas
7.3 Identificadores
7.4 Comentarios
7.5 Operadores aritméticos
7.6 Saltos
7.6.1 Saltos condicionales

96
7.6.2 Salto incondicional
7.7 Asignación a temporales
7.8 Declaración de métodos
7.9 Llamadas a métodos
7.10 Print
7.12 Entorno de ejecución
7.12.1 Estructuras del entorno de ejecución
7.12.2 Acceso a estructuras del entorno de ejecución
9 Generación de código ensamblador
9.1 Entrada de ASM
9.2 Salida de ASM
10 Manejo de errores
10.1 Tipos de errores
10.2 Contenido mínimo de tabla de errores
10.3 Método de recuperación
11 Apéndice A: Precedencia y asociatividad de operadores

12.5 Entrega del proyecto


1. La entrega será virtual y por medio de la plataforma.
2. La entrega de cada uno de los proyectos es individual.
3. Para la entrega del proyecto se deberá cumplir con todos los requerimientos
mínimos.
4. No se recibirán proyectos después de la fecha ni hora de la entrega estipulada.
5. La entrega del proyecto será mediante un archivo comprimido de extensión zip, el
formato <carnet>.zip.
6. Entrega del proyecto:

Viernes 15 de noviembre a las 23:59 horas

97

Potrebbero piacerti anche