Tarea 3 (Entrega: 1ro de julio de 2019)

Esta tarea se distribuye con un archivo zip ( base) 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!

Resumen

El objetivo de la tarea es extender un lenguaje base para soportar clases y objetos. El lenguaje base tiene números, booleanos y operaciones sobre ellos. Además contiene expresiones 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.

La tarea está dividida en 3 secciones, las cuales se describen a continuación:

- Clases y objetos como valores: En esta sección se pide extender el lenguaje base con clases y objetos, los cuales deben ser manipulados como valores en el lenguaje.

- Herencia Simple : El objetivo de esta sección es extender el lenguaje con herencia simple, incluyendo field shadowing y llamados con super.

- [Bonus] Codificando lambdas con objetos: En esta sección bonus se les pide codificar las lambdas como objetos.

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 <member>*)
        | (new <expr>)
        | (get <expr> <id>)
        | (set <expr> <id> <expr>)
        | (send <expr> <id> <expr>*)
        | this
 
<member>  ::= (field <id> <expr>)
         | (method <id> (<id>*) <expr>)

Donde:

  • class permite crear una nueva clase (anónima), con definiciones de campos y métodos.
  • 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.

Extienda el lenguaje para soportar estas expresiones, donde tanto clases y objetos son valores. Usted deber decidir los atributos que tendrán sus respectivos nodos en el AST. Modifique el parser y el intérprete 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 acción:

;;la definición de la clase tiene scope léxico
> (run-val '(seqn (local
                  [(define A
                     (class
                         (field x 2) 
                       (method m () (get this x))))]
                  10)
                (new A)))
"env-lookup: free identifier: A"
;;los identificadores dentro de una clase tienen scope léxico 
;;(note el uso de la “x” en la definición del método “m”
> (run-val '(local
            [(define x 10)
             (define A
               (class
                   (method m (y) (+ x y))))
             (define o (new A))]
            (send o m 1)))
11            
;; 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 o (new A))]
              (send o 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> <member>* )
         | (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 intérprete 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