Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
teaching:cc4101:tareas:2021-1:tarea1 [2021/03/29 16:23] – created tvallejos | teaching:cc4101:tareas:2021-1:tarea1 [2021/05/10 14:34] (current) – P3, add '}' on first example bortiz | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Tarea 1 (Entrega: | + | ====== Tarea 1 (Entrega: |
+ | ==== Tipos estáticos, tipos opcionales, contratos ==== | ||
- | Esta tarea se distribuye con un archivo zip {{teaching: | + | Ya se habrán dado cuenta |
- | Deben entregar via U-cursos **un archivo .zip** que contenga los archivos main.rkt | + | En esta tarea van a dejar atrás toda confusión al respecto! Van a implementar |
+ | Al final de esta tarea, tendrán implementado un lenguaje bastante original que permite mezclar checkeo estático y dinámico de tipos y propiedades! | ||
- | <note important> | ||
- | **¡Los tests, los contratos y las descripciones forman parte de su evaluación!** | + | ---- |
- | ===== 1. Funciones de primera clase con tipos declarados [1.5pt] ===== | ||
- | En esta tarea agregaremos anotaciones de tipos a un lenguaje con funciones de primera clase. La sintaxis concreta del lenguaje es la mostrada a continuación: | ||
- | < | ||
- | | <id> | ||
- | | {+ < | ||
- | | {- < | ||
- | | {with {<id> : < | ||
- | | {fun {<id> : < | ||
- | | {< | ||
- | <type> ::= Num | + | <note important>Consulte las normas de entrega de tareas en http:// |
- | | {< | + | Recuerden |
- | </code> | + | </note> |
- | Note que '' | + | |
- | Por ejemplo, los programas siguientes son válidos: | + | |
- | {with {x : Num 5} {+ x 3}} | + | |
- | {fun {x : Num} : Num | ||
- | {+ x 3}} | ||
- | {fun {x : Num} | + | Deben entregar via U-cursos **un archivo .zip** que contenga los siguientes archivos: '' |
- | {+ x 3}} | + | |
- | </ | + | |
- | * (0.3) Defina la función '' | ||
- | > (parse-type '{{Num -> Num} -> Num}) | ||
- | (TFun (TFun (TNum) (TNum)) (TNum)) | ||
- | > (parse-type '{Num -> }) | + | <note important> |
- | "Parse error" | + | Cada parte de esta tarea vale lo mismo: 2 puntos. La asignación de puntaje por parte sigue la misma estructura: |
- | </ | + | * 0.2 para soportar |
- | | + | * 1.5 para las funcionalidades (p.ej. interprete para la P1) |
- | > (parse '{fun {x : Num} : Num {+ x 1}}) | + | * 0.3 para testing (cobertura |
- | (fun 'x (TNum) (add (id 'x) (num 1)) (TNum)) | + | </note> |
- | > (parse '{with {y : Num 2} | + | ===== Parte 1. Lenguaje con funciones de primer orden ===== |
- | {+ x y}}) | + | En esta parte, vamos a implementar un lenguaje que incluye primitivas útiles |
- | (app (fun 'y (TNum) (add (id 'x) (id 'y)) #f) (num 2)) | + | |
- | </code> | + | |
- | **Note que se usa '' | + | Aquí está la gramática BNF del lenguaje: |
+ | <code scheme> | ||
+ | < | ||
- | * (0.3) Defina la función '' | + | <fundef> ::= {define {<id> < |
- | > (prettify (TNum)) | + | |
- | 'Num | + | |
- | > (prettify (TFun (TNum) (TFun (TNum) (TNum))) | + | < |
- | '(Num -> (Num -> Num)) | + | | <id> |
+ | | <bool> | ||
+ | | {< | ||
+ | | {< | ||
+ | | {if < | ||
+ | | {with {{< | ||
+ | | {<id> < | ||
+ | |||
+ | < | ||
+ | < | ||
</ | </ | ||
+ | Un programa está compuesto de (0 o más -- por eso la estrella '' | ||
- | ===== 2. Verificación de Tipos [1.5pt] ===== | + | < |
- | Se implementará el chequeo de tipos de una expresión en presencia de funciones de primera clase. | + | |
- | * (1.0) Implemente la función '' | + | |
- | * Para una función con el tipo de retorno anotado, se valida que la expresión del cuerpo tenga el mismo tipo que el tipo de retorno declarado. | + | |
- | * Para una función sin el tipo de retorno anotado, el tipo resultante considera el tipo calculado del cuerpo de la función. | + | |
- | * Para la aplicación de función se valida que la expresión en posición de función sea efectivamente una función. | + | |
- | * Para la aplicación de función también se valida que el tipo de la expresión de argumento de la aplicación coincide con el tipo esperado del argumento de la función. | + | |
- | * Para la aplicación de un operador numerico se valida que sus operandos tengan el tipo correcto '' | + | |
- | Los mensaje | + | Algunos ejemplos |
<code scheme> | <code scheme> | ||
- | "Type error in expression | + | { ;; Programa de Ejemplo 1 |
+ | | ||
+ | | ||
+ | {with {{x 9}} | ||
+ | {sum {max x 6} 2 -10} } | ||
+ | } | ||
+ | { ;; Programa de Ejemplo 2 | ||
+ | {with {{x 5} {y 7} {z 42}} | ||
+ | z} | ||
+ | } | ||
+ | { ;; Programa de Ejemplo 3 | ||
+ | | ||
+ | | ||
+ | {add2 {triple 2}} | ||
+ | } | ||
</ | </ | ||
- | donde E es la expresión donde ocurrió el error, n es la sub-expresión donde se manifestó, T1 es el tipo esperado y T2 el tipo encontrado. Utilice | + | En esta parte, su función |
- | **Note que la única forma de tener error de tipo de función, es cuando el tipo de retorno está anotado.** | + | **Instrucciones importantes**: |
+ | * Para implementar el lenguaje, tiene que [[https:// | ||
+ | | ||
+ | | ||
- | Para el caso cuando hay algún identificador libre, el patrón es: | + | <code scheme> |
- | <code scheme> | + | debe caerse en tiempo de ejecución con un error (se levantan con '' |
- | "Type error: | + | <code scheme>" |
- | </ | + | Recuerde que puede testear estos errores con '' |
- | donde <x> es el identificador libre. | + | |
- | Algunos ejemplos: | ||
- | |||
- | <code scheme> | ||
- | > (typeof (parse '{fun {x : Num} : Num 5})) | ||
- | (TFun (TNum) (TNum)) | ||
- | > (typeof (parse '{fun {x : Num} x})) | ||
- | (TFun (TNum) (TNum)) | ||
- | > (typeof (parse '{{fun {x : Num} x} 1})) | ||
- | (TNum) | ||
- | > (typeof (parse '{fun {x : Num} : {Num -> Num} 10})) | ||
- | "Type error in expression fun position 1: expected (Num -> Num) found Num" | ||
- | > (typeof (parse '{1 2})) | ||
- | "Type error in expression app position 1: expected (?T -> ?S) found Num" | ||
- | > (typeof (parse '{{fun {x : Num} : Num {+ x x}} {fun {x : Num} : Num 5}})) | ||
- | "Type error in expression app position 2: expected Num found (Num -> Num)" | ||
- | >(typeof (parse ' | ||
- | "Type error in expression + position 2: expected Num found (Num -> Num)" | ||
- | > (typeof (parse 'y)) | ||
- | "Type error: free identifier: y"</ | ||
- | **Note que debe manejar identificadores adecuadamente, | + | ----- |
+ | ===== Parte 2. Verificación estática y opcional de tipos ===== | ||
+ | En esta parte vamos a extender el lenguaje de la Parte 1 con anotaciones | ||
+ | | ||
+ | | ||
- | | + | Observe que las anotaciones de tipos son siempre *opcionales* (en el BNF aquí usamos |
+ | <code scheme> | ||
+ | < | ||
- | <code scheme> | + | <arg> ::= <id> | {<id> : < |
- | >(typecheck '3) | + | |
- | 'Num | + | |
- | >(typecheck | + | <expr> ::= ... | {with { {<id> [: < |
- | '(Num -> Num) | + | |
- | >(typecheck | + | <type> |
- | " | + | </code> |
+ | Note que '' | ||
- | </ | + | Para soportar |
- | ===== 3. Compilación [3.0pt]===== | + | |
- | Ahora vamos a compilar nuestro lenguaje a una máquina abstracta basada en stack. La máquina es similar a la conocida SECD(([[https:// | + | |
- | La función | + | |
- | > (exec-machine (list (INT-CONST 6) (INT-CONST 1) (ADD))) | + | |
- | 7 | + | |
- | > (exec-machine (list (INT-CONST 3) (CLOSURE (list (ACCESS 0) (RETURN))) (APPLY))) | + | |
- | 3 | + | |
- | </ | + | |
- | **Tome un tiempo para experimentar con la función '' | + | |
- | ==== Índices De Bruijn ==== | + | |
- | Una forma de eliminar nombres de variables o identificadores de una expresión, es usando índices De Bruijn. Anteriormente vimos que la máquina SECD trabaja solo con índices, por lo que en esta sección vamos a hacer el paso de identificadores a dichos números, usando indices De Bruijn((La explicación detallada de como funcionan se puede encontrar en la sección 3.5 del [[http:// | + | |
- | * (1.5) Implemente la función | + | |
- | **Hint: le podría ser útil recordar que el intérprete visto en clases usa un ambiente para asociar identificadores a valores.** | + | |
+ | < | ||
+ | Obviamente, usar el tipo '' | ||
+ | </ | ||
+ | |||
+ | Los programas siguientes son válidos, y bien tipados: | ||
<code scheme> | <code scheme> | ||
- | > (deBruijn | + | {{with |
- | | + | |
- | | + | |
- | {+ x y}}}})) | + | |
- | (add | + | |
- | (num 1) | + | |
- | | + | |
- | (fun-db | + | |
- | (app (fun-db (add (acc 1) (acc 0))) (num 2))) | + | |
- | (num 1))) | + | |
- | + | ||
- | > (deBruijn (add (num 1) (id ' | + | |
- | "Free identifier: x" | + | |
- | </ | + | |
- | ==== De AST a código de máquina ==== | + | |
- | Vamos a abordar la compilación del AST de una expresión al set de instrucciones de la SECD. | + | |
- | A continuación se presenta el esquema de compilación que permite hacer el paso del AST de una expresión a una lista de instrucciones (revise el archivo machine.rkt, | + | |
- | C((num n)) = (INT-CONST n) | + | {{define {gt42 x} : Bool {> x 42}} |
- | C((add a1 a2)) = C(a2); | + | {gt42 43}} |
- | C((sub a1 a2)) = C(a2); | + | |
- | C((app f a)) = C(a); C(f); | + | |
+ | {{define {id {x : Num}} x} | ||
+ | {id 5}} | ||
+ | |||
+ | {{define {add2 {x : Num}} {+ x 2}} | ||
+ | {with {{oops #f}} | ||
+ | {add2 oops}}} | ||
</ | </ | ||
- | La compilación de un nodo '' | + | En particular, fíjese que el último ejemplo está bien tipado solamente porque se usa '' |
- | < | + | |
- | C((acc n)) = (ACCESS n) | + | |
+ | En esta parte, deben definir una nueva función '' | ||
+ | |||
+ | **Instrucciones**: | ||
+ | * Recuerden que la gramática BNF dicta la estructura de sus definiciones | ||
+ | * La verificación de tipos de un programa consiste en verificar que todas las definiciones de función estén bien tipadas, y que la última expresión tiene un tipo (no importa cual). | ||
+ | * Para una definición de función, se valida que la expresión del cuerpo tenga el mismo tipo que el tipo de retorno declarado, suponiendo que cada argumento tiene el tipo declarado. Recuerde que si no se especifica un tipo, se considera '' | ||
+ | * Debe definir los tipos de los operadores primitivos de manera exacta, p.ej. '' | ||
+ | * Para una expresión '' | ||
+ | * Para '' | ||
+ | * En la aplicación de función se valida que el número de argumentos coincide, y que el tipo de los argumentos coincide con los tipos esperados de la función aplicada. La aplicación en sí tiene el tipo de retorno de la función aplicada. | ||
+ | |||
+ | Para los errores: | ||
+ | * Los errores de identificadores libres (o funciones no definidas) se tienen que detectar estáticamente, | ||
+ | * Los mensajes de error de tipo detectados estáticamente tienen que seguir el siguiente patrón: | ||
+ | <code scheme>" | ||
+ | |||
+ | Algunos ejemplos (no representan todos los casos, es de su responsabilidad entregar //test suites// completos): | ||
+ | < | ||
+ | > (typecheck '{3}) | ||
+ | ' | ||
+ | > (typecheck ' | ||
+ | {f {> 3 4}}}) | ||
+ | 'Any </ | ||
+ | > (typecheck ' | ||
+ | {one #t}}) | ||
+ | " | ||
+ | > (typecheck ' | ||
+ | " | ||
+ | > (typecheck '{{if 73 #t #t}}) | ||
+ | " | ||
+ | > (typecheck ' | ||
+ | z}}) | ||
+ | " | ||
+ | |||
+ | ¿Puede efectivamente convencerse de que todo programa que pasa la verificación de tipo no se cae con un error de tipo durante la ejecución? | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ===== Parte 3. Contratos en funciones de primer orden (2.0 pt) ===== | ||
+ | Ahora vamos a añadir verificación dinámica mediante contratos a las funciones de nuestro lenguaje. El único cambio en la sintaxis del lenguaje se ve reflejado en la definición de funciones, donde ahora se puede definir además un contrato para cada argumento: | ||
+ | <code scheme> | ||
+ | < | ||
+ | < | ||
+ | | {<id> [: < | ||
</ | </ | ||
- | La compilación de una función puede ser inferida del ejemplo a continuación: <code scheme> | + | Un contrato corresponde a un predicado, |
- | > (compile (deBruijn (parse '{{fun {x : Num} : Num | + | <code scheme> |
- | | + | {{define |
- | (list | + | |
- | | + | |
- | | + | {div 5 3}} |
- | | + | |
- | | + | |
- | | + | |
</ | </ | ||
+ | Donde el '' | ||
+ | Note que la información de tipo estático es opcional, por lo que uno puede especificar una función solamente mediante contratos. | ||
+ | <code scheme> | ||
+ | {{define {positive x} : Bool {> x 0}} | ||
+ | | ||
+ | {/ y x}} | ||
+ | {div 5 3}} | ||
+ | </ | ||
- | * (1.0) Implemente la función '' | + | En esta parte, su función '' |
- | | + | **Instrucciones**: |
+ | * En el intérprete, | ||
+ | * Cuando el contrato no se cumpla, el patrón de error es: <code scheme> | ||
+ | " | ||
+ | </ | ||
+ | * Para poder ser usado como contrato, una función | ||
+ | " | ||
+ | </ | ||
+ | Más ejemplos: | ||
+ | <code scheme> | ||
+ | {{define {lt100 x} {< x 100}} | ||
+ | | ||
+ | | ||
+ | | ||
+ | {/ {* y y} x}} | ||
+ | {calc 25 3}} | ||
+ | </ | ||
+ | |||
+ | <code scheme> | ||
+ | > (run ' | ||
+ | | ||
+ | #t} | ||
+ | | ||
+ | " | ||
+ | </ | ||