]> git.llucax.com Git - personal/documentos.git/blob - taller/ejercicios/ej1-20082/ej1.rst
Enunciado e implementación de referencia del ejercicio 1 de 2008-2
[personal/documentos.git] / taller / ejercicios / ej1-20082 / ej1.rst
1 .. vim: set encoding=utf-8 filetype=rst sw=2 sts=2 et tw=74 :
2
3
4 ==========================================================================
5                      Taller de Programación I (75.42)
6 ==========================================================================
7
8
9 --------------------------------------------------------------------------
10                    Ejercicio 1 - Mini Protocol Buffers
11 --------------------------------------------------------------------------
12
13
14
15 Objetivo
16 ==========================================================================
17
18 Repasar conceptos vistos en materias anteriores (algoritmos, estructuras
19 de datos, manejo de memoria, manipulación de archivos, el lenguaje de
20 programación C).
21
22
23 Introducción
24 ==========================================================================
25
26 Hay infinidad de `formatos para representación de datos`__. Según
27 para que se use, las técnicas varían drásticamente. No es lo mismo
28 la representación que se utiliza para persistir en disco que para
29 enviar datos por una red.  Incluso las formas de representación pueden
30 variar para distintos tipos de redes o para distintos medios físicos
31 (no es lo mismo un disco *flash* que un disco rígido magnético
32 convencional). Tampoco es lo mismo representar datos para que sean
33 interpretados solo por la computadora que si tuviera que poder verlos
34 directamente un usuario. La representación varía también con la
35 estructura de los datos a representar también.
36
37 __ http://es.wikipedia.org/wiki/Categoría:Protocolos_y_formatos_de_nivel_de_presentación
38
39 Un desafío importante es construir formatos que sirvan de forma genérica
40 para representar cualquier tipo de dato, definiendo previamente un
41 *esquema*. Un formato extremadamente popular que hace esto es XML__.
42 Existen numerosas implementaciones de `parsers`__ XML que permiten
43 interpretar cualquier tipo de dato dado un `esquema`__ determinado.
44
45 __ http://es.wikipedia.org/wiki/XML
46 __ http://es.wikipedia.org/wiki/Parser
47 __ http://es.wikipedia.org/wiki/DTD
48
49 Sin embargo XML tiene varias contras y no siempre es conveniente su uso (a
50 pesar de que en la industria nadie parezca darse cuenta =), por ejemplo:
51
52 * No es posible representar datos binarios sin *intervención* del usuario
53   (hay que elegir una codificación *externa* para representar datos
54   binario).
55 * Es muy ineficiente en cuanto a espacio.
56 * Es relativamente lento de interpretar.
57
58 Un formato que resuelve estas desventajas (pero que, por supuesto,
59 tiene otras) es el propuesto por `Protocol Buffers`__, un proyecto que,
60 a pesar de su temprana edad, está teniendo mucha adopción (probablemente
61 por estar auspiciado por Google__). Se trata de un formato binario, muy
62 eficiente en términos de espacio y muy fácil y veloz para interpretar.
63
64 __ http://code.google.com/p/protobuf/
65 __ http://www.google.com/
66
67 El formato permite representar pares (clave, valor), donde la clave
68 es un valor numérico siempre y el valor puede ser de varios tipos:
69 enteros variables o fijos, con signo o sin signo, cadenas de bytes. A
70 su vez, puede dársele sentido especial a una cadena de bytes para que
71 represente otro set de pares (clave, valor), formando lo que se conoce
72 como un *mensaje embebido*, pudiendo construirse mensajes (estructuras
73 de datos) de una complejidad considerable.
74
75
76 Desarrollo
77 ==========================================================================
78
79 Se pide desarrollar un intérprete genérico, pero reducido, de Protocol
80 Buffers (PB). El programa debe ser capaz de interpretar un archivo
81 binario con formato de PB (base de datos) y, a partir de otro archivo
82 (*esquema*), imprimir los datos de forma legible para el usuario.
83
84 .. figure:: esquema.eps
85   :alt: Esquema de flujo de datos
86   :align: center
87
88   Esquema de flujo de datos
89
90
91 Archivo de esquemas
92 ==========================================================================
93
94 El archivo de esquemas solicitado en este trabajo es considerablemente
95 más simple que los archivos de `definiciones`__ que utiliza PB.
96
97 __ http://code.google.com/apis/protocolbuffers/docs/proto.html
98
99
100 Léxico
101 --------------------------------------------------------------------------
102
103 Los siguientes *tokens* (especificados en ABNF_) pueden aparecer en
104 el archivo::
105
106   array = identifier "*"
107   identifier = ALPHA *(ALPHA / DIGIT)
108   eol = LF
109   sp = *WSP
110
111 .. _ABNF: http://en.wikipedia.org/wiki/Augmented_Backus–Naur_Form
112
113
114 Sintaxis
115 --------------------------------------------------------------------------
116
117 La sintaxis del archivo, tomando como base los *tokens* especificados en
118 la sección anterior), es la siguiente (también especificada en ABNF_)::
119
120   scheme = *eol *message
121   message = sp identifier sp eol *attribute eol *eol
122   attribute = sp (identifier / array) WSP sp identifier sp eol
123
124
125 Semántica
126 --------------------------------------------------------------------------
127
128 Un archivo de esquema está compuesto por 0 o más mensajes (regla
129 *message*), separados entre sí por una línea vacía. Un mensaje está
130 representado por un nombre (*identifier*) seguido de 0 o más atributos
131 (regla *attribute*), separados por un fin de línea. Un atributo a su vez,
132 se compone de un tipo (*identifier*) y un nombre (*identifier*). Un tipo
133 a su vez puede ser simple (*identifier*) o un arreglo (*array*), en cuyo
134 caso se representa con el nombre del tipo terminado con "*". Existen
135 2 tipos básicos: *int* y *string*. El resto de los tipos deben estar
136 definidos en el archivo en cualquier orden.
137
138 Cada atributo tiene asociado un *tag* (en el sentido dado por PB)
139 implícito incremental, comenzando por 1. Dada la definición::
140
141   Persona
142     string nombre
143     string telefono
144     int edad
145
146 El atributo ``nombre`` tiene el *tag* **1**, ``telefono`` el **2** y
147 ``edad`` el **3**.
148
149 Todos los campos son *requeridos*, excepto aquellos que tengan como tipo
150 un *array*, que serían *repetidos* (en el sentido dado por PB).
151
152
153 Ejemplo
154 --------------------------------------------------------------------------
155
156 Vemos como separar en estas 3 etapas la interpretación del siguiente
157 archivo de esquema::
158
159   Agenda
160     Persona* personas
161
162
163   Persona
164     string nombre
165     int edad
166     Telefono telefono
167
168   Telefono
169     string tipo
170     int numero
171
172 El primer paso es el análisis léxico: separar en *tokens*. Siguiendo lo
173 propuesto en la sección Léxico_, podemos ir viendo qué *tokens*
174 encontramos en el archivo (los "_" representan espacios en blanco):
175
176 ============= ===============
177 Contenido     Tipo de *token*
178 ============= ===============
179 "Agenda"      identifier
180 "\n"          eol
181 "__"          sp
182 "Persona*"    array
183 "_"           sp
184 "personas"    identifier
185 "\n"          eol
186 "\n"          eol
187 "\n"          eol
188 "Persona"     identifier
189 "\n"          eol
190 "__"          sp
191 "string"      identifier
192 etc.          etc.
193 ============= ===============
194
195 Por ejemplo, si en el archivo apareciera::
196
197   Agenda
198     Persona* 1persona
199
200 Esto no sería válido léxicamente, ``1persona`` no es un *token* posible.
201
202 Luego (en realidad generalmente se hace a medida que se van obteniendo los
203 *tokens*), hay que realizar el análisis sintáctico. Es decir, validar que
204 estos *tokens* encontrados, tengan el orden correcto y formen
205 construcciones sintácticas válidas. Para esto aplicamos las reglas de la
206 sección Sintaxis_.
207
208 Un esquema es una *lista* de mensajes, por lo tanto buscamos un mensaje,
209 que se compone de un identificador, una nueva línea y una lista de
210 atributos (vamos verificando que los *tokens* lleguen en el orden
211 correcto). Un atributo a su vez, es un identificador o array seguido de
212 otro identificador.
213
214 Con esta información podemos ir armando una representación estructurada
215 del archivo en memoria (conocida como AST__ o árbol abstracto de sintaxis)
216 que nos servirá para el próximo paso y para la decodificación de PB.
217
218 __ http://en.wikipedia.org/wiki/Abstract_syntax_tree
219
220 Si en el archivo apareciera::
221
222   Agenda
223     Persona* personas no_valido
224
225 Esto sería léxicamente válido (todos los *tokens* son conocidos) pero
226 sintácticamente inválido, un atributo espera que luego del segundo
227 identificador solo hayan espacios en blanco o un fin de línea (y
228 encontramos otro identificador: ``no_valido``).
229
230 Finalmente, una vez construído el AST, queda hacer el análisis semántico.
231 Es decir, el chequeo de tipos. Hay que verificar que todos los atributos
232 de todos los mensajes sean de un tipo conocido.
233
234 Si en el archivo de esquema tuvieramos la siguiente definición de
235 ``Agenda`` en vez de la original::
236
237   Agenda
238     NoExisto* personas
239
240 Esto sería léxicamente válido, sintácticamente válido, pero semánticamente
241 inválido (el tipo NoExisto no está definido).
242
243
244 Codificación de Protocol Buffers
245 ==========================================================================
246
247 A continuación se explica un *subset* de la `codificación usada por
248 Protocol Buffers`__, que es lo que debe poder interpretar la aplicación
249 a desarrollar. La explicación está basada en la documentación de
250 Protocol Buffers, de manera tal de utilizar los mismos ejemplos para
251 que sea sencillo para el alumno ampliar los conceptos visitando la
252 fuente original.
253
254 __ http://code.google.com/apis/protocolbuffers/docs/encoding.html
255
256
257 *Varints* de base 128
258 --------------------------------------------------------------------------
259
260 Antes que nada es necesario entender un concepto básico para la
261 codificación de PB, *varints*.
262
263 *Varints* es un método de serialización de enteros que utiliza uno o más
264 bytes. Números pequeños utilizan pequeñas cantidades de bytes.
265
266 Cada byte en un *varint*, excepto el último byte, tiene el bit más
267 significativo (msb__) activado; esto indica que hay más bytes. Los 7 bits
268 más *bajos* de cada byte son utilizados para guardar, en representación de
269 complemento a 2, el número en grupos de 7 bits, estando el grupo menos
270 significativo primero.
271
272 __ http://es.wikipedia.org/wiki/Bit_m%C3%A1s_significativo
273
274 Por ejemplo, esta es la representación del número **1**::
275
276   0000 0001
277   ^
278   msb
279
280 Se puede representar en un solo byte, por lo tanto el msb está
281 desactivado. El número **130**, levemente más complicado, se representa
282 de la siguiente forma::
283
284   1000 0010  0000 0001
285   ^          ^
286   msb       msb
287
288 ¿Cómo saber que es el número **130**? Primero se descarta el msb de cada
289 byte, ya que sólo nos indica si se ha encontrado el fin de la
290 representación del número (como se puede ver, está activo solo en el
291 primer byte ya que hay más de un byte en este *varint*)::
292
293   1000 0010  0000 0001
294     → 000 0010  000 0001
295
296 Luego hay que invertir los dos grupos de 7 bits porque, como recuedan, los
297 *varints* guardan el grupo menos significativo primero::
298
299   000 0010  000 0001
300     → 000 0001  000 0010
301
302 Finalmente, se concatenan para obtener el resultado final::
303
304   000 0001  000 0010
305     →  1000 0010
306
307 Cuyo valor es **130**.
308
309 .. note::
310
311   Si bien un *varint* no tiene una limitación de tamaño o longitud,
312   en este trabajo se asumirá un tamaño máximo de 9 bytes (es decir,
313   se podrán representar enteros de 9*7 = 63 bits, es decir, tendrán
314   un valor máximo de 2^63-1 = 9.223.372.036.854.775.807), garantizando
315   de esta manera que cualquier varint entre en un entero de 64 bits
316   (por ejemplo, el tipo ``uint64_t`` de C99).
317
318
319 Estructura de un mensaje
320 --------------------------------------------------------------------------
321
322 Como saben, un Protocol Buffer es una serie de pares (clave, valor). La
323 versión binaria de un mensaje utiliza el *tag* del atributo como clave
324 (el nombre y tipo de cada atributo solo puede conocerse con precisión
325 utilizando el esquema del mensaje).
326
327 Cuando un mensaje es codificado, las claves y valores son concatenados en
328 un flujo de bytes. Las claves se componen en realidad de 2 valores, el
329 *tag* asociado al atributo y un *wire type*, que provee la información
330 necesaria para saber el tamaño del valor que acompaña a la clave.
331
332 Protocol Buffers soporta varios *wire types* pero en este ejercicio solo
333 deberán reconocerse los siguientes:
334
335 ======== ========================== ======================================
336 Tipo     Significado                Utilizado para
337 ======== ========================== ======================================
338 0        *varint*                   *int*
339 2        delimitado por longitud    *string*, mensajes embebidos
340 ======== ========================== ======================================
341
342 Cada clave de un mensaje es un *varint* con el valor::
343
344   (tag << 3) | wire_type
345
346 En otras palabras, los últimos 3 bits guardan el *wire type*.
347
348 Ante un *wire type* desconocido deberá terminarse la ejecución del
349 programa con el código de error apropiado (ver `Valores de retorno`_).
350
351
352 Valores
353 --------------------------------------------------------------------------
354
355 Luego de una clave, viene un valor. Dependiendo del *wire type*, la
356 codificación de los valores varía.
357
358 *Varint*
359 ~~~~~~~~
360
361 La representación de un *varint* (*wire type* 0) es, como deben
362 imaginarse, un *varint*. En esta implementación limitada de Protocol
363 Buffers solamente el tipo básico *int* es representado con un *varint*.
364
365 Delimitado por longitud
366 ~~~~~~~~~~~~~~~~~~~~~~~
367
368 La representación de valor delimitado por tamaño  (*wire type* 2) se
369 compone del largo del valor codificado como *varint* seguido por la
370 cantidad especificada de bytes de datos.
371
372 Por ejemplo, un *string* con valor "testing", se representaría de la
373 siguiente forma (sólo se muestra el valor, se omite la clave)::
374
375   0x07     0x74 0x65 0x73 0x74 0x69 0x6e 0x67
376   ^^^^     ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
377   longitud  "t"  "e"  "s"  "t"  "i"  "n"  "g"
378
379 Tanto los atributos de tipo *string* como lo mensajes embebidos se
380 representan con valores delimitados por tamaño.
381
382 La única forma de distinguir si un valor es un *string* o un mensaje
383 embebido, es utilizando el esquema del mensaje.
384
385
386 Ejemplo de mensaje sencillo
387 --------------------------------------------------------------------------
388
389 Ahora que sabemos como se codifican los *varints*, las claves y los
390 valores, podemos ver un ejemplo de un mensaje simple basado en el
391 siguiente esquema::
392
393   Test1
394     int a
395
396 La representación de un mensaje de tipo *Test1*, cuyo valor de *a* sea
397 **150**, es la siguiente::
398
399   0x08 0x96 0x01  =  0000 1000  1001 0110  0000 0001
400
401 Como vimos, un mensaje son pares (clave, valor), por lo tanto sabemos que
402 lo primero que hay que interpretar es una clave, que a su vez, sabemos
403 que se representa con un *varint*. Vemos que el primer byte tiene el
404 msb desactivado, así que el *varint* ocupa un solo byte: 0x08 = 0000 1000
405
406 Sabemos que los último 3 bits de una clave, son el *wire type*, por lo
407 tanto en este caso es 000: 0, lo que nos indica que el valor que seguirá
408 a la clave es otro *varint*. Los restantes 4 bits (desplazados hacia la
409 derecha 3 lugares, los lugares que ocupaban los bits del *wire type*)
410 sabemos que almacenan el *tag* del atributo, en este caso 0001 = 1::
411
412   0x08 = 0000 1000
413          ^-----~~~
414          |  |   wire type = 0
415          |  |
416          | tag = 1
417          |
418         msb = 0
419
420 Ahora decodificamos el valor, que ya sabemos que es un *varint*::
421
422   0x96 0x01 = 1001 0110  0000 0001
423               ^          ^
424              msb        msb
425
426 Eliminamos msb, invertimos grupos de 7 bits y concatenamos::
427
428   001 0110  000 0001
429     → 000 0001  001 0110
430     → 1001 0110 = 0x96
431
432 El resultado es 150. Ahora recordando el esquema del mensaje, observamos
433 que ``int a`` es el primer atributo del mensaje ``Test1``, por lo tanto le
434 corresponde el *tag* **1**. Luego, hemos decodificado un mensaje ``Test1``
435 cuyo atributo ``a`` tiene el valor **150**.
436
437 Si interpretáramos *tag* para los cuales el mensaje no tuviera un atributo
438 asociado, simplemente ignoramos ese par (clave, valor) y continuamos
439 leyendo los siguientes.
440
441
442 Ejemplo de mensaje con *arrays*
443 --------------------------------------------------------------------------
444
445 Supongamos que agregamos a nuestro esquema anterior el siguiente mensaje::
446
447   Test2
448     int* b
449
450 Si un mensaje de tipo ``Test2`` tuviera como atributo ``b`` dos enteros:
451 **150** y **1**, su representación sería::
452
453   0x08 0x96 0x01 0x08 0x01
454   ---- ^^^^^^^^^ ---- ^^^^
455    |       |      |     un varint = 1
456    |       |      |
457    |       |      `-- clave, nuevamente tag = 1, wire type = 0
458    |       |
459    |   un varint = 150, igual que en el ejemplo anterior
460    |
461   0000 1010 = 0000 1000 <---- clave
462               ^-----~~~
463               |  |   wire type = 0
464               |  |
465               | tag = 1
466               |
467              msb = 0
468
469 Como puede observarse, simplemente se repite el *tag* varias veces. Y los
470 valores se agregan al *array* del atributo que corresponde a ese *tag*.
471
472
473 Ejemplo de mensaje compuesto
474 --------------------------------------------------------------------------
475
476 Supongamos que agregamos a nuestro esquema anterior el siguiente mensaje::
477
478   Test3
479     Test1 c
480
481 Si un mensaje de tipo ``Test3`` tuviera como atributo ``c`` un mensaje
482 de tipo ``Test1`` cuyo atributo ``a`` fuera **150**, su representación
483 sería::
484
485   0x0a 0x03 0x08 0x96 0x01
486   ---- ~~~~ ^^^^^^^^^^^^^^
487    |    |     igual que el ejemplo del mensaje sencillo
488    |    |
489    |   longitud del mensaje embebido como varint = 3
490    |
491   0000 1010 = 0000 1010 <---- clave
492               ^-----~~~
493               |  |   wire type = 2
494               |  |
495               | tag = 1
496               |
497              msb = 0
498
499 El *wire type* es de tipo *delimitado por longitud*, el *tag* es **1**
500 (``c`` es el primer atributo del mensaje ``Test3``) y el valor de ``c`` es
501 0x08 0x96 0x01, que coincide con la representación de un mensaje de tipo
502 ``Test1`` cuyo valor para el atributo ``a`` sea **150** sin estar embebido
503 en otro mensaje.
504
505
506 Salida
507 ==========================================================================
508
509 La salida del programa debe tener la siguiente estructura.
510
511 Cada mensaje debe ser impreso por pantalla con el siguiente formato::
512
513   NombreMensaje
514     atributo1: valor
515     atributo2: valor
516     ... etc.
517
518 Los atributos deben tener una sangría de 2 espacios y deben estar
519 ordenados por número de *tag* de forma incremental.
520
521 Valores de tipo *int* se imprimen en formato decimal, por ejemplo::
522
523   Persona
524     edad: 39
525
526 Valores de tipo *string* se imprimen entre comillas dobles ``"``,
527 reemplazando las comillas literales ``"`` por ``\"`` y las ``\``
528 literales por ``\\``. Por ejemplo::
529
530   Persona
531     nombre: "Thom Yorke"
532     descripcion: "Cantante
533   de \"Radiohead\"
534   \\x es x"
535     edad: 39
536
537 Siendo la *descripción* original::
538
539   Cantante
540   de "Radiohead"
541   \x es x
542
543 Los valores de tipo *array* deben representarse como si fueran mensajes,
544 simulando que su nombre fuera "array" y cada ítem fuera un atributo
545 con clave numérica, igual al índice del elemento en el *array*. Por
546 ejemplo, un *array* de 3 *strings* se representaría así::
547
548   Persona
549     telefonos: array
550       0: "555-4444"
551       1: "999-1123"
552       2: "113"
553
554 Los valores de tipo *mensaje* serán impresos de la misma manera que un mensaje
555 regular. Con la sola excepción de que se deberá incrementar el nivel de
556 sangría por cada nivel de anidamiento. Por ejemplo::
557
558   Persona
559     nombre: "Thom Yorke"
560     edad: 39
561     telefonos: array
562       0: Telefono
563         lugar: "Casa"
564         numero: "555-4444"
565       1: Telefono
566         lugar: "Emergencia"
567         numero: "911"
568     direccion: Direccion
569       calle: "Inglaterra 3452"
570       piso: 1
571       departamento: "B"
572
573
574 Invocación
575 ==========================================================================
576
577 El programa debe ser invocado de la siguiente forma::
578
579   ./ej1 esquema mensaje [pb]
580
581 Dónde:
582
583 esquema
584   Archivo con el esquema de los datos (obligatorio)
585 mensaje
586   Mensaje (que debe estar definido dentro del esquema) que va a
587   interpretarse
588 pb
589   Archivo con **1** mensaje de tipo ``mensaje`` codificado en formato PB
590   (opcional, si no se especifica, se toma la base de datos de la entrada
591   estándar *stdin*)
592
593 El resultado debe ser impreso por la salida estándar (*stdout*).
594
595
596 Ejemplos de invocación
597 --------------------------------------------------------------------------
598
599 ::
600
601   ./ej1 un_esquema UnMensaje una_db > salida
602
603 Lee los datos del archivo ``una_db``, el esquema del archivo
604 ``un_esquema``, interpreta un mensaje de tipo ``UnMensaje`` e imprime
605 la salida en el archivo ``salida``.
606
607 ::
608
609   ./ej1 un_esquema OtroMensaje < otra_db
610
611 Lee los datos del archivo ``otra_db``, el esquema del archivo
612 ``un_esquema``, interpreta un mensaje de tipo ``OtroMensaje`` e imprime
613 la salida por *stdout*.
614
615
616 Valores de retorno
617 ==========================================================================
618
619 A continuación se presenta una tabla con los códigos de retorno que el
620 programa debe devolver ante las distintas situaciones:
621
622 ====== ===================================================================
623 Código Situación
624 ====== ===================================================================
625 0      Ejecución exitosa
626 1      Parámetros de invocación incorrectos
627 2      Error al abrir archivo de esquema
628 3      Error al abrir archivo de PB
629 4      Error léxico en el archivo de de esquema
630 5      Error sintáctico en el archivo de de esquema
631 6      Error semántico en el archivo de de esquema
632 7      No se encontró el mensaje a interpretar en el archivo de esquema
633 8      Error al decodificar archivo de PB
634 9      Otros errores
635 ====== ===================================================================
636
637
638 Restricciones de diseño
639 ==========================================================================
640
641 * El ejercicio debe realizarse utilizando `ISO C99`__.
642 * El programa debe estar correctamente modularizado, separando claramente
643   la interpretación de archivos de esquema de la interpretación de
644   archivos de PB y la salida.
645 * La interpretación de archivos de esquema debe estar bien dividida en
646   `análisis léxico`__, `análisis sintáctico`__ y `análisis
647   semántico`__.
648 * La decodificación de PB debe estar también bien validada, saliendo con
649   el código de error correspondiente ante cualquier situación inesperada
650   (*tag* inválido, fin de archivo prematuro, *wire type* desconocido,
651   etc.).
652 * No puede asumirse un tamaño máximo de línea ni de identificadores a
653   leer, ni de cantidad de atributos en un mensaje ni de cantidad de
654   mensajes en un esquema.
655 * Puede asumirse que los *tags* vienen ordenados en un archivo de PB. Es
656   decir, que primero viene el atributo con *tag* **1**, luego el de
657   *tag* **2**, etc. Si el *tag* **3** fuera de tipo *array*, todos los
658   *tags* **3** vendrán juntos, y después que el **2** y antes que el
659   **4**.
660 * Puede asumirse que van a estar presentes todos los atributos, excepto
661   los de tipo *array* que podrían no tener elementos (por lo tanto no
662   existir el *tag* correspondiente en el archivo de PB).
663 * Se permite (y recomienda) ir imprimiendo la salida a medida que se
664   interpreta el archivo de PB.
665
666 __ http://es.wikipedia.org/wiki/Lenguaje_de_programaci%C3%B3n_C#C99
667 __ http://es.wikipedia.org/wiki/Analizador_l%C3%A9xico
668 __ http://es.wikipedia.org/wiki/Analizador_sint%C3%A1ctico
669 __ http://arantxa.ii.uam.es/~alfonsec/docs/compila5.htm
670