8.8

Entrega 3

Deadline: 22 de Octubre 2023

⚠ Importante

Recuerde las reglas del código de conducta.

Objetivos básicos de la entrega:

Objetivo extra (a elección):

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

1 Para empezar

Para partir, puede 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 conflictors podría ser muy trabajoso, 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. Recuerde 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.

luego puede actualizar el código de su intérprete de referencia 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, debe resolver los conflictos manualmente.

Recuerde hacer un git commit cada vez que avanza 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, haga un release en github con nombre entrega3 y entregue el hash del commit en U-cursos junto con un archivo README.md con lo que intentaron hacer, lo que lograron hacer, y qué decisiones de diseño importantes tomaron en el proceso.

2 Tuplas

Extienda el compilador con soporte para tuplas con la sintaxis concreta (tup e1 .. en) y la función de proyección (get e k) que recupera el k-ésimo elemento de la tupla e.

Esto siguiendo la descripción en el apunte de tuplas.

Por ejemplo:

(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

Debe también actualizar su verificación dinámica de tipos para las operaciones sobre tuplas y agregar una verificación que asegure que el índice está en el rango correcto. Por ejemplo las siguientes expresiones

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

3 Mutación

Añada 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.

Esto también siguiendo la descripción en el apunte de mutación.

Por ejemplo:

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

produce el output:

> false
> true
true

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

4 Objetivo Extra: Records (1 pto)

⚠ Hint

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

Añada 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:

(
    (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)

Note 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 y expresiones se extienden con las siguientes reglas en el BNF:

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

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

Extienda 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 código siguiente

(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úrese 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

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

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

Use 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