Está en la página 1de 11

CAPÍTULO 1

PRINCIPIOS BÁSICOS DE LOS LENGUAJES DE PROGRAMACIÓN

Aunque existen muchos lenguajes de programación, las diferencias entre ellos son insignificantes
comparadas con las diferencias entre los lenguajes naturales. En este capítulo, discutiremos los
aspectos comunes compartidos entre los diferentes lenguajes de programación. Estos aspectos
incluyen:

° los paradigmas de programación que definen cómo se expresa la computación;

° las principales características de los lenguajes de programación y su impacto en el rendimiento


de los programas escritos en los lenguajes;

° una breve revisión de la historia y el desarrollo de los lenguajes de programación;

° las estructuras léxicas, sintácticas y semánticas de los lenguajes de programación, los datos y los
tipos de datos, el procesamiento y el preprocesamiento de los programas, y los ciclos de vida del
desarrollo de programas.

Al final del capítulo, debería haber aprendido:

° qué son los paradigmas de programación

° una visión general de los diferentes lenguajes de programación y los conocimientos previos de
estos lenguajes;

° las estructuras de los lenguajes de programación y cómo se definen los lenguajes de


programación a nivel sintáctico;

° los tipos de datos, la comprobación fuerte frente a la débil;

° la relación entre las características del lenguaje y sus prestaciones;

° el procesamiento y el preprocesamiento de los lenguajes de programación, la compilación frente


a la interpretación, y los diferentes modelos de ejecución de macros, procedimientos y
procedimientos en línea;

° los pasos utilizados para el desarrollo de programas: requisitos, especificación, diseño,


implementación pruebas y demostración de la corrección de los programas.

El capítulo está organizado como sigue. La sección 1.1 presenta los paradigmas de programación,
el rendimiento características, y el desarrollo de los lenguajes de programación. La sección 1.2
esboza las estructuras y los problemas de diseño de los lenguajes de programación. La sección 1.3
discute los sistemas de tipado, incluyendo los tipos de variables la equivalencia de tipos, la
conversión de tipos y la comprobación de tipos durante la compilación. La sección 1.4 presenta el
preprocesamiento y procesamiento de los lenguajes de programación, incluyendo el
procesamiento de macros, la interpretación y la compilación. Por último, la sección 1.5 trata de los
pasos de desarrollo del programa, incluyendo la especificación la prueba y la comprobación de la
corrección.

1.1 Introducción

1.1.1 Conceptos y paradigmas de programación


Se han inventado millones de lenguajes de programación y se utilizan varios miles de ellos en uso.
En comparación con los lenguajes naturales que se desarrollaron y evolucionaron de forma
independiente, los lenguajes de programación son mucho más parecidos entre sí. Esto se debe a
que:

° diferentes lenguajes de programación comparten la misma base matemática (por ejemplo, el


álgebra booleana, la lógica);

° proporcionan una funcionalidad similar (por ejemplo, aritmética, operaciones lógicas y


procesamiento de textos)

° se basan en el mismo tipo de hardware y conjuntos de instrucciones;

° tienen objetivos de diseño comunes: encontrar lenguajes que sean sencillos de usar para los
humanos y eficientes de ejecutar para el hardware;

° los diseñadores de lenguajes de programación comparten sus experiencias de diseño.

Algunos lenguajes de programación, sin embargo, son más similares entre sí, mientras que otros
son más diferentes entre sí. Basándose en sus similitudes o en los paradigmas, los lenguajes de
programación pueden dividirse en diferentes clases. En la definición de lenguaje de programación,
el paradigma es un conjunto de principios, conceptos y métodos básicos sobre cómo se expresa un
cálculo o un algoritmo. Los principales Los principales paradigmas que estudiaremos en este texto
son el imperativo, el orientado a objetos, el funcional y el lógico.

El paradigma de programación imperativo, también llamado procedimental, expresa la


