Friday, May 9, 2008

Inferencia de Esqueleto de Clases a partir de una Rutina Cliente de Ejemplo

Los binarios de LayerD ya estan disponibles para todo el mundo, por tanto comienzo a postear algunos ejemplos. El presente ejemplo es una classfactory un tanto avanzada ya que utiliza varias funciones del CodeDOM de LayerD. Sin embargo, muestra como con classfactorys relativamente simples es posible implementar herramientas que programarlas en IDEs nos llevarían con seguridad bastante más tiempo y sólo nos servirían para ese IDE y lenguaje en particular.

Para probar éste ejemplo baje la última actualización del SDK de LayerD desde SourceForge. El SDK de las semanas anteriores no funcionará.

Los fuentes para la classfactory y el cliente de ejemplo los puede bajar aquí.

Muchas metodologías de programación promueven la escritura en primer lugar de los tests y luego la implementación de las clases y demás artefactos. Entre estas metodologías encontramos las denominadas "Test Driven Development" - Desarrollo Guiado por Pruebas - y los métodos "Agile Programming" - Programación Agíl, o métodos ágiles -.
Muchas otras veces en la medida que utilizamos una clase que nosotros escribimos nos damos cuenta que nos faltaron definir métodos, propiedades, etc.

Si bien, muchos entornos integrados - IDEs - poseen herramientas que permiten escribir la estructura de por ejemplo métodos inexistentes su funcionamiento no siempre es completo o se adecua a lo que necesitamos. Por ejemplo, en Visual Studio 2005 y 2008 podemos pedirle al entorno que escriba el prototipo de un método por nosotros, sin embargo debemos realizar una serie de clics o controles de tecla por cada método faltante, adicionalmente no permite inferir otras construcciones faltantes en clases como propiedades o indexadores.
Por tanto, dependiendo del entorno que poseamos y las capacidades "RAD" de dicho IDE tendremos o no hasta cierto punto la funcionalidad necesaria para encarar de una manera productiva este tipo de enfoque "Guiado por Pruebas".

El ejemplo de classfactory interactiva siguiente permite dado un ejemplo de uso inferir gran parte de la estructura pública de una clase incluyendo métodos y propiedades.

Una parte del código de la classfactory interactiva es el siguiente



public interactive class TypeFromSample{
public:
static exp void New(iname void className){
XplClass^ clase = null;
// Search if provided className exists on current namespace
// If class doesn't exists create one, insert on current namespace and return a new call
// to current function
clase = (XplClass^)context.CurrentNamespace.FindNode("/Class[name='" + className.Identifier + "']");
if (clase == null){
clase = writecode{
public class $className{
}
};
// Insert the class on current namespace
context.CurrentNamespace.Children().InsertAtEnd(clase);
// Return a call to this function with same parameter
return writecode( TypeFromSample::New($className) );
}
// Search for al missing members in current function, find member access binary operator expressions
XplNodeList^ items = context.CurrentFunction.FindNodes("/bo[targetMember='?']");
// For each missing member try to infer it.
for (XplBinaryoperator^ op in items){
if (op.get_targetClass().Contains(className.Identifier)){
// Insert the inferred member on target class
clase.Children().InsertAtEnd(CreateFunctionFor(op));
}
}
// Return
return null;
}
...

El método principal es "New" el cual es estático y toma como argumento un "iname", los "inames" permiten tomar como argumento de funciones identificadores, en este caso cualquier identificador incluyendo un identificador no declarado (esto es por ser un "iname void") y retorna una expresión cualquiera.
Lo primero que se realiza es verificar si existe en el espacio de nombres actual una clase con el nombre indicado en el argumento, si no existe inyecta una nueva clase con dicho nombre y retorna una llamada al mismo método con el fin de que el compilador Zoe vuelva a realizar otro ciclo de compilación y al ejecutarse nuevamente la función "New" se posea información semántica parcial sobre la manera en la que el programador intento usar al tipo que queremos inferir su estructura.
Si la clase indicada como argumento ya existe el método procede a buscar todos los accesos a miembros no resueltos, y luego procesa de dichos elementos sólo los que se correspondan con un acceso al tipo de la clase indicada y los procesa uno por uno con la ayuda de la función "CreateFunctionFor".
La función "CreateFunctionFor" de la classfactory utiliza la información de los nodos del CodeDOM para determinar si la expresión padre del acceso a miembro se trata de una llamada a función o de una asignación. Si la expresión padre es una llamada a función infiere los parámetros y tipo de retorno de la función, si es una asignación infiere una propiedad para la clase.

Si el programa cliente de la classfactory es el siguiente:

static int Main(string^[] args){

// Llamanda a Classfactory Interactiva
// para crear el prototipo de una clase a
// partir de un ejemplo de uso
TypeFromSample::New(MyClass);

// Código de ejemplo de uso a partir
// del cual deseo armar el prototipo
// de MyClass
MyClass^ var = new MyClass();
var.Add(1, 2);
var.CurrentValue = "string";
var[2] = "test";
int n;
n = var.HashOf("hola");
n = var.foo(1, "chau", var, args);
return 0;
}

Al compilar el cliente en modo interactivo se inferirá la siguiente estructura para MyClass:

public class MyClass{
void Add(int p1, int p2){
throw new NotImplementedException();
}
string^ property CurrentValue {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
int HashOf(string^ p1){
throw new NotImplementedException();
}
int foo(int p1, string^ p2, Sample::MyClass^ p3, string^[] p4){
throw new NotImplementedException();
}
}

La classfactory "TypeFromSample" puede mejorarse en muchas formas, por ejemplo:
  • Permitiendo inferir indexadores y constructores
  • Inferir todos los tipos faltantes (más de un tipo a la vez)
  • Presentar interfaz de usuario al programador para consultar sobre una desición de inferencia

La classfactory interactiva tiene menos de 100 líneas de código útil y permite implementar la inferencia de una clase de forma más completa que Visual Studio. Al tratarse de una classfactory interactiva en el futuro también podrá utilizarse para cualquier otro lenguaje LayerD de alto nivel que no sea Meta D++.

A diferencia de una herramienta en un entorno intergrado la classfactory funciona de forma completamente independiente de cualquier entorno e incluso de cualquier editor de texto por ser procesada por los compiladores LayerD directamente, por tanto podemos utilizar su funcionalidad aunque programemos con Notepad. También es posible adaptar su comportamiento a lo que nosotros necesitemos, por ejemplo agregando inferencia de constructores, interfaces, métodos de colección, eventos, etc. y el trabajo que realizamos en dicha utilidad no perderá vigencia porque salga un nuevo entorno de desarrollo.

Es preciso notar que en la actualidad no es posible desarrollar nada similar con ningún lenguaje main-stream sin utilizar el modelo de extensión de algún entorno en particular. En su lugar, LayerD permite desarrollar este tipo de herramientas típicas de entornos IDE directamente en el mismo lenguaje en el cual escribimos todos nuestros programas y usando el mismo conocimiento que poseemos del lenguaje y la programación de classfactorys en general sin atarnos a ningún entorno, versión, plataforma o fabricante en particular.

No comments: