8.8

Entrega 2

Deadline: 8 de Octubre 2023

⚠ 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, puede iniciar un nuevo branch en git con:

$ git checkout -b entrega2
luego (si no lo ha hecho antes), debe agregar el repositorio de referencia CDI-reference como remote con
$ git remote add cdi https://github.com/pleiad/CDI-reference.git
Importante! Recuerde revisar su correo y aceptar la invitación al team CDI-2022-students, de lo contrario no tendrá acceso al repositorio de referencia, y los siguientes comandos fallarán!

Finalmente, puede actualizar el código de su intérprete de referencia con

$ git fetch cdi
$ git merge cdi/entrega2
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 para los auxiliares en caso de que necesite ayuda.

Para entregar su solución, haga un release en github con nombre entrega2 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 Gestión dinámica de errores de tipos

Deben compilar un mecanismo de chequeo de tipos dinámico. El mecanismo debe detener el programa e imprimir un mensaje de error cuando se encuentra un error de tipos en tiempo de ejecución.

Por ejemplo (+ 1 false) y (not 3) deberían compilar correctamente pero detener el programa e imprimir "Type error: Expected integer but got false" y "Type error: Expected boolean but got 3" respectivamente.

Los mensajes de error de tipo deben tener el siguiente formato:

Type error: Expected <type> but got <value>
donde
<type>  ::= integer | boolean
<value> ::= true | false | <number>

Recuerde testear este comportamiento.

3 Compilación de llamados a funciones externas

Añada una primitiva print para imprimir un valor (llamando a una función print en C definida en rt/sys.c). La función debe retornar el mismo valor que recibió como argumento.

Empiece cada linea de print con el símbolo especial >. Esto le ayudará cuando escriba sus tests para hacer la diferencia con el resultado del programa.

Por ejemplo:

(let (x (print (+ 2 3))) (+ x 1))

produce el output:

> 5
6

4 Compilación de funciones de primer orden

Extienda el lenguage fuente del compilador para que se pueda definir un ambiente de funciones de primer orden, al top-level. Por ejemplo:

(
  (def (g x) (if (<= x 5) 5 x))
  (def (f x y) (+ (+ 2 x) (g y)))
  (f 5 3)
)

Deben detectar en tiempo de compilación errores de aplicación de función malformados. Es decir, deben verificar que

Los mensajes de error deben tener respectivamente los siguientes formatos:

Undefined function: <id>
Arity mismatch: <id> expected <number> arguments but got <number>

Por ejemplo, considerando las definiciones del programa de ejemplo de arriba, las aplicaciones (f 8) o (g 4 5 6) deberían producir respectivamente los siguientes errores:

Arity mismatch: f expected 2 arguments but got 1
Arity mismatch: g expected 1 arguments but got 3
Asimismo, la aplicación (h 1) debe producir el error
Undefined function: h

5 Objetivos extras

Para los objetivos extras elija una de las dos alternativas:

5.1 Gestión de errores aritméticos (1pto)

Las operaciones aritméticas +, *, - y / pueden generar errores si sus argumentos no son adecuados. Agregue las operaciones aritméticas *, - y / y una opción de compilación -safe que genera código assembly adicional que hace la gestión de overflows para + y *, de underflows para - y de división por 0.

Los mensajes de error por overflow o underflow deben tener el siguiente formato:

Arithmetic error: <op> produced an <over|under>flow
donde <op> ::= + | * | - es la operación que produjo el error.

Por ejemplo, en caso de que una multiplicación produzca un overflow, el mensaje de error debería ser:

Arithmetic error: * produced an overflow

Para el error de división por cero, el mensaje debe ser:

Arithmetic error: Division by 0
Por ejemplo, el programa (/ 1 (- 5 5)) compilado con el flag -safe debe imprimir el mensaje de error "Arithmetic error: Division by 0" y terminar, mientras que sin -safe termina con un error de sistema.

5.2 Interfaz de funciones forasteras (1pto)

Implemente un mecanismo genérico (my_C_function arg1 arg2 ..) para llamar funciones C. Las funciones C se registran con una declaración (defsys my_C_function tipo1 tipo2 .. -> tipo_retorno). tipo1, tipo, tipo_retorno es una información de tipo que puede ser int, bool o any (cualquier tipo de dato).

Cabe remarcar que, como en el intérprete de referencia, estas funciones foráneas comparten namespace con las funciones de primer orden ya descritas.

Se implementará la convención de llamada a función x86-64, ocupando registros para los 6 primeros argumentos en vez de la pila, que se puede encontrar en la sección 2 de las notas del curso.

Se deben emplear las declaraciones de tipos int y bool en la función para hacer un check dinámico de los argumentos, y también convertir los datos para quitar el tagging.

Por ejemplo, se puede declarar (defsys max int int -> int), suponiendo una función C int64_t max(int64_t x, int64_t y), y la llamada (max 7 3) debería funcionar, pero ambas llamadas (max 5) y (max 1 true) deberían arrojar errores, puues la primera no cumple con la aridad de max, y la segunda tiene un segundo argumento booleano. El mensaje de error de tipos debe seguir el mismo formato expuesto en la sección 2. Asimismo, los mensajes de error por llamadas mal formadas (función no definida o error de aridad) deben seguir los formatos descrito en la sección 4.

Para testear estas funciones usando el oráculo pueden re-implementar sus funciones de test en las listas dedicadas para esto que se encuentran en interp.ml. Estas listas son: sys_func_prelude y defs_prelude (ya contienen ejemplos).

Un ejemplo de uso de any sería (defsys print any -> any), y la llamada (print 1) debería comportarse igual que antes; es decir el código C es responsable de hacer el untagging para argumentos especificados con any.

Un ejemplo de test (suponiendo que max y print están definidos en el código C):


NAME: C calls (max, print)
SRC:
(
  (defsys max int int -> int)
  (defsys print any -> any)
  (print (+ 2 (max 5 (+ 4 6))))
)
EXPECTED:
> 12
12

6 Testing

⚠ Importante

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

$ make uninstall
$ git pull
$ make install

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

Note que la librería bbctester también permite probar un programa utilizando un intérprete. Para esto, escriba |ORACLE en la sección EXPECTED: de su archivo .bbc, adapte los tests de la entrega pasada para que coincidan con el nuevo formato.

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

Ejemplo de error:

NAME: if condition not bool
DESCRIPTION: breaks with runtime error because the condition is a number
STATUS: RT error
SRC:
(if 5
    true
    false)
EXPECTED:
Type error: Expected boolean but got 5

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 condition not bool
DESCRIPTION: breaks with runtime error because the condition is a number
SRC:
(if 5
    true
    false)
EXPECTED:
|ORACLE

Ejemplo de test usando interprete 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