computación mediante la manipulación totalmente especificada y controlada de y totalmente
controlada de los datos nombrados de una manera escalonada. En otras palabras, los datos o
valores se almacenan inicialmente en variables (posiciones de memoria), se sacan de la memoria
(se leen), se manipulan en la ALU (unidad aritmética lógica), y luego se almacenan de nuevo en las
mismas o diferentes variables (ubicaciones de memoria). de memoria). Finalmente, los valores de
las variables se envían a los dispositivos de E/S como salida. La base de Los lenguajes imperativos
se basa en el concepto de programa almacenado y en la organización y arquitectura del hardware
del ordenador (máquina de von Neumann). El concepto de programa almacenado se explicará con
más detalle en el siguiente capítulo. Los lenguajes de programación imperativos típicos incluyen
todos los lenguajes ensambladores y los primeros lenguajes de alto nivel como Fortran, Algol, Ada,
Pascal y C.

El paradigma de la programación orientada a objetos es básicamente el mismo que el paradigma


imperativo, salvo que las variables relacionadas y las operaciones sobre las variables se organizan
en clases de objetos. Los privilegios de acceso de las variables y los métodos (operaciones) en los
objetos pueden definirse para reducir (simplificar) la interacción entre los objetos. Los objetos se
consideran los principales bloques de construcción de los programas, que soportan las
características del lenguaje como la herencia, la jerarquía de clases y el polimorfismo. Los
lenguajes de programación orientados a objetos típicos son: Smalltalk, C++, Java y C#.

El paradigma de programación funcional, también llamado aplicativo, expresa la computación en


términos de funciones matemáticas. Dado que expresamos la computación en funciones
matemáticas en muchos de los cursos de matemáticas, se supone que la programación funcional
es fácil de entender y sencilla de utilizar.

Sin embargo, como la programación funcional es muy diferente de la programación imperativa u


orientada a objetos la programación imperativa u orientada a objetos, y la mayoría de los
programadores se acostumbran primero a escribir programas en paradigmas imperativos u
orientados a objetos orientados a objetos, resulta difícil cambiar a la programación funcional. La
principal diferencia es que en la programación funcional no existe el concepto de ubicaciones de
memoria en los lenguajes de programación funcional. Cada función tomará un número de valores
como entrada (parámetros) y produce un único valor de retorno (salida de la función). El valor de
retorno no puede almacenarse para su uso posterior. Tiene que ser utilizado como la salida final o
inmediatamente como el como valor de parámetro de otra función. La programación funcional
consiste en definir funciones y organizar los valores de retorno de una o varias funciones como
parámetros de otra función. Los lenguajes de programación funcional se basan principalmente en
el cálculo lambda, que se tratará en el capítulo 4. Los lenguajes de programación funcional típicos
son ML, SML y Lisp/Scheme.

El paradigma de programación lógica, también llamado declarativo, expresa la computación en


términos de predicados lógicos. Un programa lógico es un conjunto de hechos, reglas y preguntas.
El proceso de ejecución de un programa lógico consiste en comparar una pregunta con cada hecho
y regla de la base de hechos y reglas dada. Si la pregunta encuentra una coincidencia, recibimos un
sí como respuesta a la pregunta. En caso contrario, recibimos un "no" como respuesta a la
pregunta. La programación lógica consiste en encontrar hechos, definir reglas basadas en los
hechos y escribir preguntas para expresar los problemas que deseamos resolver. Prolog es el único
lenguaje de programación lógica importante.

Cabe destacar que muchos lenguajes pertenecen a múltiples paradigmas. Por ejemplo, podemos
decir que C++ es un lenguaje de programación orientado a objetos. Sin embargo, C++ incluye casi
todas las características de C y, por tanto, es también un lenguaje de programación imperativo.
Podemos utilizar C++ para escribir programas en C. Java está más orientado a objetos, pero sigue
incluyendo muchas características imperativas. Por ejemplo, las variables de tipo primitivo de Java
no obtienen memoria del montón del lenguaje como otros objetos. Lisp contiene muchas
características no funcionales. Scheme puede considerarse un subconjunto de Lisp con menos
características no funcionales. Las operaciones aritméticas de Prolog se basan en el paradigma
imperativo.

