Introducción a MGrammar: Crear una gramática para reconocer “Menús del día” (parte 1 de 2)

Sirva este post para poner de manifiesto mi devoción por la cocina española, algo que como podéis imaginar, no abunda demasiado por tierras canadienses. Al mismo tiempo, me gustaría introducir una serie de conceptos básicos acerca de MGrammar, uno de los dialectos del lenguaje M que nos permite definir gramáticas para nuestras propias DSLs. Para aquellos de vosotros con conocimientos en el ámbito de las gramáticas y lenguajes, comentar que con MGrammar somos capaces de crear gramáticas independientes de contexto (GIC), que tienen la potencia expresiva suficiente para describir cualquiera de los lenguajes de programación empleados en la actualidad.

Quien más y quien menos se habrá encontrado en alguna ocasión con el siguiente modelo de datos, el cual representa una abstracción de nuestro dominio específico.

A pesar de que el camarero que escribió este texto en la pizarra a primera hora de la mañana jamás ha utilizado Visual Studio, ni conoce las diferentes categorías de lenguajes establecidas por el OMG, podemos apreciar cómo claramente está empleando su propio lenguaje específico de dominio, el cual se rige por unas normas fácilmente formalizables mediante el uso de una gramática.

Nosotros, en un intento de convertir a cualquier persona en desarrollador, a pesar de que ellos no lo sepan, vamos a aportarles una potente herramienta que permitirá traducir automáticamente lo que hay escrito en esta pizarra en una representación intermedia en forma de grafo (MGraph) que posteriormente podremos proyectar sobre una base de datos o consumir directamente desde .Net Framework (WPF, WCF/WF, ASP.Net…). De la generación de proyecciones nos encargaremos en la segunda parte de esta serie, en este primer capítulo nos centraremos en crear la gramática que define las reglas de nuestro lenguaje.

Para estandarizar un poco el lenguaje, supongamos que el contenido de nuestra pizarra es el siguiente:

Primeros
* Gazpacho Andaluz.
* Ensalada Mediterránea.
* Consomé de Pollo.
Segundos
* Solomillo a la Plancha.
* Merluza a la Romana.
* Macarrones Bolognesa.
* Paella Alicantina.
Postres
* Flan.
* Arroz con leche.
* Macedonia de frutas.
* Tarta de queso.
Vino

En el caso de la última línea, el camarero podría elegir entre incluir Vino o Café en el menú.

Una vez tenemos clara la estructura de nuestro lenguaje, vamos a definir sus reglas gramaticales. Para ello, emplearemos Intellipad. Intellipad es un IDE textual que viene incluído con la SDK de Oslo. Algunos de vosotros lo conoceréis por su nombre anterior: Emacs.Net

image

Lo primero que haremos será crear el archivo para describir nuestra gramática: MenuDSL.mg, que constará de las siguientes reglas:

   1:  module MLanguage
   2:  {
   3:      language Menu
   4:      {
   5:          // Keywords
   6:          token kwPrimeros = "Primeros";
   7:          token kwSegundos = "Segundos";
   8:          token kwPostres = "Postres";    
   9:          token kwVino = "Vino";
  10:          token kwCafe = "Cafe";
  11:          
  12:          token EntryStart = "*" " "*;
  13:          token EntryText = ^"."+;
  14:          token EntryEnd = ".";
  15:          nest syntax Entry = EntryStart EntryText EntryEnd;
  16:          
  17:          syntax Primeros = kwPrimeros Entry+;
  18:          syntax Segundos = kwSegundos Entry+;
  19:          syntax Postres = kwPostres Entry+;
  20:          syntax Extras = kwVino | kwCafe;
  21:          
  22:          syntax Main = Primeros Segundos Postres Extras;
  23:          
  24:          interleave Skippable = " " | "r" | "n";
  25:      }
  26:  }

Acabamos de escribir nuestro primer módulo en MGrammar. Un módulo no es ni más ni menos que la forma que tenemos en MGrammar de crear un ámbito, dentro del cual escribiremos una serie de reglas para nuestro lenguaje. Pasemos a analizar la sintaxis del mismo detenidamente.

Dentro de nuestro módulo MLanguage, hemos creado un lenguaje denominado Menu. Este lenguaje contiene un conjunto de reglas y de tokens. Un token es un elemento terminal de nuestra gramática, es decir, un elemento cuya derivación no involucra el uso de reglas adicionales. Por el contrario, syntax se emplea para definir reglas que derivan en otras reglas, como sucede por ejemplo con la siguiente:

