YACAMLQT (Yet another CAML query tool) Redux (1)

Hace un mes, John Holliday, me pidió a ver si podíamos integrar su CAML.net con mi YACAMLQT, a la vez que ampliar mi herramienta para soportar otro tipo de consultas como adds y updates.

YACAMLQT, es una utilidad que convierte una sentencia SQL en CAML, el lenguaje de consulta de Sharepoint. (Véase YACAMLQT, YACAMLQT2 y YACAMLQT-CAML.Net)

Estos días entre rato bueno y rato malo, he reescrito totalmente el código de YACAMLQT, para hacerlo más sencillo (por supuesto usando TDD), en principio el objetivo ha sido emular el antiguo YACAMLQT, pero dotándolo de un diseño más sencillo y ampliable.

YACAMLQT, es un programa que convierte una sintaxis tipo SQL en CAML (el lenguaje de consulta de SharePoint).

Es decir esto:

WHERE ((Column1 = «Value1») AND (Column2 = «Value2»)) OR ((Column3 = 10)
AND (Column3 <> NULL)) GROUPBY Column1 ORDERBY Column1, Column2 ASC, Column3 DESC

En esto:

<Query>
  <Where>
    <Or>
      <And>
        <Eq>
          <FieldRef Name="Column1" />
          <Value Type="Text">Value1</Value>
        </Eq>
        <Eq>
          <FieldRef Name="Column2" />
          <Value Type="Text">Value2</Value>
        </Eq>
      </And>
      <And>
        <Eq>
          <FieldRef Name="Column3" />
          <Value Type="Integer">10</Value>
        </Eq>
        <IsNotNull>
          <FieldRef Name="Column3" />
        </IsNotNull>
      </And>
    </Or>
  </Where>
  <GroupBy>
    <FieldRef Name="Column1" />
  </GroupBy>
  <OrderBy>
    <FieldRef Name="Column1" />
    <FieldRef Name="Column2" Ascending="True" />
    <FieldRef Name="Column3" Ascending="False" />
  </OrderBy>
</Query>

El proyecto completo lo subiré a CodePlex en unos días, con su código fuente que poco a poco y con ayuda espero ir ampliando.

Por si alguien quiere echar una mano en el proyecto, voy a contar alguno de los entresijos del diseño de la aplicación.

Lo primero que he diseñado es el analizador morfológico (lexer) que se encarga de identificar dentro de un string, las distintas unidades sintácticas (tokens) con las que construiremos un analizador sintáctico y semántico (parser) con el cual construiremos un árbol sintáctico (AST) que por último recorreremos para transformar el SQL en CAML u otra variante como CAML.Net.

El lexer, está compuesto por una clase base, ScannerBase que contiene las partes más básicas del lexer, he extraído esta clase base una vez que tenía el analizador morfológico completo ya que de esta manera podemos realizar otro tipo de analizadores.

Las funciones básicas como comerse los espacios (EatSpaces()), detectar si es el final de línea (EndOfLine()) , saltar caracteres (SkipChar()) son parte de ScannerBase. Lo más importante de esta clase es la propiedad CurrentChar que devuelve el último carácter leído y el método GetCharMoveNext(), que obtiene un carácter y se mueve a la siguiente posición.

Para los que habéis usado el unix flex, GetCharMoveNext() es similar a input().

Esta clase ScannerBase utiliza internamente una clase ScannerState que mantiene el estado para poder releer un token ó unidad sintáctica.

Los tokens en el caso de YACAMLQT, consisten en las palabras reservadas propias de SQL, así como los distintos operadores, los campos y los valores (cadena, fecha, lógico y numérico).

A diferencia del unix flex, esto no se trata de un unput(), ya que mediante este volveríamos al carácter anterior. En este caso, como lo importante del analizador es obtener una unidad sintáctica, o token, lo que he hecho es implementar un método llamado BackToken() que lo que hace es posicionar el lexer justo al comienzo del último token obtenido, de modo que GetToken() volverá a devolvernos el mismo token.

La clase Scanner, hereda como es de suponer de ScannerBase, e implementa GetToken(), el responsable de de devolver un token, y CheckCorrectBracketsAndQuotes() que es el responsable de comprobar que los paréntesis y las comillas están correctamente.

Dentro de la clase Scanner, el método GetToken() es el responsable de identificar cada uno de los tokens, para lo cual he realizado un método para identificar cada uno de los distintos tokens. GetToken() usa los métodos ScanDate(), ScanString(), ScanOperator(), ScanNumber() y ScanReservedWordOrSymbol().

ScanOperator() y ScanReservedWordOrSymbol() usan un diccionario para identificar los distintos operadores y palabras reservadas. En el caso de ScanReservedWordOrSymbol(), si el token leído no se encuentra en el diccionario de palabras reservadas estaremos identificando un símbolo ó identificador.