No obstante, nos centraremos en las características relacionadas con el paradigma de los


lenguajes cuando estudiemos los lenguajes de muestra en los próximos cuatro capítulos. de los
lenguajes en los próximos cuatro capítulos. Estudiaremos las características imperativas de C en el
capítulo 2, las características orientadas a objetos de C de C++ en el capítulo 3, y las características
funcionales de Scheme y las lógicas de Prolog en los capítulos 4 y 5, respectivamente.

1.1.2 Rendimiento de los programas y características de los lenguajes de programación

Las características de un lenguaje de programación incluyen la ortogonalidad o simplicidad, las


estructuras de control disponibles, los tipos de datos y las estructuras de datos, el diseño de la
sintaxis, el apoyo a la abstracción, la expresividad, la equivalencia de tipos la comprobación de
tipos fuertes o débiles, el manejo de excepciones y el aliasing restringido. Estas características se
explicarán con más detalle en el resto del libro. El rendimiento de un programa, incluyendo la
fiabilidad, la legibilidad, legibilidad, capacidad de escritura, reutilización y eficiencia, está
determinado en gran medida por la forma en que el programador escribe el algoritmo y selecciona
las estructuras de datos. algoritmo y selecciona las estructuras de datos, así como otros detalles
de implementación. Sin embargo, las características de del lenguaje de programación son vitales
para apoyar y obligar a los programadores a utilizar los mecanismos adecuados del lenguaje para
implementar los algoritmos y los datos. mecanismos del lenguaje para implementar los algoritmos
y las estructuras de datos. La tabla 1.1 muestra la influencia de las características de un lenguaje
en el rendimiento de un programa escrito en ese lenguaje. La tabla indica que la simplicidad, las
estructuras de control, los tipos de datos y las estructuras de datos tienen un impacto significativo
en todos los aspectos del rendimiento. El diseño de la sintaxis y el apoyo a la abstracción son
importantes para la legibilidad, la reutilización reutilización, escritura y fiabilidad. Sin embargo, no
tienen un impacto significativo en la eficiencia del programa. La expresividad favorece la escritura,
pero puede tener un impacto negativo en la fiabilidad del programa. programa. La comprobación
de tipos fuerte y el aliasing restringido reducen la expresividad de los programas de escritura, pero
generalmente se considera que producen programas más fiables. El manejo de excepciones evita
que el programa evita que el programa se bloquee debido a circunstancias inesperadas y a errores
semánticos en el programa. Todas las características del lenguaje serán discutidas en este libro.

1.1.3 Desarrollo de los lenguajes de programación

El desarrollo de los lenguajes de programación se ha visto influenciado por el desarrollo del


hardware, el desarrollo de la tecnología de los compiladores y la necesidad del usuario de escribir
programas de alto rendimiento en términos de fiabilidad, legibilidad, capacidad de escritura,
reutilización y eficiencia. Las limitaciones del hardware y del compilador han obligado a los
primeros lenguajes de programación a acercarse al lenguaje máquina. Los lenguajes máquina son
los lenguajes nativos de los ordenadores y la primera generación de lenguajes de programación
utilizados por los humanos para comunicarse con el ordenador.

Los lenguajes máquina consisten en instrucciones de números binarios puros que son difíciles de
recordar para los humanos. recordar. El siguiente paso en el desarrollo del lenguaje de
programación es el uso de mnemotecnia que permite ciertos símbolos para representar patrones
de bits de uso frecuente. El lenguaje de máquina con El lenguaje de máquina con un uso
sofisticado de mnemónicos se llama lenguaje ensamblador. Un lenguaje ensamblador
normalmente permite variables simples, bifurcarse a una dirección de etiqueta, diferentes modos
de direccionamiento y macros que representan un número de instrucciones. Se utiliza un
ensamblador para traducir un programa en lenguaje ensamblador al programa en lenguaje
máquina. máquina. El trabajo típico que realiza un ensamblador es traducir los símbolos
mnemónicos en números binarios correspondientes, sustituir las variables por números de
registro o posiciones de memoria, y calcular la dirección de destino de las instrucciones de
bifurcación según la posición de las etiquetas en el programa.