syntax Primeros = kwPrimeros Entry+;

 

En esta regla además hacemos uso de un signo de repetición en nuestra gramática. Disponemos de 3 símbolos para representar repeticiones en MGrammar: + (1 o más repeticiones), * (0 o más repeticiones) y ? (0 o 1 repetición).

Por otra parte, también hacemos uso de la siguiente instrucción para definir aquellas secuencias de caracteres que pueden ser ignoradas a la hora de procesar nuestra entrada (en este caso, espacios, retornos de carro y saltos de línea).

interleave Skippable = " " | "r" | "n";

 

Guardaremos nuestra gramática con el nombre MenuDSL.mg y crearemos un fichero input.txt conteniendo nuestro menú del día expresado tal cual hacíamos al principio del post:

Primeros
* Gazpacho Andaluz.
* Ensalada Mediterránea.
* Consomé de Pollo.
Segundos
* Solomillo a la Plancha.
* Merluza a la Romana.
* Macarrones Bolognesa.
* Paella Alicantina.
Postres
* Flan.
* Arroz con leche.
* Macedonia de frutas.
* Tarta de queso.
Vino

Una vez tenemos definida nuestra gramática y nuestra entrada, vamos a hacer uso de un Modo especial de Intellipad, denominado MGrammar Mode, el cual nos permitirá visualizar cuatro paneles al mismo tiempo en nuestro IDE.

image

Al seleccionar MGrammar Mode, observaremos que aparece una nueva pestaña en nuestro IDE, denominada MGrammar Mode. Si pinchamos en ella podremos seleccionar la opción Tree Preview, que nos permitirá visualizar el grafo generado por nuestra gramática tras procesar el archivo de entrada.

image

Intellipad abrirá una ventana de selección de archivo de entrada para nuestra gramática. Seleccionaremos nuestro archivo input.txt y, si hemos hecho todo bien, obtendremos la siguiente vista.

image

Esta vista se actualiza de forma instantánea, de modo que podemos modificar en el panel de la izquierda la entrada (crear nuevos platos, etc) o incluso nuestras reglas gramaticales, y observaríamos automáticamente el resultado generado en forma de árbol en el panel de la derecha. Eventualmente, si introducimos algún elemento no soportado por nuestra gramática, el panel inferior nos ofrecerá una descripción del error, tal cual vemos en la siguiente captura en el supuesto de que introduzcamos una serie de “Aperitivos” al principio de nuestro menú.

image

Si añadimos un nuevo Token y una nueva regla a nuestra gramática para soportar la inclusión de aperitivos en nuestro menú, los errores desaparecerían y la representación en forma de grafo sería generada sin necesidad de guardar o refrescar ninguno de los paneles.

Estas serían las dos líneas a añadir:

token kwAperitivos = "Aperitivos";
syntax Aperitivos = kwAperitivos Entry+;

Por último, deberemos añadir a nuestra regla principal el elemento Aperitivos al comienzo de la misma, para indicar que el elemento inicial del Menú pertenece a este grupo.

syntax Main = Aperitivos Primeros Segundos Postres Extras;

De este modo, hemos creado una gramática que nos permite procesar el menú del día escrito por nuestro querido camarero (quien jamás en su vida ha picado una línea de código) en una representación en forma de grafo, generada en base a una serie de reglas definidas mediante el uso de MGrammar. Para todos aquellos que lo estén pensando ahora mismo: la creación de gramáticas no es algo nuevo ni mucho menos. Sin embargo, Oslo proporciona una serie de valores añadidos al proceso para la creación de gramáticas implementado por otras tecnologías hasta ahora:

  1. IDE Textual que facilita significativamente la tarea para la creación y procesamiento de gramáticas: Intellipad.
  2. Una traducción automática a MGraph de nuestro resultado, que puede ser fácilmente empleada desde diferentes entornos de ejecución.
  3. Una API para .Net que nos permite incorporar nuestras gramáticas a entornos de ejecución .Net, tal cual veremos en el próximo capítulo de esta serie.
  4. Una herramienta gráfica para la creación de modelos (Quadrant).

¡Y todo ello en menos de 30 líneas de código!

Un comentario en “Introducción a MGrammar: Crear una gramática para reconocer “Menús del día” (parte 1 de 2)”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *