Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez Herramientas desarrolladas con la teoría de la programación de sistemas. Las herramientas de programación, son aquellas que permiten realizar aplicativos, programas, rutinas, utilitarios y sistemas; para que la parte física del computador u ordenador, funcione y pueda producir resultados. Hoy en día existen múltiples herramientas de programación en el mercado, tanto para analistas expertos como para analistas inexpertos. Las herramientas de programación más comunes del mercado, cuentan con programas de depuración o “debugger”, que son utilitarios que nos permiten detectar los posibles errores en tiempo de ejecución o corrida de rutinas y programas. Muchas herramientas de software que manipulan programas fuente realizan primero algún tipo de análisis a la estructura del código. Algunos ejemplos de tales herramientas son: Editores de estructuras. Un editor de estructuras toma como entrada una secuencia de órdenes para construir un programa fuente. El editor de estructuras no sólo realiza las funciones de creación y modificación de textos de un editor de textos ordinario, sino que también analiza el texto del programa, imponiendo al programa fuente una estructura jerárquica apropiada. De esa manera, el editor de estructuras puede realizar tareas adicionales útiles para la preparación de programas. Por ejemplo, puede comprobar si la entrada está formada correctamente, puede proporcionar palabras clave de manera automática (por ejemplo, cuando el usuario escribe while, el editor proporciona el correspondiente do y le recuerda al usuario que entre las dos palabras debe ir un condicional) y puede saltar desde un begin o un paréntesis izquierdo hasta su correspondiente end o paréntesis derecho. Además, la salida de tal editor suele ser similar a la salida de la fase de análisis de un compilador. Impresoras estéticas. Una impresora estética analiza un programa y lo imprime de forma que la estructura del programa resulte claramente visible. Por ejemplo, los comentarios pueden aparecer con un tipo de letra especial, y las proposiciones pueden aparecer con una indentación proporcional a la profundidad de su anidamiento en la organización jerárquica de las proposiciones. Verificadores estáticos. Un verificador estático lee un programa, lo analiza e intenta descubrir errores potenciales sin ejecutar el programa. La parte de análisis a menudo es similar a la que se encuentra en los compiladores de optimización. Así, un verificador estático puede detectar si hay partes de un programa que nunca se podrán ejecutar o si cierta variable se usa antes de ser definida. Además, puede detectar errores de lógica, como intentar utilizar una variable real como apuntador, empleando las técnicas de verificación de tipos. Intérpretes. En lugar de producir un programa objeto como resultado de una traducción, un intérprete realiza las operaciones que implica el programa fuente. Muchas veces los intérpretes se usan para ejecutar lenguajes de órdenes, pues cada operador que se ejecuta en un lenguaje de órdenes suele ser una invocación de una rutina compleja, como un editor o un compilador. Del mismo modo algunos lenguajes de “muy alto nivel”, normalmente son interpretados, porque hay muchas cosas sobre los datos, como el tamaño y la forma de las matrices, que no se pueden deducir en el momento de la compilación. Compiladores. Tradicionalmente, se concibe un compilador como un programa que traduce un programa fuente, como FORTRAN, al lenguaje ensamblador o de máquina de algún computador. Sin embargo, hay lugares, al parecer, no relacionados donde la tecnología de los compiladores se usa con regularidad. Formadores de textos. Un formador de textos toma como entrada una cadena de caracteres, la mayor parte de la cual es texto para componer, pero alguna incluye órdenes para indicar párrafos, figuras o estructuras matemáticas, como subíndices o superíndices. Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez Compiladores de circuitos de silicio. Un compilador de circuitos de silicio tiene un lenguaje fuente similar o idéntico a un lenguaje de programación convencional. Sin embargo las variables del lenguaje no representan localidades de memoria, sino señales lógicas (0 o 1) o grupos de señales en un circuito de conmutación. La salida es el diseño de un circuito en un lenguaje apropiado. Intérpretes de consultas. Un intérprete de consultas traduce un predicado que contiene operadores relacionales y boléanos a órdenes para buscar en una base de datos los registros que satisfagan ese predicado. Lenguaje. Se llama lenguaje a cualquier tipo de código semiótico estructurado, para el que existe un contexto de uso y ciertos principios combinatorios formales. Existen muchos contextos tanto naturales como artificiales donde aparecen lenguajes. El lenguaje humano se basa en la capacidad de los seres humanos para comunicarse mediante signos. Principalmente lo hacemos utilizando el signo lingüístico. Aún así, hay diversos tipos de lenguaje. El lenguaje animal se basa en el uso de señales sonoras, visuales y olfativas a modo de signos para referirse a un referente o un significado diferente de dichas señales. Dentro del lenguaje animal están los gritos de alarma, el lenguaje de las abejas, etc. Los lenguajes formales son construcciones artificiales humanas, que se usan en matemática y otras disciplinas formales, incluyendo lenguajes de programación. Estas construcciones tienen estructuras internas que comparten con el lenguaje humano natural, por lo que pueden ser en parte analizados con los mismos conceptos que éste. Ya sea que se trate de un distrito escolar, un banco, un sistema de transporte o una planta manufacturera, la comunicación eficaz es esencial. ¿Qué es comunicación? La comunicación se puede definir como la transferencia de información y entendimiento entre personas por medio de símbolos con significados. La comunicación en cualquier circunstancia de nuestras vidas, es de vital importancia, para poder expresar nuestros objetivos, necesidades, emociones, etc., de la misma manera se vuelve imperiosa dentro de las empresas, por lo que se pueden mencionar algunos beneficios en los cuales ayuda: · Establecer y difundir los propósitos y metas de la empresa. · Desarrollar planes para lograr dichos propósitos. · Organizar los recursos humanos en forma efectiva. · Seleccionar, desarrollar y evaluar los miembros de dicha empresa. · Originar un clima de amplia participación. · Controlar el desempeño de los miembros. · Influir en la motivación de nuestros trabajadores. El proceso de la comunicación. Para llevar a cabo una comunicación eficaz, es necesario que en el flujo de la transmisión de ideas, intervengan varios elementos, los que se describen a continuación: 1. Emisor (codificador). Fuente de información e iniciador del proceso de comunicación. Es su responsabilidad elegir el tipo de mensaje y canal más eficaces, tras de lo cual codifica el mensaje. En una empresa será la persona que tiene una información, necesidades o deseos y la intención de comunicarlos a una o más personas. La Codificación consiste en la traducción de información a una serie de símbolos para la comunicación. Esta es necesaria porque la información únicamente puede transmitirse de una persona a otra por medio de representaciones o símbolos. 2. Receptor (decodificador). Es el individuo que recibe, por medio de sus sentidos, el mensaje del transmisor. Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez La decodificación es la interpretación y traducción de un mensaje para que la información tenga sentido. Uno de los principales requisitos que debe cubrir el receptor es la capacidad de escuchar. Escuchar es el acto de prestar atención a un mensaje, no únicamente oírlo. Lineamientos para la escucha eficaz. 3. Mensaje. Es la información codificada que el transmisor envía al recepto. El mensaje puede darse en cualquier forma susceptible de ser captada y entendida por uno o más de los sentidos del receptor. 4. Canales. Es el medio de comunicación entre el transmisor y el receptor, algo así como el aire es para las palabras y el papel para las letras. 5. Retroalimentación. Es la respuesta del receptor al mensaje del emisor. Es la mejor manera de comprobar que el mensaje se recibió y comprendió. 6. Percepción. Es el significado atribuido a un mensaje por un emisor o receptor. Las percepciones se ven influidas por lo que ven los individuos, por la manera en que organizan esos elementos en su memoria y por los significados que les atribuyen. Diagrama del Proceso de la Comunicación. Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez Barreras en la Comunicación. Son las condiciones o factores que interfieren en la transmisión efectiva de un mensaje, y entre ellas se encuentran: Barreras Interpersonales. · La interpretación que realiza el receptor del mensaje. · Defectos fisiológicos del emisor o receptor. · Los sentimientos y emociones del emisor o receptor. · Las condiciones medio-ambientales en que se da el mensaje. · El uso de la semántica, tales como diferencias de lenguaje, palabras no adecuadas, información insuficiente, etc. Barreras Organizacionales. Cuando una empresa crece, su estructura orgánica se amplía mucho, originando problemas en la comunicación, con lo que se generan situaciones donde se agrega, se modifica, se elimina o se cambia totalmente un mensaje, y más si el mensaje ha sido transmitido en forma oral. Otra barrera se traduce en la autoridad administrativa, donde se debe recordar que la autoridad es una característica de toda empresa, por lo que, esa supervisión genera una barrera, y por último, la especialización es otra barrera que afecta la eficacia de la comunicación. Lenguajes de Programación. Un lenguaje de programación está formado por un conjunto de símbolos básicos (alfabeto) y un conjunto de reglas que especifican como manipularlos. También debe darle significado a las cadenas formadas al manipular los símbolos básicos. Los lenguajes de programación pueden clasificarse de acuerdo a su semejanza con el lenguajes maquina o a su semejanza con el lenguaje humano (generalmente inglés). Los lenguajes que tiene mayor semejanza con el lenguaje humano se les llama lenguajes de alto nivel, mientras que los lenguajes más parecidos al lenguaje de maquina son conocidos como de bajo nivel. Entre los lenguajes de bajo nivel se encuentra el lenguajes ensamblador. Algunos ejemplos de lenguajes de alto nivel son: Algol, Basic, C, Cobol, Fortran, Modula 2, Pascal, Prolog. Traductor y su estructura. Un traductor es un programa que recibe como entrada código escrito en un cierto lenguaje y produce como salida código en otro lenguaje. Generalmente el lenguaje de entrada es de más alto nivel que el de salida. Ejemplos de traductores son los ensambladores y los compiladores. En un sentido orientado hacia la computación, un traductor, de manera general, es un software que toma como entrada un programa escrito en un código llamado fuente y genera como salida otro programa en un código llamado objeto. Un ensamblador, es un programa que traduce de un lenguaje ensamblador a lenguaje máquina, mientras que un compilador es un programa que traduce de un lenguaje de alto nivel a un lenguaje de bajo nivel o a lenguaje máquina. Si el código fuente está en un lenguaje abstracto o de alto nivel y si el objetivo es un lenguaje de ensamble de bajo nivel o de máquina, el traductor que usamos es un compilador. Algunos ejemplos de traductores son los compiladores (toma como entrada código en alto nivel y genera como salida código en bajo nivel), los intérpretes (toma como entrada código en alto nivel y genera como salida un código intermedio), los preprocesadores (toma como entrada código en alto nivel y genera como salida código en alto nivel) y el ensamblador (toma como entrada código en ensamblador y genera como salida código en bajo nivel). Su estructura podría ser expresada de la siguiente manera: código fuente → traductor → código objeto Intérpretes o Traductores. Definición. Un intérprete es un programa que ejecuta un programa escrito en un determinado lenguaje examinando y ejecutando cada sentencia del programa una a una, por separado y sin realizar un proceso de compilación previo. Al contrario de lo que se podría pensar el intérprete NO traduce la sentencia a código máquina antes de ejecutarla. En su lugar, identifica el tipo de sentencia y la ejecuta directamente. El código para ejecutar la sentencia está contenido dentro del intérprete que contendrá rutinas para: evaluar una expresión, almacenar información en Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez memoria, recuperar datos de memoria, etc. El intérprete contendrá el número de rutinas necesarias para poder ejecutar cualquier sentencia del lenguaje que está interpretando. Ejemplo: C=A+B 1. El intérprete lee la sentencia y determina que es una asignación. 2. Llamará a una de sus rutinas para evaluar la expresión a la derecha de la asignación. 3. Esta rutina toma los símbolos A y B, determina donde están almacenados, obtiene sus valores actuales y los suma. 4. El intérprete toma el valor calculado y lo almacena en la dirección de memoria a la que hace referencia el símbolo C. Al igual que un compilador para llevar a cabo su tarea el intérprete usará un analizador léxico-gráfico y guardará la información necesaria para la traducción en una tabla de símbolos. Interpretar una sentencia es relativamente complejo, al tener que ser escaneada y analizada, los símbolos tienen que ser buscados, hay que llevar a cabo lo indicado por la sentencia, etc. Esto significa que el número de instrucciones máquinas a ejecutar para interpretar una sentencia puede ser mucho mayor que el código generado por un compilador. Además, cada sentencia tendrá que ser interpretada cada vez que aparezca, por lo que una sentencia en un bucle sería interpretada varias veces. Además de elegir entre compilar o interpretar un programa, otra opción es combinar ambos procesos de traducción. En este caso, se realiza una compilación “parcial” a un código intermedio que es seguida de un proceso de interpretación. Uno de los lenguajes más utilizados hoy en día, sobre todo para el desarrollo de aplicaciones para la Web, que sigue esta propuesta es el lenguaje Java. En el lenguaje Java el código primero se compila con el compilador de Java javac. Este compilador genera un código virtual llamado Byte Code. Este código es luego interpretado por una Máquina Virtual de Java (o JVM de Java Virtual Machine) que debe estar instalada y ejecutándose en la máquina en la que se desee ejecutar el código compilado con javac. Comparación entre compiladores e intérpretes Históricamente, debido a la escasez de memoria de los primeros ordenadores, se puso de moda el uso de intérpretes frente a los compiladores, pues el programa fuente sin traducir y el intérprete juntos requerían una cantidad de memoria menor que la del compilador. Por ello, los primeros ordenadores personales (Spectrum, Commodore VIC20, PC XT de IBM, etc.) iban siempre acompañados de un intérprete de BASIC. La mejor información sobre los errores por parte del compilador así como una mayor velocidad de ejecución del código resultante hizo que poco a poco se impusieran los compiladores. Hoy en día, y con el problema de la memoria prácticamente resuelto, se puede hablar de un gran predominio de los compiladores frente a los intérpretes, aunque intérpretes como los incluidos en los navegadores de Internet para Java son la gran excepción. Algunas de las ventajas de compilar frente a interpretar son: • Se compila una vez; se ejecuta muchas veces. • La ejecución del programa objeto es mucho más rápida que si se interpreta el programa fuente. • El compilador tiene una visión global del programa, por lo que la información de mensajes de error es más detallada. Por otro lado, algunas de las ventajas de interpretar frente a compilar son: • Un intérprete necesita menos memoria que un compilador. • Permiten una mayor interactividad con el código en tiempo de desarrollo. • En algunos lenguajes (Smalltalk, Prolog, LISP, Java) está permitido y es frecuente añadir código según se ejecuta otro código, y esta característica solamente es posible implementarla en un intérprete. Estructura de un Compilador. Cualquier compilador debe realizar dos tareas principales: análisis del programa a compilar y síntesis de un programa en lenguaje maquina que, cuando se ejecute, realizara correctamente las actividades descritas en el programa fuente. Para el estudio de un compilador, es necesario dividir su trabajo en fases. Cada fase representa una transformación al código fuente para obtener el código objeto. La siguiente figura representa los componentes en que se divide un compilador. Las tres primeras fases realizan la tarea de análisis, y las demás la síntesis. En cada una de las fases se utiliza un administrador de la tabla de símbolos y un manejador de errores. Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez Análisis Léxico. En la fase de análisis léxico se leen los caracteres del programa fuente y se agrupan en cadenas que representan los componentes léxicos. Cada componente léxico es una secuencia lógicamente coherente de caracteres relativa a un identificador, una palabra reservada, un operador o un carácter de puntuación. A la secuencia de caracteres que representa un componente léxico se le llama lexema (o con su nombre en inglés token). En el caso de los identificadores creados por el programador no solo se genera un componente léxico, sino que se genera otro lexema en la tabla de símbolos. Análisis Sintáctico. En esta fase, los componentes léxicos se agrupan en frases gramaticales que el compilador utiliza para sintetizar la salida. Análisis Semántico. La fase de análisis semántico se intenta detectar instrucciones que tengan la estructura sintáctica correcta, pero que no tengan significado para la operación implicada. Generación de código Intermedio. Algunos compiladores generan una representación intermedia explícita del programa fuente, una vez que se han realizado las fases de análisis. Se puede considerar esta operación intermedia como un subprograma para una máquina abstracta. Esta representación intermedia debe tener dos propiedades importantes: debe ser fácil de producir y fácil de traducir al programa objeto. Optimización de Código. En esta fase se trata de mejorar el código intermedio, de modo que resulte un código de máquina más rápido de ejecutar. Generación de Código. Esta constituye la fase final de un compilador. En ella se genera el código objeto que por lo general consiste en código en lenguaje máquina (código relocalizable) o código en lenguaje ensamblador. Administrador de la tabla de símbolos. Una tabla de símbolos es una estructura de datos que contiene un registro por cada identificador. El registro incluye los campos para los atributos del identificador. El administrador de la tabla de símbolos se encarga de manejar los accesos a la tabla de símbolos, en cada una de las etapas de compilación de un programa. Manejador de errores. En cada fase del proceso de compilación es posibles encontrar errores. Es conveniente que el tratamiento de los errores se haga de manera centralizada a través de un manejador de errores. De esta forma podrán controlarse más eficientemente los errores encontrados en cada una de las fases de la compilación de un programa. Diseño de lenguajes. El lenguaje de programación puede definirse al describir 1. Lo que parecen sus programas (la sintaxis del lenguaje). 2. Lo que significan sus programas (la semántica del lenguaje). La sintaxis del lenguaje se presenta ampliamente con una notación denominada gramática libre de contexto o BNF. (Backus-Naur Form). La semántica del lenguaje es más difícil de expresar que la sintaxis y generalmente se decide por especificarla usando descripciones informales y ejemplos. Ambas situaciones deben ser consideradas por quien diseña un nuevo lenguaje para satisfacer las necesidades de los usuarios potenciales. Por ejemplo, alguien puede hacer el siguiente requerimiento: Diseñar un lenguaje para proporcionar órdenes a un robot para que éste se desplace. Aunque no se especifica qué tipo de órdenes se darán, el diseñador deberá responder a dos preguntas: Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez 1. ¿Cómo expresar lo que queremos que realice la máquina? 2. ¿Cómo debe entender la máquina las órdenes que expresamos? Puede ser que el tipo de órdenes que se darán sean del tipo: inicio, norte, sur, este, oeste. De manera que cuando se dé al programa, inicio norte norte este este sur el robot tenga el siguiente comportamiento: Si el robot no fuese real sino que tuviera que simularse, la semántica de cada orden sería determinada por modificar las coordenadas de la posición del robot a través de asociar acciones a las reglas gramaticales, como a continuación se indican: Producción regla semántica. secuencia inicio secuencia.x:=0 secuencia.y:=0 secuencia secuencia instrucción secuencia.x:=secuencia1.x+instrucción.dx secuencia.y:=secuencia1.y + instrucción.dy secuencia este instrucción.dx:=1 instrucción.dy:=0 secuencia norte instrucción.dx:= 0 instrucción.dy:=1 secuencia oeste instrucción.dx:=-1 instrucción.dy:=0 secuencia sur instrucción.dx:=0 instrucción.dy:=1 Unidad I – Segunda Parte / Programación de Sistemas / Ing. Leonardo Ramírez Generación de código. En programación, la generación de código es una de las fases mediante el cual un compilador convierte un programa sintácticamente correcto en una serie de instrucciones a ser interpretadas por una máquina. La entrada en esta fase viene representada, típicamente, por un Árbol Sintáctico, un Árbol de Sintaxis Abstracta, o una Representación Intermedia; la máquina destino puede ser un microprocesador o una máquina abstracta tal como una máquina virtual o un lenguaje intermedio, legible por un humano. Compiladores más sofisticados realizan múltiples traducciones en cadena (pipelining) con el fin de poder construir código para múltiples plataformas y evitar tener que construir todas las capas del compilador. En términos más generales, la generación de código: es usada para construir programas de una manera automática evitando que los programadores tengan que escribir el código a mano. La generación de código puede realizarse en tiempo de ejecución, Tiempo de carga, o Tiempo de compilación. Los compiladores JIT (Just In Time) son un ejemplo de generadores de código.