Este texto se centra en la introducción de lenguajes de programación de alto nivel en los


paradigmas imperativo, orientado a objetos, funcional y lógico.

El primer lenguaje de programación de alto nivel se remonta al sistema de programación


Plankalkül de Konrad Zuse en Alemania en 1946. Zuse desarrolló sus máquinas Z1, Z2, Z3 y Z4 a
finales de los años 30 y principios de los 40, y el sistema Plankalkül se desarrolló en la máquina Z4
de la ETH (Eidgenössisch Technische Hochschule) de Zúrich, con la que Zuse diseñó un programa
para jugar al ajedrez.

El primer lenguaje de programación de alto nivel que se utilizó realmente en un dispositivo


informático electrónico se desarrolló en 1949. El lenguaje se llamaba Short Code. No había ningún
compilador diseñado para el lenguaje, y los programas escritos en el lenguaje tenían que ser
compilados a mano en el código de la máquina.

La invención del compilador se atribuye a Grace Hopper, que diseñó el primer compilador
ampliamente conocido conocido, llamado A0, en 1951.

El primer compilador primitivo, llamado Autocoder, fue escrito por Alick E. Glennie en 1952.
Traducía programas Autocode en declaraciones simbólicas a lenguaje de máquina para el
ordenador Manchester Mark I. Autocode podía manejar identificadores de una sola letra y
fórmulas sencillas.

El primer lenguaje ampliamente utilizado, Fortran (FORmula TRANslating), fue desarrollado por el
equipo dirigido por John Backus en IBM entre 1954 y 1957. Backus fue también el codiseñador del
sistema IBM 704 que ejecutó el primer compilador de Fortran. Posteriormente, Backus participó
en el desarrollo del lenguaje Algol y de la forma Backus-Naur (BNF). BNF era una notación formal
utilizada para definir la sintaxis de los lenguajes de programación. Fortran II llegó en 1958. Fortran
III llegó a finales de 1958, pero nunca se hizo público. Otras versiones de Fortran son ASA Fortran
66 (Fortran IV) en 1966, ANSI Fortran 77 (Fortran V) en 1978, ISO Fortran 90 en 1991 e ISO Fortran
95 en 1997. A diferencia de los lenguajes ensambladores, las primeras versiones de Fortran
permitían diferentes tipos de variables (reales, enteras, matrices), admitían la llamada a
procedimientos e incluían estructuras de control sencillas.

Los programas escritos en lenguajes de programación antes de la aparición de los conceptos de


programación estructurada se caracterizaban por ser programación espagueti o programación
monolítica. La programación estructurada es una técnica para organizar los programas en una
jerarquía de módulos. Cada módulo tenía una única entrada y un único punto de salida. El control
se transmitía hacia abajo a través de la estructura sin ramas incondicionales (por ejemplo,
sentencias goto 5) a niveles superiores de la estructura. Sólo se utilizaron tres tipos de estructuras
de control: secuencial rama condicional, e iteración.

Basado en la experiencia de Fortran I, Algol 58 se anunció en 1958. Dos años más tarde, se
presentó Algol 60, el primer lenguaje estructurado en bloques. El lenguaje fue revisado en 1963 y
1968. Edsger

Dijkstra es el responsable del diseño del primer compilador de Algol 60. Es famoso por ser el líder
en la introducción de la programación estructurada y en la abolición de la sentencia goto de la
programación.

Basado en Algol, Pascal fue desarrollado por Niklaus Wirth entre 1968 y 1970. Además, desarrolló
Modula como sucesor de Pascal en 1977, luego Modula-2 en 1980 y Oberon en 1988. El lenguaje
Oberon tenía una sintaxis similar a la de Pascal, pero estaba fuertemente tipado. También ofrecía
una extensión de tipos (herencia) que que soportaba la programación orientada a objetos. En
Oberon-2, se introdujeron los procedimientos ligados al tipo (como los métodos en los lenguajes
de orientados a objetos).

