Hay tantos lenguajes de programación que admiten la inclusión de mini-idiomas. PHP está incrustado en HTML. XML se puede incrustar dentro de JavaScript. Linq se puede incrustar dentro de C#. Las expresiones regulares se pueden incrustar en Perl.Gramáticas compostables
// JavaScript example
var a = <node><child/></node>
Pensando en ello, la mayoría de los lenguajes de programación se pueden modelar como mini-idiomas diferentes. Java, por ejemplo, podría ser dividida en unos menos cuatro mini-idiomas distintos:
- Un langauge de declaración de tipo (Directiva paquete, las directivas de importación, declaración de la clase)
- Un lenguaje miembro-declaración (modificadores de acceso, declaraciones de métodos, miembro de vARs)
- Un lenguaje de declaración (flujo de control, ejecución secuencial)
- Un lenguaje de expresión (literales, las tareas, las comparaciones, la aritmética)
Pudiendo implementar esos cuatro lenguajes conceptuales como cuatro gramáticas distintas ciertamente reduciría mucho del espaguetiismo que suelo ver en las complejas implementaciones de compilador y analizador.
He implementado analizadores para diferentes tipos de idiomas antes (usando ANTLR, JavaCC, y analizadores sintácticos de descenso recursivo), y cuando el lenguaje se vuelve realmente grande y complejo, por lo general se termina con una gramática huuuuuuge, y la implementación del analizador se pone muy fea realmente rápido.
Lo ideal sería que, al escribir un analizador para uno de esos idiomas, sería bueno implementarlo como una colección de analizadores sintácticos, pasando el control hacia adelante y hacia atrás entre ellos.
Lo complicado es que, a menudo, el idioma contenedor (por ejemplo, Perl) define su propio término centinela para el lenguaje contenido (por ejemplo, expresiones regulares). He aquí un buen ejemplo:
my $result ~= m|abc.*xyz|i;
En este código, el código principal del Perl define un terminal no estándar "|" para la expresión regular. Implementar el analizador de expresiones regulares como algo completamente distinto del analizador de perl sería realmente difícil, porque el analizador de expresiones regulares no sabría cómo encontrar el término de expresiones sin consultar el analizador padre.
O, digamos que tenía un lenguaje que permitió la inclusión de expresiones LINQ, pero en lugar de terminar con un punto y coma (como C# hace), que quería obligar aparecen las expresiones LINQ entre corchetes:
var linq_expression = [from n in numbers where n < 5 select n]
Si definí la gramática de Linq dentro de la gramática del lenguaje parental, podría escribir fácilmente una producción no ambigua para un "LinqExpression" utilizando la búsqueda sintáctica para encontrar los cercados del bracket. Pero entonces la gramática de mi padre tendría que absorber toda la especificación de Linq. Y eso es un lastre Por otro lado, un analizador de Linq secundario aislado tendría dificultades para determinar dónde detenerse, ya que tendría que implementar la búsqueda anticipada de tipos de tokens extranjeros.
Y eso descartaría casi por completo el uso de fases lexing/analíticas separadas, ya que el analizador de Linq definiría un conjunto completamente diferente de reglas de tokenización que el analizador padre. Si está escaneando un token a la vez, ¿cómo sabe cuándo pasar el control nuevamente al analizador léxico del idioma principal?
¿Qué piensan?¿Cuáles son las mejores técnicas disponibles en la actualidad para implementar gramáticas distintas, desacopladas y compostables para la inclusión de mini-idiomas dentro de lenguajes padres más grandes?
OMeta tiene esto! Puedes componer múltiples gramáticas juntas, o incluso heredar gramáticas existentes en estilo OOP. – CMCDragonkai