Esta tarea se distribuye con un archivo zip ( base) que contiene 4 archivos: main_p1.rkt, tests_p1.rkt, main_p2.rkt y tests_p2.rkt. Los archivos 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_p1.rkt, tests_p1.rkt, main_p2.rkt y tests_p2.rkt.
¡Los tests, los contratos y las descripciones forman parte de su evaluación!
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 begin
y with
para definir múltiples identificadores, pero no tiene soporte para funciones. Tome un tiempo de experimentar con el lenguaje entregado antes de comenzar a implementar las extensiones requeridas.
La tarea está dividida en 2 secciones, las cuales se describen a continuación:
- Clases y objetos: En esta sección se pide extender el lenguaje base con clases y objetos. En particular las clases deben ser entidades de primera clase, es decir, son valores del lenguaje.
- Herencia Simple : El objetivo de esta sección es extender el lenguaje con herencia simple, incluyendo field shadowing y llamados con super
.
A continuación se presenta la sintaxis del lenguaje extendido (se omiten las del lenguaje base):
<expr> ::= ... (expresiones del lenguaje entregado) ... | {class {<id>*} <method>*} | {new <expr> {<expr>*}} | {get <expr> <id>} | {set <id> <expr>} | {-> <expr> <id> <expr>*} | self <method> ::= {def <id> {<id>*} <expr>}
Donde:
class
permite crear una nueva clase anónima, con una lista de 0 o más identificadores para los campos seguida de 0 o más métodos.new
permite crear una instancia de una clase dada (primera expr), inicializando sus campos con una lista de expresiones entregadas después (más abajo se detallan ciertas consideraciones a tener en cuenta). get
permite acceder al campo <id>
de un objeto dado.set
permite modificar el campo <id>
de un objeto. Solamente es válido usar set
dentro del cuerpo de un método y para modificar campos propios del objeto desde donde se llama. No es posible modificar campos de un objeto externo.→
permite invocar un método de un objeto dado, con 0 o más argumentos.self
permite acceder al objeto actual. Solamente es válido usar self
dentro del cuerpo de un método.def
permite definir un método, donde se especifica el nombre del método, 0 o más parámetros, y el cuerpo del método.
Además, para la creación de un objeto, los constructores son simplemente métodos llamados init
. Es posible definir varios métodos init
, sin embargo, definir varios constructores con la misma aridad es un error (en tiempo de creación de la clase). Cuando se ejecuta un new
, se selecciona el constructor que corresponda al número de argumentos pasados (error si no hay ninguno que calza). Si no hay ningún constructor declarado, solo se puede usar {new c {}}
sin argumentos (constructor por defecto). .
Extienda el lenguaje para soportar estas expresiones, donde tanto clases y objetos son valores. Usted debe decidir los atributos que tendrán sus respectivos nodos en el AST. Si se encuentra con casos no especificados anteriormente debe tomar supuestos e indicarlo en su entrega. Modifique el parser y el intérprete para soportar el lenguaje extendido.
Los errores deben manejarse de la siguiente forma:
set
fuera de un método debe lanzar el error “set outside method exception”
.self
fuera de un método debe lanzar el error “self outside method exception”
.“field not found exception”
.“field not initialized exception”
.“method not found exception”
.“constructor not found exception”
.“same arity constructor exception”
Veamos algunos ejemplos de clases y objetos en acción:
;; comportamiento esperado > (run-val '{with {{c {class {x y} {def init {} {begin {set x 1} {set y 2}}} {def init {init-x init-y} {begin {set x init-x} {set y init-y}}} {def sum {z} {+ {get self x} {+ {get self y} z}}} {def set-x {val} {set x val}}}} {o {new c {3 4}}}} {begin {-> o set-x {+ 1 3}} {+ {-> o sum 3} {get o y}}}}) 15
;; las clases son valores > (run-val '{with {{A {class {} {def apply {c} {-> {new c {}} m}}}} {o {new A {}}}} {-> o apply {class {x} {def init {} {set x 2}} {def m {} {get self x}}}}}) 2
;;la definición de la clase tiene scope léxico > (run-val '{begin {with {{A {class {x} {def init {} {set x 2}} {def init {init-x} {set x init-x}} {def m {} {get self x}}}}} 10} {new A {}}}) "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 '{with {{x 10} {A {class {} {def m {y} {+ x y}}}} {o {new A {}}}} {-> o m 1}}) 11
;; No se puede usar set fuera de un método > (run-val '{set x 1}) "error: set outside method exception"
;; No se puede usar self fuera de un método > (run-val 'self) "error: self outside method exception"
;; Acceder a un campo no definido de una clase > (run-val '{with {{A {class {}}} {o {new A {}}}} {get o m}}) "error: field not found exception"
;; Acceder a un campo no inicializado de una clase > (run-val '{with {{A {class {x y} {def init {init-x init-y} {set x init-x}}}} {o {new A {1 2}}}} {get o y}}) "error: field not initialized exception"
;; Invocar un método no definido de una clase > (run-val '{with {{A {class {}}} {o {new A {}}}} {-> o m}}) "error: method not found exception"
;; Una clase sin constructores puede ser creado solo con {new class {}}, sin argumentos > (run-val '{with {{x 10} {A {class {x}}} {o {new A {x}}}} 1}) "error: constructor not found exception"
;; Tener 2 init con la misma aridad es un error en tiempo de creación de la clase > (run-val '{begin {with {{A {class {x} {def init {init-x} {set x init-x}} {def init {init-x} {set x 12}}}}} 10} {new A {}}}) "error: same arity constructor exception"
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) ... ;; Cambio para incluir superclase | {class [: <expr>] {<id>*} <method>*} | {super <id> <expr>*}
Se modifica la sintaxis class
para incluir un parametro opcional donde irá la superclase de la clase que se crea. En caso de no incluir una superclase, se debe extender la clase raíz Object
, que usted tendrá que definir.
;; el método f se busca en c y luego en su superclase > (run-val '{with {{A {class {} {def f {z} {< z 7}}}} {B {class : A {}}} {o {new B {}}}} {-> o f 20}}) #f
super
3).;; llamada a super de método no definido en el padre directo (run-val '{with {{A {class {} {def h {x} {+ x 1}}}} {B {class : A {} {def f {} #f}}} {C {class : B {} {def g {} {super h 10}} {def h {} {+ 1 x}}}} {o {new C {}}}} {-> o g}}) 11
> (run-val '{with {{A {class {x y} {def init {} {begin {set x 1} {set y 0}}} {def ax {} {get self x}}}} {B {class : A {x} {def init {} {set x 2}} {def bx {} {get self x}}}} {b {new B {}}}} {-> b ax}}) 1
Nota:
super
solo pueden ocurrir desde los métodos, en caso de que sean llamados fuera de un método debe arrojar el error “super outside method exception”
.;; No se puede usar super fuera de un método > (run-val {'super 'x}) "error: super outside method exception"
Object
en el ambiente inicial de ejecución.