El lenguaje de programación C fue inventado e implementado por primera vez por Dennis Ritchie
en DEC entre 1969 y 1973, como lenguaje de implementación de sistemas para el naciente sistema
operativo Unix. Pronto se convirtió uno de los lenguajes dominantes en la época e incluso en la
actualidad. Los predecesores de C fueron el lenguaje sin tipos BCPL (Basic Combined Programming
Language) de Martin Richards en 1967 y luego el B escrito por Ken Thompson en 1969. C tenía una
estructura de comprobación de tipos débil para permitir un mayor nivel de flexibilidad en la
programación.

Los conceptos de programación orientada a objetos se introdujeron e implementaron por primera


vez en el lenguaje Simula que fue diseñado por Ole-Johan Dahl y Kristen Nygaard en el Centro de
Computación Noruego (NCC) entre 1962 y 1967. La versión original, Simula I, se diseñó como un
lenguaje para la simulación de eventos discretos. de eventos discretos. Sin embargo, su versión
revisada, Simula 67, era un lenguaje de programación de propósito general de propósito general.
Aunque Simula nunca llegó a ser ampliamente utilizado, el lenguaje tuvo una gran influencia en los
modernos paradigmas de programación modernos. Introdujo importantes conceptos orientados a
objetos, como las clases y los objetos la herencia y la vinculación tardía.

Uno de los sucesores orientados a objetos de Simula fue Smalltalk, diseñado en Xerox PARC,
dirigido por Alan Kay. Las versiones desarrolladas fueron Smalltalk-72, Smalltalk-74, Smalltalk-76 y
Smalltalk-80. Smalltalk también heredó características de programación funcional de Lisp.

Basado en Simula 67 y C, Bjarne Stroustrup desarrolló en 1980 en los Laboratorios Bell un lenguaje
llamado "C con clases", que fue revisado y renombrado como C++ en 1983. C++ se consideraba un
C mejor (por ejemplo, con una fuerte comprobación de tipos), además de soportar la abstracción
de datos y la programación orientada a objetos heredada de Simula 67.

Java fue escrito por James Gosling, Patrick Naughton, Chris Warth, Ed Frank y Mike Sheridan en
Sun Microsystems. Al principio se llamaba Oak y luego pasó a llamarse Java cuando se anunció
públicamente en 1995. Los predecesores de Java fueron C++ y Smalltalk. Java eliminó la mayoría
de las características no orientadas a objetos de C++ y era un lenguaje de programación más
sencillo y mejor orientado a objetos. Su concepto de procesamiento de programas en dos niveles
de procesamiento de programas en dos niveles (es decir, compilación en un bytecode intermedio
y luego interpretación del bytecode mediante una pequeña máquina virtual) lo convirtió en el
lenguaje dominante para la programación de aplicaciones de Internet. Java todavía no era un
lenguaje de programación orientado a objetos puro. Sus tipos primitivos, enteros, de punto
flotante de punto flotante, booleanos, etc., no eran clases, y sus asignaciones de memoria se
hacían desde la pila del lenguaje en lugar de la pila del lenguaje.

El lenguaje C# de Microsoft se anunció por primera vez en junio de 2000. El lenguaje se derivó de
C++ y Java. Se implementó como un lenguaje completamente orientado a objetos sin tipos
"primitivos". C# también C# también hace hincapié en la programación orientada a componentes,
que es una versión refinada de la programación orientada a objetos. orientada a objetos. La idea
es poder ensamblar sistemas de software a partir de componentes prefabricados.

Los lenguajes de programación funcional son relativamente independientes del proceso de