Para terminar con esta primera parte, y siguiendo el principio de responsabilidad única, se ha implementado la clase Token como un contenedor y TokenFactory como una factória encargada de crear los distintos tipos de tokens ó unidades sintácticas. La clase Scanner es la que usa la factória para crear los distintos Tokens.

La clase Token, puede contener los distintos tipos de tokens, en principio todos aunque pueden ser de diferentes tipos, mantendrán su valor como un string. El resto de propiedades para identificar el tipo de token (TType) ó el tipo de valor (ValueType) son una enumeración.

Mi particular visión del Test Driven Development (TDD)

Cuando nos enfrentamos al diseño de un programa sea el que sea, partimos de un estado que podemos llamar “A” problema, y como es obvio un estado “B” en el que tenemos resuelto el problema, por medio de un programa.

Si la programación fuera como las matemáticas, cosa que “no es” aunque  se fundamente en ello; la solución ideal sería la línea recta. Una línea recta que nos lleva del punto “A” al punto “B”, directamente, sin rodeos.

Es la solución más ELEGANTE, por qué es la solución más CLARA y BREVE que se puede dar.

Hay partes de la programación en donde existen uno o varios algoritmos que podemos usar y que son como la línea recta es decir ya están optimizados y no hay manera de mejorar (algoritmos de ordenación, búsqueda, etc..) pero cuando hablamos de un sistema de mayor tamaño, donde se ven involucrados más componentes la cosa cambia.

Existen miles de maneras de llegar de “A” a “B”, podemos hacer tirabuzones, hipérbolas, curvas mágicas y un sinfín de figuras geométricas que nos llevarán también de “A” a “B”. Los programadores somos capaces de crear miles y miles de esas formas mágicas. (Es nuestra naturaleza, como le dijo el escorpión a la rana)

De modo que nos enfrentamos a un problema doble, llegar del punto “A”, problema, al punto “B”, programa  sin morir en el intento. Y hacerlo de la manera más ELEGANTE.

No hace mucho, hablaba Rodrigo, del “Principio KISS” y del “Divide y Vencerás”, este último es sin duda la práctica que seguimos todos los programadores desde que tenemos conocimiento de nosotros mismos, es decir de que somos eso “Programadores”.
 
Debemos resolver un problema, es decir crear un programa “B”, que resuelva “A”, y hay miles de soluciones ó caminos posibles que nos llevarán de “A” a “B”.

Nosotros no disponemos de un algoritmo determinado, de una solución magistral como la ecuación de la recta que pasa por dos puntos, cada programador es un mundo y su percepción tanto del problema como del modo de llegar a la solución (diseño) podemos decir que es casi única, cuya aproximación es inversamente proporcional a la complejidad del problema.

A un problema más sencillo, hay más posibilidades de que dos programadores sigan el mismo camino, a un problema más complejo la desviación entre las soluciones tiende a distar más.

No existe la certeza de que nuestra solución sea la más optima, clara y concisa y lo que es peor tampoco podemos medir desviación alguna, puesto que cuanto más complejo es el programa más variantes tiene y por ende para poder medir dicha desviación, deberíamos conocer la línea recta, cosa que a priori es imposible.

Partiendo como base de que la línea recta sería la solución “perfecta” del problema, tenemos una complicada tarea.

Pero por otro lado una línea no es más que una sucesión de puntos, de modo, que podemos interpretar cada punto como una parte de la solución, y aquí volvemos al “Divide y Vencerás”.

Gracias a que tenemos técnicas como el TDD o ATDD, podemos ir punto por punto trazando nuestra línea.

“Solo escribimos el código necesario para pasar la prueba”, esta frase que resume en esencia que es “Test Driven Development”, también es la mejor manera que conozco de ir punto por punto trazando la solución de “A” a “B”, siguiendo esa imaginaría línea recta que sería la solución más ELEGANTE, CLARA y BREVE que se puede dar.

Veneno en los dedos

En la vida de todo programador hay un “día especial”, algunos tendréis que hacer memoria, otros mucha memoria y algunos no lo recordareis pero es el día en que uno toma conciencia de sí mismo y se da cuenta de que es un “PROGRAMADOR”. (¡Qué fea palabra!… léase “CODER” please)

No estoy hablando de ser “programador”, si no de ser “PROGRAMADOR”. Me refiero al momento en que te das cuenta de que el código es importante, que eres una máquina de tirar líneas que buscas mejorar día a día y de que el veneno de los 1s y los 0s está dentro de ti, del momento en que te das cuenta que todo es programable, de que a la silla de ruedas de tu abuelo se le puede poner un motor y programarlo, de que la gameboy de tu primo lleva un Z80 y seguro que hay alguna forma de meterle mano y…..en fin, te das cuenta de que el teclado es una extensión de tus dedos.

