Tarea 3 (Entrega: 24 de agosto de 2018)

Esta tarea se distribuye con un archivo zip (base-tarea3) que contiene 2 archivos: main.rkt y tests.rkt. Los archivos main.rkt y tests.rkt están incompletos, y en ellos tienen que implementar lo que se solicita en las preguntas siguientes.

Deben entregar via U-cursos un archivo .zip que contenga los archivos main.rkt y tests.rkt.

Consulte las normas de entrega de tareas en http://pleiad.cl/teaching/cc4101

¡Los tests, los contratos y las descripciones forman parte de su evaluación!

En esta tarea extenderemos un lenguaje base para soportar clases y objetos. El lenguaje base tiene números, booleanos y operaciones sobre ellos. Además contiene expressiones if, seqn, local y define, pero no tiene soporte para funciones. Tome un tiempo de experimentar con el lenguaje entregado antes de comenzar a implementar las extensiones pedidas.

Clases y objetos como valores (3,0 ptos.)

A continuación se presenta la sintaxis del lenguaje extendido (se omiten las del lenguaje base):

<expr> ::= ... (expresiones del lenguage entregado) ...
        | (class <def>*)
        | (new <expr>)
        | (get <expr> <id>)
        | (set <expr> <id> <expr>)
        | (send <expr> <id> <expr>*)
        | this
 
<def>  ::= (field <id> <expr>)
         | (method <id> (<id>*) <expr>)

Donde:

  • class permite crear una nueva clase (anónima), con definiciones de campos y metodos.
  • new permite instanciar una clase dada.
  • get y set permiten acceder a un campo de un objeto dado o modificarlo.
  • send permite invocar un método de un objeto dado, con 0 o más argumentos.
  • this permite acceder al objeto actual. Solamente es válido usar this dentro del cuerpo de un método.
  • una definición de campo (field) incluye una expresión de inicialización, que se ejecuta nuevamente para cada instancia creada.
  • una definición de método (method) especifica 0 o más parámetros, y el cuerpo del método.

Tanto clases como objetos son valores. Usted deber decidir los attributos que tendrán sus respectivos nodos en el AST. Modifique el parser y el interprete para soportar el lenguaje extendido. Además, los errores deben manejarse de la siguiente forma:

  • El acceso a un campo inexistente de un objeto debe arrojar el error “field not found”.
  • La invocación de un método inexistente debe lanzar el error “method not found”.

Veamos algunos ejemplos de clases y objetos en accción:

;; comportamiento esperado
> (run-val '(local
             [(define c (class
                            (field x 1)
                          (field y 2)
                          (method sum (z) (+ (get this x) (+ (get this y) z)))
                          (method set-x (val) (set this x val))))
              (define o (new c))]
             (seqn
              (send o set-x (+ 1 3))
              (+ (send o sum 3) (get o y)))))
11
;; las clases son valores
> (run-val '(local
              [(define A
                 (class
                     (method apply (c)
                             (send (new c) m))))
               (define ins (new A))]
              (send ins apply (class
                                (field x 2) 
                                (method m () (get this x))))))
2

Herencia Simple (3,0 ptos.)

Extienda su lenguaje con herencia simple, incluyendo field shadowing y llamados con super. Asegúrese de estudiar la sección de herencia del OOPLAI1) para comprender la semántica deseada.

La sintaxis extendida es:

<expr> ::= ... (todo lo anterior) ...
         | ( class <: <expr> <def>* )
         | ( super <id> <expr>* )

Se agrega una forma para crear clases, especificando su superclase. Sigue siendo válido crear una clase sin especificar su superclase, en cuyo caso extenderá de la clase raíz Object, que usted tendrá que definir.

  1. (1.0) Implemente la búsqueda de métodos 2) correspondiente a la herencia
    ;; el método f se busca en c y luego en su superclase
    > (run-val '(local
                  [(define c1 (class
                                  (method f (z) (< z 7))))
                   (define c (class <: c1))
                   (define o (new c))]
                  (send o f 20)))
    #f
  2. (1.0) Implemente el soporte para llamados con super 3).
    ;; llamada a super de metodo no definido en el padre directo
    (run-val '(local
              [(define c2 (class
                              (method h (x) (+ x 1))))
               (define c1 (class <: c2
                            (method f () #f)))
               (define c (class <: c1                       
                           (method g () (super h 10))))
               (define o (new c))]
              (send o g)))
    11
  3. (1.0) Implemente la semántica de field shadowing 4)para sobreescritura de campos.
    > (run-val '(local
                  [(define A (class 
                               [field x 1]
                               [field y 0]
                               [method ax () (get this x)]))
                   (define B (class <: A
                               [field x 2]
                               [method bx () (get this x)]))
                   (define b (new B))]
                  (send b ax)))
    1

Nota:

  • Los llamados con super solo pueden ocurrir desde los métodos.
  • No olvide incluir a la clase Object en el ambiente inicial de ejecución.

[Bonus] Codificando lambdas con objetos (0,5 ptos.)

Ahora incorporamos lambdas a nuestro lenguaje. A diferencia de lo visto durante el curso, en esta ocasión no daremos una interpretación directa de las funciones, usted debe ingeniar una manera de codificar una lambda como un objeto. Esto significa que no puede modificar el interprete para soportar funciones, ni aplicaciones de funciones. Las modificaciones que debe hacer son en el parser.

<expr> ::= ...
         | (fun (<id>*) <expr>)
         | (<expr> <expr>*)

Ejemplo de uso:

> (run-val '(local
              [(define f (fun (x)
                              (+ x x)))]
              (f 5)))
10