Precedentes (I) TEMA 2. DISEÑO Y PARADIGMAS DE LOS LENGUAJES DE PROGRAMACIÓN. Precedentes (II) • El diseño de lenguajes es un problema complejo • En los lenguajes de programación hay muchos errores de diseño – C • Se han considerado ideas erróneas – FORTRAN – C++ • el programador sabe lo que hace – Obtener direcciones de variables locales – Printf no comprueba el tipo y número de los argumentos • se puede extender C al paradigma orientado a objeto (demasiados compromisos de compatibilidad) • Uso intensivo de apuntadores a objetos • Lenguaje demasiado complicado • uso del GOTO • declaración implícita de variables – COBOL • Sentencias demasiado largas • Farragoso de escribir y leer • Uso del GOTO – APL • si puede significar algo, lo significa – LISP • una sintaxis simple es más fácil de aprender • Los paréntesis dificultan escribir programas • La lectura es compleja por una sintaxis demasiado homogénea Compiladores I (13/08/2003 07:52) - 2.1 - Compiladores I (13/08/2003 07:52) - 2.2 - – Un lenguaje se puede utilizar para todo tipo de desarrollo de software • Aun con muchos errores de diseño es importante estudiar los lenguajes de programación existentes ya que aportan muchas ideas Compiladores I (13/08/2003 07:52) - 2.3 - 1 Consideraciones Preliminares Fuentes de Ideas • ¿Cual es el propósito del lenguaje? – No hay un lenguaje bueno para todo – Aplicación específica • Bases de datos, sistemas expertos, cálculo numérico, programación simbólica, diseño algorítmico, etc. • ¿Es necesario diseñar un nuevo lenguaje? – Ya existe un lenguaje apropiado • El nuevo lenguaje se diferencia de los existentes – Se consume demasiado tiempo en el diseño e implementación de un nuevo lenguaje • Es demasiado fácil diseñar un lenguaje incompleto • Lenguaje demasiado especializado • Sacrificar características del lenguaje por un compilador simple. – Otras opciones • Lenguaje natural (COBOL) – Fácil de leer y escribir – Ambiguo – Solo se puede implementar un subconjunto • Lenguajes matemáticos (APL) – No ambiguos – Crípticos – La programación no es matemáticas • Lenguajes de programación – Errores de diseño (pasado) • Comunicación humana • Prevención y detección de errores • Usabilidad • Efectividad • Compilabilidad • Eficiencia • Independencia de la máquina • Simplicidad • Hay que valorar cada característica de un lenguaje existente antes de incluirla en el nuevo lenguaje • Uniformidad • Ortogonalidad • Generalización y especialización • Otras filosofías de diseño • Pruebas y experimentos – Programar con el lenguaje antes de haber acabado su diseño • Sirve para detectar los problemas del diseño y tomar decisiones difíciles. • Un modulo o librería de funciones • Ampliar un lenguaje de programación Compiladores I (13/08/2003 07:52) Objetivos y Filosofías de Diseño - 2.4 - Compiladores I (13/08/2003 07:52) - 2.5 - Compiladores I (13/08/2003 07:52) - 2.6 - 2 Comunicación humana (I) Comunicación humana (II) • Se busca una comunicación eficiente entre el programador y el ordenador • Un buen nivel de comunicación se da cuando los programas son leíbles – No ha de ser necesaria una documentación externa al programa (minimizar) – Es más importante que un programa sea leíble que escribible • Un programa se escribe una vez, pero se lee muchas durante su depuración, documentación y mantenimiento. • Tendencia actual a separar la interfaz de la implementación de un módulo Prevención y detección de errores – El lenguaje ha de representar los patrones de pensamiento humanos • No hay que crear una sintaxis pensada exclusivamente para • El programador comete errores – Hay que prevenir los errores • El programador es su fuente – un modelo de cómputo teórico (λ-calculus) – un conjunto de instrucciones de la máquina – facilitar la compilación (forth) – El programador no sabe lo que hace y el compilador ha de limitar sus acciones (EUCLID, PASCAL) – Hacer imposible cierto tipo de errores – El programador no es un ordenador • Ejecutar datos -> control de flujo limitado • Errores en el uso de datos -> Tipado fuerte • Apuntadores erróneos -> Gestión de memoria implícita (LISP, PROLOG, ML, etc). • Que el compilador entienda una estructura es posible que el programador no – Evitar incluso la posibilidad de escribirlas – Reducir el conocimiento contextual – El programador no funciona con una pila como el programa compilado. – La sintaxis ha de reflejar la semántica – Hay que facilitar su detección, identificación y corrección • Redundancia – Tener que declarar antes de utilizar. – Evitar coerciones inductoras de errores • Reducir las manipulaciones implícitas – Coerciones (coerciones de PL/I o C) • float a int por su perdida de precisión – ON de BASIC para eventos o excepciones – Constructores y destructores de C++ (necesarios, pero complican el seguimiento del flujo de ejecución) Compiladores I (13/08/2003 07:52) - 2.7 - • Comprobaciones en tiempo de ejecución – Indice de array fuera de limites – Control sobre los apuntadores a NULL Compiladores I (13/08/2003 07:52) - 2.8 - Compiladores I (13/08/2003 07:52) - 2.9 - 3 Lenguaje Utilizable y Efectivo Otras filosofías de diseño • Un lenguaje ha de ser fácil de utilizar – Un lenguaje ha de ser fácil de aprender y recordar • Evitar la necesidad de consultar el manual (C++ no cumple) • Lenguaje simple (C++ no cumple) • Aprendizaje incremental (PROLOG no cumple, LISP si cumple) – El comportamiento del lenguaje ha de ser predecible • el uso de void* de C++ es incomprensible • Efectividad – Los detalles de implementación no han de oscurecer las intenciones del programador • Soportar abstracción • Modularidad: Separar especificación de implementación – Los Efectos de un cambio han de quedar localizados – Evitar los trucos (programas ilegible) Compiladores I (13/08/2003 07:52) - 2.10 - Diseño Detallado • Compilabilidad – Se ha de poder compilar programa en un tiempo reducido – Se ha de poder depurar o aplicar otras herramientas de análisis • Eficiencia: La ejecución ha de ser rápida • Independencia de la máquina • Estructura de las expresiones • Estructuras de datos • Estructuras de control • Estructura de compilación • Estructura de la entrada/salida • Simplicidad • Uniformidad: lenguaje predecible • Ortogonalidad – Todas las características del lenguaje se han de poder combinar • Generalización y especialización – La generalización dice que algo similar también es correcto, pero es difícil de implementar – Hay que especializar para facilitar la implementación sin perder la utilidad del lenguaje Compiladores I (13/08/2003 07:52) - 2.11 - Compiladores I (13/08/2003 07:52) - 2.12 - 4 Ejemplos: Lenguajes de Programación y su Diseño • El lenguaje C – Aplicación: programación de sistemas – Programador: experto – Sacar el mayor rendimiento posible del ordenador • PASCAL – Aplicación: docencia – Programador: inexperto • LISP – Aplicación: procesamiento simbólico – Desarrollo rápido • PROLOG – Aplicación: procesamiento simbólico – Programación lógica – Desarrollo rápido El Lenguaje C (I) El Lenguaje C (II) – En contra de la velocidad • Lenguaje pensado para el desarrollo de sistemas operativos (unix) – Rendimiento/velocidad de ejecución – Programador experto – Código compacto • Redimiento/Velocidad de ejecución – Operaciones básicas simples – Apuntadores • Cálculos en coma flotante con la máxima precisión posible • Programador experto – Amplio uso de coerciones • Implícitas – char -> int -> double – double <- int <.- char – void * -> tipo*, tipo* -> void * • uso para los strings y arrays • Estructuras de datos referenciadas por apuntadores (paso por referencia explícito) - 2.13 - Compiladores I (13/08/2003 07:52) – El programador puede convertir cualquier tipo de datos en cualquier otro. • int -> void* – Gestión de memoria explícita – En las primeras versiones las funciones solo retornan datos que puedan ir en un registro del procesador – Optimización explícita – Flujo de ejecución • break y continue • goto local • long jump – Obtención de direcciones • Incremento y decremento • Operaciones con asignación incluida • variables register Compiladores I (13/08/2003 07:52) • Explícitas • Se puede obtener la dirección de cualquier variable aunque esta sea local - 2.14 - Compiladores I (13/08/2003 07:52) - 2.15 - 5 El Lenguaje C (III) El Lenguaje C (IV) – Prioridades de operadores complejas • Casi cualquier combinación de operadores es aceptada por el compilador • Otras cuestiones – Falta de un método por defecto para la gestión de excepciones • Códigos de error retornados por las funciones de la librería que continuamente ha de verificar el programa – Faltan comentarios de una línea – Palabras clave con diferentes significados según el contexto PASCAL – Sintaxis de los comentarios confundible (A=b/*p) • Código compacto – La mayor parte de las instrucciones se traducen a unas pocas instrucciones de código máquina – Faltan instrucciones de alto nivel • Docencia – Estructura muy rígida de los programas – Un buen nivel de abstracción – Transportable – Lenguaje simplificado • Falta de módulos y librerías • Librería básica muy limitada • Strings – Tratamiento por apuntadores – Gestión explicita de la memoria – Considerar continuamente si la reserva de memoria es suficiente • Todo lo que hace el programa se indica explícitamente – Pensado para la resolución de problemas por subdivisión • No hay copia de datos compuestos • static – Asignación array a array – Semántica ambigua – Escritura farragosa de los programas – Minimización del uso de coerciones • Definiciones anidables de funciones y procedimientos • Incrementos en una expresión – Métodos para la escritura de programas transportables soportados por el programador – Tipado fuerte • Compilación condicional • sizeof Compiladores I (13/08/2003 07:52) - 2.16 - Compiladores I (13/08/2003 07:52) - 2.17 - Compiladores I (13/08/2003 07:52) - 2.18 - 6 LISP PROLOG • Sintaxis demasiado simple – Difícil escritura y lectura de los programas • Comprobaciones semánticas en tiempo de ejecución – Variables declaradas – Tipos de datos • Enfoque pragmático – Programación imperativa y funcional • Orientado a optimizar el desarrollo frente a la velocidad de ejecución – Gestión de memoria implícita – Uso de listas para todo – Amplia librería de funciones C++ • Programación declarativa y lógica • Implementación del cálculo de predicados – Faltan funciones (λ-prolog) • Difícil modularidad – Un método de programación demasiado alejado de la programación habitual – Gramática de operadores extensible – Totalmente transportable – Modulariza las búsquedas • Orientación a objeto de C – Mantener la compatibilidad con C • Uso masivo de apuntadores • Tratamiento de excepciones no soportado por las librerías estándar – Rendimiento • Complica el lenguaje – Static en métodos – Métodos virtuales y no virtuales – Templates • Convierte los bucles de una búsqueda en una secuencia lineal de aplicación de predicados • Destructores por gestión de memoria explícita • Apuntadores y referencias • Otros – comentarios de una línea fáciles de escribir y leer (repetición de //) – Implementación parcial de las coerciones • Diseño por etapas – Funciones repetidas • setq y setf – Parches para mantener compatibilidad • forma de creación de los places Compiladores I (13/08/2003 07:52) - 2.19 - Compiladores I (13/08/2003 07:52) - 2.20 - Compiladores I (13/08/2003 07:52) - 2.21 - 7 ADA Opciones en el Diseño (I) • Lenguaje pensado para grandes proyectos de software – Facilitar el uso de librerías • Interfaz separa de la implementación del módulo • Control exhaustivo sobre la interfaz – Fácil lectura y detección de posibles bugs • Sintaxis de las instrucciones de control de flujo con marcadores claros de inicio y fin (If then else end if) • Etiquetas visibles <<etiqueta>> • Coerciones explicitas – Reducción de los errores • Definición de excepciones para controlar errores de ejecución – Acceso a array fuera de rango – Un case del que no se selecciona ninguna opción • Atributos asociados a las variables: – un array tiene asociado el atributo de su tamaño Compiladores I (13/08/2003 07:52) - 2.22 - Opciones en el Diseño (II) • Sintaxis de control de flujo – Instrucción compuesta • C, PASCAL (for (;;) ; {...} – Instrucciones de control de flujo con claros marcadores de inicio y fin • ADA – Instrucción vacía • Explicita ADA ( null) • Implícita C, PASCAL (; o nada) • Gestión de memoria – Explicita: C, PASCAL, ADA • Lenguaje orientado a – Instrucción: ADA, PASCAL – Expresión: C, funcionales • Velocidad – Implícita: LISP, PROLOG, Java, funcionales • Marcadores de las estructuras sintácticas – Si: PASCAL, ADA – No: C, C++ • Expresiones – Riqueza de operadores C, C++ – Pobreza de operadores PASCAL, ADA Compiladores I (13/08/2003 07:52) • Sistema de tipos – No tipado: LISP, BASIC – Tipado: C, PASCAL, ADA, etc. • Código genérico – Datos marcados con su tipo en ejecución – Tipos paramétricos – templates - 2.23 - • Simplicidad • Gestión de errores de ejecución – Responsabilidad del programador: C, C++ – Responsabilidad del lenguaje ADA – Excepciones sin declarar C++ – Excepciones declaradas Java Compiladores I (13/08/2003 07:52) - 2.24 - 8 Opciones en el Diseño (III) • Estructura declaraciones – Rígida: PASCAL, ADA – Poco rígida: C – Libre: C++ • Ámbitos – Expresión LISP – Instrucción compuesta C, C++, Java – Función C, C++, PASCAL, ADA – Espacios de nombres – Clases – Paquetes – Módulos • Coerciones – Explicitas: ADA – Pocas implícitas: PASCAL – Muchas implícitas: C, C++ Compiladores I (13/08/2003 07:52) - 2.25 - 9