desarrollo de los lenguajes de programación imperativos y orientados a objetos. El primer y más
importante lenguaje de programación funcional Lisp, abreviatura de LISt Processing, fue
desarrollado por John McCarthy en el MIT. Lisp se publicó por primera vez en 1958. Luego
apareció Lisp 1 en 1959, Lisp 1.5 en 1962 y Lisp 2 en 1966. Lisp se desarrolló específicamente para
aplicaciones de inteligencia artificial y se basó en el cálculo lambda. Heredó su sintaxis algebraica
de Fortran y su manipulación de símbolos del Lenguaje de Procesamiento de Información, o IPL.
Posteriormente se diseñaron varios dialectos de Lisp, por ejemplo, Scheme, InterLisp, FranzLisp,
MacLisp y ZetaLisp.

Como dialecto de Lisp, Scheme fue desarrollado por primera vez por G. L. Steele y G. J. Sussman en
1975 en el MIT. Varias importantes mejoras en sus versiones posteriores, incluyendo una mejor
regla de alcance, procedimientos (funciones) como objetos de primera clase, la eliminación de los
bucles y la dependencia exclusiva de las llamadas a procedimientos recursivos para para expresar
los bucles. Scheme fue estandarizado por el IEEE en 1989.

En 1981 comenzaron los esfuerzos para desarrollar un dialecto común de Lisp, denominado
Common Lisp. Common Lisp pretendía ser compatible con todas las versiones existentes de los
dialectos de Lisp y crear un gran producto comercial. Sin embargo, el intento de fusionar Scheme
con Lisp fracasó, y Scheme sigue siendo hoy un dialecto de Lisp independiente. dialecto
independiente de Lisp. Common Lisp fue estandarizado por el IEEE en 1992.

Además de Lisp, el lenguaje FP de John Backus también forma parte de los primeros lenguajes de
programación funcional. FP no se basaba en el cálculo lambda, sino en unas cuantas reglas para
combinar formas de funciones. Backus consideraba que la expresividad del cálculo lambda sobre
las funciones computables era mucho más amplia de lo necesario. A conjunto de reglas
simplificadas podría hacer un mejor trabajo.

Al mismo tiempo que se desarrollaba FP en Estados Unidos, apareció ML (Meta Language) en el


Reino Unido. Al igual que Lisp, ML se basaba en el cálculo lambda. Sin embargo, Lisp no estaba
tipificado (no es necesario declarar ninguna variable), mientras que variable tiene que ser
declarada), mientras que ML estaba fuertemente tipado, aunque los usuarios no tenían que
declarar variables que podían ser determinadas inferencialmente por el compilador.

Miranda es un lenguaje de programación funcional puro desarrollado por David Turner en la


Universidad de Kent en 1985-86. Miranda logra la transparencia referencial (sin efectos
secundarios) prohibiendo la modificación a las variables globales. Combina las principales
características de SASL (St. Andrews Static Language) y KRC (Kent Recursive Calculator) con una
tipificación fuerte similar a la de ML. SASL y KRC son dos lenguajes de programación de
programación funcional diseñados por Turner en la Universidad de St Andrews en 1976 y en la
Andrews en 1976 y en la Universidad de Kent en 1981, respectivamente.

Existen muchos lenguajes de programación basados en la lógica. Por ejemplo, ALF (Algebraic Logic
Functional language) es un lenguaje funcional y lógico integrado basado en cláusulas de Horn para
la programación lógica, y en funciones y ecuaciones para la programación funcional. Gödel es un
lenguaje de programación lógica fuertemente tipado. El sistema de tipos se basa en una lógica
multiordenada con polimorfismo paramétrico. RELFUN amplía la lógica de Horn mediante el uso
de una sintaxis de orden superior, dominios finitos de primera clase y expresiones de funciones no
deterministas y sin base, que se distinguen explícitamente de las estructuras.

El miembro más importante de la familia de lenguajes de programación lógica es el Prolog, basado


