8.12

Entrega 3🔗

Deadline: 20 de Octubre 2024

⚠ Importante

Recuerde las reglas del código de conducta.

Objetivos básicos de la entrega:

Objetivo extra (elegir uno de los dos):

La evaluación se hace con specs grading, (vea los detalles aquí):

1 Para empezar🔗

Para partir, pueden iniciar un nuevo branch en git con:

$ git checkout -b entrega3

⚠ Comentario

A partir de esta entrega, es opcional que hagan el merge con el repositorio de referencia cdi, porque resolver/debuggear los conflictor podría ser muy laborioso, especialmente si implementaron tags en su AST. Si deciden no hacer el merge, miren el código del nuevo parser y AST como referencia para los cambios que deben hacer. Recuerden seguir la misma sintaxis concreta que usa el código de referencia y que se detalla en este enunciado. Esto es importante, pues su código será revisado con tests escritos usando dicha sintaxis.

Si deciden hacer el merge, pueden hacerlo con:

$ git fetch cdi
$ git merge cdi/entrega3
es muy posible que la operación merge no funcione directamente a causa de conflictos. En este caso, deberán resolverlos manualmente.

Recuerden hacer un git commit cada vez que avanzan en su desarrollo, y subir el código a su repositorio Github para facilitar el acceso al código a los auxiliares en caso de que necesiten ayuda.

Para entregar su solución, hagan un release en Github con nombre entrega3 y entreguen el hash del commit correspondiente en el comentario de la tarea de U-cursos.

Recuerden documentar lo que intentaron y lograron hacer, y cualquier otro detalle relevante a su implementación (e.g. cuál spec avanzada eligieron, decisiones de diseño que tomaron, etc). Para esto tienen dos alternativas:

2 Tuplas🔗

Siguiendo la descripción del apunte de tuplas, extiendan el compilador para que soporte tuplas con la sintaxis concreta (tup e1 .. en), y la primitiva de proyección (get e k) que recupera el k-ésimo elemento de la tupla e.

Por ejemplo, el programa:

(let (t (tup (tup) true 42)) t)

produce el output:

(tup (tup) true 42)

Y además:

(get
  (if
    (<= 0 1)
    (tup (print false) -3 42)
    (tup true false 7))
  (+ 1 1))

produce el output:

> false
42

Actualicen su verificación dinámica de tipos para que al realizar la operación (get e k) se chequee que:

Por ejemplo, las siguientes expresiones

(get (+ 3 4) 1)
(get (tup 2) -3)
lanzan errores en tiempo de ejecución con los siguientes mensajes:
Type error: Expected tuple but got 7
Index out of bounds: Tried to access index -3 of (tup 2)
Note que primero se verifican los tipos y luego que los índices estén en el rango correcto.

3 Mutación🔗

Siguiendo lo descrito en el apunte de mutación, agreguen la operación de mutación sobre tuplas (set e k v) que asigna el k-ésimo valor de la tupla e a un nuevo valor v.

Esta operación no crea una tupla nueva, sino que muta la memoria de la tupla existente.

Por ejemplo:

(get
  (set
    (tup (print false) -3 42)
    (+ -1 1)
    (print true))
  0)

produce el output:

> false
> true
true

Recuerden añadir la verificación dinámica de tipos y el chequeo de índice para la operación set.

Por ejemplo, las siguientes expresiones

(set (tup 2 true) (tup) 12)
(set (tup true false) 2 false)
lanzan errores en tiempo de ejecución con los siguientes mensajes:
Type error: Expected integer but got (tup)
Index out of bounds: Tried to access index 2 of (tup true false)

4 Objetivo Extra: Records (1 pto)🔗

⚠ Hint

Para estos dos objetivos extra deberán alterar el parser del lenguaje, estudien la implementación existente para guiarse en cómo hacerlo.

Añadan la definición de records, es decir tuplas que se construyen con un constructor identificado por un record-id y con campos nombrados que se emplean para proyectar.

Por ejemplo, el siguiente programa:

(
    (record point3d x y z)
    (def (addpoint3d p1 p2)
        (point3d (+ (point3d-x p1) (point3d-x p2))
                (+ (point3d-y p1) (point3d-y p2))
                (+ (point3d-z p1) (point3d-z p2))))
    (let (p (point3d 1 1 1))
      (addpoint3d p p))
)

produce el output:

(tup 2 2 2)

Noten que, al igual que las definiciones de funciones de primer orden, las definiciones de records son declaraciones y se ubican antes del cuerpo del programa. Es decir, la sintaxis concreta de las declaraciones se extiende con la siguiente regla en el BNF:

‹decl›: ... | ( record recordId fieldId ... )

5 Objetivo Extra: Pattern-Matching de Tuplas (1 pto)🔗

Extiendan la funcionalidad de un let-binding para poder destruir una tupla de acuerdo con las siguientes reglas:

‹binding›: ( IDENTIFIER ‹expr› ) | ( ( tup IDENTIFIER ... ) ‹expr› ) ‹expr›: ... | ( let ‹binding› ... ‹expr› )

Por ejemplo, el siguiente código

(def (f t)
  (let
    ((tup x y z) t)
    body))

es una sintaxis concisa para

(def (f t)
  (let (x (get 0 t))
    (let (y (get 1 t))
      (let (z (get 2 t))
        body))))

Y por lo tanto, el programa:

(
    (def (f t)
      (let ((tup x y z) t)
        (+ (+ x y) z)))
    (f (tup 2 3 5))
)

produce el output:

10

6 Testing🔗

⚠ Importante

Asegúrense de estar utilizando la última versión de BBCTester, para ello ejecute las siguientes instrucciones en la raíz del repositorio del BBCTester.

$ make uninstall
$ git pull
$ make install

Recuerden que la documentación para escribir los tests se encuentra en este enlace.

Recuerden que la librería BBCTester también permite probar códigos de test utilizando un intérprete usando |ORACLE en la sección expected.

Usen distintos directorios para definir tests que prueban funcionalidades diferentes, por ejemplo, tests/tup/create y tests/tup/get. También puede testear errores ocupando el campo STATUS: de los tests con CT error (compile time error) o RT error (runtime error) y el campo EXPECTED: con el mensaje de error que debería occurir.

Ejemplo de error:

NAME: if expression not tuple
DESCRIPTION: breaks with runtime error because the expression of a get isn't a tuple
STATUS: RT error
SRC:
(get true 0)
EXPECTED:
Type error: Expected tuple but got true

Además el oráculo debería simular estos errores, por ejemplo este test tiene el mismo resultado que el código anterior:

NAME: if expression not tuple
DESCRIPTION: breaks with runtime error because the expression of a get isn't a tuple
SRC:
(get true 0)
EXPECTED:
|ORACLE

Ejemplo de test usando intérprete de referencia sin errores:

NAME: conditional and arithmetic
DESCRIPTION: basic example using reference interpreter
SRC:
(add1 (if true
          (+ 32 1)
          (* 5 3)))
EXPECTED:
|ORACLE

⚠ Importante

Al poner un status de error, el mensaje dentro de EXPECTED: se interpreta como una expresión regular. La sintáxis de las expresiones y su interpretación se pueden ver en la documentación del modulo Str. En particular, los caracteres especiales $^\.*+?[] se deben escapar con un \, por ejemplo (\+ 2 1).

Recuerde empezar cada linea de print con el símbolo especial >.

NAME: printing within add1
DESCRIPTION: prints 1 and add 1 to the result
SRC:
(add1 (print 1))
EXPECTED:
> 1
2