Descarga la aplicación para disfrutar aún más
Vista previa del material en texto
LENGUAJE INTERMEDIO PARA SISTEMAS DE AVIACIÓN Presentado por: Juan José Rodríguez Sánchez 201812319 E-mail: jj.rodriguezs@uniandes.edu.co Profesor asesor: Nicolas Cardozo Álvarez UNIVERSIDAD DE LOS ANDES FACULTAD DE INGENIERÍA DEPARTAMENTO DE INGENIERÍA DE SISTEMAS Y COMPUTACIÓN BOGOTÁ, 2022 2 Dedicado a … A mis padres por su amor incondicional y todo su apoyo en las decisiones que tome, gracias a ellos soy la persona en que me he convertido. A mi hermana por su inigualable ejemplo de estudio y trabajo duro para lograr sus metas ante cualquier situación. Al profesor Nicolas Cardozo por el acompañamiento que me brindó durante el semestre y el fuerte apoyo que mantuvo para lograr sacar adelante este proyecto, junto al gran estudio dedicado en este tema. 3 Agradecimientos Principalmente le agradezco a mis padres, por todo su sacrificio y su trabajo duro para lograr darme una educación privilegiada para que yo pudiera alcanzar todas mis metas, sueños y objetivos. Por su amor incondicional ante cualquier situación y problema que se me presentó. Por sus sinceros consejos para sobrellevar cada situación. Por el apoyo incondicional ante las decisiones que tomé y me formaron como persona. Les debo mi vida entera y estaré siempre agradecido por haberlos tenido en ella. No pude haberle pedido a Dios unos mejores guías. A mi hermana le agradezco toda la ayuda que me brindó en la vida universitaria, recibí de ella consejos para trabajar de la manera más eficiente cada semestre y aprovechar al máximo las oportunidades que se me presentaron. Al profesor Nicolás Cardozo Álvarez quisiera darle las gracias por aceptar realizar este trabajo. El resultado logrado en este estudio fue gracias al constante apoyo que me brindó y a la fuente de temáticas que me proporcionó. La aeronáutica es un área del saber muy impresionante y estoy fuertemente agradecido con él por el resultado logrado en este trabajo, a pesar de todas las dificultades. 4 Tabla de contenido Listado de Figuras ........................................................................................................................ 6 Listado de Tablas .......................................................................................................................... 6 1. Introducción ........................................................................................................................... 7 1.1. Objetivos .......................................................................................................................... 8 1.1.1. Objetivo General ............................................................................................... 9 1.1.2. Objetivos Específicos ....................................................................................... 9 1.2. Estructura del Documento ............................................................................................. 9 2. Marco Teórico ...................................................................................................................... 10 2.1. Una Aproximación Gramatical ................................................................................... 10 2.2. Pasos para la Construcción de un Traductor ............................................................ 11 2.2.1. Paso 1: Recuperación de la gramática base. ................................................... 11 2.2.2. Paso 2: Ajustar la gramática. .......................................................................... 12 2.2.3. Paso 3: Anotaciones de la gramática. ............................................................. 12 2.2.4. Paso 4: Preocuparse de los errores o incongruencias encontradas. ................ 13 2.2.5. Paso 5: Traductor completo. ........................................................................... 14 3. Procedimiento ...................................................................................................................... 17 3.1. Gramática Anotada ...................................................................................................... 17 3.2. Paso 1: Conocimiento general de la gramática de los lenguajes a partir de su BNF. ........................................................................................................................................................ 18 3.2.1. Statements ....................................................................................................... 18 3.2.2. Variables ......................................................................................................... 19 3.2.3. Expressions/Formulas/Type_Specifiers .......................................................... 20 3.2.4. Functions ......................................................................................................... 21 3.2.5. Identifiers ........................................................................................................ 22 3.3. Paso 2: Identificar las sentencias similares entre las generalidades de los BNF ..... 23 3.3.1. Statements ....................................................................................................... 23 3.3.2. Variables ......................................................................................................... 23 3.3.3. Expressions/Formulas/Type_Specifiers .......................................................... 23 3.3.4. Functions ......................................................................................................... 24 3.3.5. Identifiers ........................................................................................................ 24 3.4. Paso 3: Llegar a un traductor objetivo (Source to source Translator). ................... 24 3.4.1. Statements ....................................................................................................... 25 3.4.2. Variables ......................................................................................................... 25 3.4.3. Expressions/Formulas/Type_Specifiers .......................................................... 25 3.4.4. Functions ......................................................................................................... 25 3.4.5. Identifiers ........................................................................................................ 26 3.5. Paso 4: Adaptar las sentencias no similares ............................................................... 26 3.6. Paso 5: Traductor Final ............................................................................................... 26 3.7. Funcionamiento del Traductor .................................................................................... 27 5 4. Validación ............................................................................................................................. 29 4.1. Preparación Experimental ........................................................................................... 29 4.2. Traducción de lenguajes a DIANA ............................................................................. 29 4.2.1. ADA ................................................................................................................ 29 4.2.2. JOVIAL .......................................................................................................... 30 4.2.3. C ...................................................................................................................... 31 4.2.4. Comentarios Generales ................................................................................... 32 4.3. Conversión entre lenguajes ..........................................................................................32 4.3.1. ADA ................................................................................................................ 32 4.3.2. JOVIAL .......................................................................................................... 34 4.3.3. C ...................................................................................................................... 36 4.3.4. Comentarios Generales ................................................................................... 38 5. Conclusión y Trabajo Futuro ............................................................................................. 39 5.1. Conclusión ..................................................................................................................... 39 5.2. Trabajo a Futuro .......................................................................................................... 39 6. Referencias ........................................................................................................................... 41 6 Listado de Figuras Figura 1: Estructura para la construcción un traductor parcial general (Elaboración propia adaptado de Ordoñez) .............................................................................................. 15 Figura 2: Interfaz inicial para el programa traductor. .................................................. 27 Figura 3: Interfaz para introducir la entrada del traductor en los componentes BNF . 27 Figura 4: Interfaz de respuesta tras el proceso de traducción en el programa. ............ 28 Listado de Tablas Tabla 1: Constitución de las sentencias en el BNF de cada lenguaje. .......................... 18 Tabla 2: Constitución de las variables en el BNF de cada lenguaje. ............................ 19 Tabla 3: Constitución de las expresiones en el BNF de cada lenguaje. ........................ 20 Tabla 4: Constitución de las funciones en el BNF de cada lenguaje. ............................ 21 Tabla 5: Constitución de los identificadores en el BNF de cada lenguaje. ................... 22 Tabla 6: Componentes similares sobre sentencias en el BNF de cada lenguaje. .......... 23 Tabla 7: Componentes similares sobre variables en el BNF de cada lenguaje. ............ 23 Tabla 8: Componentes similares sobre expresiones en el BNF de cada lenguaje. ........ 23 Tabla 9: Componentes similares sobre funciones en el BNF de cada lenguaje. ........... 24 Tabla 10: Componentes similares sobre identificadores en el BNF de cada lenguaje .. 24 Tabla 11: Ruta de traducción seguida para sentencias en el BNF de cada lenguaje. ... 25 Tabla 12: Ruta de traducción seguida para variables en el BNF de cada lenguaje. .... 25 Tabla 13: Ruta de traducción seguida para expresiones en el BNF de cada lenguaje. 25 Tabla 14: Ruta de traducción seguida para funciones en el BNF de cada lenguaje. .... 26 Tabla 15: Ruta de traducción seguida para identificadores en el BNF de cada lenguaje. .............................................................................................................................................. 26 7 1. Introducción Esta investigación quiere profundizar en el estudio de los lenguajes de programación en la aeronáutica con el fin de generar un medio de traducción entre estos. Se desea entender la amplia magnitud de información que maneja esta industria, sin importar el lenguaje que se haya implementado, enfatizando la importancia de lograr la traducción entre estos lenguajes para poder utilizar un diseño universal que funcione comúnmente en el desarrollo de prototipos de aviación. La industria aeronáutica maneja una gran cantidad de lenguajes de programación, debido a los diferentes tipos de sistemas controlados por software al momento de volar un avión. Es fundamental comprender las funciones que cumplen los lenguajes de programación, para compartir la información y los datos entre los componentes de software de una aeronave, con el fin de evitar que se gaste tiempo de compilación en diferentes lenguajes y se permita eficiencia en el uso del software. Sin el uso de una herramienta que permita la traducción, se incurre en la dificultad de recurrir a diferentes proveedores fabricantes de los sistemas del avión y a un mayor trabajo en el acoplamiento de dichos componentes, por estar desarrollados en un lenguaje específico. Por tanto, con la implementación de un lenguaje único se pretende reducir la complejidad en el diseño y construcción de prototipos para generar máquinas más eficientes. Así pues, se permite tener un medio de verificación que logre distinguir y optimizar las características de cada uno de los lenguajes, para no requerir cambios constantes en el código. La aviación es actualmente el medio de transporte más seguro del mundo, debido a la implementación de programas y tecnologías que permiten generar ayudas para el manejo automático de las aeronaves, con el fin de minimizar los errores en la atención y el desempeño humano. La codificación y programación de estos vehículos consiste de diferentes componentes de software especializado para el desarrollo de modelos de navegación, comunicación, alerta de colisión, aproximación, disciplina militar, entre otros. La programación es una herramienta en aviación desarrollada para la fabricación y construcción de aeronaves de nueva generación. Es por esto que se han diseñado distintos lenguajes de programación para cumplir diferentes propósitos y funcionalidades específicas en una aeronave. Sin embargo, el conocimiento adquirido en el momento de construir nuevos diseños está dividido en los diferentes lenguajes existentes, los cuales deben coexistir y cumplir las certificaciones requeridas en el software de un avión. El ideal es obtener un traductor parcial que haga posible el desarrollo de una traducción a un estándar que sea común para los 3 lenguajes que se estudian. Para lograr esto, se propone un lenguaje que logre unificar los lenguajes involucrados. Este será DIANA [6], que funcionará como un lenguaje intermedio, para el cual se generan traducciones desde cada uno de los lenguajes específicos. La construcción del traductor se hace a partir del estudio de las gramáticas de los lenguajes dados por su definición en Backus-Naur Form (BNF) [7]. 8 La traducción de un lenguaje a uno nuevo se realiza en 2 procesos. El primero, es hacer un traspaso desde el lenguaje inicial hasta el lenguaje intermedio, para comprender la gramática general; aquí se debe pasar manualmente de las líneas del código fuente a su representación en BNF, para luego ingresar estos componentes en el traductor y obtener una respuesta en DIANA. Desde esta gramática, podemos obtener una traducción hacia cualquiera de los tres lenguajes de desarrollo. El segundo proceso, es hacer un traspaso desde el lenguaje intermedio hacia el lenguaje final; aquí únicamente se ingresa la gramática obtenida en DIANA y el traductor responde con los componentes BNF del lenguaje final. Con este procedimiento es posible tener una traducción orientada a la funcionalidad del código involucrado, por lo que a pesar de no basarse en el contexto es posible interpretar con el BNF las sentencias que se desean traducir. Sin embargo, al implementar esta metodología se está saltando la parte de verificar el lenguaje, es decir, hacer una revisión de que las sentencias traducidas tengan el sentido esperado. En este caso, se evita construir un “parser” [8] que analice la sintaxis y el orden de las cadenas de código, y también un “lexer” [9] que identifique los símbolos válidos del programa. Esto es debido a que no se está trabajando con líneas de código, sino que se tiene un enfoque netamente gramatical; por eso el traspaso a BNF se hace de manera manual. Es necesario incluir un procesamiento posterior de verificación, para que sea posible llegar a un traductor completo.Para demostrar lo mencionado en el párrafo anterior, se realizarán dos casos de validación, cada uno de los cuales pretende mostrar el funcionamiento del traductor. El primer caso, es pasar desde cada uno de los lenguajes involucrados hacia DIANA; en este se busca que se cumpla el rol de un lenguaje intermedio, para que logre reunir y unificar los componentes particulares de las gramáticas en su propio BNF, para el correcto entendimiento de las sentencias. El segundo caso, es realizar el proceso de traducción de un lenguaje hacia otro diferente; aquí se busca probar la conversión entre los distintos lenguajes con el traspaso entre diferentes gramáticas, para probar el funcionamiento del traductor y el correcto desarrollo de una traducción parcial de la gramática. En los casos de validación, es normal que se encuentren errores debido al orden sintáctico de la traducción o situaciones en las que se resalten características específicas del código que se usen únicamente en uno de los lenguajes. Es por ello que se debe comprender la necesidad de un procesamiento posterior que permita revisar las traducciones logradas y adaptarlas a las gramáticas de salida, sin cambiar la semántica especificada por la gramática con la que se inició. A pesar de que no se usan líneas de código sino únicamente los componentes en BNF, el traductor actual es capaz de capturar las abstracciones básicas de la gramática de cada uno de los lenguajes para adaptarlas en un lenguaje intermedio que entienda la funcionalidad del código. Esta es la base del traductor para que luego pueda llegar en gran potencia a programas más complejos. 1.1. Objetivos Se presentan los objetivos del proyecto junto al enfoque implementado en esta investigación. Se maneja un estudio de las gramáticas para poder implementar un lenguaje intermedio que 9 logre distinguir las características específicas de cada lenguaje involucrado, para que se permita una traducción eficiente. 1.1.1. Objetivo General Proponer un lenguaje intermedio para el desarrollo, validación y verificación de los lenguajes de programación utilizados dentro de la industria aeronáutica. 1.1.2. Objetivos Específicos • Construir un traductor (parcial) entre los lenguajes trabajados en la fabricación y la programación de aviones. • Identificar los componentes principales de la sintaxis y la semántica en la estructura de los lenguajes de programación existentes en la industria aeronáutica. • Fortalecer el entendimiento de la estructura de la programación de software en la aeronáutica integrando sus bases en un lenguaje intermedio. • Reducir la complejidad de la traducción entre lenguajes haciendo uso de los componentes en BNF de cada lenguaje. • Validar el funcionamiento del traductor construido mediante casos de estudio. • Identificar y aplicar los pasos a seguir en el proceso de construcción de un traductor. 1.2. Estructura del documento Este trabajo sigue como se menciona a continuación. La Sección 2 presenta la parte teórica sobre el diseño de un traductor entre gramáticas, haciendo un enfoque en la implementación de los BNF como base del conocimiento de la gramática. La Sección 3 presenta el procedimiento para la construcción del traductor siguiendo la terminología explicada en la Sección 2 y las adaptaciones para la transformación al lenguaje intermedio. La Sección 4 presenta los casos de estudio implementados y sus resultados, que validan el funcionamiento de traductor. Para los resultados de cada caso se desarrollan 3 situaciones (una por cada lenguaje), se presentan las líneas de código utilizadas y luego su respectiva representación a BNF, y al final se muestra la respuesta en BNF del traductor. La Sección 5 presenta las conclusiones y el trabajo a futuro a desarrollar. 10 2. Marco Teórico La creación de un traductor de programas desde un único lenguaje origen a uno de destino requiere de tiempo y esfuerzo considerables, debido a la complejidad inherente de cada paso del proceso. Existe una gran variedad de familias de lenguajes, por lo que el desarrollo de un traductor se basa en un conocimiento exhaustivo pero relativo, dado que se desea en lo posible mantener intacta la semántica de cada una de las partes. Se pretende seguir un enfoque de ingeniería inversa de convergencia gramatical a partir de los BNF (Backus-Naur Form) que permita extraer modelos comunes de la programación de los lenguajes y programas, distinguiendo parámetros gramaticales similares al lenguaje de enfoque, que proporcione las técnicas específicas de traducción y transformación de la sintaxis. Traducir automáticamente el código fuente de un lenguaje de programación a otro, mientras se preserva la semántica, es difícil. Este tipo de traductores solo existen para un conjunto muy limitado de lenguajes de origen y destino; aun así, hay casos en los que se necesitan tales herramientas y en los que construirlos es factible, como en la aeronáutica. En esta parte, se describe el enfoque desde la perspectiva de los BNF de los lenguajes, para generar la traducción entre dos lenguajes. Esto conlleva un alto grado de automatización, ya que los traductores pueden generarse a partir de anotaciones y definiciones de sintaxis. 2.1. Una Aproximación Gramatical El desarrollo del traductor se basa en el uso correcto de la gramática y en encontrar secuencias similares del lenguaje [1]. Es decir, al conocer correctamente las descripciones de los componentes en BNF a traducir, se puede hacer una comparación entre las gramáticas y distinguir elementos que hacen referencia a una misma funcionalidad en el código, con el objetivo de promover la reusabilidad del lenguaje. Se debe encontrar y detectar los elementos comunes entre ambos lenguajes [1], importante para determinar qué factores de la gramática son compartidos para el desarrollo de la traducción. Cuando se encuentren elementos no comunes, estos se deben adaptar a la sintaxis que maneja el lenguaje de destino; todo este proceso se basa en un análisis de los principios y reglas subyacentes a las gramáticas utilizadas para definir cada lenguaje [1]. Desde esta aproximación se pretenden tratar dos problemas para la traducción, los cuales se trabajan a partir de una base gramatical. Un problema es llegar a tener un traductor genérico, sin depender de los lenguajes usados [1]. Esto se trabaja con los componentes del BNF que describen las características específicas de las líneas de código, lo que permite partir desde cualquier tipo de sentencia, ya que se debe transformar siempre a su BNF. El siguiente problema es lograr asegurar la precisión de cada una de las traducciones generadas, al verificar la equivalencia de los lenguajes [1]. Esto se hace también con las descripciones sacadas de los BNF de los lenguajes involucrados, ya que en la comparación de la traducción se buscan los componentes que representen funcionalidades similares en ambos lenguajes, lo cual permite que no haya cambios en la semántica. Entonces, al mantener el significado de 11 cada sentencia se está asegurando una buena traducción, lo que se explicará en mayor profundidad a continuación. El primer problema, se maneja automatizando el proceso de traducción a partir de las similitudes existentes entre los lenguajes a traducir [1]. Con esto, es posible hacer una búsqueda constante de los modelos comunes que se conocen de cada lenguaje y realizar cada transformación. Se asignan los lenguajes de origen a los de destino anotando sus gramáticas y proporcionando estas gramáticas anotadas al sistema construido, para producir un traductor automático. Se crea un traductor de un objetivo a otro (target to target translator). El segundo problema, se puede manejar al adaptar los elementos no compatibles al nuevo lenguaje y realizar una verificación de la salida generada [1]. Se aumenta el modelo de gramáticas anotadas, con un conjunto de anotacionessemánticas ligeras genéricas que se usan para verificar la traducción [1]. Estas anotaciones serán componentes del BNF del lenguaje objetivo en cual se haya aceptado la traducción de las sentencias no coincidentes; este proceso consiste en almacenar dichos componentes para entender la referencia que tiene en el nuevo lenguaje. Con esto se anotan las funciones del código de un lenguaje específico para construir un flujo de control a partir de la semántica. Este flujo es una ruta desde la salida de un componente en el BNF del lenguaje de origen a su respectiva respuesta en el BNF del lenguaje objetivo. Así pues, lo que se hace es una lectura del componente BNF el cual está ligado a un componente BNF en el lenguaje objetivo. Toda la dinámica del traductor se centre en el BNF de cada uno de los lenguajes a traducir. Como anotaciones al BNF, se identifican los llamados generales de cada función y operación existente en el lenguaje de origen para luego traducirlos a los llamados existentes en el lenguaje objetivo. 2.2. Pasos para la Construcción de un Traductor En esta parte, se sigue el proceso teórico de construcción explicado por Ordoñez [1]. Esta base usa como referencia los SDF de los lenguajes y la librería de “APPAREIL”. La aplicación empleada en este estudio se basa estrictamente en el uso de la gramática aplicada en los BNF de cada lenguaje; se busca una traducción al nivel de la gramática. Para explicar cada uno de los pasos se presenta la terminología “APPAREIL” [1] y luego la adaptación que se definió para aplicar APPAREIL a nivel del BNF. 2.2.1. Paso 1: Recuperación de la gramática base. En el primer paso, se parte desde la documentación que proveen las especificaciones y las definiciones del lenguaje. Dado que a menudo esta documentación no está completa o es demasiado informal para ser utilizada directamente, se necesita completarla y estructurarla. Este primer paso corresponde a la recuperación de una gramática base para cada uno de los lenguajes. Si la gramática no existe para algún lenguaje, es necesario reconstruirla dentro de esta etapa del proceso [1]. Se pretende reconocer las características del lenguaje a partir de cada uno de los componentes en el BNF. Se debe adquirir todo ese conocimiento sobre 12 cada uno de los dos lenguajes objetivos, la descripción de la sintaxis y su semántica general. Es posible que no haya una representación precisa de la sentencia que se quiere pasar al BNF, por esto se debe conocer muy bien la gramática que maneje el lenguaje para optar por un componente adecuado. La recuperación de la gramática será ajustar el código a su descripción en BNF; es necesario identificar las líneas de código con un único componente, para no obtener resultados generales en la traducción. El paso a BNF se hace de manera manual, lo cual esta ajustado en el conocimiento que se le brinde al traductor sobre las gramáticas. 2.2.2. Paso 2: Ajustar la gramática. Para personalizar la gramática de un lenguaje, se debe utilizar el meta-entorno ASF+SDF que reúne las características del lenguaje como herramienta de apoyo para diseñar una gramática SDF que funcione tanto para el lenguaje de origen como para el de destino. Se registran esas gramáticas con anotaciones adicionales que definen las construcciones importantes del lenguaje junto a su semántica [1]. Para este proyecto, se construye la gramática básica con anotaciones a partir de la descripción del BNF de cada lenguaje. Las anotaciones de la gramática son los componentes del BNF que describen de la manera más precisa las sentencias para entender la implementación y el funcionamiento del código. Con el conocimiento adquirido en el paso anterior, se comienza la búsqueda de componentes que representen una funcionalidad similar del código escrito en ambos lenguajes. Es posible encontrar características que son equivalentes en ambas gramáticas, es decir, funcionalidades existentes en el uso de los dos lenguajes. Por ejemplo, puede que los lenguajes manejen ciclos, condicionales o llamados a funciones, pero estos pueden ser identificados con nombres distintos en cada BNF. Por esto se debe conocer las descripciones de funcionalidad a las que se refieren los componentes BNF de cada lenguaje y asociarlos para saber que son equivalentes. Esta aproximación gramatical (explicada en el literal 2.1) evita la necesidad de saber de dónde vienen las líneas de código y su implementación, ya que el traspaso a BNF permite que se conozca únicamente el significado de estas, sin importar el uso que se les preste. Así se puede llegar a una gramática libre del contexto. En este punto se utilizan los BNF de los lenguajes involucrados como herramienta de apoyo para las asignaciones de los puntos de traducción entre ambas gramáticas. Estos serán puntos de partida que se identifican con los componentes asociados entre gramáticas para comprender la dinámica de la traducción en el siguiente paso. 2.2.3. Paso 3: Anotaciones de la gramática. Este paso toma como entrada las gramáticas anotadas de ambos lenguajes, y produce un traductor parcial entre lenguajes. Se toman las definiciones 13 específicas de los componentes del lenguaje origen y se hace una adaptación al nuevo lenguaje. Este traductor, dependiendo de cuán diferentes sean ambos lenguajes, puede requerir una adaptación manual adicional para completar el proceso o realizar verificaciones sobre la traducción [1]. Para la gramática anotada se debe especificar la sentencia que se desea traducir y tener un enfoque para pasarlo al nuevo lenguaje. Se desea encontrar una secuencia de tokens finales que se encuentren presentes o sean similares en el lenguaje objetivo. Es necesario recolectar las definiciones especificas del lenguaje en el BNF, para realizar la traducción entre los lenguajes a este nivel de abstracción. Es por esto que se utilizan los componentes asociados entre los BNF que representan las equivalencias entre los lenguajes. Estas serán las anotaciones de la gramática, ya que son los componentes que representan de la manera más precisa la funcionalidad y el significado del código que se desea transmitir. Con las anotaciones de la gramática se hace referencia a los atributos particulares que existen en la implementación de un lenguaje específico, debido a la sintaxis que maneja y al contexto en el que se desea desarrollar la implementación. Con los atributos se hace referencia a las características del lenguaje y al diseño que posee para escribir líneas de código con un propósito funcional específico dada su área de implementación. Por ejemplo, pueden manejar atajos o saltos de línea particulares que permitan agilidad en la construcción de un algoritmo en diferentes situaciones, y pueden manejarse de manera distinta en cada lenguaje. La importancia de este paso radica en el hecho que es bastante complejo generalizar la traducción de lenguajes sin tener en cuenta el contexto ni la aplicación que se esté desarrollando en ambos casos. A pesar de todo, los atributos específicos deben ser capaces de adaptarse a los lineamientos del lenguaje de salida. Por lo tanto, es importante obtener los componentes similares entre los lenguajes y asociarlos, ya que permite construir una ruta entre componentes de BNF de cada lenguaje para saber desde un punto de partida en un lenguaje de origen cuál será su respectiva salida como respuesta en el lenguaje objetivo, sin afectar la semántica en cada caso. 2.2.4. Paso 4: Preocuparse de los errores o incongruencias encontradas. Este paso se ocupa de aquellos casos en los que la naturaleza de las discrepancias entre los lenguajes es tal que no se puede derivar una transformación automática basándose únicamente en anotaciones o en identificar que componentes son similares. El paso anterior ya proporciona información relevante, extraída de un análisis de los mapeos, señalando los lugares donde se han encontrado desajustese incompatibilidades. Aquí se crean las transformaciones adicionales, a partir de componentes en una biblioteca de transformaciones dedicada, para incluirlas en el traductor. Este 14 es un paso de verificación del lenguaje para saber reconocer y acoger las características del lenguaje inicial, y pasarlo al objetivo [1]. Después de encontrar sentencias especificas del lenguaje, se debe entender que no es posible encontrar un resultado similar en el lenguaje objetivo, y de aquí la necesidad de un traductor. Es por esto que los errores o fallos encontrados en las líneas de la gramática que no se han podido traducir, se deben alinear a los requerimientos específicos del lenguaje final. En este punto no se busca tanto una similitud, sino que se desea encajar las líneas de código restantes en una forma adecuada que haga que la semántica no se vea afectada. Al final se busca obtener el mismo significado que en el lenguaje inicial, razón por la cual esta parte es tan importante para saber adaptar y entender que es lo que se está traduciendo. En este paso se demuestra que no hay una manera exacta de realizar una traducción y que el proceso depende de la complejidad del código implementado. Es necesario que con el conocimiento adquirido de ambos lenguajes (explicado en el paso 1) se logre comprender la semántica de los programas, teniendo en cuenta que es difícil no modificarla debido a diferencias fundamentales entre los lenguajes. 2.2.5. Paso 5: Traductor completo. En el paso final del proceso se usa el traductor completo, proporcionándole los procedimientos del programa de prueba para traducir. Se verifica el resultado de esta traducción con un módulo de verificación, que verificará la equivalencia entre los procedimientos original y traducido [1]. La parte final del traductor es hacer retoques al resultado final y verificar la traducción a la cual se llegó. En este punto, se deben comparar las líneas de código y los BNF que se obtuvieron al final, para corroborar que se mantuvo el funcionamiento correcto del programa a traducir. Esta parte de verificación esta fuera del alcance de este proyecto, pero se debe mencionar debido a la necesidad de realizarla para tener una traducción completa y evitar errores específicos. El objetivo final siempre será tener la entrada de un lenguaje específico y llegar a una salida similar en uno totalmente diferente. Después de identificar los pasos a seguir en el proceso del desarrollo de un traductor, se propone desde esta investigación desarrollar un traductor parcial que generalice el proceso de traducción entre lenguajes y que optimice la traducción al utilizar un lenguaje intermedio que comparta las dinámicas características de cada lenguaje involucrado. La estructura del traductor desarrollado se puede evidenciar en la Figura 1. 15 Figura 1: Estructura para la construcción un traductor parcial general (Elaboración propia adaptado de Ordoñez) [1]. En este caso, se presenta la construcción de un traductor completo a partir del estudio de los BNF. Sin embargo, el objetivo es llegar a un traductor parcial que demuestre la existencia de las traducciones generales entre los lenguajes involucrados. Para lograr esto, se utiliza un lenguaje intermedio, común a los 3 lenguajes, que reduzca los BNF a la gramática básica de los lenguajes y realice la traducción entre ellos. Para este escenario, “G.Leng” y “G.Inter” representa la gramática general del lenguaje a traducir y el lenguaje intermedio. Luego, “A.G.Leng” y “A.G.Inter” son las gramáticas anotadas del lenguaje de entrada y del lenguaje intermedio, donde ya se tiene una especificación más precisa de las sentencia a traducir. El siguiente paso, es realizar y corroborar la traducción a partir de los BNF de ambos lenguajes, para pasar de una gramática a otra nueva. Al final, se tiene una traducción parcial basada únicamente en las estructuras de los BNF. Los pasos de verificación y revisión de las sentencias están fuera del alcance de este proyecto. Generalizar la traducción y tener un lenguaje intermedio, tiene como objetivo hacer la traducción entre diferentes lenguajes de programación de aviación con el fin de mantener una única base de código para la ejecución de programas; además, se tiene una capa para razonar de manera uniforme sobre los diferentes lenguajes y mantener las características de seguridad y confiabilidad de las sentencias que se realicen. El propósito de tener un lenguaje intermedio, es entonces mantener un solo lenguaje de verificación a nivel abstracto para cada uno de los lenguajes fuente, evitando el requerir un proceso de verificación para cada uno de estos. Sin embargo, cabe resaltar que si bien el lenguaje intermedio es el mismo, la traducción de los fragmentos de código no siempre será igual. Por lo tanto, si bien el modelo de verificación es uniforme, puede ser necesario definir procesos independientes para cada traducción lograda. De forma similar, con el lenguaje intermedio se busca optimizar el cumplimiento de las regulaciones aeronáuticas en los diferentes lenguajes de programación, ya que es una reglamentación obligatoria para su uso. Con este desarrollo se promueve que, si se tiene Lenguajes a traducir: ADA, Jovial, C. G. Leng G. Inter A.G.Leng A.G.Inter Traductor construido a partir de BNFs Traductor parcial Verificador Traductor completo Pruebas de la correctitud de la traducción 16 cumplimiento en el lenguaje intermedio para el desarrollo de la traducción, se garantice el cumplimiento en los demás lenguajes involucrados en el proceso. 17 3. Procedimiento En esta sección se explica el objetivo del desarrollo de un traductor entre lenguajes y como se desarrolla para este trabajo. La construcción actual consiste de un traductor parcial (Partial Translator) a partir de la gramática de cada lenguaje y no un traductor completo (Full Translator) basado en las verificaciones de código. El levantamiento del traductor se basa en un traductor objetivo (Source-to-source Translator) que convierte sentencias entre los lenguajes involucrados en el proceso a nivel de abstracción del BNF. El procedimiento se realiza con base en la aproximación desde la gramática y la construcción del traductor parcial a través de los 5 pasos. 3.1. Gramática Anotada La traducción directa por sintaxis es una metodología usada al construir un compilador, para traducir de un lenguaje fuente a un lenguaje objetivo, donde se busca entender la escritura y la sintaxis utilizada. En nuestro caso, la gramática anotada son los componentes del BNF que logren delimitar la gramática a su descripción funcional más específica y desde estos se realizan las transformaciones entre los programas manteniendo la semántica. Ahora bien, como se especificó en el paso 3 de la construcción de un traductor, en esta parte no se utilizan líneas de código, sino que se vela por una correcta distinción de las características de cada uno de los lenguajes que puedan ser representadas por su BNF en cada caso. En programación cada lenguaje es diferente, debido a la magnitud de aplicaciones en las que se pueden ver involucrados y la funcionalidad que se desea destacar en algún programa (habilidad matemática, mejor compilación, estructuras dicientes, etc.). Es necesario que en el momento de la asignación de código hacia el componente respectivo en BNF se logré utilizar aquel que sea capaz de representar dicha sentencia con mayor precisión. El BNF es una herramienta que trata de abarcar todo el lenguaje de una manera muy abierta; es importante para el traductor identificar cuáles son los componentes puntuales que reproduzcan el código; esto delimitará la magnitud y efectividad de la traducción al momento de realizar el paso entre gramáticas de cada lenguaje. Los lenguajes involucrados en larealización de este estudio son: - ADA: Es un lenguaje de programación orientado a objetos y fuertemente tipado de forma estática, que fue diseñado por encargo del Departamento de Defensa de los Estados Unidos. Es un lenguaje multipropósito y concurrente cuya sintaxis está basada en Pascal; fue diseñado con propósitos de seguridad, con una filosofía orientada a la reducción de errores comunes y difíciles de descubrir, por lo que cuenta con chequeos en el tiempo de ejecución y es capaz de sincronizar tareas entre varios programas. ADA se usa principalmente en entornos en los que se necesita una gran seguridad y fiabilidad como el sector defensa (área militar), la aeronáutica (Boeing o Airbus), la gestión del tráfico aéreo y la industria aeroespacial [11]. 18 - JOVIAL: Es un lenguaje de programación de segunda generación que viene de una base del lenguaje ALGOL 58, con extensiones para la programación en tiempo real a gran escala y tiene la capacidad de centralizar las comunicaciones entre los programas. Las estructuras de datos que pueden compartirse son variables y tablas. JOVIAL se usa ampliamente en la Fuerza Aérea de los Estados Unidos, en la mayoría del software del Airborne Early Warning and Control System - AWACS (Sistema de Alerta Temprana y Control Aerotransportado) y del Airborne Operational Computer Programme - AOCP (Programa Computarizado Operativo Aerotransportado) [3] [12]. - C: Es un lenguaje independiente del hardware, con el cual se desarrollan tanto aplicaciones como sistemas operativos, a la vez que forma la base de otros lenguajes más actuales como Java, C++ o C#. Este lenguaje se escogió por ser uno de los más utilizados y conocidos en la actualidad, así como de los más generales por sus bases hacia otros lenguajes diferentes. El desarrollo del lenguaje C toma protagonismo para la creación del sistema operativo UNIX [5] [10]. - DIANA: El “Descriptive Intermediate Attributed Notation for ADA” es un lenguaje intermedio utilizado para la estructuración de código en ADA, utilizado para la compilación y estructuración de sentencias. Este es capaz de organizar los tipos de datos y objetos en el lenguaje. Los usos iniciales de este lenguaje fueron para la comunicación entre el Front-end y el Back-end de un compilador para ADA [6]. 3.2. Paso 1: Conocimiento general de la gramática de los lenguajes a partir de su BNF El primer paso para un traductor es tener el conocimiento adecuado de cada uno de los lenguajes con los cuales se desarrollará el proceso. Se debe disponer de una gramática general de la cual partir para dividir las sentencias en los principales atributos semánticos que representen. Del mismo modo, se debe adquirir el conocimiento del lenguaje apoyado en la lectura y distinción de componentes en su BNF para identificar la representación adecuada de las líneas de código que se deseen traducir hacia el lenguaje objetivo. Para ello, se hace la siguiente distinción entre los lenguajes: 3.2.1. Statements: JOVIAL ADA statement ::= simple-statement | complex- statement | compound-statement simple statement := assignment-statement | exchange-statement | go-to-statement | test- statement | stop-statement | return-statement | procedure-statement | input-statement | output- statement statement ::= { label } ( simple_statement | compound_statement ) label ::= "<<" statement_identifier ">>" simple_statement ::= null_statement | assignment_statement | exit_statement | goto_statement | procedure_call_statement | return_statement | entry_call_statement | requeue_statement | delay_statement 19 complex statement ::= conditional-statement | loop-statement | direct-code-statement | alternative-statement | closed-statement compound-statement ::= BEGIN o statement-list o END | abort_statement | raise_statement | code_statement compound_statement ::= if_statement | case_statement | loop_statement | block_statement | accept_statement | select_statement C DIANA <statement> ::= <labeled-statement> | <expression-statement> | <compound-statement> | <selection-statement> | <iteration-statement> | <jump-statement> STM ::= if | case | name_stm | LOOP | block | accept | select | cond_entry | timed_entry STM ::= null_stm | exit | return | goto | entry_call | delay | abort | raise | code STM ::= pragma; STM ::= terminate; STM_S ::= stm_s; Tabla 1: Constitución de las sentencias en el BNF de cada lenguaje. Las sentencias (Statements) son la línea básica de código en donde se representan las fórmulas, funciones y expresiones del código general. Aquí se encuentran ciclos, asignaciones, condicionales, saltos de línea, entre otros (Tabla 1). En DIANA, la representación de una sentencia es STM como la línea básica de código en donde se encuentran funciones, fórmulas o cualquier expresión de una línea de código general. Al ser más simplificado, denota una mejor condición para su rol como intermediario. 3.2.2. Variables: JOVIAL ADA variable ::= numeric-variable | literal-variable | status-variable | boolean-variable index ::= numeric-formula index-list ::= index o (null | , o index-list) subscript ::= ($ o index-list o $) numeric-variable ::= name o (null | subscript) literal-variable ::= name o (null | subscript) status-variable ::= name o (null | subscript) boolean-variable ::= name o (null | subscript) variable_name ::= name name ::= direct_name | explicit_dereference | indexed_component | slice | selected_component | attribute_reference | type_conversion | function_call | character_literal C DIANA <primary-expression> ::= <identifier> | <constant> | <string> | ( <expression> ) <constant> ::= <integer-constant> | <character-constant> | <floating-constant> var => as id s:ID_S, as_type_spec: TYPE_SPEC, as_object_def: OBJECT_DEF, lx_srcpos:source_position, lx_comments:comments var_id => lx_srcpos: source_positton, lx_comments :comments, lx_symrep: symbol_rep, 20 | <enumeration-constant> sm_obj_type:TYPE_SPEC, sm_address:EXP VOID, sm_obj_def: OBJECT_DEF variant => as_choice_s:CHOICE_S, as_record: INNER_RECORD, lx_srcpos :source position, lx_comments:comments variant_part => as_name:NAME, as_variant_s: VARIANT_S, lx_srcpos:source_position, lx_comments: comments variant_s => as_list:seq of VARIANT, lx_srcpos:source position, lx_comments: comments; Tabla 2: Constitución de las variables en el BNF de cada lenguaje. Al declarar variables (variables) en ADA o en JOVIAL se tiene una distinción específica del tipo de dato, mientras que en C se tiene una distinción más general llamada “primary expression” que denota un conjunto más abierto de datos; sin embargo, los lenguajes manejan el mismo tipo de variables (Tabla 2). Es posible identificar que los BNF de ADA y de JOVIAL tienen estructuras más afines, debido a que son programas implementados en aviación. En cambio, el BNF de C se puede diferenciar por ser un lenguaje más general y capaz de entrar en diferentes ámbitos. DIANA reúne muchas características generales al declarar una variable, por lo que es bastante abierto para lograr aceptar características de cualquier lenguaje. También, es posible notar que en DIANA la representación de variables se asemeja bastante a ADA, ya que se especifica la distinción del tipo de dato que se vaya a manejar en el código. 3.2.3. Expressions/Formulas/Type_Specifiers: JOVIAL ADA formula ::= numeric-formula | literal-formula | status-formula | boolean-formula nurmeric-formula ::= numeric-constant | numeric- variable | function_of_numeric_type | (+|-) o numeric-formula | ( o numericformula o ) | (/ o numeric-formula o /) | numeric-formula o arithmetic-operator o numeric-formula declarator :: = ITEM| MODE | ARRAY | TABLE | STRING | OVERLAY | DEFINE | SWITCH | PROCEDURE | FILE expression ::= relation { "and" relation } | relation { "and" "then" relation } | relation { "or" relation } | relation { "or" "else" relation } | relation { "xor" relation } relation ::= ( simple_expression [ ( "=" | "/=" | "<" | "<=" | ">" | ">=" ) simple_expression ] ) | ( simple_expression [ "not" ] "in" ( range | subtype_mark ) ) simple_expression ::= [ ( "+" | "-" ) ] term { ( "+" | "-" | "&" ) term } type_definition ::= enumeration_type_definition | integer_type_definition | real_type_definition | array_type_definition | record_type_definition | access_type_definition | derived_type_definition C DIANA <expression> ::= <assignment-expression> EXP ::= NAME | numeric_literal | null_access 21 | <expression> , <assignment-expression> <assignment-expression> ::= <conditional-expression> | <unary-expression> <assignment-operator> | <assignment-expression> <type-specifier> ::= void | char | short | int | long | float | double | signed | unsigned | <struct-or-union-specifier> | <enum-specifier> | <typedef-name> | aggregate | string_literal | allocator | conversion | qualified | parenthesized | binary | membership EXP_CONSTRAINED ;:= EXP | CONSTRAINED EXP_S ::= exp_s; EXP_VOID ::= EXP | void TYPE_SPEC ::= CONSTRAINED | FORMAL_TYPE_SPEC TYPE_SPEC ::= enum_literal_s | integer | fixed | float | array | record | access | derived TYPE_SPEC ::= private | task_spec TYPE_SPEC::= universal_integer | universal_fixed universal_real | void Tabla 3: Constitución de las expresiones en el BNF de cada lenguaje. Las expresiones (expressions) son una combinación de constantes, variables o funciones, que se interpretan de acuerdo a las normas particulares de precedencia y asociación para el lenguaje de programación en particular. JOVIAL presenta este tipo de referencia como fórmulas generales (formulas), debido a que es un tipo de programación que abarca secuencias más específicas. Los demás lenguajes comparten cierta similitud en el momento de llamar una expresión, al basarse en el tipo de funcionalidad de ésta (condicional, asignación, etc.). Así mismo, se debe resaltar que los únicos que manejan el tipo de especificación de los datos (type-specifiers) son ADA, C y DIANA; JOVIAL es más general en este aspecto, aunque el más cercano en la gramática puede ser el declarador (declarator) (Tabla 3). 3.2.4. Functions: JOVIAL ADA function ::= name_of_procedure o ( o (null [ actual-input-parameter_list) o ) actual-input-parameter-list ::= (formula | name_of_array_or_table o (null | ° actual-input- parameter-list) formal-input-parameter-list ::= name o (null | o formal-input-parameter-list} actual-output-parameter-list ::= (variable | name_of_array_or_table | name_of_statement o ) o (null | actual-output-parameter-list) formal-output-parameter-list ::= (name | name o .) o (null | o formal-output-parameter-list) function_call ::= ( function_name | prefix ) [ actual_parameter_part ] actual_parameter_part ::= "(" parameter_association { "," parameter_association } ")" parameter_association ::= [ selector_name "=>" ] ( expression | name ) C DIANA <function-definition> ::= {<declaration- specifier>}* <declarator> {<declaration>}* <compound-statement> <declaration-specifier> ::= <storage-class- specifier> | <type-specifier> | <type-qualifier> function => as_param_s:PARAM_S, as_name_void : NAMEVOID; function => lx_srcpos:source_position, lx_comments: comments; 22 <declarator> ::= {<pointer>}? <direct- declarator> <direct-declarator> ::= <identifier> | ( <declarator> ) | <direct-declarator> [ {<constant-expression>}? ] | <direct-declarator> ( <parameter-type-list> ) | <direct-declarator> ( {<identifier>}* ) function_call => as_name:NAME, function_call => ~ src_pos:sourc_position, lx_comments: comments; function_call => sm_exp_type:TYPE_SPEC, sm_value: value, sm_normalized_param_s: EXP_S, Tabla 4: Constitución de las funciones en el BNF de cada lenguaje. Una función (function) se basa en la ejecución de las diferentes líneas de código para sobrellevar a una respuesta. El llamado y declaración de funciones en este punto se aprecia bastante similar en los 4 lenguajes, debido a que siguen una estructura de nombre, contenido y resultado. El llamado de funciones se desarrolla a partir del nombre de la función requerida, y en el caso de C, se hace un llamado a la declaración que se realice. DIANA reúne los dos casos, uno para la declaración de funciones y otro para el llamado de éstas (Tabla 4). 3.2.5. Identifiers: JOVIAL ADA identifier ::= loop-counter | name loop-counter ::= letter name ::= (letter | name) (null | ") (letter | numeral) identifier ::= identifier_letter { [ "_" ] ( identifier_letter | digit ) } C DIANA <direct-declarator> ::= <identifier> | ( <declarator> ) | <direct-declarator> [ {<constant-expression>}? ] | <direct-declarator> ( <parameter-type-list> ) | <direct-declarator> ( {<identifier>}* ) DEF ID ::= attr_id | ARGUMENT | comp_id | const_id | dscrmt_id | entry_id | enum_id | exception_id | function_id | generic_id | iteration_id | label_id | named_stm_id | number_id | package_id | private_type_id | proc_id DEF_ID ::= in_id | in_out_id | out_id DEF_ID := subtype | task body_id | type_td | var_id DEF_OCCURRENCE ::= DEF_ID | DEF_OP | DEF_CHAR DEF_OP ::= def_op DESIGNATOR ::= ID | OP DESIGNATOR_CHAR ::= DESIGNATOR | used_char Tabla 5: Constitución de los identificadores en el BNF de cada lenguaje. Los identificadores (identifiers) son nombres, dígitos o íconos que se le dan a variables, procedimientos y demás valores, que sirven para distinguir los elementos de un programa. En general, cada uno de los lenguajes maneja identificadores como medida básica para separar el código. Por lo tanto, no hay mucha diferencia que notar, ya que se entiende que existen identificadores en cada lenguaje. Solo se debe resaltar que C llama a un declarador como identificador, pero semánticamente es igual (Tabla 5). 23 3.3. Paso 2: Identificar las sentencias similares entre las generalidades de los BNF Al tener un conocimiento adquirido de los lenguajes que se desean implementar en la traducción, es posible desarrollar rutas entre los componentes similares para que se desarrolle una traducción más autónoma. Es por esto que se debe entender la semántica de los componentes de cada BNF, para lograr ajustar la traducción de manera más precisa. 3.3.1. Statements: Las sentencias como unidades sintácticas del lenguaje de programación imperativo que expresan alguna acción a realizar son las unidades ejecutables más pequeñas de un programa. Los componentes similares en cada lenguaje se muestran en la Tabla 6: JOVIAL ADA statement statement C DIANA <statement> STM Tabla 6: Componentes similares sobre sentencias en el BNF de cada lenguaje. 3.3.2. Variables: Una variable es el valor en el cual se almacenan y se recuperan los datos de un programa. Los componentes similares en cada lenguaje se muestran en la Tabla 7: JOVIAL ADA variable variable_name C DIANA <primary-expression> var Tabla 7: Componentes similares sobre variables en el BNF de cada lenguaje. 3.3.3. Expressions/Formulas/Type_Specifier: Las expresiones/formulas serán las combinaciones de variables y valores que denoten operaciones en el programa. Los especificadores de tipo denotan el tipo de valor que se esté manejando en el procedimiento. Los componentes similares en cada lenguaje se muestran en la Tabla 8: JOVIAL ADA formula, declarator expression, type_definition C DIANA <expression>, <type-specifier> EXP, TYPE_SPEC Tabla 8: Componentes similares sobre expresiones en el BNF de cada lenguaje. 24 3.3.4. Functions: Las funcionesdenotan operaciones más específicas de código que pretenden entregar un resultado final o realizar alguna acción particular. Los componentes similares en cada lenguaje se muestran en la Tabla 9: JOVIAL ADA function function_call C DIANA <function-definition> function Tabla 9: Componentes similares sobre funciones en el BNF de cada lenguaje. 3.3.5. Identifiers: Los identificadores, hacen referencia a los nombres o etiquetas que permiten distinguir las diferentes partes del código. Los componentes similares en cada lenguaje se muestran en la Tabla 10: JOVIAL ADA identifier identifier C DIANA <direct-declarator> DEF_ID Tabla 10: Componentes similares sobre identificadores en el BNF de cada lenguaje. 3.4. Paso 3: Llegar a un traductor objetivo (Source to Source Traslator) Llegados a este punto, se posee el conocimiento básico y necesario para realizar la traducción comparando las gramáticas de los lenguajes involucrados, por lo cual es posible desarrollar ciertas rutas automáticas que el traductor logra reconocer para hacer un traspaso inicial de la semántica del lenguaje hacia una nueva. Con lo anterior, se debe tener en cuenta que el traductor no debe ni puede cambiar la semántica, sino que busca hacer una traducción correcta y un traspaso que sea aceptado por las especificaciones del lenguaje objetivo. DIANA es el lenguaje intermedio en la traducción, es decir, el leguaje al que se desea llegar y el lenguaje del cual se va a salir al hacer el traspaso entre los lenguajes involucrados (C, ADA, JOVIAL). En este sentido, se van a desarrollar dos procesos que completen la traducción entre los 3 lenguajes. Por lo cual, en una traducción para ir desde un lenguaje X hasta un lenguaje Y, se debe seguir lo siguiente: primero, se realiza una traducción desde el lenguaje X que se desee cambiar hasta DIANA, y segundo, se debe pasar desde esa traducción entendida en DIANA hacia el lenguaje Y al que se desea llegar. Por eso se entiende que DIANA es el lenguaje que media para llegar a una traducción correcta sin cambios en la semántica, dada aquí su importancia para cumplir el rol de mantener un significado similar o igual en el proceso. 25 Ahora bien, el estudio busca que todo se quede en DIANA para que se pueda entender concretamente la estructura general de la gramática en cada lenguaje. Aquí es donde se reunirán los conocimientos y funcionalidades concretas que se delimiten por las especificaciones del código a traducir, pasado a los componentes del BNF. El objetivo es verificar en DIANA que todas las partes del código hayan sido entendidas, y así el correcto desarrollo de una estructura en DIANA implica que el siguiente traspaso en el nuevo lenguaje será el adecuado. En las Tablas 11, 12, 13, 14 y 15, se presentan ciertos casos en el proceso de traducción con los componentes similares de cada lenguaje al seguir un orden desde los lenguajes fuente hasta DIANA. Se debe comprender que funciona en este sentido y viceversa para completar los 2 procesos de la traducción. 3.4.1. Statements: El paso entre sentencias se da en la ruta mostrada en la Tabla 11: Entrada (C, ADA, JOVIAL) Salida (DIANA) <statement>, statement, statement STM Tabla 11: Ruta de traducción seguida para sentencias en el BNF de cada lenguaje. 3.4.2. Variables: El paso entre variables se da en la ruta mostrada en la Tabla 12: Entrada (C, ADA, JOVIAL) Salida (DIANA) <primary-expression>, variable_name, variable var Tabla 12: Ruta de traducción seguida para variables en el BNF de cada lenguaje. 3.4.3. Expressions/Formulas/Type_Specifier: La generalidad de las expresiones implementadas en un código puede variar al llegar a pasarlo a BNF, por lo cual se debe tener especial cuidado con el significado. Los componentes de la ruta se muestran en la Tabla 13: Entrada (C, ADA, JOVIAL) Salida (DIANA) (<expression>, <type-specifier>), (expression, type_definition), (formula, declarator) EXP, TYPE_SPEC Tabla 13: Ruta de traducción seguida para expresiones en el BNF de cada lenguaje. 3.4.4. Functions: La ruta seguida al encontrar funciones se muestra en la Tabla 14: 26 Entrada (C, ADA, JOVIAL) Salida (DIANA) <function-definition>, function_call, function function Tabla 14: Ruta de traducción seguida para funciones en el BNF de cada lenguaje. 3.4.5. Identifiers: Cualquier nombre o etiqueta se debe referir a un identificador especifico y la ruta se muestra en la Tabla 15: Entrada (C, ADA, JOVIAL) Salida (DIANA) <direct-declarator>, identifier, identifier DEF_ID Tabla 15: Ruta de traducción seguida para identificadores en el BNF de cada lenguaje. 3.5. Paso 4: Adaptar las sentencias no similares Este paso radica en que no es posible identificar y asignar en un solo componente el traspaso de sentencias de manera precisa. Siempre habrá datos particulares de cada lenguaje que puedan tener otro significado o que simplemente no existan en el lenguaje objetivo. La precisión de una traducción, depende de la capacidad de saber asignar los componentes que no tienen un objetivo asignado en el siguiente lenguaje, sin haber cambiado la semántica del código, o por lo menos al cambiarla en lo más mínimo. En este proyecto el traductor que se realiza no adapta las sentencias que no corresponden, ya que lo que se desarrolló es un programa que sigue la trayectoria de un traductor objetivo que logre distinguir los componentes similares en el lenguaje para hacer el traspaso entre las gramáticas. No es del alcance de este estudio el paso de verificación del lenguaje, por lo que solo se basa en las rutas de sentencias destinadas entre los 4 lenguajes que se conocen. Cuando no se tenga un componente similar, el programa detalla la sentencia del componente BNF especificado y la resalta para que sea responsabilidad del usuario o programador el adaptar dicha parte de código en el nuevo lenguaje cuando se utilice el traductor. Cuando se tengan líneas de código que posean un mayor grado de complejidad, es posible que se encuentren errores de este tipo, y esta es la razón por la cual se tiene un traductor parcial. Aunque el paso de verificación esta fuera del alcance del proyecto, debido a que se quiere tener un enfoque específico en el trabajo del cambio de gramáticas y su entendimiento para la implementación de un lenguaje intermedio que logre unificar los componentes, es de suma importancia mencionarla para conocimiento del lector. 3.6. Paso 5: Traductor final Para la construcción del traductor final, se desarrolló un prototipo en Java con Eclipse IDE, en el cual se tiene las referencias de similitudes ente ambos lenguajes en archivos de texto (.txt). Lo que se hace con el programa, es obtener una entrada de sentencia BNF en el lenguaje 27 inicial para convertirla a la sentencia especificada en BNF del lenguaje objetivo. La idea detrás de desarrollar un lector de archivos de texto, es simplificar el programa prototipo a detallar y señalar las relaciones entre las sentencias BNF que son la base de la traducción. El procedimiento detallado en los pasos 1 al 4 en esta sección, se realiza en la construcción de los archivos de texto, para que estos sean los que contengan la información del traductor sobre ambos lenguajes de programación. Para el desarrollo de cada uno de los archivos, se sigue el proceso de describir en detalle los componentes BNF de un lenguaje hacia DIANA y viceversa, así el programa realiza la lectura de los archivos e imprime la ruta seguida en BNF para mostrar un resultado traducido. Esto se entenderá mejor en la sección de validación. El traductor final, es la combinación de los procesos de traducción entre DIANA y los lenguajes involucrados. 3.7. Funcionamiento del traductor Se construyó un programa en Java que hace una lectura de los archivos de texto como diccionarios donde se encuentran las similitudes entre los2 lenguajes involucrados. El programa tiene una interfaz que señala 6 procesos de traducción, 3 de ida y 3 de vuelta, que se seleccionan con los 6 dígitos y proporcionan la lectura de los archivos (Figura 2). Figura 2: Interfaz inicial para el programa traductor. Luego, según la opción elegida, se le pide al usuario que escriba la sentencia BNF que se debe traducir. Se debe tener en cuenta que hay un separador diferente para cada proceso que puede ser “-” o “--” (Figura 3). Figura 3: Interfaz para introducir la entrada del traductor en los componentes BNF. Al final el traductor imprime una respuesta en la sentencia BNF del lenguaje objetivo. Si es el caso, cuando no encuentra una similitud resalta el componente erróneo y lo vuelve a imprimir en una lista al final (Figura 4). 28 Figura 4: Interfaz de respuesta tras el proceso de traducción en el programa. Las opciones disponibles en el proceso siempre deberán pasar por una traducción desde o hacia DIANA. Es por esto que se asume que el segundo lenguaje en el proceso es DIANA. 29 4. Validación En esta sección se pretende presentar los resultados del proyecto a partir de 2 casos de estudio planteados para probar y evaluar el funcionamiento del programa. Cada situación presenta líneas de código reales y se hace un cambio a BNF en cada caso. Se mostrará el código junto a la entrada y la salida del traductor. 4.1. Preparación experimental Aquí se explican las características del ambiente del programa para la realización de los casos de estudio: o Se utiliza la versión 2018-09 (4.9.0) de ECLIPSE IDE usando Java como lenguaje de programación. o Se implementa la librería “java.io” para las funciones BufferedReader, File y FileReader. o Se implementa la librería “java.util” para las funciones Scanner y ArrayList. o Los 4 lenguajes involucrados en el estudio son ADA (aviación comercial), JOVIAL (aviación militar), C (base de los lenguajes de programación actuales) y DIANA (lenguaje intermedio de compilación para ADA). o Se utiliza un MacBook Pro modelo 2010, con las siguiente características: § Sistema operativo macOS Catalina versión 10.15.7. § Procesador Intel Core i5 de 2,5 GHz § Memoria RAM de 16GB o Los archivos de texto se construyeron en Visual Studio Code versión 1.73.0. 4.2. Traducción de lenguajes a DIANA Esta situación se plantea para evaluar los casos más simples y conocer que no se cambia la estructura de las sentencias cuando se pasa al lenguaje intermedio. Se presenta el código de cada lenguaje fuente, luego se pasa a los componentes respectivos en BNF, se obtiene una respuesta en el BNF de DIANA y al final se vuelve a pasar al lenguaje fuente. De aquí, se comparan los BNF iniciales y finales durante el proceso de traducción. 4.2.1. ADA El código en ADA es el siguiente: procedure Main is -- Variable declarations A, B : Integer := 0; C : Integer := 100; D : Integer; begin 30 -- Ada uses a regular assignment statement for incrementation. A := A + 1; -- Regular addition D := A + B + C; end Main; Este programa es para declarar y modificar distintas variables. Así pues, los componentes BNF que mejor describen este código son los siguientes: <procedure_call_statement>-<variable_name>-<variable_name>- <integer_type_definition>-<assigment_statement>-<numeric_literal>- <variable_name>-<integer_type_definition>-<assigment_statement>- <numeric_literal>-<variable_name>-<integer_type_definition>- <begin>-<variable_name>-<assigment_statement>-<variable_name>- <expression>-<numeric_literal>-<variable_name>- <assigment_statement>-<variable_name>-<expression>- <numeric_literal>-<end> Lo anterior representa la entrada al programa del traductor para hacer el paso a DIANA. La respuesta obtenida es: procedure_call-var-var-integer-assign-numeric_literal-var-integer- assign-numeric_literal-var-integer-begin-var-assign-var-EXP- numeric_literal-var-assign-var-EXP-numeric_literal-end Con esta respuesta es posible verificar que el programa es capaz de comprender la estructura del lenguaje fuente para pasarlo a DIANA, y al efectuar el proceso inverso, se tiene la misma entrada en BNF de ADA. Así mismo, es posible distinguir las diferentes partes del código, debido a la asignación de diferentes componentes a lo largo de la sentencia en DIANA. 4.2.2. JOVIAL El código en JOVIAL es el siguiente: PROC RETRIEVE(CODE:VALUE); BEGIN ITEM CODE U; ITEM VALUE F; VALUE = -99999.; FOR I:0 BY 1 WHILE I<1000; IF CODE = TABCODE(I); BEGIN VALUE = TABVALUE(I); EXIT; END END Este código hace la acción de buscar un valor “code” y reemplazarlo en otra variable. El paso a BNF es el siguiente: 31 <function>--<BEGIN>--<item>--<variable>--<name_of_literal_item>-- <item>--<variable>--<name_of_literal_item>--<variable>--<assigment- statement>--<numeric-constant>--<loop-statement>--<conditional- statement>--<BEGIN>--<variable>--<assigment-statement>--<procedure- statement>--<output-statement>--<END>--<END> Nótese para este caso que el separador es doble guion “--”. Después de la separación de componentes se obtiene una respuesta en DIANA, tal como: function-begin-name_stm-var-named_stm_id-name_stm-var-as_id_s-var- assign-integer-LOOP-if-begin-var-assign-procedure_call-exit-end-end La respuesta en DIANA se aprecia sin errores y se logran identificar los diferentes componentes del código. También es posible efectuar el procedimiento inverso y obtener el mismo BNF en JOVIAL. 4.2.3. C El código en C es el siguiente: void main() { int num; printf("Enter a number: \n"); scanf("%d", &num); if (num > 0) printf("%d is a positive number \n", num); else if (num < 0) printf("%d is a negative number \n", num); else printf("0 is neither positive nor negative"); } Este es un programa que puede identificar si el número de entrada es positivo o negativo e imprime un mensaje. El paso a BNF de código es el siguiente: <function-definition>--<{>--<primary-expression>--<integer- constant>--<expression-statement>--<expression-statement>-- <selection-statement>--<expression-statement>--<selection- statement>--<expression-statement>--<selection-statement>-- <expression-statement>--<}> En este caso, también se usa el doble guion como separador de los componentes. La respuesta en DIANA es la siguiente: function-begin-var-numeric_literal-procedure_call-procedure_call- if-procedure_call-if-procedure_call-if-procedure_call-end 32 Se logra un buen entendimiento del lenguaje, a pesar de que C es el más diferente de los involucrados. Tanto ADA como JOVIAL y DIANA han sido pensados para trabajar en la industria aeronáutica, pero la generalidad de C le ayuda a adaptarse correctamente al orden gramatical. 4.2.4. Comentarios generales Es primordial evidenciar que el traductor logra distinguir de manera satisfactoria los rasgos básicos de las líneas de código en cada situación. Se debe mencionar que en este caso de estudio no se tiene en cuenta el contexto en el que el código opera, sino que solo se está haciendo referencia al cambio entre gramáticas y a la preservación de la semántica. Cada componente de BNF que se identifica en el código se basa en el rol funcional que cumple la línea específica seleccionada, que puede tener un grado de generalidad mayor o menor. Por lo tanto, se tiene una dependencia importante en la transformación de las líneas de código hacia BNF para no presenciar diferencias significativas al hacer una traducción. 4.3. Conversión entre lenguajes Esta situación se plantea para evaluar casos con código de complejidad mayor para poder identificar y resaltar casos en los cuales la verificación del código es necesaria. En este tipo de casos se esperanerrores menores en los cuales no se pueda encontrar alguna similitud entre los lenguajes, debido a que el código pueda llegar a ser muy específico o característico en su funcionalidad. Así pues, se debe mencionar que no en todas las situaciones va a fallar, pero es lo que se desea evaluar en este caso. Se presenta primero el código del lenguaje fuente, luego se hace un paso a los componentes de su respectivo BNF, después se hace la traducción a DIANA, y al final se traduce al lenguaje objetivo al que se quiere llegar. De aquí, se promueve entender los resultados en los diferentes lenguajes y verificar que estén correctamente realizados. 4.3.1. ADA El código en ADA es el siguiente: procedure Arr1 is A: array(1..5) of Integer; -- Array subscripts 1 to 5. I: Integer; begin -- Read 'em in. for I in 1..5 loop Put("> "); Get(A(I)); end loop; 33 -- Put 'em out in reverse order. Put("["); for I in reverse A'Range loop Put(A(I)); if I > A'First then Put(' '); end if; end loop; Put_Line("]"); end Arr1; Este es un código más complejo que usa una búsqueda de números en un arreglo para ponerlos en el orden contrario a este. El paso a BNF del código es: <procedure_call_statement>-<array_type_definition>- <integer_type_definition>-<begin>-<loop_statement>-<expression>- <expression>-<expression>-<loop_statement>-<expression>- <if_statement>-<expression>-<expression>-<end> Posteriormente, se hace el paso al lenguaje intermedio para determinar el entendimiento de la gramática inicial. En DIANA la respuesta es: procedure_call-array-integer-begin-LOOP-EXP-EXP-EXP-LOOP-EXP-if- EXP-EXP-end Con este BNF, se traduce a los nuevos lenguajes, como se muestra a continuación: • JOVIAL <procedure-statement>-<array-declaration>-<numeric-constant>- <BEGIN>-<loop-statement>-<formula>-<formula>-<formula>-<loop- statement>-<formula>-<conditional-statement>-<formula>- <formula>-<END> • C <expression-statement>-<struct-declarator-list>-<integer- constant>-<{>-<iteration-statement>-<struct-declarator>-<struct- declarator>-<struct-declarator>-<iteration-statement>-<struct- declarator>-<selection-statement>-<struct-declarator>-<struct- declarator>-<}> Es posible hacer una comparación de las gramáticas finales y se nota cierta diferencia entre JOVIAL y C. En este caso, con JOVIAL es más fácil entender la sintaxis proveniente de DIANA, debido a que como se mencionó, estos lenguajes vienen de la misma industria. En el caso de C, al ser un lenguaje más general se nota el primer error sintáctico en el orden dónde van los 34 corchetes “{ }”. Sin embargo, tras la complejidad del código inicial, la gramática fue entendida satisfactoriamente y los componentes esenciales del código fueron traducidos a los objetivos. 4.3.2. JOVIAL El código en JOVIAL es el siguiente: START FILE MYOUTPUT ... $ ''Insufficient information to complete this declaration'' PROC SIEVEE $ '' define the sieve data structure '' ARRAY CANDIDATES 1000 B $ FOR I =0,1,999 $ BEGIN '' everything is potentially prime until proven otherwise '' CANDIDATES($I$) = 1$ END '' Neither 1 nor 0 is prime, so flag them off '' CANDIDATES($0$) = 0$ CANDIDATES($1$) = 0$ '' start the sieve with the integer 0 '' FOR I = 0$ BEGIN IF I GE 1000$ GOTO DONE$ '' advance to the next un-crossed out number. '' '' this number must be a prime '' NEXTI. IF I LS 1000 AND Candidates($i$) EQ 0 $ BEGIN I = I + 1 $ GOTO NEXTI $ END '' insure against running off the end of the data structure '' IF I LT 1000 $ BEGIN '' cross out all multiples of the prime, starting with 2*p. '' FOR J=2 $ FOR K=0 $ BEGIN K = J * I $ IF K GT 999 $ GOTO ADV $ CANDIDATES($K$) = 0 $ J = J + 1 $ END '' advance to the next candidate ADV. I = I + 1 $ 35 END END '' all uncrossed-out numbers are prime (and only those numbers) '' '' print all primes OPEN OUTPUT MYOUTPUT $ '' DONE. FOR I=0,1,999$ END IF CANDIDATES($I$) NQ 0$ BEGIN OUTPUT MYOUTPUT I $ END END TERM$ Este código presenta el uso de múltiples condicionales, ciclos y saltos de código. Se tiene un código de mayor complejidad para lograr entender su gramática correctamente. El paso a BNF es el siguiente: <entry-modifier>--<file-declaration>--<procedure-statement>-- <array-declaration>--<loop-statement>--<BEGIN>--<assigment- statement>--<END>--<assigment-statement>--<assigment-statement>-- <loop-statement>--<BEGIN>--<conditional-statement>--<go-to- statement>--<entry-modifier>--<conditional-statement>--<BEGIN>-- <numeric-formula>--<go-to-statement>--<END>--<conditional- statement>--<BEGIN>--<loop-statement>--<loop-statement>--<BEGIN>-- <numeric-formula>--<conditional-statement>--<go-to-statement>-- <assigment-statement>--<numeric-formula>--<END>--<entry-modifier>-- <numeric-formula>--<END>--<END>--<output-statement>--<entry- modifier>--<loop-statement>--<BEGIN>--<conditional-statement>-- <BEGIN>--<output-statement>--<END>--<END>--<entry-modifier> Se realiza el proceso de entendimiento de la gramática en el lenguaje intermedio, así: entry_call-record-procedure_call-array-LOOP-begin-assign-end- assign-assign-LOOP-begin-if-goto-entry_call-if-begin- numeric_literal-goto-end-if-begin-LOOP-LOOP-begin-numeric_literal- if-goto-assign-numeric_literal-end-entry_call-numeric_literal-end- end-exit-entry_call-LOOP-begin-if-begin-exit-end-end-entry_call Este punto denota una distinción general de la gran cantidad de componentes en la gramática de JOVIAL y se logra reducir a las funcionalidades específicas requeridas para conocer la estructura. La traducción a las nuevas gramáticas se obtiene como se muestra a continuación: • ADA <entry_call_statement>-<record_type_definition>- <procedure_call_statement>-<array_type_definition>- <loop_statement>-<begin>-<assigment_statement>-<end>- 36 <assigment_statement>-<assigment_statement>-<loop_statement>- <begin>-<if_statement>-<goto_statement>-<entry_call_statement>- <if_statement>-<begin>-<numeric_literal>-<goto_statement>-<end>- <if_statement>-<begin>-<loop_statement>-<loop_statement>- <begin>-<numeric_literal>-<if_statement>-<goto_statement>- <assigment_statement>-<numeric_literal>-<end>- <entry_call_statement>-<numeric_literal>-<end>-<end>- <exit_statement>-<entry_call_statement>-<loop_statement>- <begin>-<if_statement>-<begin>-<exit_statement>-<end>-<end>- <entry_call_statement> • C ¡entry_call!-<storage-class-specifier>-<expression-statement>- <struct-declarator-list>-<iteration-statement>-<{>-<assignment- expression>-<}>-<assignment-expression>-<assignment-expression>- <iteration-statement>-<{>-<selection-statement>-<jump- statement>-¡entry_call!-<selection-statement>-<{>-<integer- constant>-<jump-statement>-<}>-<selection-statement>-<{>- <iteration-statement>-<iteration-statement>-<{>-<integer- constant>-<selection-statement>-<jump-statement>-<assignment- expression>-<integer-constant>-<}>-¡entry_call!-<integer- constant>-<}>-<}>-¡exit!-¡entry_call!-<iteration-statement>-<{>- <selection-statement>-<{>-¡exit!-<}>-<}>-¡entry_call! Se deben verificar las siguientes sentencias: entry_call-exit La comparación de la gramática es clara, JOVIAL logra comprender la sintaxis general del lenguaje, mientras que a C le cuesta comprender ciertos componentes específicos del funcionamiento del código. Los errores evidenciados en C demuestran su separación con respecto a rasgos particulares de los lenguajes anteriores y están relacionados a la falta del paso de verificación de código para adaptar la completitud de sentencias en la nueva gramática. Es por eso, que la dependencia en saber transformar correctamente
Compartir