stringtranslate.com

Sintaxis (lenguajes de programación)

El resaltado de sintaxis y el estilo de sangría se utilizan a menudo para ayudar a los programadores a reconocer elementos del código fuente. Este código Python utiliza resaltado codificado por colores.

En informática , la sintaxis de un lenguaje informático son las reglas que definen las combinaciones de símbolos que se consideran enunciados o expresiones correctamente estructuradas en ese lenguaje. Esto se aplica tanto a los lenguajes de programación , donde el documento representa el código fuente , como a los lenguajes de marcado , donde el documento representa datos.

La sintaxis de un idioma define su forma superficial. [1] Los lenguajes informáticos basados ​​en texto se basan en secuencias de caracteres , mientras que los lenguajes de programación visual se basan en la disposición espacial y las conexiones entre símbolos (que pueden ser textuales o gráficos). Se dice que los documentos que no son sintácticamente válidos tienen un error de sintaxis . Al diseñar la sintaxis de un lenguaje, un diseñador podría comenzar escribiendo ejemplos de cadenas legales e ilegales , antes de intentar descubrir las reglas generales a partir de estos ejemplos. [2]

Por lo tanto, la sintaxis se refiere a la forma del código y se contrasta con la semántica : el significado . En el procesamiento de lenguajes informáticos, el procesamiento semántico generalmente viene después del procesamiento sintáctico; sin embargo, en algunos casos, el procesamiento semántico es necesario para un análisis sintáctico completo, y estos se realizan de forma conjunta o simultánea . En un compilador , el análisis sintáctico comprende el frontend , mientras que el análisis semántico comprende el backend (y el middle end, si se distingue esta fase).

Niveles de sintaxis

La sintaxis del lenguaje informático generalmente se distingue en tres niveles:

Distinguir de esta manera produce modularidad, lo que permite describir y procesar cada nivel por separado y, a menudo, de forma independiente.

Primero, un lexer convierte la secuencia lineal de caracteres en una secuencia lineal de fichas; esto se conoce como " análisis léxico " o "lexing". [3]

En segundo lugar, el analizador convierte la secuencia lineal de tokens en un árbol de sintaxis jerárquica; esto se conoce como " análisis " en sentido estricto. Esto asegura que la línea de tokens se ajuste a las gramáticas formales del lenguaje de programación. La etapa de análisis en sí se puede dividir en dos partes: el árbol de análisis , o "árbol de sintaxis concreta", que está determinado por la gramática, pero generalmente es demasiado detallado para un uso práctico, y el árbol de sintaxis abstracta (AST), que simplifica esto en una forma utilizable. Los pasos de AST y análisis contextual pueden considerarse una forma de análisis semántico, ya que agregan significado e interpretación a la sintaxis o, alternativamente, implementaciones manuales informales de reglas sintácticas que serían difíciles o incómodas de describir o implementar formalmente.

En tercer lugar, el análisis contextual resuelve nombres y tipos de cheques. Esta modularidad a veces es posible, pero en muchos lenguajes del mundo real un paso anterior depende de un paso posterior; por ejemplo, el truco de Lexer en C se debe a que la tokenización depende del contexto. Incluso en estos casos, a menudo se considera que el análisis sintáctico se aproxima a este modelo ideal.

Los niveles generalmente corresponden a niveles de la jerarquía de Chomsky . Las palabras están en un lenguaje regular , especificado en la gramática léxica , que es una gramática de tipo 3, generalmente dada como expresiones regulares . Las frases están en un lenguaje libre de contexto (CFL), generalmente un lenguaje determinista libre de contexto (DCFL), especificado en una gramática de estructura de frase , que es una gramática de tipo 2, generalmente dada como reglas de producción en forma Backus-Naur (BNF). ). Las gramáticas de frases a menudo se especifican en gramáticas mucho más restringidas que las gramáticas libres de contexto , para que sean más fáciles de analizar; Mientras que el analizador LR puede analizar cualquier DCFL en tiempo lineal, el analizador LALR simple y el analizador LL aún más simple son más eficientes, pero solo pueden analizar gramáticas cuyas reglas de producción están restringidas. En principio, la estructura contextual puede describirse mediante una gramática sensible al contexto y analizarse automáticamente mediante gramáticas de atributos , aunque, en general, este paso se realiza manualmente, mediante reglas de resolución de nombres y verificación de tipos , y se implementa mediante una tabla de símbolos. que almacena nombres y tipos para cada ámbito.