en la lógica de Horn. Prolog fue inventado por Alain Colmerauer y Philippe Roussel en la
Universidad de Aix- Marsella en 1971. La primera versión se implementó en 1972 utilizando Algol.
Prolog fue diseñado originalmente para el procesamiento del lenguaje natural, pero se ha
convertido en uno de los lenguajes más utilizados para la inteligencia artificial. Han aparecido
muchas implementaciones después del trabajo original. Las primeras implementaciones C-Prolog,
ESLPDPRO, Frolic, LM-Prolog, Open Prolog, SB-Prolog y UPMAIL Tricia Prolog. Hoy en día, los
Prologs comunes en uso son AMZI Prolog, GNU Prolog, LPA Prolog, Quintus Prolog, SICSTUS
Prolog, SNI Prolog y SWI-Prolog.

1.2 Estructuras de los lenguajes de programación

Esta sección estudia las estructuras de los lenguajes de programación en función de cuatro capas
estructurales: léxica, sintáctica, contextual y semántica.

1.2.1 Estructura léxica


La estructura léxica define el vocabulario de un lenguaje. Las unidades léxicas se consideran los
bloques de construcción de los lenguajes de programación. Las estructuras léxicas de todos los
lenguajes de programación son similares y normalmente incluyen los siguientes tipos de unidades:

° Identificadores: Nombres que pueden ser elegidos por los programadores para representar
objetos como variables, etiquetas, procedimientos y funciones. La mayoría de los lenguajes de
programación requieren que un identificador comience con una letra alfabética y puede ser
seguido opcionalmente por letras, dígitos y algunos caracteres especiales.

° Palabras clave: Nombres reservados por el diseñador del lenguaje y utilizados para formar la
estructura sintáctica del mismo.

° Operadores: Símbolos utilizados para representar las operaciones. Todos los lenguajes de
programación de propósito general deben proporcionar ciertos operadores mínimos, como
operadores matemáticos como +, , *, /, operadores relacionales como <, , ==, >, , y operadores
lógicos como AND, OR, NOT, etc.

° Separadores: Símbolos utilizados para separar unidades léxicas o sintácticas del lenguaje. Se
utilizan como separadores el espacio, la coma, los dos puntos, el punto y coma y los paréntesis.

° Literales: Valores que se pueden asignar a variables de diferentes tipos. Por ejemplo, los literales
de tipo entero son números enteros, los literales de tipo carácter son cualquier carácter del
conjunto de caracteres del lenguaje, y los literales de tipo cadena son cualquier cadena de
caracteres.

° Comentarios: Cualquier texto explicativo incrustado en el programa. Los comentarios comienzan


con una palabra clave palabra clave o separador. Cuando el compilador traduce un programa a
código máquina, todos los comentarios serán ignorados.

1.2.2 Estructura sintáctica

La estructura sintáctica define la gramática de la formación de oraciones o enunciados mediante


las unidades léxicas. Un lenguaje de programación imperativo ofrece normalmente los siguientes
tipos de sentencias:

° Asignaciones: Una sentencia de asignación asigna un valor literal o una expresión a una variable.

° Sentencias condicionales: Una sentencia condicional comprueba una condición y pasa a una
determinada sentencia en función del resultado de la prueba (verdadero o falso). Las sentencias
condicionales típicas son if-then, ifthen- else y switch (case).

° Sentencias de bucle: Una sentencia de bucle comprueba una condición y entra en el cuerpo del
bucle o sale de él en función del resultado de la prueba (verdadero o falso). Las sentencias de
bucle típicas son for-loop y while-loop.

La definición formal de las estructuras léxicas y sintácticas se tratará en el apartado 1.2.5.

1.2.3 Estructura contextual

La estructura contextual (también llamada semántica estática) define la semántica del programa
antes de la ejecución dinámica. Incluye la declaración de variables, la inicialización y la
comprobación de tipos.

Algunos lenguajes imperativos requieren que todas las variables sean inicializadas cuando son
declaradas en la capa contextual, mientras que otros lenguajes no requieren que las variables sean
inicializadas cuando son declaradas, siempre y cuando las variables sean inicializadas antes de que
sus valores sean utilizados. Esto significa que la inicialización puede realizarse tanto en la capa
contextual como en la capa semántica.