Os contaré como fue mi momento.

Yo cacharreaba con ordenadores como muchos de vosotros, que si un ZX81, un Commodore 64, etc…,etc… Allá por el año 1989 (yo tenía 19 años), un amigo de mi cuadrilla (Iñaki) me propuso hacer un programa para la empresa en que trabajaba su padre, era una empresa de excavaciones pequeña, CAMES, que hacía cosas generalmente para empresas más grandes.

El programa demandado consistía en llevar un control de los partes de los empleados y las máquinas; había que controlar el número de horas que se empleaba en cada obra. Tuvimos algunas reuniones en la oficina de la empresa y me explicaron con todo detalle cómo se llevaban los partes manualmente y los controles que se debían hacer semanalmente y mensualmente, así como los trabajos que había que re-facturar a terceros.
Durante las reuniones tome un montón de notas tratando de no perder detalle de lo que querían que el programa hiciera exactamente.
Por aquel entonces, había aprendido algunos leguajes de programación, como Basic, Pascal, C, algo de Lisp y Prolog, y dBase3, para el asunto me decante por dBase3, no por nada especial, pero las librerías btreeve que usaba entonces en Basic y en C, daban bastante guerra. También pensé en la comodidad de que dBase contaba con campos de entrada que admitían validaciones sin mucho esfuerzo, así como poder examinar las tablas y ver cualquier detalle de los datos. Otro de los factores importantes en la decisión fueron los listados, ya que hacer listados con dBase era coser y cantar. En fin dBase era perfecto para hacerlo todo sin demasiado esfuerzo.

De modo que me encerré en casa e hice el programa; unos días más tarde recibí en mi casa (la de mis padres en realidad y en mi habitación más concretamente) a las personas a las que debía enseñar el programa. Estuvimos toda la tarde revisando las especificaciones y surgieron varias cosas. Al terminar, yo no estaba del todo satisfecho con el programa a pesar de que cumplía con las especificaciones salvo en algún pequeño detalle que surgió durante la demo.

Un amigo me hablo de Clipper el compilador de dBase, me dejo un manual y me dijo que sería mucho más profesional hacerlo con Clipper (87). Así que rehíce el programa completamente desde cero.

Finalmente entregué el programa y cobre 60.000 pesetas (un dineral en el año 89) y muy agradecido asesoré a la empresa también acerca del ordenador y la impresora que debían comprar.

Los primeros viernes de cada semana, momento en que se pagaban las horas extras a los empleados, yo tenía que bajar a la empresa exclusivamente por si surgía algún problema o alguno de los cálculos no salían correctamente. El argumento de “Benito”, el encargado,  era que los empleados (tíos muy curtidos de la obra y con carácter más bien duro) sabían mejor que nadie cuanto debían cobrar de modo que, por si acaso, sería mejor que yo estuviese allí en caso de que no coincidieran las cifras.

Lo estuvimos haciendo aproximadamente durante un mes hasta que lo dieron por válido, ya que, los cálculos siempre salían correctamente si se habían introducido bien los datos.

Ese fue mi primer programa de ordenador, y mis primeros ingresos como “programador”.

Aún así, todavía no era un programador.

Tiempo después, mientras estudiaba FP por las mañanas, entre a trabajar por las tardes en una empresa de programación. Ellos programaban en Clipper pero no muy bien, de modo que, les di algunas lecciones, suena chulesco lo sé, pero lo mejor de todo es que mi profesor de COBOL de FP, trabajaba allí por las tardes, de modo que, él por la mañana me daba COBOL y yo por la tarde le daba Clipper a él. Cosas de la vida.

Bueno, estando allí hice multitud de programas, facturaciones, contabilidad,  etc…etc …

Aún así, no era un programador de los que hablaba al principio, pero el momento llegó y lo recuerdo perfectamente.

No estaba satisfecho con algunas de las cosas repetitivas que hacíamos programando, así que, cree un meta lenguaje que nos ahorraba horas y horas de programación de esa repetitiva, lo hice en casa por las noches e incluso hice un manual para que todo el mundo en la empresa pudiese usarlo. Ese fue el momento que arruinó mi vida :-).

Ese momento en que no te basta con lo que los lenguajes te dan, cuando empiezas a crear bibliotecas de código, a optimizar en ensamblador rutinas lentas, cuando creas un sistema que es capaz de ahorrar multitud de horas de programación, cuando los días y las noches se funden en la pantalla tirando líneas de código, leyendo programas de otros para aprender más y más, cuando no puedes pasar sin programar algo, lo que sea aunque sea solo por auténtico ocio, por ver si eres capaz de hacerlo o por la razón que sea, que seguro que es lo suficientemente buena como excusa para teclear.

¿Cómo te entro a ti el veneno?