AjSharp: un lenguaje dinámico en C#

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;");

Include ejecuta los comandos dentro de un archivo. Evalue analiza y evalúa una expresión. Execute compila y ejecuta comandos.

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

http://www.ajlopez.com
http://twitter.com/ajlopez

























This entry was posted in 1389, 6145, 8870, 8926. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>