Durante el desarrollo de AjGenesis, mi proyecto de generación de código, definí y usé un lenguaje interpretado, llamdado AjBasic, que me dió libertad para implementar tareas, plantillas y acceso a .NET, de una forma muy flexible. El año pasado comencé a separar la implementación de AjBasic del núcleo de AjGenesis, para poder irlo mejorando. Escribí el núcleo de un intérprete, AjInterpreter, y como prueba de concepto, construí sobre él a AjBasic, y a otro lenguaje AjSharp, con una sintaxis más tipo C#. Más információn en:
AjSharp- a C Sharp-like interpreter, work in progress
AjSharp – un intérprete a la C# trabajo en progreso
Ahora, este año, lo reescribí en una implementación nueva, dentro de mi AjCodeKatas Google Code. El núcleo del intérprete es ahora AjLanguage, y AjSharp es el lenguaje con un parser que se apoya en la “virtual machine” que da el AjLanguage, para armar y ejecutar un árbol de comandos y expresiones:
Pueden ver el código en desarrollo y bajarlo desde:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Variables, expresiones y comandos
Las variables no son tipadas, y son automáticamente declaradas cuando son usadas:
a = 1;
b = 2;
Estas variables contienen valores enteros, pero pueden asignarles valores de otros tipos en cualquier momento:
a = “one”;
b = “two”;
Los comandos habituales son soportados:
if (k>0)
return;
for (k=1; k<=10; k++)
sum = k+sum;
while (j<10)
DoProcess(j);
foreach (element in elements)
AddElement(element);
Las condiciones puede ser cualquier expresión, no solamente una que de resultado booleano. Ver más adelante, la explicación sobre el valor False.
Funciones y rutinas
Esta es la sintaxies para escribir una función factorial.
function Factorial(n)
{
if (n<=1)
return 1;
return n * Factorial(n-1);
}
La palabra sub puede ser usada para definir subrutinas.
Funciones y subrutinas son como cualquier otrosvalores. Puede ser definidos sin nombre y asignados a variables:
Add1 = function (n) { return n+1; }
two = Add1(1);
function Apply(func,values)
{
list = new List();
foreach (value in values)
list.Add(func(value));
return list;
}
numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
function Square(n) { return n*n; }
squared = Apply(Square, numbers);
squared2 = Apply(function (n) { return n*n; }, numbers);
Manejo de objetos .NET nativos
Uno de los objetivos de diseño de AjLanguage es que pudiera acceder a objetos .NET. Pueden ser creados con la palabra new:
ds = new System.Data.DataSet();
dinfo = new System.IO.DirectoryInfo(“.”);
foreach (fi in dinfo.GetFiles()) {
PrintLine(fi.FullName);
}
Objetos dynámicos
Pueden ser creados usando la palabra new. Un objeto dinámico acepta nuevos miembros (variables y métodos):
dynobj = new DynamicObject();
dynobj.FirstName = “Adam”;
dynobj.LastName = "Genesis”;
dynobj.Age = 800;
dynobj.FullName = function() { return FirstName + “ “ + LastName; }
Otra notación:
dynobj = new { Name = “Adam”, Age = 800 };
Otra notación:
dynobj = new { var FirstName = “Adam”; var LastName = “Genesis”;
function FullName() { return FirstName + “ “ + LastName; }
Los objetos dinámicos se definen automáticamente, poniendo valores en sus propiedades:
Project.Database.Provider = “…”;
Project.Database.ConnectionString = “…”;
crea el objeto dinámico Project, con una propiedada Database que es a su vez otro objeto dinámico. Ese código equivale a:
Project = new DynamicObject();
Project.Database = new DynamicObject();
Project.Database.Provider = “…”;
Project.Database.ConnectionString = “…”;
Un experimento (veré si queda en la implementación final) las litas se definen automáticamente usando el método Add:
Project.Entities.Add(new { Name = “Customer”, Table = “dbo.Customers” });
equivale a:
Project = new DynamicObject();
Project.Entities = new List();
Project.Entities.Add(new { Name = “Customer”, Table = “dbo.Customers” });
Clases dinámicas
Una clase puede ser definida usando esta sintaxis:
class Person {
var FirstName;
var LastName;
var Age;
function FullName() {
return FirstName + “ “ +
LastName;
}
}
Una nueva instancia puede ser creada como siempre:
adam = new Person() { FirstName = “Adam”, LastName = “Genesis”, Age = 800 };
La instancia, a su vez, es dinámica: nuevos miembros pueder serle adosados, y pueden agregar o redefinir métodos:
adam.FullName = function() { return “The “ + FirstName; };
Pueden crear una clase como un valor:
Person = new DynamicClass();
pero todavía tengo que definir bien la interfaz para agregar miembros de instancia.
Clases ya definidas
Hay algunas clases predefinidas:
dynobj = new DynamicObject();
list = new List(); // implementing IList
dict= new Dictionary(); // implementing IDictionary
Funciones primitivas
Hay unas pocas funciones y subrutinas definidas:
Print(“Hello”);
PrintLine(“Hello World”);
Hay tres funciones predefenidas para ejecutar y evaluar código dinámicamente:
Include("program.ajs");
Evaluate("k+1");
Execute("k=1;");
El valor false
Cualquier valor que sea false, null, 0 o el string vacío, será evaluado como falso dentro de una expresión condicional:
if (k)
PrintLine(“true”);
else
PrintLine(“false”);
El ejemplo de arriba imprime “false” en ejecución, si k es zero o indefinida. Si una variable es indefinida, cualquier acceso a sus miembros (que no sean métodos) retorna null, no una exception:
if (Project.Database.ConnectionString)
PrintLine(“true”);
else
PrintLine(“false”);
Este comando imprime de nuevo “false”, si la variable Project está indefinida.
Arreglos, listas y diccionarios
Vectores nativos puede ser definidos, usando una longitud:
firstprimes = new int[10];
o con valores:
firstprimes = new int[] { 1, 2, 3, 5, 7, 9 };
Una lista es creada dinámicamente si comenzamos a asignar valores con subíndices consecutivos, desde 0, a una variable no definida aún:
numbers[0] = “zero”;
numbers[1] = “one”;
numbers[2] = “two”;
numbers[3] = “three”;
Un diccionario es automáticamente creado si los subíndices no son numéricos:
numbers[“one”] = 1;
numbers[“two”] = 2;
numbers[“three”] = 3;
Si necesitan más características, siempre pueden usar el framework .NET nativo.
Interfaz de consola
El proyecto AjSharp.Console es una aplicación de consola, donde pueden ingresar y ejecutar comandos AjSharp (no expresiones):
No hay comando para salir, todavía. Simplemente, Control+C en Windows (no lo probé, pero todo el código debería ser recompilable en Mono).
Próximos pasos
Hay varias cosas que quisiera agregar. Una lista parcial:
– Implementar AjBasic como otro lenguaje que use AjLanguage
– Soporte de Generics
– Soporte de plantillas/templates (como en AjGenesis)
– Integrar a la generación de código de AjGenesis
– Compilar el AST a Dynamic Language Runtime (DLR)
Nos leemos!
Angel “Java” Lopez