Se han escrito herramientas que generan automáticamente un lexer a partir de una especificación léxica escrita en expresiones regulares y un analizador a partir de la gramática de frases escrita en BNF: esto permite utilizar programación declarativa , en lugar de necesitar programación procedimental o funcional. Un ejemplo notable es el par lex - yacc . Estos producen automáticamente un árbol de sintaxis concreto ; Luego, el escritor del analizador debe escribir manualmente el código que describa cómo se convierte en un árbol de sintaxis abstracta . El análisis contextual también se implementa generalmente de forma manual. A pesar de la existencia de estas herramientas automáticas, el análisis a menudo se implementa manualmente, por varias razones: tal vez la estructura de la frase no esté libre de contexto, o una implementación alternativa mejore el rendimiento o el informe de errores, o permita cambiar la gramática más fácilmente. Los analizadores suelen estar escritos en lenguajes funcionales, como Haskell , o en lenguajes de scripting, como Python o Perl , o en C o C++ .

Ejemplos de errores

Como ejemplo, (add 1 1)hay un programa Lisp sintácticamente válido (asumiendo que existe la función 'agregar', de lo contrario falla la resolución del nombre), agregando 1 y 1. Sin embargo, lo siguiente no es válido:

(_ 1 1) error léxico: '_' no es válido(agregue 1 1 error de análisis: falta el cierre ')'

El lexer no puede identificar el primer error; todo lo que sabe es que, después de producir el token LEFT_PAREN, '(' el resto del programa no es válido, ya que ninguna regla de palabras comienza con '_'. El segundo error se detecta en el Etapa de análisis: el analizador ha identificado la regla de producción de "lista" debido al token '(' (como la única coincidencia) y, por lo tanto, puede generar un mensaje de error; en general, puede ser ambiguo .

Los errores de tipo y los errores de variables no declarados a veces se consideran errores de sintaxis cuando se detectan en tiempo de compilación (lo que suele ser el caso cuando se compilan lenguajes fuertemente tipados), aunque es común clasificar este tipo de errores como errores semánticos . [4] [5] [6]

Como ejemplo, el código Python

'una' + 1

contiene un error de tipo porque agrega un literal de cadena a un literal entero. Los errores de tipo de este tipo se pueden detectar en tiempo de compilación: se pueden detectar durante el análisis (análisis de frases) si el compilador usa reglas separadas que permiten "integerLiteral + integerLiteral" pero no "stringLiteral + integerLiteral", aunque es más probable que el compilador utilizará una regla de análisis que permite todas las expresiones del formulario "LiteralOrIdentifier + LiteralOrIdentifier" y luego el error se detectará durante el análisis contextual (cuando se produce la verificación de tipos). En algunos casos, esta validación no la realiza el compilador y estos errores solo se detectan en tiempo de ejecución.

En un lenguaje de tipo dinámico, donde el tipo sólo se puede determinar en tiempo de ejecución, muchos errores de tipo sólo se pueden detectar en tiempo de ejecución. Por ejemplo, el código Python

a+b

es sintácticamente válido a nivel de frase, pero la exactitud de los tipos de a y b solo se puede determinar en tiempo de ejecución, ya que las variables no tienen tipos en Python, solo los valores. Mientras que hay desacuerdo sobre si un error de tipo detectado por el compilador debe denominarse error de sintaxis (en lugar de error semántico estático ), los errores de tipo que sólo pueden detectarse en el momento de la ejecución del programa siempre se consideran errores semánticos en lugar de errores de sintaxis.

Definición de sintaxis

Árbol de análisis de código Python con tokenización insertada

La sintaxis de los lenguajes de programación textual generalmente se define utilizando una combinación de expresiones regulares (para la estructura léxica ) y la forma Backus-Naur (un metalenguaje para la estructura gramatical ) para especificar inductivamente categorías sintácticas ( no terminales ) y símbolos terminales . [7] Las categorías sintácticas se definen mediante reglas llamadas producciones , que especifican los valores que pertenecen a una categoría sintáctica particular. [1] Los símbolos terminales son caracteres concretos o cadenas de caracteres (por ejemplo, palabras clave como define , if , let o void ) a partir de los cuales se construyen programas sintácticamente válidos.

La sintaxis se puede dividir en sintaxis libre de contexto y sintaxis sensible al contexto. [7] La ​​sintaxis libre de contexto son reglas dirigidas por el metalenguaje del lenguaje de programación. Estos no estarían restringidos por el contexto que rodea o hace referencia a esa parte de la sintaxis, mientras que la sintaxis sensible al contexto sí lo estaría.

Un idioma puede tener diferentes gramáticas equivalentes, como expresiones regulares equivalentes (en los niveles léxicos) o diferentes reglas de frases que generan el mismo idioma. El uso de una categoría más amplia de gramáticas, como las gramáticas LR, puede permitir gramáticas más cortas o más simples en comparación con categorías más restringidas, como la gramática LL, que puede requerir gramáticas más largas con más reglas. Gramáticas de frases diferentes pero equivalentes producen diferentes árboles de análisis, aunque el lenguaje subyacente (conjunto de documentos válidos) es el mismo.

Ejemplo: expresiones S de Lisp

A continuación se muestra una gramática simple, definida utilizando la notación de expresiones regulares y la forma extendida de Backus-Naur . Describe la sintaxis de expresiones S , una sintaxis de datos del lenguaje de programación Lisp , que define producciones para las categorías sintácticas expresión , átomo , número , símbolo y lista :

expresión =  átomo |  lista átomo =  número |  número de símbolo = [ + - ] ? [ '0' - '9' ] + símbolo = [ 'A' - 'Z' ][ 'A' - 'Z''0' - '9' ]. * lista = '(' , expresión * , ')'     

Esta gramática especifica lo siguiente:

Aquí los dígitos decimales, los caracteres en mayúsculas y minúsculas y los paréntesis son símbolos terminales.

Los siguientes son ejemplos de secuencias de tokens bien formadas en esta gramática: ' 12345', ' ()', ' (A B C232 (1))'

Gramáticas complejas

La gramática necesaria para especificar un lenguaje de programación se puede clasificar según su posición en la jerarquía de Chomsky . La gramática de frases de la mayoría de los lenguajes de programación se puede especificar utilizando una gramática de tipo 2, es decir, son gramáticas libres de contexto , [8] aunque la sintaxis general es sensible al contexto (debido a declaraciones de variables y ámbitos anidados), por lo tanto, tipo- 1. Sin embargo, hay excepciones y, en algunos idiomas, la gramática de la frase es Tipo 0 (Turing-completa).

En algunos lenguajes como Perl y Lisp, la especificación (o implementación) del lenguaje permite construcciones que se ejecutan durante la fase de análisis. Además, estos lenguajes tienen construcciones que permiten al programador alterar el comportamiento del analizador. Esta combinación efectivamente desdibuja la distinción entre análisis y ejecución, y hace que el análisis de sintaxis sea un problema indecidible en estos lenguajes, lo que significa que es posible que la fase de análisis no finalice. Por ejemplo, en Perl es posible ejecutar código durante el análisis utilizando una BEGINdeclaración, y los prototipos de funciones de Perl pueden alterar la interpretación sintáctica y posiblemente incluso la validez sintáctica del código restante. [9] [10] Coloquialmente esto se conoce como "sólo Perl puede analizar Perl" (porque el código debe ejecutarse durante el análisis y puede modificar la gramática), o más fuertemente "ni siquiera Perl puede analizar Perl" (porque es indecidible ). De manera similar, las macros Lisp introducidas por la defmacrosintaxis también se ejecutan durante el análisis, lo que significa que un compilador Lisp debe tener presente un sistema de tiempo de ejecución Lisp completo. Por el contrario, las macros de C son simplemente reemplazos de cadenas y no requieren la ejecución de código. [11] [12]

Sintaxis versus semántica

La sintaxis de un lenguaje describe la forma de un programa válido, pero no proporciona ninguna información sobre el significado del programa o los resultados de su ejecución. El significado dado a una combinación de símbolos se maneja mediante la semántica (ya sea formal o codificada en una implementación de referencia ). Se debe establecer una sintaxis válida antes de que la semántica pueda darle significado. [7] No todos los programas sintácticamente correctos son semánticamente correctos. No obstante, muchos programas sintácticamente correctos están mal formados, según las reglas del lenguaje; y puede (dependiendo de la especificación del idioma y la solidez de la implementación) resultar en un error de traducción o ejecución. En algunos casos, dichos programas pueden exhibir un comportamiento indefinido . Incluso cuando un programa está bien definido dentro de un lenguaje, aún puede tener un significado que no es el previsto por la persona que lo escribió.

Usando el lenguaje natural como ejemplo, puede que no sea posible asignar un significado a una oración gramaticalmente correcta o que la oración sea falsa:

El siguiente fragmento de lenguaje C es sintácticamente correcto, pero realiza una operación que no está semánticamente definida (porque pes un puntero nulo , las operaciones y no tienen significado):p->realp->im

 complejo * p = NULL ; abs_p complejo = sqrt ( p -> real * p -> real + p -> im * p -> im );              

Como ejemplo más simple,

 intx ;printf ( "%d" , x );   

es sintácticamente válido, pero no semánticamente definido, ya que utiliza una variable no inicializada . Aunque los compiladores de algunos lenguajes de programación (por ejemplo, Java y C#) detectarían errores de este tipo en variables no inicializadas, deberían considerarse errores semánticos y no errores de sintaxis. [6] [13]

Ver también

Para comparar rápidamente la sintaxis de varios lenguajes de programación, eche un vistazo a la lista "¡Hola, mundo!" ejemplos de programas :

Referencias

  1. ^ ab Friedman, Daniel P.; Varita Mitchell; Christopher T. Haynes (1992). Fundamentos de los lenguajes de programación (1ª ed.). La prensa del MIT. ISBN 0-262-06145-7.
  2. ^ Smith, Dennis (1999). Diseño de software mantenible . Medios de ciencia y negocios de Springer.
  3. ^ Pai, Vaikunta; Aithal, PS (31 de diciembre de 2020). "Una revisión sistemática de la literatura sobre técnicas de implementación de analizadores léxicos en el diseño de compiladores". Revista Internacional de Letras de Gestión e Ingeniería Aplicada (IJAEML) . 4 (2): 285–301. ISSN  2581-7000.
  4. ^ Ah, Alfred V.; Mónica S. Lam; Ravi Sethi; Jeffrey D. Ullman (2007). Compiladores: principios, técnicas y herramientas (2ª ed.). Addison Wesley. ISBN 0-321-48681-1.Sección 4.1.3: Manejo de errores de sintaxis, páginas 194-195.
  5. ^ Louden, Kenneth C. (1997). Construcción del compilador: principios y práctica . Brooks/Cole. ISBN 981-243-694-4.Ejercicio 1.3, págs. 27–28.
  6. ^ ab Errores semánticos en Java
  7. ^ a b C Slonegagger, Kenneth; Kurtz, Barry (1995). Sintaxis formal y semántica de lenguajes de programación . Compañía editorial Addison-Wesley . ISBN 0-201-65697-3.{{cite book}}: Mantenimiento CS1: fecha y año ( enlace )
  8. ^ Michael Sipser (1997). "2.2 Autómatas de empuje". Introducción a la Teoría de la Computación. Publicación PWS. págs. 101-114. ISBN 0-534-94728-X.
  9. ^ Comentario de LtU que aclara que el problema indecidible es la pertenencia a la clase de programas Perl.
  10. ^ Ejemplo cromático de código Perl que da un error de sintaxis dependiendo del valor de la variable aleatoria
  11. ^ "Introducción a las macros Lisp comunes". Apl.jhu.edu. 1996-02-08. Archivado desde el original el 6 de agosto de 2013 . Consultado el 17 de agosto de 2013 .
  12. ^ "El libro de cocina de Common Lisp: macros y comillas". Cl-cookbook.sourceforge.net. 2007-01-16 . Consultado el 17 de agosto de 2013 .
  13. ^ ¿ Cuestión de sintaxis o semántica?

enlaces externos