Nombre del proyecto: Compilador Cr. Objetivo general del proyecto: Desarrollar un compilador para analizar códigos escritos en lenguaje Cr (C reducido) y traduciros a código equivalente en ensamblador para una máquina hipotética tipo RISC empleando en este proceso las herramientas Lex, Yacc, GTK+ y Dev-Cpp. Objetivos particulares y ponderación1: − Implementar la interfaz gráfica de usuario − Desarrollar la gramática de análisis − Validar la semántica del código − Generar el código ensamblador [20%] [30%] [20%] [30%] Descripción de los objetivos particulares: - Implementar la interfaz gráfica de usuario: Se trata de una interfaz de desarrollo integrado que permita: 1. Edición con resaltado tipográfico2 de: palabras reservadas (int, void, if, else, switch, case, break, for, while y do), operadores (+, -, ++, --, =, ==, >, <, >=, += y !=), símbolos (;, ,, (, ), [, ], {, } y :), constantes literales de tipo entero y comentarios, mostrando en todo momento el número de línea y columna del cursor de edición en una barra de estado. 2. Visualización de errores sintácticos y semánticos lo más descriptivo posible indicando el número de línea donde se produce, la causa o causas del posible error y el último token involucrado. 3. Visualización del código objeto generado, solo en el caso de que el código fuente no contenga errores. 4. Funcionalidades comunes de Nuevo, Abrir, Guardar, Acerca de, y Salir, además de un botón para compilar. Las funcionalidades de Abrir y Guardar deberán utilizar los cuadros de diálogo estándar diseñados para tal fin. - Desarrollar la gramática de análisis: El código fuente es de tipo C reducido con las siguientes características: 1. La inclusión de librerías no es necesaria dado que no se tendrán sentencias de entrada/salida. 2. El código inicia con una sección de declaración de variables, las cuales solo pueden ser de tipo entero positivo o negativo. 3. Las variables pueden estar inicializadas o no. 4. Es posible declarar variables simples o arreglos. 5. Se cuenta con una única función main de tipo void. En esta función no se permite la declaración de variables. 6. Las únicas operaciones aritméticas válidas son la suma y la resta empleando para ello los operadores de +, -, ++ y --. 7. Los operadores relacionales válidos son: ==, <, >, >=, <=, y !=. 8. Todas las sentencias de asignación y/o cálculo deben terminar con el símbolo de punto y coma, inclusive las sentencias de declaración de variables. 9. Las sentencias de control (if, if-else, switch, for, while, do-while) siempre deben ir acompañadas de un bloque de sentencias encerrado entre llaves, aún cuando se trate de una sola sentencia asociada a la estructura de control. 10. Los anidamientos entre estructuras de control están permitidos sin restricciones. 11. Los comentarios pueden aparecer en cualquier parte del código y pueden ser de una línea (//) o de un bloque de líneas (/**/). 1 La ponderación es tentativa. Los colores mostrados en el texto son solo una sugerencia, sin embargo si se requiere que cada resaltado tipográfico tenga un color distinto y diferente de negro. 2 El siguiente código ejemplifica la mayoría del conjunto de características antes mencionadas: /*Ordenamiento burbuja*/ int n = 5, a[5] = {5, 8, 6, 2, 9}, aux; int i, j; void main( ) { for(i = 2; i < n; i++){ for(j = 0; j < n-i; j++){ if( a[j] > a[j+1] ){ aux = a[j]; a[j] = a[j+1]; a[j+1] = aux; } } } } Validar la semántica del código: Las validaciones semánticas a realizar son las siguientes: Los enteros (constantes literales) deben estar en el rango de -32768 a 32767 (Advertencia). Todas las variables declaradas deben utilizarse en el código de la función principal (Advertencia). A una variable de tipo arreglo no se le puede asignar un simple valor (Error). Una variable de tipo arreglo no puede ser usada directamente en una operación de cálculo o asignación, siempre deberá utilizarse indicando un elemento (Error). Si el elemento de un arreglo es indicado mediante un valor literal, este no debe ser mayor al tamaño del arreglo (Error). Una variable de tipo arreglo no puede ser usada como índice de un arreglo directamente, solo a través de uno de sus elementos (Error). Verificar que en un ciclo for el incremento o decremento del contador sea coherente con la condición de paro (Advertencia). Verificar en los ciclos de tipo while y do-while que el valor de al menos una de las variables involucradas en la condición cambien en el cuerpo del ciclo (Advertencia). Generación de código ensamblador: El código ensamblador a generar supone una máquina RISC (Acrónimo en inglés para Computador de Conjunto Reducido de Instrucciones) de 16 bits en una arquitectura Harvard (memoria de datos y programa separados). El conjunto de instrucciones disponibles es el siguiente: Instrucción LWI LW SWI SW ADD ADDI SUB SUBI BEQI Ejemplo LWI Rd, num16 LW Rd, D(Rt) SWI Rd, D SW Rd, D(Rt) ADD Rd, Rt, Rs ADDI Rd, Rt, num12 SUB Rd, Rt, Rs SUBI Rd, Rt, num12 BEQI Rd, Rt, D Significado Rd = num16 Rd = Mem[Rt+D] Mem[D] = Rd Mem[Rt+D] = Rd Rd = Rt + Rs Rd = Rt + num12 Rd = Rt – Rs Rd = Rt – num12 if(Rd == Rt) goto D Instrucción BNEI BLTI BGTI CMP BEQ BNE BLT BGT B Ejemplo BNEI Rd, Rt, D BLTI Rd, Rt, D BGTI Rd, Rt, D CMP Rt, Rs BEQ D BNE D BLT D BGT D BD Significado if(Rd != Rt) goto D if(Rd < Rt) goto D if(Rd > Rt) goto D Rt – Rs if(Rt == Rs) goto D if(Rt != Rs) goto D if(Rt < Rs) goto D if(Rt > Rs) goto D PC – D