racket/collects/tests/eopl/chapter9/classes/classes.rkt
David Van Horn 7491e172ea EOPL test suite re-written in Racket-based #lang eopl and rackunit
The eopl language is now racket-based rather than mzscheme-based.  This
test-suite, which was originally distributed on the book's web-site has
been re-written in the new language.  Changes include dropping all
drscheme-init.scm and top.scm files.  Remaining files were renamed to
use the .rkt extension and edited to use the #lang syntax (instead of
modulue).  Require and provide forms were changed to reflect racket's
syntax instead of mzscheme's (eg, only-in vs. only).  Several
occurrences of one-armed ifs were changed to use when and unless.  All
tests have been run successfully.
2012-02-24 14:46:18 -05:00

230 lines
6.1 KiB
Racket
Executable File

#lang eopl
(require "store.rkt")
(require "lang.rkt")
;; object interface
(provide object object? new-object object->class-name object->fields)
;; method interface
(provide method method? a-method find-method)
;; class interface
(provide lookup-class initialize-class-env!)
;;;;;;;;;;;;;;;; objects ;;;;;;;;;;;;;;;;
;; an object consists of a symbol denoting its class, and a list of
;; references representing the managed storage for the all the fields.
(define identifier? symbol?)
(define-datatype object object?
(an-object
(class-name identifier?)
(fields (list-of reference?))))
;; new-object : ClassName -> Obj
;; Page 340
(define new-object
(lambda (class-name)
(an-object
class-name
(map
(lambda (field-name)
(newref (list 'uninitialized-field field-name)))
(class->field-names (lookup-class class-name))))))
;;;;;;;;;;;;;;;; methods and method environments ;;;;;;;;;;;;;;;;
(define-datatype method method?
(a-method
(vars (list-of symbol?))
(body expression?)
(super-name symbol?)
(field-names (list-of symbol?))))
;;;;;;;;;;;;;;;; method environments ;;;;;;;;;;;;;;;;
;; a method environment looks like ((method-name method) ...)
(define method-environment?
(list-of
(lambda (p)
(and
(pair? p)
(symbol? (car p))
(method? (cadr p))))))
;; method-env * id -> (maybe method)
(define assq-method-env
(lambda (m-env id)
(cond
((assq id m-env) => cadr)
(else #f))))
;; find-method : Sym * Sym -> Method
;; Page: 345
(define find-method
(lambda (c-name name)
(let ((m-env (class->method-env (lookup-class c-name))))
(let ((maybe-pair (assq name m-env)))
(if (pair? maybe-pair) (cadr maybe-pair)
(report-method-not-found name))))))
(define report-method-not-found
(lambda (name)
(eopl:error 'find-method "unknown method ~s" name)))
;; merge-method-envs : MethodEnv * MethodEnv -> MethodEnv
;; Page: 345
(define merge-method-envs
(lambda (super-m-env new-m-env)
(append new-m-env super-m-env)))
;; method-decls->method-env :
;; Listof(MethodDecl) * ClassName * Listof(FieldName) -> MethodEnv
;; Page: 345
(define method-decls->method-env
(lambda (m-decls super-name field-names)
(map
(lambda (m-decl)
(cases method-decl m-decl
(a-method-decl (method-name vars body)
(list method-name
(a-method vars body super-name field-names)))))
m-decls)))
;;;;;;;;;;;;;;;; classes ;;;;;;;;;;;;;;;;
(define-datatype class class?
(a-class
(super-name (maybe symbol?))
(field-names (list-of symbol?))
(method-env method-environment?)))
;;;;;;;;;;;;;;;; class environments ;;;;;;;;;;;;;;;;
;; the-class-env will look like ((class-name class) ...)
;; the-class-env : ClassEnv
;; Page: 343
(define the-class-env '())
;; add-to-class-env! : ClassName * Class -> Unspecified
;; Page: 343
(define add-to-class-env!
(lambda (class-name class)
(set! the-class-env
(cons
(list class-name class)
the-class-env))))
;; lookup-class : ClassName -> Class
(define lookup-class
(lambda (name)
(let ((maybe-pair (assq name the-class-env)))
(if maybe-pair (cadr maybe-pair)
(report-unknown-class name)))))
(define report-unknown-class
(lambda (name)
(eopl:error 'lookup-class "Unknown class ~s" name)))
;; constructing classes
;; initialize-class-env! : Listof(ClassDecl) -> Unspecified
;; Page: 344
(define initialize-class-env!
(lambda (c-decls)
(set! the-class-env
(list
(list 'object (a-class #f '() '()))))
(for-each initialize-class-decl! c-decls)))
;; initialize-class-decl! : ClassDecl -> Unspecified
(define initialize-class-decl!
(lambda (c-decl)
(cases class-decl c-decl
(a-class-decl (c-name s-name f-names m-decls)
(let ((f-names
(append-field-names
(class->field-names (lookup-class s-name))
f-names)))
(add-to-class-env!
c-name
(a-class s-name f-names
(merge-method-envs
(class->method-env (lookup-class s-name))
(method-decls->method-env
m-decls s-name f-names)))))))))
;; exercise: rewrite this so there's only one set! to the-class-env.
;; append-field-names : Listof(FieldName) * Listof(FieldName)
;; -> Listof(FieldName)
;; Page: 344
;; like append, except that any super-field that is shadowed by a
;; new-field is replaced by a gensym
(define append-field-names
(lambda (super-fields new-fields)
(cond
((null? super-fields) new-fields)
(else
(cons
(if (memq (car super-fields) new-fields)
(fresh-identifier (car super-fields))
(car super-fields))
(append-field-names
(cdr super-fields) new-fields))))))
;;;;;;;;;;;;;;;; selectors ;;;;;;;;;;;;;;;;
(define class->super-name
(lambda (c-struct)
(cases class c-struct
(a-class (super-name field-names method-env)
super-name))))
(define class->field-names
(lambda (c-struct)
(cases class c-struct
(a-class (super-name field-names method-env)
field-names))))
(define class->method-env
(lambda (c-struct)
(cases class c-struct
(a-class (super-name field-names method-env)
method-env))))
(define object->class-name
(lambda (obj)
(cases object obj
(an-object (class-name fields)
class-name))))
(define object->fields
(lambda (obj)
(cases object obj
(an-object (class-decl fields)
fields))))
(define fresh-identifier
(let ((sn 0))
(lambda (identifier)
(set! sn (+ sn 1))
(string->symbol
(string-append
(symbol->string identifier)
"%" ; this can't appear in an input identifier
(number->string sn))))))
(define maybe
(lambda (pred)
(lambda (v)
(or (not v) (pred v)))))