La estructura contextual empieza a ocuparse del significado del programa. Una declaración que es
léxicamente correcta puede no ser contextualmente correcta. Por ejemplo:

Todas las declaraciones son léxicamente correctas, pero la última declaración es contextualmente
incorrecta porque no tiene sentido añadir una variable entera a una variable de cadena.

En la Sección 1.3 se tratará más sobre los tipos de datos, la comprobación de tipos y la
equivalencia de tipos.

1.2.4 Estructura semántica

La estructura semántica describe el significado de un programa, o lo que el programa hace durante


la ejecución. La semántica de un lenguaje suele ser muy compleja. En la mayoría de los lenguajes
imperativos, no existe una definición formal de la estructura semántica. Normalmente se utilizan
descripciones informales para explicar lo que hace cada declaración. Las estructuras semánticas de
los lenguajes de programación funcionales y lógicos se definen normalmente basándose

en los fundamentos matemáticos y lógicos en los que se basan los lenguajes. Por ejemplo, los
significados de los procedimientos de Scheme son los mismos que los significados de las
expresiones lambda en el cálculo lambda, en el que se basa Scheme. en el que se basa Scheme, y
los significados de las cláusulas de Prolog son los mismos que los de las cláusulas de la lógica de
lógica de Horn en la que se basa Prolog.

1.2.5 Notación BNF

BNF (Backus-Naur Form) es un metalenguaje que puede utilizarse para definir las estructuras
léxicas y sintácticas de otro lenguaje. En lugar de aprender primero el lenguaje BNF y luego usar
BNF para definir un nuevo lenguaje, primero usaremos BNF para definir un lenguaje inglés
simplificado con el que estemos familiarizados, y luego aprenderemos BNF a partir de la propia
definición.

Una frase simple en inglés está formada por un sujeto, un verbo y un objeto. El sujeto, a su vez,
está formado por uno o varios adjetivos seguidos de un sustantivo. El objeto tiene la misma
estructura gramatical. Los verbos y adjetivos deben proceder del vocabulario. Formalmente,
podemos definir una frase inglesa simple como sigue:

En las definiciones, el símbolo "::=" significa que el nombre del lado izquierdo está definido por la
expresión del lado derecho. El nombre entre un par de paréntesis angulares "<>" es no terminal, lo
que significa que el nombre debe definirse con más detalle. La barra vertical "|" representa una
relación "o". Los nombres en negrita son terminales, lo que significa que los nombres no necesitan
definirse más. Forman el vocabulario del lenguaje.

Podemos utilizar la definición de la frase para comprobar si las siguientes frases son
sintácticamente correctas.

La primera frase es sintácticamente correcta, aunque no tiene mucho sentido. Los tres adjetivos
de la frase son correctos porque la definición de adjetivo permite utilizar cualquier número de
adjetivos en el sujeto y en el objeto de la frase. La segunda y tercera frases también son
sintácticamente correctas según la definición. Las frases cuarta y quinta son sintácticamente
incorrectas porque falta un sustantivo en el objeto de las frases. La sexta frase es incorrecta
porque "no" no es un terminal. La última frase es incorrecta porque la definición no permite que la
frase comience con un verbo.

Después de tener una comprensión básica de BNF, podemos utilizarlo para definir un pequeño
lenguaje de programación. Las primeras cinco líneas definen la estructura léxica, y el resto define
la estructura sintáctica del lenguaje.

Ahora utilizamos la definición para comprobar cuáles de las siguientes afirmaciones son
sintácticamente correctas.
Según la definición BNF de la lengua, las afirmaciones 1 y 2 son correctas. Las afirmaciones 3 y 4
son incorrectas porque 3b y 2sum no son identificadores ni expresiones aceptables. La afirmación
5 es incorrecta. La afirmación 6 es incorrecta porque un identificador debe empezar por una letra.
La afirmación 7 es incorrecta porque el lado izquierdo de una sentencia de asignación debe ser un
identificador.

También podría gustarte