Nota del autor

Si la entrada que estás leyendo carece de imágenes, no se ve el vídeo que teóricamente lleva incrustado o el código fuente mostrado aparece sin formato, podéis conocer los motivos aquí. Poco a poco iré restableciendo la normalidad en el blog.
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.

martes, 29 de abril de 2008

Generación de código con DSL Tools

Una de las posibilidades más interesantes de DSL Tools para Visual Studio es la generación de artefactos y código a partir del elementos pertenecientes al modelo de dominio que definamos para nuestro DSL. En el ejemplo de hoy usaremos el proyecto más básico de cuantos ofrece Visual Studio DSL Tools para obtener un generador de código básico que permitirá, definir un "lenguaje de programación visual" para nuestros usuarios. Obviamente, el alcance de la programación será algo limitado, máxime si tenemos en cuenta que se trata de un mero ejemplo de generación de código, pero nos permitirá vislumbrar hasta qué punto es potente y flexible esta herramienta.


En primer lugar, entramos a Visual Studio y creamos un nuevo proyecto Domain-Specific Languaje Designer, dentro de las plantillas Extensibility. Daremos un nombre a la solución, por ejemplo, MyDSLGenerator, y al pulsar en OK aparecerá un asistente para definir algunos de los parámetros de nuestro DSL. Seleccionaremos la plantilla MinimalTemplate, la extensión que tendrán los archivos de nuestro lenguaje (.mydlsg, en este ejemplo), y otras características como el espacio de nombres raíz para los proyectos de la solución. Hecho esto, pulsaremos en Finalizar, y tendremos ante nosotros el diseñador del DSL. Lo primero que nos preguntará siempre (a menos que indiquemos que no deseamos ser advertidos de ello) es que va a procederse a regenerar todos los archivos de la solución a partir de los Text Templates (archivos .tt) de la misma. Aceptamos, y nos encontraremos con algo similar a lo siguiente:



dsl1.PNG


Aunque podemos personalizar todo el DSL desde el proyecto Dsl de la solución, optaremos en esta entrada introductoria por hacer los mínimos cambios posibles. En primer lugar, hemos renombrado el nombre de los elementos del diagrama (ExampleShape, ExampleConnector, etc.).



dsl2.PNG


Lo más importante aquí va a ser definir una nueva propiedad de dominio sobre la clase xxElement (en nuestro caso, ActivityElement), ya creada por la propia plantilla del DSL. A esta propiedad podremos darle un valor, posteriormente, para cada Shape correspondiente a la clase que dibujemos en nuestro diseñador, o lo que es lo mismo, para cada objeto instanciado de la clase. Introduciremos una actividad denominada "Activity", de tipo string.



dsl21.PNG


Dentro del DSL Explorer haremos lo propio con las opciones correspondientes al Editor (Editor->Toolbox Tabs-> ActivitiesDesigner->Tools), para que aparezcan también los nombres en el Visual Studio Hive, y en la aplicación DSL una vez que la instalemos en otro equipo.



dsl3.PNG


Volvemos a generar el código del proyecto, pulsando sobre el botón Transform All Templates, y ejecutamos (F5). Aparecerá el Visual Studio Hive.


Creamos un diagrama acorde con lo especificado en nuestro DSL. Por ahora, lo único que hará es asignar una serie de propiedades a los Shapes (los rectángulos correspondientes a nuestras actividades). Estas propiedades son las que definimos anteriormente en el diseñador del DSL. Por un lado, la propiedad Name, que ya venía definida con la propia clase, y la propiedad Activity, que incluimos en ese momento.



dsl5.PNG


Para cada Shape, damos valor a las propiedades Name y Activity.



dsl6.PNG


Ahora vamos a preparar nuestro generador de código. Se tratará de un TextTemplate, de modo que añadimos un nuevo elemento al proyecto, de tipo fichero de texto, con el nombre LibraryCode.tt, y que va a permitir la generación de código a partir de los elementos existentes en nuestros diagramas. Más adelante nos quedará ver cómo llevar a cabo esta acción automáticamente cada vez que añadamos un elemento de nuestro lenguaje (archivo con extensión .mydlsg, que es la que definimos con este fin). De momento, lo generamos a mano, y le damos contenido.








using System;
using System.Collections.Generic;
using System.Text;
using LOBOSOFT.MyDSLGenerator;

