Intérprete para un lenguaje OO sencillo

Anuncio
Intérprete para un lenguaje OO sencillo
Eduardo Bonelli
Departamento de Computación, FCEyN, UBA
27 de mayo de 2008
La clase pasada
I
Introdujimos los conceptos fundamentales del paradigma
I
I
I
I
I
Objetos, clases, modelo de cómputo, herencia
Polimorfismo de subclase y dynamic method dispatch
Super y static method dispatch
Sobrecarga y redefinición de métodos
Los ilustramos con ejemplos en el lenguaje SOOL
I
I
Es una extensión de SFLA con orientación a objetos
Lenguaje con pocas construcciones pero representativo del
paradigma
I
Estamos completando la visión del paradigma usando
Smalltalk (Práctica)
I
Vamos a implementar un intérprete para SOOL
Intérprete para SOOL
I
Seguimos el capı́tulo 5 del texto “Essentials of
Programming Languages” (ver bibliografı́a de la materia
en la web para más detalles sobre este texto)
I
Con la diferencia de que usamos Haskell en lugar de
Scheme
I
La implementación es una extensión del intérprete ya visto
para el lenguaje funcional con asignación SFL
I
Priorizaremos claridad por sobre eficiencia
Sintaxis concreta de SOOL (extensión de SFLA)
<program> ::= <class-decl>∗ <expr>
<class-decl> ::= class <ident> extends <ident>
field <ident>∗ <method-decl>∗
<method-decl> ::= method <ident> (<ident>∗(,) ) <expr>
<expr> ::= ...
| new <ident> (<expr>∗(,) )
| send <expr> <ident> (<expr>∗(,) )
| super <ident> (<expr>∗(,) )
Ejemplo
class point extends object
field x
field y
method initialize (initx,inity)
begin
set x = initx;
set y = inity
end
method move (dx,dy)
begin
set x = +(x,dx);
set y = +(y,dy)
end
method get_location () list(x,y)
let p = new point(3,4) in p
Sintaxis abstracta
I
Un programa consiste en una lista de declaraciones de clases y
una expresión inicial
data Program = Pgm [ClassDecl] Exp
I
La expresión inicial es una expresión de SFLA extendida con
tres nuevas construcciones: NewExp, SendExp y SuperExp
data Exp =
|
|
|
...
NewExp Symbol [Exp]
SendExp Exp Symbol [Exp]
SuperExp Symbol [Exp]
type Symbol = String
Sintaxis abstracta - declaraciones de clase, campo y
método
data ClassDecl
= Class {classDecl2name::Symbol,
classDecl2super::Symbol,
classDecl2fields::[FieldDecl],
classDecl2methods::[MethodDecl]}
data FieldDecl
= Field {fieldDecl2name::Symbol}
data MethodDecl = Method {methodDecl2name::Symbol,
methodDecl2params::[Symbol],
methodDecl2body::Exp}
Valores
data Value =
|
|
|
Nro Int
Closure [Symbol] Exp Env
Object [Part]
Lista [Value]
type Reference = Int
-- referencias como enteros
Intérprete de programas
I
Toma un programa como entrada y devuelve el resultado
junto con el store (i.e. la memoria)
data Answer = AnAnswer {value::Value,
store::Store}
evalProgram :: Program -> Answer
evalProgram (Pgm classDecls body) =
evalExpression body emptyEnv emptyStore classDecls
I
La lista de declaraciones de clases se utilizará en su
representación original (i.e. como tipo algebraico)
Intérprete de expresiones
I
El intérprete de expresiones se extiende con tres nuevos casos,
uno por cada uno de los nuevos constructores
I
I
I
NewExp: creación de un nuevo objeto
SendExp: envı́o de mensaje
SuperExp: llamado super
I
Asimismo, además de la expresión a interpretar, el entorno y
el store, ahora también toma la lista de declaraciones de clase
como entrada (pues precisará consultarla)
I
Esto se podrı́a evitar en Scheme almacenando las
declaraciones de clases en una variable global, en Haskell se
podrı́an usar mónadas
Intérprete de expresiones
evalExpression :: Exp -> Env -> Store -> [ClassDecl] -> Answer
evalExpression (LitExp n) env st cds =
AnAnswer (Nro n) st
evalExpression (VarExp id) env st cds =
AnAnswer (applyEnv env st id) st
...
evalExpression (NewExp className exps) env st cds = ...
evalExpression (SendExp recip methodName rands) env st cds = ...
evalExpression (SuperExp methodName rands) env st cds = ...
I
El objetivo es completar esta definición
Lookup de declaraciones de clase y método
I
En ocasiones tendremos que ir a buscar la declaración de una
clase o método conociendo el nombre de la misma (o del
mismo)
I
Para ello usaremos la siguiente función
lookupDecl :: [a] -> (a->Symbol) -> Symbol -> Maybe a
lookupDecl [] f nombre = Nothing
lookupDecl (decl:decls) f nombre
| (f decl) == nombre = Just decl
| otherwise
= lookupDecl decls f nombre
Representación de objetos
I
Se representan como una lista de partes donde cada parte
I
I
se corresponde con una clase en la cadena de herencia
consiste de un nombre de clase y un entorno (que, junto con el
store, contiene el estado de la parte)
I
la primera parte de la lista representa el punto más bajo de la
cadena de clases
I
a medida que avanzamos en la lista, nos acercamos más a la
cima de la jerarquı́a
data Value = ...
| Object [Part]
data Part = APart {part2name::Symbol,
part2env::Env}
Creación de objetos
I
La función newObject
I
I
toma como entrada: un nombre de clase, un store y una lista
de declaraciones de clases
arroja como salida: un objeto
I
Dado que la creación de un objeto puede exigir la alocación
de espacio en la memoria (i.e. el store), newObject precisa el
store como entrada (también va a retornar, junto con el
objeto creado, el store actualizado)
I
Veamos un ejemplo antes de presentar el código de
newObject
Ejemplo
class c1 extends object
class c2 extends c1
field x,y
field y
method initialize ()
method initialize ()
begin
begin
set x = 11;
super initialize ();
set y = 12
set y = 22
end
end
class c3 extends c2
field x, z
method initialize ()
begin
super intialize ();
set x = 31;
set z = 32
end
let o3 = new c3() in send o3 m1(7,8)
Ejemplo (cont.)
La expresión new c3() va a retornar un nuevo objeto o3
representado por la lista de partes que se ilustra debajo:
o3
APart
"c3"
APart
1
2
x
z
Referencia
Expressed Value
(sin "Nro")
"c2"
APart
"c1"
3
4
y
1
2
3
4
5
31
32
22
11
12
APart
x
Store
5
"object"
EmptyEnv
Creación de objetos - newObject
I
La función newObject crea un objeto
I
Tal como se mencionó, la creación de un objeto puede exigir la
alocación de espacio en la memoria (i.e. el store), newObject
retorna junto con el objeto creado el store actualizado
I
La función auxiliar constructParts construye las partes que
conforman el nuevo objeto
newObject :: Symbol -> Store -> [ClassDecl] -> Answer
newObject className st cds =
let (parts,s1) = constructParts className st cds
in AnAnswer (Object parts) s1
Creación de objetos - constructParts
I
Esta función construye una lista de partes, donde cada parte
se corresponde con una clase de la cadena de jerarquı́a
I
El primer argumento indica la clase a partir de la cual se
quiere crear las partes (se subirá por la jerarquı́a comenzando
desde allı́)
I
En el caso en que este argumento sea "object" tenemos
constructParts :: Symbol->Store->[ClassDecl]->([Part],Store)
constructParts "object" st cds =
([APart "object" emptyEnv],st)
Creación de objetos - constructParts
I
En los demás casos tenemos
constructParts className st cds
= case (lookupDecl cds classDecl2name className) of
Nothing -> error ("Clase inexistente"++ className)
(Just cDecl) ->
let campos = map fieldDecl2name
(classDecl2fields cDecl)
entorno = extendEnv campos (nextAvail st) emptyEnv
s1 = extendStore st (replicate (length campos)
(Nro 1))
(xs,s2) = constructParts (classDecl2super cDecl)
s1 cds
in
((APart (classDecl2name cDecl) entorno):xs,s2)
Creación de objetos - intérprete de expresiones
El intérprete de expresiones se extiende de la siguiente manera para
la creación de objetos
1. Se evalúan los argumentos que se le pasarán al método
intialize
2. Se crea el objeto propiamente dicho usando newObject (tal
como acabamos de ver)
3. Se busca el método initialize y se lo ejecuta
4. Se retorna el objeto actualizado y el store
Creación de objetos - intérprete de expresiones
evalExpression (NewExp className exps) env st cds =
let (args,s1) = evalExpressions exps env st cds
(AnAnswer obj s2) = newObject className s1 cds
(AnAnswer _ s3)
= findMethodAndApply "initialize"
className obj
(reverse args) s2 cds
in
AnAnswer obj s3
I
Se evalúan los argumentos que se le pasarán al método
intialize
Creación de objetos - intérprete de expresiones
evalExpression (NewExp className exps) env st cds =
let (args,s1) = evalExpressions exps env st cds
(AnAnswer obj s2) = newObject className s1 cds
(AnAnswer _ s3) = findMethodAndApply "initialize"
className obj
(reverse args) s2 cds
in
AnAnswer obj s3
I
Se crea el objeto propiamente dicho usando newObject
Creación de objetos - intérprete de expresiones
evalExpression (NewExp className exps) env st cds =
let (args,s1) = evalExpressions exps env st cds
(AnAnswer obj s2) = newObject className s1 cds
(AnAnswer _ s3) = findMethodAndApply "initialize"
className obj
(reverse args) s2 cds
in
AnAnswer obj s3
I
Se busca el método initialize y se lo ejecuta
Creación de objetos - intérprete de expresiones
evalExpression (NewExp className exps) env st cds =
let (args,s1) = evalExpressions exps env st cds
(AnAnswer obj s2) = newObject className s1 cds
(AnAnswer _ s3) = findMethodAndApply "initialize"
className obj
(reverse args) s2 cds
in
AnAnswer obj s3
I
Se retorna el objeto actualizado y el store
Creación de objetos - intérprete de expresiones
evalExpression (NewExp className exps) env st cds =
let (args,s1) = evalExpressions exps env st cds
(AnAnswer obj s2) = newObject className s1 cds
(AnAnswer _ s3) = findMethodAndApply "initialize"
className obj
(reverse args) s2 cds
in
AnAnswer obj s3
I
En breve veremos la función findMethodAndApply
LLamados a métodos
Para evaluar un send
1. evaluamos los operandos
evalExpression (SendExp recip methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
(AnAnswer obj s2) = evalExpression recipient env s1 cd
in findMethodAndApply methodName
(object2className obj)
obj
(reverse args) s2 cds
LLamados a métodos
Para evaluar un send
1. evaluamos los operandos
2. evaluamos la expresión receptora
evalExpression (SendExp recip methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
(AnAnswer obj s2) = evalExpression recipient env s1 cd
in findMethodAndApply methodName
(object2className obj)
obj
(reverse args) s2 cds
LLamados a métodos
Para evaluar un send
1. evaluamos los operandos
2. evaluamos la expresión receptora
3. buscamos el método asociado con el nombre entre las
declaraciones de método del objeto y aplicamos el método con
los argumentos
evalExpression (SendExp recip methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
(AnAnswer obj s2) = evalExpression recipient env s1 cd
in findMethodAndApply methodName
(object2className obj)
obj
(reverse args) s2 cds
Implementando findMethodAndApply
Esta función deberı́a
I
I
buscar clases a través de la cadena de herencia hasta hallar la
clase que declara el método que coincida con methodName
al encontrarla, llamar a applyMethod con
1. la declaración de método que se halló
2. la declaración de la clase (se usará solamente para acceder al
nombre de la misma y al de su superclase)
3. self (el objeto receptor)
4. los argumentos
5. el store
6. la lista de declaraciones de clases
NB: abreviamos findMethodAndApply como fMAA
Implementando findMethodAndApply
I
Comenzamos con el caso sencillo: si, en su búsqueda, fMAA
llega a la clase object el método no existe
fMAA :: Symbol -> Symbol -> Value ->
[Value] -> Store -> [ClassDecl] -> Answer
fMAA methodName "object" (Object parts) args st cds =
error ("metodo no hallado"++methodName)
I
En caso contrario, debe fijarse en las declaraciones de métodos
de la clase si encuentra una declaración para methodName
Implementando findMethodAndApply
1. obtener declaración de clase cDecl para hostName
fMAA methodName hostName self@(Object parts) args st cds =
case (lookupDecl cds classDecl2name hostName) of
Nothing
-> error ("clase no hallada"++hostName)
(Just cDecl) -> case (lookupDecl (classDecl2methods cDecl)
methodDecl2name methodName) of
Nothing -> fMAA methodName (classDecl2super cDecl)
self args st cds
(Just mDecl) -> applyMethod mDecl cDecl self args st
Implementando findMethodAndApply
1. obtener declaración de clase cDecl para hostName
2. buscar declaración de método para methodName en cDecl
fMAA methodName hostName self@(Object parts) args st cds =
case (lookupDecl cds classDecl2name hostName) of
Nothing
-> error ("clase no hallada"++hostName)
(Just cDecl) -> case (lookupDecl (classDecl2methods cDecl)
methodDecl2name methodName) of
Nothing -> fMAA methodName (classDecl2super cDecl)
self args st cds
(Just mDecl) -> applyMethod mDecl cDecl self args st
Implementando findMethodAndApply
1. obtener declaración de clase cDecl para hostName
2. buscar declaración de método para methodName en cDecl
3. Si no está, seguir buscando en superclase
fMAA methodName hostName self@(Object parts) args st cds =
case (lookupDecl cds classDecl2name hostName) of
Nothing
-> error ("clase no hallada"++hostName)
(Just cDecl) -> case (lookupDecl (classDecl2methods cDecl)
methodDecl2name methodName) of
Nothing -> fMAA methodName (classDecl2super cDecl)
self args st cds
(Just mDecl) -> applyMethod mDecl cDecl self args st
Implementando findMethodAndApply
1. obtener declaración de clase cDecl para hostName
2. buscar declaración de método para methodName en cDecl
3. Si no está, seguir buscando en superclase
4. Si está, aplicar el método
fMAA methodName hostName self@(Object parts) args st cds =
case (lookupDecl cds classDecl2name hostName) of
Nothing
-> error ("clase no hallada"++hostName)
(Just cDecl) -> case (lookupDecl (classDecl2methods cDecl)
methodDecl2name methodName) of
Nothing -> fMAA methodName (classDecl2super cDecl)
self args st cds
(Just mDecl) -> applyMethod mDecl cDecl self args st
Aplicación de un método
Aplicar un método es parecido a aplicar una clausura: el cuerpo del
método debe ser ejecutado bajo un entorno en el que cada variable
es ligada a su valor apropiado.
El entorno debe contar con bindings para:
I
%super: nombre de una clase (la clase padre de la clase
anfitriona del método cuyo código se está por ejecutar)
I
self: un objeto (el receptor del mensaje cuyo código se está
por ejecutar)
I
los parámetros formales
I
todos los campos que son visibles desde el código del método
actualmente por ejecutarse
Aplicación de un método
I
Los campos que son visibles del método por ejecutarse son
aquellas partes del objeto receptor comenzando desde la clase
anfitriona
I
Volvamos al ejemplo que vimos antes
I
Si ejecutamos send o3 m2(), los campos visibles desde m2
son aquellos de o3 que comienzan en la clase anfitriona de m2
(c2) y todos los que hereda de sus ancestors
I
Observar que m2 no puede ver el campo x de c3
Ejemplo
class c1 extends object
class c2 extends c1
field x,y
field y
method initialize ()
method initialize ()
begin
begin
set x = 11;
super initialize ();
set y = 12
set y = 22
end
end
class c3 extends c2
method m2() x
field x, z
method initialize ()
begin
super intialize ();
set x = 31;
set z = 32
end
let o3 = new c3() in send o3 m2()
Aplicación de un método
I
Por lo tanto, el nombre de una clase determina una vista de
un objeto
I
Los campos visibles desde c son todos aquellos declarados en
c más aquellos que se heredan
I
La función que calcula la vista de un objeto es viewObjectAs
viewObjectAs :: [Part] -> Symbol -> [Part]
viewObjectAs parts className =
dropWhile (not . (==className) . part2name) parts
Aplicación de un método
I
A partir de la vista de un objeto podemos construir un
entorno (environment) que consiste en un rib por cada parte.
I
A modo de función auxiliar usamos composeEnv que permite
componer entornos
buildFieldEnv :: [Part] -> Env
buildFieldEnv ps =
foldr (\part-> composeEnv (part2env part)) emptyEnv ps
Aplicación de un método
Ahora estamos en condiciones de completar applyMethod
applyMethod :: MethodDecl -> ClassDecl -> Value ->
[Value] -> Store -> [ClassDecl] -> Answer
applyMethod mDecl cDecl self@(Object parts) args st cds =
let ids = methodDecl2params mDecl
s1 = extendStore
st
((Object [APart (classDecl2super cDecl) emptyEnv]):self:args)
in evalExpression (methodDecl2body mDecl)
(extendEnv ("%super":"self":ids)
(nextAvail st)
(buildFieldEnv (viewObjectAs parts
(classDecl2name cDecl)))))
s1
cds
Llamada super
Un llamado super es igual que un llamado a método salvo que el
lookup comienza en la superclase de la clase anfitriona de la
expresión
evalExpression (SuperExp methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
obj
= applyEnv env s1 "self"
(Object [APart super _]) = applyEnv env s1 "%super"
in fMAA methodName super obj (reverse args) s1 cds
I
Vamos a analizar este caso, paso por paso
Llamada super
1. Evaluar argumentos
evalExpression (SuperExp methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
obj
= applyEnv env s1 "self"
(Object [APart super _]) = applyEnv env s1 "%super"
in fMAA methodName super obj (reverse args) s1 cds
Llamada super
1. Evaluar argumentos
2. Obtener objeto receptor (self) del entorno
evalExpression (SuperExp methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
obj
= applyEnv env s1 "self"
(Object [APart super _]) = applyEnv env s1 "%super"
in fMAA methodName super obj (reverse args) s1 cds
Llamada super
1. Evaluar argumentos
2. Obtener objeto receptor (self) del entorno
3. Obtener nombre de superclase del entorno
evalExpression (SuperExp methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
obj
= applyEnv env s1 "self"
(Object [APart super _]) = applyEnv env s1 "%super"
in fMAA methodName super obj (reverse args) s1 cds
Llamada super
1. Evaluar argumentos
2. Obtener objeto receptor (self) del entorno
3. Obtener nombre de superclase del entorno
4. Buscar método a ejecutar (methodName) comenzando
búsqueda desde la superclase
evalExpression (SuperExp methodName rands) env st cds =
let (args,s1) = evalExpressions rands env st cds
obj
= applyEnv env s1 "self"
(Object [APart super _]) = applyEnv env s1 "%super"
in fMAA methodName super obj (reverse args) s1 cds
Resumen
I
Hemos implementado un intérprete para un lenguaje orientado
a objetos sencillo y representativo del paradigma
I
Algunas extensiones posibles a SOOL a modo de ejercicio:
I
I
Agregar una sentencia instanceof(e,clase) que determina
si el objeto resultante de evaluar e es instancia de la clase
clase
Incorporar calificadores private, protected y public para
las declaraciones de métodos que restringen su acceso: los
públicos se pueden llamar desde cualquier lado, los protegidos
sólo desde la clase anfitriona o sus subclases, los privados sólo
desde la clase anfitriona
Descargar