SILLY

El caso es que se trata de un lenguaje orientado a la creación de objetos; de ahí lo de Instantiation. Resulta también que hay un gran números de circunstancias en los que esa "categoría" de minilenguajes resulta útil. ¿Quiere otro ejemplo? Ahí tiene la generación de compiladores. Freya utiliza GOLD: un sistema que recibe una gramática y produce una tabla de análisis sintáctico. Pero es más común el uso de sistemas como Yacc y Bison, que permiten asociar instrucciones en C/C++/C# a la gramática. Hace poco encontré este otro generador, de Wayne Kelly, de la Queensland University of Technology. Incluye el código fuente completo del generador, en C#, y es un código limpio y legible.
Tengo, desde hace un tiempo, la idea de ensayar el uso de un lenguaje tipo SILLY con un generador de compiladores para .NET. Como el análisis sintáctico suele utilizarse para crear, en paralelo, un Arbol de Sintaxis Abstracta (AST), el lenguaje se especializaría en la creación de objetos. Algunas ideas sencillas:
- Nada de operador new. Cuando un nombre de tipo se usa como función, significa la construcción de una instancia.
- El lenguaje soportaría inicializadores de objetos... al estilo Freya, claro. Es decir, se podrían inicializar campos y propiedades del objeto creado usando una sintaxis similar a la de los parámetros con nombre.
- Cada "no terminal" de la gramática tendría un tipo de datos asociado.
- Cada "regla", o "producción", tendría una expresión asociada, de un tipo derivado del tipo asociado al no terminal.
Veamos un ejemplo sencillo:
<Exp> ::= : AstExp
<Exp> '+' <Term> : AstBinary($Exp, '+', $Term)
<Term> : $Term
La primera línea advierte que las expresiones van asociadas a nodos de la clase AstExp. En la segunda, cada vez que se detecta una suma, se crea un nodo AstBinary a partir de los nodos de las partes constituyentes. La tercera línea indica que se copie, simplemente, el nodo asociado al término. Este es, naturalmente, el caso más sencillo y frecuente. También es frecuente el uso de listas, por lo que esta variante de SILLY debería soportarlas:
<VarGroup> ::= : List[AstVar]
<VarGroup> ',' <Var> : $VarGroup + { $Var }
<Var> : { $Var }
Es decir, las llaves se utilizarían para delimitar literales de listas, y el signo + significaría "unión", o concatenación de listas.
Ahora mire un "invento" curioso, asociado a la regla sintáctica correspondiente al operador existencial de Freya:
<Exp> ::= : AstExp
'*' IN <Exp> : if $Exp is AstRange
then AstBin($Exp.Lo, "<=", $Exp.Hi)
else AstExists($Exp)
Para empezar, observe que he mostrado una expresión condicional, no una instrucción condicional. Lo interesante es lo que ocurre en la rama then de la expresión: como esa rama se evalúa cuando $Exp es un AstRange, cambiamos el tipo declarado para $Exp de esa rama para abajo (en el árbol de expresiones); por eso permitimos las referencias a las propiedades Lo y Hi (low y high), definidas para AstRange. Creo que un convenio de este tipo ahorraría mucho trabajo y sería muy útil en lenguajes de propósito general... aunque tengo que pensarlo un poco más, antes de dar el recurso por bueno.
Naturalmente, se permitiría expresiones let/where, y quizás sería necesario algún "aplicador" para actuar sobre elementos de una lista. El "compilador" tendría que encargarse también de algo que podemos clasificar como inyección de código: se supone que, a la vez que se construye el árbol sintáctico, los nodos de éste deben irse asociando a intervalos o rangos de texto dentro del código fuente. A los nodos devueltos en cada reducción, por ejemplo, se les podría asociar automáticamente al rango determinado por el texto reducido. Pero habría casos más complicados:
<Exp> ::= : AstExp
<Exp> IS NOT <Ref> : AstNeg(AstCast($1, $3))
El nodo AstNeg es el inmediatamente devuelto por la reducción, por lo que es fácil asignarle un rango. En cambio, el nodo AstCast se esconde dentro del nodo principal. Una solución laboriosa sería indicar explícitamente la asignación del rango:
AstNeg(AstCast($1, $3, Range := @1 + @3))
Este es un ejemplo de los inicializadores de objetos ya mencionados. Observe que @1 se refiere al rango asociado al primer nodo de la regla. Otra solución más elegante sería permitir que el compilador dedujese dicha asignación, a partir de los parámetros detectados en la llamada al constructor AstCast.
¿Qué le parece?
Etiquetas: ideas, programación funcional, XSight