namespace LOBOSOFT.Activity
{


///
/// The MainClass is the enter point for the functionality of this assembly
///
public class MainClass
{
public void MainMethod()
{
<# /* Calls to the private classes */
foreach(ActivityElement activity in this.ActivitiesModel.Elements){ #>
// Sequential caller
Class oClass = new Class();
oClass.m();


}
}

<# /* Creating classes for the Diagram Activities */
foreach(ActivityElement activity in this.ActivitiesModel.Elements)
{
#>
///
/// The Class gives functionality for managed class (test case)
///
public class Class
{
public void m()
{
o = new ();
o.MainMethod();
}
}
<#
}
#>

}

A grandes rasgos, este código se encarga de recorrer los elementos de nuestro diagrama, generando código según los valores que hemos definido para las propiedades. En primer lugar, se encuentran una serie de directivas.


<#@ template language="C#v3.5"..., por ejemplo, indica el lenguaje usado para la plantilla, ya que después de generar el código, será validado sintácticamente según las reglas del lenguaje, y compilado en una DLL cuando compilemos nuestro proyecto. El v3.5 es opcional, y sería necesario para aplicaciones que usen C# del .NET Framework 3.5 (como por ejemplo, que hagan uso de LINQ).
indica que haremos uso del archivo .mydslsg llamado Sample1. Por tanto, cada TextTemplate (.tt) irá vinculado inicialmente a un .mydslg, aunque veremos más adelante que esto podrá automatizarse.

El resto del template "escribe" el código en C# que necesitamos. Si pulsamos con el botón derecho del ratón sobre el archivo y seleccionamos la opción Run Custom Tool, o bien pulsamos sobre el botón Transform All Templates se generará el código C# asociado. En nuestro caso, el código correspondiente es el siguiente:


[csharp]
using System;
using System.Collections.Generic;
using System.Text;
using LOBOSOFT.ManagedDLLEncapsulation;

namespace LOBOSOFT.Activity
{

///
/// The MainClass is the enter point for the functionality of this assembly
///
public class MainClass
{
public void MainMethod()
{
// Sequential caller
pruebaClass opruebaClass = new pruebaClass();
opruebaClass.mprueba();

}
}

///
/// The pruebaClass gives functionality for prueba managed class (test case)
///
public class pruebaClass
{
public void mprueba()
{
prueba oprueba = new prueba();
oprueba.MainMethod();
}
}

}
[/csharp]

El código será generado e incluso compilado si hemos incluido en las referencias del proyecto las DLLs que estamos usando.

Nos quedará por ver cómo automatizar varios de estos procesos, y cómo realizar el despliegue en otro ordenador de este DSL gráfico, pero eso será en una próxima entrega.

3 comentarios:

  1. [...] Generación de código con DSL Tools [...]

    ResponderEliminar
  2. Mithdraug, antes de nada gracias por tu pronta respuesta. Espero no tener que seguir dandote la lata y poder empezar a ser independiente en este tema ya mismo.

    La SDK la tenía descargada e instalada en el VS2008, igualmente sigo sin encontrar las plantillas de extensibilidad. :(

    Sobre el ejemplo de IronPython, ya lo he descargado y me dispongo a probarlo, si no encuentro nada más sencillo, tendré que optar por ello, pero por contextualizar un poco, en realidad, tengo que entregar una practica de universidad, donde pueda demostrar que los DSL´s existen y son aplicables desde VS2008, y no hace falta que yo los implemente (salvo que sea inevitable), y luego desarrollar una pequeña aplicación (minima) con el DSL elegido o implementado.

    Mi idea es ver si alguien ya ha implementado un DSL del estilo del ejemplo de la hamburguesería o algo parecido, y luego crear algo minimo, para poder mostrarlo.

    Existe un condicionante que es el idioma, y es que tiene que estar en español, y en el caso del IronPhyton tendría que traducirlo todo, que es un mal menor y que si hace falta, lo hago, pero si puedo evitarlo, pues también mejor.

    En fin, que no se si se te ocurre alguna manera de satisfacer estos pre-requisitos, o si existe, o si simplemente, lo que me toca es echarle muchas horas a pesar de que este proyecto no tenga demasiada importancia (aunque hacerlo, lo tengo que hacer obligatoriamente).

    De nuevo gracias por tu aportación.

    Un saludo

    ResponderEliminar
  3. Buenas de nuevo, Raúl, y disculpa ahora sí la tardanza en la respuesta. Espero que la leas, igualmente, y que hayas avanzado bastante en el proyecto. El condicionante sobre el idioma que me comentas la verdad es que sí que constituye una pega bastante grande respecto a Iron Python, y es que traducir ese proyecto puede ser, cuando menos, insufrible. Eso sí, la comunidad en español seguro que te estaba agradecida, jejeje.

    En cuanto al modelado de algún otro DSL usando las herramientas de Microsoft, la verdad es que no me consta, aunque habrá algunos ejemplos por Internet, posiblemente, aunque casi seguro que de una envergadura bastante limitada.

    Lo que sí me extraa´es lo de las plantillas de extensibilidad, en mi caso tuve únicamente que instalar esta SDK y las tenía disponibles. Eso sí, mi Visual studio es un Team System, puede que tenga algo que ver.

    Resumiendo, que espero que hayas avanzado lo suficiente con el proyecto como para poder entregarlo y avanzar en esa asignatura. Ya nos contarás, si te place, qué DSL usaste o implementaste finalmente.

    ¡Un saludo!

    ResponderEliminar