# Laboratorio de la semana 14: parser de expresiones de Mini

Esta semana hay **una sola sesión** de laboratorio, pensada para unos 90 minutos de trabajo. La otra es para la Práctica 4.
Los comandos se corren desde `code/week-14`. Las ideas están en los apuntes, en la sección "El laboratorio, paso a paso".

| Archivo | Qué es | ¿Lo cambian? |
|---|---|---|
| `expr.h`, `expr.c` | El módulo `Expr` de la semana 13, terminado, con las comparaciones agregadas | No |
| `env.h`, `env.c` | La tabla de variables | No |
| `lexer.h` | La interfaz del analizador léxico: `TokenKind`, `Token` y tres funciones | No |
| `lexer.c` | El analizador léxico, sin comparadores ni comentarios | **Sí** |
| `lexer_tests.c` | Sus pruebas con `assert` | **Sí** |
| `show_tokens.c` | Escribe los tokens de un texto | No |
| `parser.h` | La interfaz: `ParseError` y `parse_expression` | No |
| `parser.c` | El parser. `advance`, `parse_expr` y `parse_expression` ya están hechas | **Sí** |
| `parse_main.c` | Analiza una expresión, escribe el árbol y lo evalúa con `x` = 2 e `y` = 3 | No |

```sh
make lab                  # compila todo en build/lab/
make test-lab             # comprueba todo el laboratorio; falla hasta que terminen
```

`make test-lab` agrupa las pruebas por parte. Se puede correr un grupo solo:

```sh
LAB_DIR=lab python3 -m unittest test_lab.Lexer
```

Todo se compila con ASan y UBSan. Una fuga hace fallar la prueba, aunque el resultado sea correcto.

## Parte 1: el analizador léxico (unos 25 minutos)

Lean `lexer.h` y `lexer.c` completos, y luego `lexer_tests.c`. Respondan por escrito, sin ejecutar nada:

1. ¿Por qué `lexer_new` pide que el texto siga vivo? ¿Qué pasaría si se libera antes que el lexer?
2. ¿Qué tokens produce `1.5.2`? ¿Y `12abc`? Compruébenlo con `build/lab/show_tokens`.
3. ¿Qué devuelve `lexer_next` con un nombre de 100 letras? ¿Por qué no se escribe fuera de `text`?

Luego, en `lexer.c`:

1. Agreguen los comparadores `<`, `<=`, `>`, `>=`, `==` y `!=`. Hay que mirar un carácter más, y `=` ya no siempre es
   `TOKEN_ASSIGN`. Un `!` solo es un error.
2. Hagan que `skip_blanks` salte los comentarios: desde `#` hasta el final de la línea. El salto de línea no es parte del
   comentario, y una línea debe seguir contándose.
3. En `lexer_tests.c`, agreguen una prueba de los comparadores y otra de los comentarios.

```sh
make lab
build/lab/lexer_tests
build/lab/show_tokens 'x <= 1 # hola'
LAB_DIR=lab python3 -m unittest test_lab.Lexer
```

## Parte 2: el parser (unos 45 minutos)

Lean `parser.h` y `parser.c`. Las funciones `parse_*` están encadenadas, pero solo pasan el trabajo a la siguiente.
Escríbanlas en este orden, y prueben después de cada una:

1. `parse_primary`: un número, un nombre o una expresión entre paréntesis. Ya se puede probar `42`, `x` y `((x))`.
2. `parse_term` y `parse_sum`: con un ciclo. Prueben `1 + 2 * 3` y `1 - 2 - 3`.
3. `parse_unary`: `-x` se construye como `0 - x`.
4. `parse_compare`: un comparador como máximo. Por eso no lleva ciclo.

Cada función libera lo suyo antes de devolver `NULL`, y revisa lo que devuelven los constructores. Háganlo desde el
inicio, no al final. `expr_binary` toma posesión de sus hijos incluso si falla. Los apuntes tienen el mismo patrón en
`logic_parser.c`.

```sh
make lab
build/lab/parse_main '1 + 2 * 3'
build/lab/parse_main '(1 + 2) * x < -y'
LAB_DIR=lab python3 -m unittest test_lab.Trees
```

## Parte 3: errores (unos 20 minutos)

1. Escriban `syntax_error`: la línea y la columna del token, y el mensaje. Si el token es `TOKEN_ERROR`, el mensaje es el
   que trae el token.
2. Prueben entradas malas, y digan antes de correr en qué columna debe marcar:

   ```sh
   build/lab/parse_main '1 + * 2'
   build/lab/parse_main '(1 + 2'
   build/lab/parse_main '1 < 2 < 3'
   build/lab/parse_main '1 + @'
   ```

3. Dibujen qué está vivo cuando falla `1 + 2 * (3 +`. ¿Quién libera cada nodo?
4. Corran todo:

   ```sh
   make test-lab
   ```

Una prueba hace que cada `malloc`, uno por uno, devuelva `NULL`. Si falla, revisen qué pasa cuando un constructor devuelve
`NULL`: no es un error de sintaxis, pero también hay que llenar el mensaje.

**Si no alcanza el tiempo:** lo que falte lo terminan antes de la próxima clase. La semana 15 trae el parser hecho, pero
agrega `if`, llamadas e instrucciones sobre esta misma forma, así que conviene llegar con esto claro.

## Si sobra tiempo

1. Agreguen a `parse_main` un modo que escriba el árbol con sangría, un nodo por línea.
2. Prueben `build/lab/parse_main` con 100000 paréntesis abiertos. ¿Qué pasa, y por qué? Propongan un arreglo sin escribirlo.
3. ¿Qué tendría que cambiar para que `2 ^ 3 ^ 2` existiera y asociara por la derecha? Escriban primero la regla.
