Entrega 2
Deadline: 8 de Octubre 2023
⚠ Importante
Recuerde las reglas del código de conducta.
Objetivos básicos de la entrega:
Gestión de errores de tipos
Añadir funciones externas (FFI) como
print
Compilar funciones de primer orden
Objetivo extra (elegir uno de los dos):
Gestión de errores de recursos (overflows,...)
Convención de llamada a función x86-64
La evaluación se hace con specs grading (vea los detalles aquí).
spec básica 4 pts
spec avanzada 1 pt
testing 0.5 pts
calidad del código 0.5 pts
1 Para empezar
Para partir, puede iniciar un nuevo branch en git con:
$ git checkout -b entrega2
$ git remote add cdi https://github.com/pleiad/CDI-reference.git
Finalmente, puede actualizar el código de su intérprete de referencia con
$ git fetch cdi
$ git merge cdi/entrega2
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>
<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
la función esté definida
la aridad de la función coincida con el número de argumentos recibidos
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
(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
<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
(/ 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