En una palabra
Los lenguajes de programación se componen de una sintaxis que representa el programa como cadenas de caracteres y una semántica que es el significado previsto del programa.
Los lenguajes formales son sintaxis sin significado. Su objetivo es estudiar la estructura de conjuntos de cadenas definidas formalmente, sin por lo general atribuir significado a esas cadenas.
La expresión regular y otros formalismos (como las gramáticas sin contexto) se utilizan para definir lenguajes formales, utilizados como componentes sintácticos de programación y lenguajes naturales, es decir, para representar oraciones de forma estructurada. Se utilizan otros mecanismos para relacionar esa estructura con la semántica de los lenguajes de programación.
Mucho aquí se simplifica considerablemente, particularmente con respecto al lenguaje natural.
Con muchos mas detalles
Para responder a su pregunta, debemos comenzar desde el principio. Un lenguaje en el sentido habitual es, informalmente, un medio para transmitir información o ideas. En un lenguaje, generalmente se distingue entre sintaxis y semántica. La semántica es de lo que quieres hablar / escribir. la información que quieres transmitir. La sintaxis es el medio que utiliza para transmitirla, es decir, una representación convencional que se puede intercambiar entre personas, y ahora también entre personas y dispositivos, o entre dispositivos (computadoras).
Por lo general, usará la palabra dog
para transmitir la idea de un perro. La palabra dog
está hecha de tres letras, o algún sonido equivalente, y está destinada a ser la representación de algún tipo de animal. La idea clave es que la comunicación se realiza a través de la representación de lo que se debe comunicar. Las estructuras de representación generalmente se llaman sintaxis, mientras que lo que se representa se llama semántica. Esto va más o menos para el lenguaje natural, así como para los lenguajes de programación.
Las palabras son entidades sintácticas para representar conceptos semánticos más o menos elementales. Pero estos conceptos elementales tienen que reunirse de varias maneras para dar un significado más complejo. Escribimos
the dog
para transmitir que nos referimos a un perro específico, y the dog bites the cat
para transmitir una idea más compleja. Pero la forma en que se organizan las palabras tiene que estar fijada por reglas, de modo que podamos saber cuál de los perros y gatos realmente muerde al otro.
Por lo tanto, tenemos reglas como esas sentence -> subject verb complement
que se supone que coinciden con las oraciones y nos dicen cómo se articulan las ideas asociadas con cada parte. Estas reglas son reglas sintácticas, ya que nos dicen cómo se debe organizar la representación de nuestro mensaje. El subject
mismo puede ser definido por una regla subject -> article noun
, y así sucesivamente.
2 x + 1 = 23X123
equation -> expression "=" expression
expression -> expression "+" expression
expression -> number
La estructura de los lenguajes de programación es la misma. Los lenguajes de programación están semánticamente especializados en expresar cálculos a realizar, en lugar de expresar problemas a resolver, prueba de teoremas o relaciones amistosas entre animales. Pero esa es la principal diferencia.
Las representaciones utilizadas en la sintaxis suelen ser cadenas de caracteres o de sonidos para los idiomas hablados. La semántica generalmente pertenece al dominio abstracto, o posiblemente a la realidad, pero aún se abstrae en nuestros procesos de pensamiento, o al dominio conductual de los dispositivos. La comunicación implica codificar la información / idea en sintaxis, que es transmitida y decodificada por el receptor. El resultado se interpreta de cualquier manera por el receptor.
Entonces, lo que vemos del lenguaje es principalmente la sintaxis y su estructura. Los ejemplos anteriores son solo una de las formas más comunes de definir cadenas sintácticas y su organización estructural. Hay otros. Para un idioma dado, a algunas cadenas se les puede asignar una estructura, y se dice que pertenecen al idioma, mientras que otras no.
Lo mismo es cierto para las palabras. Algunas secuencias de letras (o sonido) son palabras legítimas, mientras que otras no lo son.
Los lenguajes formales son solo sintaxis sin semántica. Definen con un conjunto de reglas qué secuencias se pueden construir, utilizando los elementos básicos de un alfabeto. Cuáles son las reglas pueden ser muy variables, a veces complejas. Pero los lenguajes formales se usan para muchos propósitos matemáticos más allá de la comunicación lingüística, ya sea para lenguajes naturales o de programación. El conjunto de reglas que definen las cadenas en un idioma se llama gramática. Pero hay muchas otras formas de definir idiomas.
En la práctica, un lenguaje está estructurado en dos niveles. El nivel léxico define palabras construidas a partir de un alfabeto de caracteres. El nivel sintáctico define oraciones o programas construidos a partir de un alfabeto de palabras (o más precisamente de familias de palabras, para que siga siendo un alfabeto finito). Esto es necesariamente algo simplificado.
La estructura de las palabras es bastante simple en la mayoría de los lenguajes (de programación o naturales), por lo que generalmente se definen con lo que generalmente se considera el tipo más simple de lenguaje formal: los lenguajes regulares. Se pueden definir con expresiones regulares (regexp) y se identifican con bastante facilidad con dispositivos programados llamados autómatas de estado finito. En los casos de lenguajes de programación, los ejemplos de una palabra son un identificador, un número entero, una cadena, un número real, una palabra reservada comoif
o repeat
, un símbolo de puntuación o un paréntesis abierto. Ejemplos de familias de palabras son identificador, cadena, entero.
El nivel sintáctico generalmente se define por un tipo de lenguaje formal un poco más complejo: los lenguajes libres de contexto, que usan las palabras como alfabeto. Las reglas que hemos visto anteriormente son reglas sin contexto para el lenguaje natural. En el caso de los lenguajes de programación, las reglas pueden ser:
statement -> assignment
statement -> loop
loop -> "while" expression "do" statement
assignment -> "identifier" "=" expression
expression -> "identifier"
expression -> "integer"
expression -> expression "operator" expression
Con tales reglas puedes escribir:
while aaa /= bbb do aaa = aaa + bbb / 6
Que es una declaración.
Y la forma en que se produjo puede representarse mediante una estructura de árbol llamada árbol de análisis o árbol de sintaxis (no completo aquí):
statement
|
_______________ loop _______________
/ / \ \
"while" expression "do" statement
__________|_________ |
/ | \ assignment
expression "operator" expression _______|_______
| | | / | \
"identifier" "/=" "identifier" "identifier" "=" expression
| | | |
aaa bbb aaa ... ...
Los nombres que aparecen a la izquierda de una regla se llaman no terminales, mientras que las palabras también se llaman terminales, ya que están en el alfabeto del idioma (por encima del nivel léxico). Los no terminales representan las diferentes estructuras sintácticas que se pueden usar para componer un programa.
Dichas reglas se denominan libres de contexto, porque un no terminal puede reemplazarse arbitrariamente utilizando cualquiera de las reglas correspondientes, independientemente del contexto en el que aparece. El conjunto de reglas que definen el lenguaje se llama gramática libre de contexto.
En realidad, existen restricciones al respecto, cuando los identificadores deben declararse por primera vez, o cuando una expresión debe satisfacer las restricciones de tipo. Pero dicha restricción puede considerarse semántica, en lugar de sintáctica. En realidad, algunos profesionales los ubican en lo que llaman
semántica estática .
Dada cualquier oración, cualquier programa, el significado de esa oración se extrae analizando la estructura dada por el árbol de análisis para esta oración. Por lo tanto, es muy importante desarrollar algoritmos, llamados analizadores, que puedan recuperar la estructura de árbol correspondiente a un programa, cuando se les da el programa.
El analizador léxico precede al analizador que reconoce las palabras y determina la familia a la que pertenecen. Luego, la secuencia de palabras, o elementos léxicos, se le da al analizador que recupera la estructura de árbol subyacente. A partir de esta estructura, el compilador puede determinar cómo generar código, que es la parte semántica del procesamiento del programa en el lado del compilador.
El analizador de un compilador puede construir una estructura de datos correspondiente al árbol de análisis y pasarlo a las etapas posteriores del proceso de compilación, pero no es necesario. Ejecutar el algoritmo de análisis equivale a desarrollar una estrategia computacional para explorar el árbol de sintaxis implícito en el texto del programa. Este árbol de sintaxis / análisis puede o no ser explicitado en el proceso, dependiendo de la estrategia de compilación (número de etapas). Sin embargo, lo que es necesario es que finalmente haya al menos una exploración ascendente del árbol de análisis, ya sea explícito o dejado implícito en la estructura de cálculo.
La razón de esto, intuitivamente, es que una forma formal estándar para definir la semántica asociada a una estructura de árbol sintáctico es por medio de lo que se llama homomorfismo. No temas a la gran palabra. La idea es considerar que el significado del todo se construye a partir del significado de las partes, sobre la base del operador que las conecta.
Por ejemplo, la oración the dog bites the cat
se puede analizar con la regla sentence -> subject verb complement
. Conocer el significado de los 3 subárboles subject
, verb
y complement
la regla que los compone nos dice que el sujeto está haciendo la acción y que el gato es el que es mordido.
Esta es solo una explicación intuitiva, pero se puede formalizar. La semántica se construye hacia arriba a partir de los constituyentes. Pero esto esconde mucha complejidad.
El funcionamiento interno de un compilador se puede descomponer en varias etapas. El compilador real puede trabajar etapa por etapa, utilizando representaciones intermedias. También puede fusionar algunas etapas. Esto depende de la tecnología utilizada y de la complejidad de compilar el idioma en cuestión.