970 lines
47 KiB
Scheme
970 lines
47 KiB
Scheme
(module types mzscheme
|
|
|
|
(require
|
|
(only srfi/1 lset-intersection)
|
|
mzlib/etc
|
|
mzlib/pretty
|
|
mzlib/list
|
|
mzlib/class
|
|
"ast.ss")
|
|
|
|
(provide (all-defined-except number-assign-conversions remove-dups meth-member?
|
|
contained-in? consolidate-lists subset? depth conversion-steps
|
|
generate-require-spec))
|
|
|
|
;; symbol-type = 'null | 'string | 'boolean | 'char | 'byte | 'short | 'int
|
|
;; | 'long | 'float | 'double | 'void | 'dynamic
|
|
;; reference-type = 'null | 'string | (make-ref-type string (list string))
|
|
;; array-type = (make-array-type type int)
|
|
;; type = symbol-type
|
|
;; | reference-type
|
|
;; | array-type
|
|
;; | dynamic-val
|
|
;; | unknown-ref
|
|
|
|
(define-struct ref-type (class/iface path) (make-inspector))
|
|
(define-struct array-type (type dim))
|
|
|
|
(define object-type (make-ref-type "Object" `("java" "lang")))
|
|
(define string-type (make-ref-type "String" `("java" "lang")))
|
|
(define throw-type (make-ref-type "Throwable" `("java" "lang")))
|
|
(define runtime-exn-type (make-ref-type "RuntimeException" `("java" "lang")))
|
|
(define serializable-type (make-ref-type "Serializable" `("java" "io")))
|
|
(define comparable-type (make-ref-type "Comparable" `("java" "lang")))
|
|
(define cloneable-type (make-ref-type "Cloneable" `("java" "lang")))
|
|
|
|
(define (object-method? m-rec)
|
|
(or
|
|
(and (equal? (method-record-name m-rec) "equals")
|
|
(eq? (method-record-rtype m-rec) 'boolean)
|
|
(= 1 (length (method-record-atypes m-rec)))
|
|
(type=? object-type (car (method-record-atypes m-rec))))
|
|
(and (equal? (method-record-name m-rec) "hashcode")
|
|
(eq? (method-record-rtype m-rec) 'int)
|
|
(= 0 (length (method-record-atypes m-rec))))
|
|
))
|
|
|
|
;
|
|
;
|
|
; ; ; ;
|
|
; ; ;
|
|
; ;;;;;;; ; ;
|
|
; ; ; ;
|
|
; ; ; ; ; ;;; ;;;; ;;; ; ;;; ;;;; ;;; ; ; ;;; ; ;;; ;;; ;
|
|
; ; ; ; ;; ;; ; ;; ; ; ;; ; ; ;; ; ; ; ; ; ;; ; ;; ;;
|
|
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ; ;;;;;; ; ; ; ;;;;;; ; ;;; ; ; ; ; ;
|
|
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ; ;; ;; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ;;
|
|
; ; ; ; ;;; ;;; ;;; ; ; ;;; ;;; ; ; ;;;;; ; ; ;;; ;
|
|
; ; ; ;
|
|
; ; ; ; ;;
|
|
; ;; ; ;;;
|
|
;
|
|
|
|
|
|
;; reference-type: 'a -> boolean
|
|
(define (reference-type? x)
|
|
(if (and (dynamic-val? x) (dynamic-val-type x))
|
|
(reference-type? (dynamic-val-type x))
|
|
(or (dynamic-val? x)
|
|
(unknown-ref? x)
|
|
(ref-type? x)
|
|
(memq x `(null string)))))
|
|
|
|
;;reference-or-array-type: 'a -> boolean
|
|
(define (reference-or-array-type? x)
|
|
(or (reference-type? x)
|
|
(array-type? x)))
|
|
|
|
;;is-string?: 'a -> boolean
|
|
(define (is-string-type? s)
|
|
(if (dynamic-val? s)
|
|
(is-string-type? (dynamic-val-type s))
|
|
(and (reference-type? s)
|
|
(or (eq? 'string s) (type=? s string-type)))))
|
|
|
|
;; 4.2
|
|
;; prim-integral-type?: 'a -> boolean
|
|
(define (prim-integral-type? t)
|
|
(cond
|
|
((and (dynamic-val? t) (dynamic-val-type t))
|
|
(prim-integral-type? (dynamic-val-type t)))
|
|
((dynamic-val? t) #t)
|
|
(else (memq t `(byte short int long char)))))
|
|
;; prim-numeric-type?: 'a -> boolean
|
|
(define (prim-numeric-type? t)
|
|
(cond
|
|
((and (dynamic-val? t) (dynamic-val-type t))
|
|
(prim-numeric-type? (dynamic-val-type t)))
|
|
((dynamic-val? t) #t)
|
|
(else (or (prim-integral-type? t) (memq t `(float double))))))
|
|
|
|
;; type=?: type type -> boolean
|
|
(define (type=? t1 t2)
|
|
(cond
|
|
((and (symbol? t1) (symbol? t2))
|
|
(symbol=? t1 t2))
|
|
((and (ref-type? t1) (ref-type? t2))
|
|
(and (string=? (ref-type-class/iface t1) (ref-type-class/iface t2))
|
|
(= (length (ref-type-path t1)) (length (ref-type-path t2)))
|
|
(andmap
|
|
(lambda (x y)
|
|
(string=? x y))
|
|
(ref-type-path t1)
|
|
(ref-type-path t2))))
|
|
((and (array-type? t1) (array-type? t2))
|
|
(and (= (array-type-dim t1) (array-type-dim t2))
|
|
(type=? (array-type-type t1) (array-type-type t2))))
|
|
((or (symbol? t1) (symbol? t2))
|
|
(or (or (and (eq? t1 'null) (ref-type? t2))
|
|
(and (eq? t2 'null) (ref-type? t1)))
|
|
(and (eq? t1 'string) (type=? t2 string-type))
|
|
(and (eq? t2 'string) (type=? t1 string-type))))
|
|
(else #f)))
|
|
|
|
;; 5.1.2
|
|
;; widening-prim-conversion: symbol-type symbol-type -> boolean
|
|
(define (widening-prim-conversion to from)
|
|
(cond
|
|
((symbol=? to from) #t)
|
|
((symbol=? to 'char) #f)
|
|
((symbol=? 'short to)
|
|
(symbol=? 'byte from))
|
|
((symbol=? 'int to)
|
|
(memq from `(byte short char)))
|
|
((symbol=? 'long to)
|
|
(memq from `(byte short char int)))
|
|
((symbol=? 'float to)
|
|
(memq from `(byte short char int long)))
|
|
((symbol=? 'double to)
|
|
(memq from `(byte short char int long float)))))
|
|
|
|
;; 5.1.4
|
|
;; widening-ref-conversion: type type type-records -> boolean
|
|
(define (widening-ref-conversion to from type-recs)
|
|
(cond
|
|
((and (symbol? from) (symbol=? from 'null))
|
|
(or (ref-type? to) (symbol=? 'string to) (array-type? to)))
|
|
((and (symbol? from) (symbol=? from 'string))
|
|
(or (type=? to object-type)
|
|
(type=? to serializable-type)
|
|
(type=? to comparable-type)))
|
|
((and (ref-type? from) (ref-type? to))
|
|
(or (is-subclass? from to type-recs)
|
|
(implements? from to type-recs)
|
|
(and (is-interface? from type-recs)
|
|
(type=? object-type to))))
|
|
((array-type? from)
|
|
(or (type=? object-type to)
|
|
(type=? cloneable-type to)
|
|
(type=? serializable-type to)
|
|
(and (array-type? to) (= (array-type-dim from) (array-type-dim to))
|
|
(assignment-conversion (array-type-type to) (array-type-type from) type-recs))))
|
|
(else #f)))
|
|
|
|
;; 5.2
|
|
;; SKIP - possible narrowing conversion for constants
|
|
;; assignment-conversion: type type type-records -> boolean
|
|
(define (assignment-conversion to from type-recs)
|
|
(cond
|
|
((dynamic-val? to)
|
|
(cond
|
|
((dynamic-val-type to) => (lambda (t) (assignment-conversion t from type-recs)))
|
|
(else (set-dynamic-val-type! to from) #t)))
|
|
((dynamic-val? from)
|
|
(cond
|
|
((dynamic-val-type from) => (lambda (t) (assignment-conversion to t type-recs)))
|
|
(else (set-dynamic-val-type! from to) #t)))
|
|
((eq? to 'dynamic) #t)
|
|
((type=? to from) #t)
|
|
((and (prim-numeric-type? to) (prim-numeric-type? from))
|
|
(widening-prim-conversion to from))
|
|
(else
|
|
(widening-ref-conversion to from type-recs))))
|
|
|
|
;castable?: reference-type reference-type type-records -> boolean
|
|
(define (castable? from to type-recs)
|
|
(or (dynamic-val? from)
|
|
(dynamic-val? to)
|
|
(eq? 'dynamic to)
|
|
(eq? 'null from)
|
|
(eq? 'null to)
|
|
(let ((from-record (and (not (array-type? from)) (send type-recs get-class-record from)))
|
|
(to-record (and (not (array-type? to))
|
|
(get-record (send type-recs get-class-record to) type-recs))))
|
|
(cond
|
|
((and to-record from-record
|
|
(class-record-class? from-record)
|
|
(class-record-class? to-record))
|
|
(or (is-eq-subclass? from to type-recs)
|
|
(is-eq-subclass? to from type-recs)))
|
|
((and to-record from-record (class-record-class? from-record))
|
|
(or (not (memq 'final (class-record-modifiers from-record)))
|
|
(implements? from to type-recs)))
|
|
((and (not to-record) from-record (class-record-class? from-record))
|
|
(type=? object-type from))
|
|
((and to-record from-record (class-record-class? to-record))
|
|
(or (not (memq 'final (class-record-modifiers to-record)))
|
|
(implements? to from type-recs)))
|
|
((and to-record from-record (not (class-record-class? to-record)))
|
|
(not (signature-conflicts? (class-record-methods to-record)
|
|
(class-record-methods from-record))))
|
|
((and (not from-record) to-record (class-record-class? to-record))
|
|
(type=? object-type to))
|
|
((and (not from-record) to-record)
|
|
(or (type=? serializable-type to type-recs)
|
|
(type=? cloneable-type to type-recs)))
|
|
(else
|
|
(or (type=? (array-type-type to) (array-type-type from))
|
|
(castable? (array-type-type from)
|
|
(array-type-type to)
|
|
type-recs)))))))
|
|
|
|
;Do the two lists of method signatures have conflicting methods
|
|
;signature-conflicts? (list method-record) (list method-record) -> bool
|
|
(define (signature-conflicts? methods1 methods2)
|
|
(let ((same-sigs (lset-intersection signature-equals? methods1 methods2))
|
|
(same-rets (lset-intersection full-signature-equals? methods1 methods2)))
|
|
(not (= (length same-sigs) (length same-rets)))))
|
|
|
|
;Do the two methods have same name and argument types
|
|
;signature-equals? method-record method-record -> bool
|
|
(define (signature-equals? m1 m2)
|
|
(and (equal? (method-record-name m1)
|
|
(method-record-name m2))
|
|
(= (length (method-record-atypes m1))
|
|
(length (method-record-atypes m2)))
|
|
(andmap type=? (method-record-atypes m1) (method-record-atypes m2))))
|
|
;Do the two methods have the same name, arguments and return types
|
|
;full-signagure-equals? method-record method-record -> bool
|
|
(define (full-signature-equals? m1 m2)
|
|
(and (signature-equals? m1 m2)
|
|
(type=? (method-record-rtype m1) (method-record-rtype m2))))
|
|
|
|
;;equal-greater-access? (list symbol) (list symbol) -> boolean
|
|
(define (equal-greater-access? mods-l mods-r)
|
|
(let ([eq-gt?
|
|
(lambda (acc-l acc-r)
|
|
(case acc-l
|
|
[(public) (memq acc-r '(package protected public))]
|
|
[(protected) (memq acc-r '(package protected))]
|
|
[(package) (memq acc-r '(package))]
|
|
[else #f]))])
|
|
(eq-gt? (extract-access mods-l) (extract-access mods-r))))
|
|
|
|
(define (extract-access mods)
|
|
(cond
|
|
[(memq 'public mods) 'public]
|
|
[(memq 'protected mods) 'protected]
|
|
[(memq 'private mods) 'private]
|
|
[else 'package]))
|
|
|
|
;; type-spec-to-type: type-spec (U #f (list string) symbol type-records -> type
|
|
(define (type-spec-to-type ts container-class level type-recs)
|
|
(let* ((ts-name (type-spec-name ts))
|
|
(t (cond
|
|
((memq ts-name `(null string boolean char byte short int long float double void ctor dynamic)) ts-name)
|
|
((name? ts-name) (name->type ts-name container-class (type-spec-src ts) level type-recs)))))
|
|
(if (> (type-spec-dim ts) 0)
|
|
(make-array-type t (type-spec-dim ts))
|
|
t)))
|
|
|
|
;name->type: name (U (list string) #f) src symbol type-records -> type
|
|
(define (name->type n container-class src level type-recs)
|
|
(let* ((name (id-string (name-id n)))
|
|
(path (map id-string (name-path n)))
|
|
(rec (type-exists? name path container-class src level type-recs)))
|
|
(if (class-record? rec)
|
|
(make-ref-type (car (class-record-name rec))
|
|
(cdr (class-record-name rec)))
|
|
(make-ref-type name (if (null? path)
|
|
(send type-recs lookup-path name (lambda () null)) path)))))
|
|
|
|
|
|
;; type-exists: string (list string) (U (list string) #f) src symbol type-records -> (U record procedure)
|
|
(define (type-exists? name path container-class src level type-recs)
|
|
(send type-recs get-class-record (cons name path) container-class
|
|
((get-importer type-recs) (cons name path) type-recs level src)))
|
|
|
|
;; is-interface?: (U type (list string) 'string) type-records-> boolean
|
|
(define (is-interface? t type-recs)
|
|
(not (class-record-class?
|
|
(get-record (send type-recs get-class-record t) type-recs))))
|
|
|
|
;;Is c1 a subclass of c2?
|
|
;; is-subclass?: (U type (list string) 'string) ref-type type-records -> boolean
|
|
(define (is-subclass? c1 c2 type-recs)
|
|
(or (type=? object-type c2)
|
|
(let ((cr (get-record (send type-recs get-class-record c1) type-recs)))
|
|
(member (cons (ref-type-class/iface c2) (ref-type-path c2))
|
|
(class-record-parents cr)))))
|
|
|
|
;Does c1 implement c2?
|
|
;; implements?: (U type (list string) 'string) ref-type type-records -> boolean
|
|
(define (implements? c1 c2 type-recs)
|
|
(let ((cr (get-record (send type-recs get-class-record c1) type-recs)))
|
|
(member (cons (ref-type-class/iface c2) (ref-type-path c2))
|
|
(class-record-ifaces cr))))
|
|
|
|
;;Is class1 a subclass or equal to class2?
|
|
;is-eq-subclass: type type type-records -> boolean
|
|
(define (is-eq-subclass? class1 class2 type-recs)
|
|
(or (type=? class1 class2)
|
|
(and (reference-type? class1)
|
|
(reference-type? class2)
|
|
(is-subclass? class1 class2 type-recs))))
|
|
|
|
;
|
|
;
|
|
;
|
|
; ;;; ;
|
|
; ;;;; ; ;;;;; ;
|
|
; ; ; ; ; ;; ;
|
|
; ;; ; ; ; ;
|
|
; ; ; ;;;; ;;;; ;;;; ; ; ;;;; ;;; ;;;; ; ;;; ;;; ; ;;;;
|
|
; ; ; ; ; ; ; ; ; ;; ; ;; ; ; ;; ;; ;; ;; ;; ; ;
|
|
; ; ; ;;;;; ;; ;; ;;;;;; ; ; ; ; ; ; ; ; ;;
|
|
; ; ; ;; ; ;;;; ;;;; ; ; ;;;;;; ; ; ; ; ; ; ;;;;
|
|
; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ;; ;; ; ;; ;; ; ;
|
|
; ;;;; ;;;;; ;;; ; ;;;; ;;;; ; ; ;;; ;;; ;;;; ; ;;; ; ;;;;
|
|
;
|
|
;
|
|
;
|
|
|
|
;; (make-class-record (list string) (list symbol) boolean boolean (list field-record)
|
|
;; (list method-records) (list inner-record) (list (list strings)) (list (list strings)))
|
|
;; After full processing fields and methods should contain all inherited fields
|
|
;; and methods. Also parents and ifaces should contain all super-classes/ifaces
|
|
(define-struct class-record (name modifiers class? object? fields methods inners parents ifaces) (make-inspector))
|
|
|
|
(define interactions-record (make-class-record (list "interactions") null #f #f null null null null null))
|
|
|
|
;; (make-field-record string (list symbol) bool (list string) type)
|
|
(define-struct field-record (name modifiers init? class type) (make-inspector))
|
|
|
|
;; (make-method-record string (list symbol) type (list type) (list type) (U bool method-record) string)
|
|
(define-struct method-record (name modifiers rtype atypes throws override class) (make-inspector))
|
|
|
|
;;(make-inner-record string string (list symbol) bool)
|
|
(define-struct inner-record (name full-name modifiers class?) (make-inspector))
|
|
|
|
;;(make-scheme-record string (list string) path (list dynamic-val))
|
|
(define-struct scheme-record (name path dir provides))
|
|
|
|
;;(make-dynamic-val (U type method-contract unknown-ref))
|
|
(define-struct dynamic-val (type) (make-inspector))
|
|
|
|
;;(make-unknown-ref (U method-contract field-contract))
|
|
(define-struct unknown-ref (access) (make-inspector))
|
|
|
|
;;(make-method-contract string type (list type) (U #f string))
|
|
(define-struct method-contract (name return args prefix) (make-inspector))
|
|
|
|
;;(make-field-contract string type)
|
|
(define-struct field-contract (name type))
|
|
|
|
;
|
|
; ;;
|
|
; ; ;
|
|
; ; ;
|
|
; ;;;;; ;;; ;;;; ;;; ;;; ; ;;; ;;; ;;; ;;; ; ;;; ;;;; ;;;
|
|
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ; ;;;;; ;;;;; ; ;;;;; ; ; ; ; ; ; ;;;
|
|
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ; ; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ;;; ; ;;;; ;;; ;;;; ;;; ;;; ;;; ;;;; ;;; ; ;;;
|
|
; ; ;
|
|
; ; ;
|
|
; ;; ;;;
|
|
|
|
;Class to store various information per package compilation
|
|
(define type-records
|
|
(class object%
|
|
|
|
(field (importer
|
|
(lambda ()
|
|
(error 'internal-error "type-records importer field was not set"))))
|
|
|
|
;Stores type information and require syntax per compile or execution
|
|
(define records (make-hash-table 'equal))
|
|
(define requires (make-hash-table 'equal))
|
|
(define package-contents (make-hash-table 'equal))
|
|
|
|
;Stores per-class information accessed by location
|
|
(define class-environment (make-hash-table))
|
|
(define class-require (make-hash-table))
|
|
|
|
(define compilation-location (make-hash-table))
|
|
|
|
(define class-reqs null)
|
|
(define location #f)
|
|
|
|
;add-class-record: class-record -> void
|
|
(define/public (add-class-record r)
|
|
(hash-table-put! records (class-record-name r) r))
|
|
;add-to-records: (list string) ( -> 'a) -> void
|
|
(define/public (add-to-records key thunk)
|
|
(hash-table-put! records key thunk))
|
|
|
|
;; get-class-record: (U type (list string) 'string) (U (list string) #f) ( -> 'a) ->
|
|
;; (U class-record scheme-record procedure)
|
|
(define/public get-class-record
|
|
(opt-lambda (ctype [container #f] [fail (lambda () null)])
|
|
;(printf "get-class-record: ctype->~a container->~a ~n" ctype container)
|
|
(let*-values (((key key-path) (normalize-key ctype))
|
|
((key-inner) (when (cons? container) (string-append (car container) "." key)))
|
|
((outer-record) (when (cons? container) (get-class-record container)))
|
|
((path) (if (null? key-path) (lookup-path key (lambda () null)) key-path))
|
|
((inner-path) (if (null? key-path) (lookup-path key-inner (lambda () null)) key-path))
|
|
((new-search)
|
|
(lambda ()
|
|
(cond
|
|
((null? path) (fail))
|
|
(else
|
|
(let ((back-path (reverse path)))
|
|
(search-for-record key (car back-path)
|
|
(reverse (cdr back-path)) (lambda () #f) fail)))))))
|
|
;(printf "key ~a key-path ~a path ~a location ~a ~n" key key-path path location)
|
|
;(printf "get-class-record: ~a~n" ctype)
|
|
;(hash-table-for-each records (lambda (k v) (printf "~a -> ~a~n" k v)))
|
|
(cond
|
|
((and container
|
|
(not (null? outer-record))
|
|
(not (eq? outer-record 'in-progress))
|
|
(member key (map inner-record-name (class-record-inners (get-record outer-record this)))))
|
|
(hash-table-get records (cons key-inner (cdr container)) fail))
|
|
((and container (not (null? outer-record)) (eq? outer-record 'in-progress))
|
|
(let ((res (hash-table-get records (cons key-inner inner-path) (lambda () #f))))
|
|
(or res
|
|
(hash-table-get records (cons key path) new-search))))
|
|
(else
|
|
(hash-table-get records (cons key path) new-search))))))
|
|
|
|
;normalize-key: (U 'strung ref-type (list string)) -> (values string (list string))
|
|
(define/private (normalize-key ctype)
|
|
(cond
|
|
((eq? ctype 'string) (values "String" `("java" "lang")))
|
|
((ref-type? ctype) (values (ref-type-class/iface ctype) (ref-type-path ctype)))
|
|
((cons? ctype) (values (car ctype) (cdr ctype)))
|
|
(else (values ctype null))))
|
|
|
|
;search-for-record string string (list string) (-> #f) (-> 'a) -> class-record
|
|
(define/private (search-for-record class-name new-prefix path test-fail fail)
|
|
(let* ((new-class-name (string-append new-prefix "." class-name))
|
|
(rec? (hash-table-get records (cons new-class-name path) test-fail))
|
|
(back-path (reverse path)))
|
|
(cond
|
|
(rec? rec?)
|
|
((null? path) (fail))
|
|
(else (search-for-record new-class-name (car back-path) (reverse (cdr back-path)) test-fail fail)))))
|
|
|
|
;add-package-contents: (list string) (list string) -> void
|
|
(define/public (add-package-contents package classes)
|
|
(let ((existing-classes (hash-table-get package-contents package (lambda () null))))
|
|
(if (null? existing-classes)
|
|
(hash-table-put! package-contents package classes)
|
|
(hash-table-put! package-contents package (non-dup-append classes existing-classes)))))
|
|
|
|
(define/private (non-dup-append cl pa)
|
|
(cond
|
|
((null? cl) pa)
|
|
((member (car cl) pa) (non-dup-append (cdr cl) pa))
|
|
(else (cons (car cl) (non-dup-append (cdr cl) pa)))))
|
|
|
|
;get-package-contents: (list string) ( -> 'a) -> (list string)
|
|
(define/public (get-package-contents package fail)
|
|
(hash-table-get package-contents package fail))
|
|
|
|
;add-to-env: string (list string) file -> void
|
|
(define/public (add-to-env class path loc)
|
|
#;(printf "add-to-env class ~a path ~a loc ~a~n~n" class path loc)
|
|
(unless (hash-table-get (hash-table-get class-environment loc
|
|
(lambda ()
|
|
(let ([new-t (make-hash-table 'equal)])
|
|
(hash-table-put! class-environment loc new-t)
|
|
new-t)))
|
|
class (lambda () #f))
|
|
(hash-table-put! (hash-table-get class-environment loc) class path)))
|
|
|
|
;Returns the environment of classes for the current location
|
|
;get-class-env: -> (list string)
|
|
(define/public (get-class-env)
|
|
(hash-table-map (hash-table-get class-environment location) (lambda (key val) key)))
|
|
|
|
(define (env-failure)
|
|
(error 'class-environment "Internal Error: environment does not have location"))
|
|
|
|
;lookup-path: string ( -> 'a) -> (U (list string) #f)
|
|
(define/public (lookup-path class fail)
|
|
#;(printf "class ~a location ~a~n" class location)
|
|
#;(printf "lookup ~a~n" class)
|
|
#;(hash-table-for-each (hash-table-get class-environment location)
|
|
(lambda (k v) (printf "~a -> ~a~n" k v)))
|
|
(if location
|
|
(hash-table-get (hash-table-get class-environment
|
|
location
|
|
env-failure)
|
|
class fail)
|
|
(fail)))
|
|
|
|
;add-require-syntax: (list string) (list syntax syntax) -> void
|
|
(define/public (add-require-syntax name syn)
|
|
(get-require-syntax #t name (lambda () (hash-table-put! requires (cons #t name) (car syn))))
|
|
(get-require-syntax #f name (lambda () (hash-table-put! requires (cons #f name) (cadr syn)))))
|
|
|
|
(define (syntax-fail)
|
|
(error 'syntax "Internal Error: syntax did not have given req"))
|
|
|
|
;get-require-syntax: bool (list string) . ( -> 'a) -> syntax
|
|
(define/public (get-require-syntax prefix? name . fail)
|
|
(hash-table-get requires (cons prefix? name) (if (null? fail) syntax-fail (car fail))))
|
|
|
|
;add-class-req: name boolean location -> void
|
|
(define/public (add-class-req name pre loc)
|
|
(hash-table-put! (hash-table-get class-require
|
|
loc
|
|
(lambda () (let ((new-t (make-hash-table 'equal)))
|
|
(hash-table-put! class-require loc new-t)
|
|
new-t)))
|
|
name pre))
|
|
|
|
;require-fail
|
|
(define (require-fail)
|
|
(error 'require-prefix "Internal Error: require does not have location"))
|
|
|
|
;require-prefix?: (list string) ( -> 'a) -> bool
|
|
(define/public (require-prefix? name fail)
|
|
(hash-table-get (hash-table-get class-require location require-fail) name fail))
|
|
|
|
(define/private (member-req req reqs)
|
|
(and (not (null? reqs))
|
|
(or (and (equal? (req-class req) (req-class (car reqs)))
|
|
(equal? (req-path req) (req-path (car reqs))))
|
|
(member-req req (cdr reqs)))))
|
|
|
|
(define/public (set-compilation-location loc dir) (hash-table-put! compilation-location loc dir))
|
|
(define/public (get-compilation-location)
|
|
(hash-table-get compilation-location location
|
|
(lambda () (error 'get-compilation-location "Internal error: location not found"))))
|
|
(define/public (set-composite-location name dir) (hash-table-put! compilation-location name dir))
|
|
(define/public (get-composite-location name)
|
|
;(printf "get-composite-location for ~a~n" name)
|
|
;(hash-table-for-each compilation-location
|
|
; (lambda (k v) (printf "~a -> ~a~n" k v)))
|
|
(hash-table-get compilation-location name
|
|
(lambda () (error 'get-composite-location "Internal error: name not found"))))
|
|
|
|
(define/public (add-req req)
|
|
(unless (member-req req class-reqs)
|
|
(set! class-reqs (cons req class-reqs))))
|
|
(define/public (get-class-reqs) class-reqs)
|
|
(define/public (set-class-reqs reqs) (set! class-reqs reqs))
|
|
|
|
(define/public (set-location! l) (set! location l))
|
|
(define/public (get-location) location)
|
|
|
|
(define interaction-package null)
|
|
(define interaction-fields null)
|
|
(define interaction-boxes null)
|
|
(define execution-loc #f)
|
|
|
|
(define/public (set-interactions-package p) (set! interaction-package p))
|
|
(define/public (get-interactions-package) interaction-package)
|
|
(define/public (add-interactions-field rec)
|
|
(set! interaction-fields (cons rec interaction-fields)))
|
|
(define/public (get-interactions-fields)
|
|
interaction-fields)
|
|
(define/public (clear-interactions)
|
|
(set! interaction-fields null))
|
|
(define/public (add-interactions-box box)
|
|
(set! interaction-boxes (cons box interaction-boxes)))
|
|
(define/public (get-interactions-boxes) (reverse interaction-boxes))
|
|
(define/public (set-execution-loc! loc) (set! execution-loc loc))
|
|
|
|
(define/public (give-interaction-execution-names)
|
|
(when execution-loc
|
|
(hash-table-for-each (hash-table-get class-environment execution-loc)
|
|
(lambda (k v) (add-to-env k v 'interactions)))
|
|
(set! execution-loc #f)))
|
|
|
|
(define test-classes null)
|
|
(define/public (add-test-class name)
|
|
(set! test-classes (cons name test-classes)))
|
|
(define/public (get-test-classes) test-classes)
|
|
|
|
(super-instantiate ())))
|
|
|
|
(define get-importer (class-field-accessor type-records importer))
|
|
(define set-importer! (class-field-mutator type-records importer))
|
|
|
|
;
|
|
|
|
;
|
|
; ;;;; ; ;
|
|
; ; ; ; ;
|
|
; ; ;;;; ;;;;;; ;;;;;; ;;;; ; ;; ;;;;
|
|
; ; ; ;; ; ; ; ;; ;; ; ; ;
|
|
; ; ;; ; ; ; ; ; ; ; ;;
|
|
; ; ; ;;;;;; ; ; ;;;;;; ; ;;;;
|
|
; ; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ; ; ; ; ; ; ;
|
|
; ;;;; ;;; ;;; ;;; ;;; ; ;;;;
|
|
;
|
|
;
|
|
|
|
;get-record: (U class-record procedure) type-records -> class-record
|
|
(define (get-record rec type-recs)
|
|
(cond
|
|
((procedure? rec)
|
|
(let ((location (send type-recs get-location)))
|
|
(begin0 (rec)
|
|
(send type-recs set-location! location))))
|
|
(else rec)))
|
|
|
|
;; get-field-record: string class-record (-> 'a) -> field-record
|
|
(define (get-field-record fname c fail)
|
|
(let ((frec (filter (lambda (f)
|
|
(string=? (field-record-name f) fname))
|
|
(class-record-fields c))))
|
|
(cond
|
|
((null? frec) (fail))
|
|
(else (car frec)))))
|
|
|
|
;get-field-records: class-record -> (list field-record)
|
|
(define (get-field-records c) (class-record-fields c))
|
|
|
|
;; get-method-records: string class-record type-records -> (list method-record)
|
|
(define (get-method-records mname c type-recs)
|
|
(filter (lambda (m)
|
|
(string=? (method-record-name m) mname))
|
|
(if (class-record-class? c)
|
|
(class-record-methods c)
|
|
(append (class-record-methods c) (get-object-methods type-recs)))))
|
|
|
|
(define (get-object-methods type-recs)
|
|
(class-record-methods (send type-recs get-class-record object-type)))
|
|
|
|
;remove-dups: (list method-record) -> (list method-record)
|
|
(define (remove-dups methods)
|
|
(cond
|
|
((null? methods) methods)
|
|
((meth-member? (car methods) (cdr methods))
|
|
(remove-dups (cdr methods)))
|
|
(else (cons (car methods) (remove-dups (cdr methods))))))
|
|
|
|
;meth-member? method-record (list method-record) -> bool
|
|
(define (meth-member? meth methods)
|
|
(and (not (null? methods))
|
|
(or (andmap type=? (method-record-atypes meth)
|
|
(method-record-atypes (car methods)))
|
|
(meth-member? meth (cdr methods)))))
|
|
|
|
;depth: 'a int (listof 'a) -> (U int #f)
|
|
;The position in elt-list that elt is at, starting with 1
|
|
(define (depth elt start elt-list)
|
|
(letrec ((d
|
|
(lambda (elt-list cnt)
|
|
#;(printf "d: elt ~a elt-list ~a~n" elt elt-list)
|
|
(cond
|
|
((null? elt-list) +inf.0)
|
|
((equal? (car elt-list) elt) cnt)
|
|
(else (d (cdr elt-list) (add1 cnt)))))))
|
|
(d elt-list start)))
|
|
|
|
;consolidate-lists: (listof (listof alpha)) -> (listof (listof alpha))
|
|
(define (consolidate-lists lsts)
|
|
(cond
|
|
((or (null? lsts) (null? (cdr lsts))) lsts)
|
|
((contained-in? (car lsts) (cdr lsts))
|
|
(consolidate-lists (cdr lsts)))
|
|
(else
|
|
(cons (car lsts) (consolidate-lists (cdr lsts))))))
|
|
|
|
;contained-in? (listof alpha) (listof (listof alpha)) -> boolean
|
|
(define (contained-in? current rest)
|
|
(and (not (null? rest))
|
|
(or (subset? (reverse current)
|
|
(reverse (car rest)))
|
|
(contained-in? current (cdr rest)))))
|
|
|
|
(define (subset? smaller bigger)
|
|
(or (null? smaller)
|
|
(and (equal? (car smaller) (car bigger))
|
|
(subset? (cdr smaller) (cdr bigger)))))
|
|
|
|
;iface-depth: (list string) (list (list string)) type-records -> int
|
|
(define (iface-depth elt ifaces type-recs)
|
|
(if (= 1 (length ifaces))
|
|
1
|
|
(let* ([iface-trees (map (lambda (iface)
|
|
(cons iface
|
|
(class-record-parents
|
|
(get-record (send type-recs get-class-record iface)
|
|
type-recs))))
|
|
ifaces)]
|
|
[sorted-ifaces (sort iface-trees
|
|
(lambda (a b) (< (length a) (length b))))]
|
|
[ifaces (consolidate-lists sorted-ifaces)])
|
|
#;(printf "iface-depth ~a ~a ~a ~n" elt
|
|
iface-trees (map (lambda (i-list) (depth elt 0 i-list)) iface-trees))
|
|
(if (null? ifaces)
|
|
0
|
|
(apply min (map (lambda (i-list) (depth elt 0 i-list)) ifaces))))))
|
|
|
|
;conversion-steps: type type -> int
|
|
(define (conversion-steps from to type-recs)
|
|
#;(printf "conversion-steps ~a ~a~n" from to)
|
|
(cond
|
|
((ref-type? from)
|
|
(let* ((to-name (cons (ref-type-class/iface to) (ref-type-path to)))
|
|
(from-class (send type-recs get-class-record from))
|
|
(from-class-parents (class-record-parents from-class))
|
|
(from-class-ifaces (class-record-ifaces from-class)))
|
|
(cond
|
|
((eq? to 'dynamic) (length from-class-parents))
|
|
((null? from-class-parents)
|
|
(iface-depth to-name from-class-ifaces type-recs))
|
|
((null? from-class-ifaces)
|
|
(depth to-name 1 from-class-parents))
|
|
(else (min (depth to-name 1 from-class-parents)
|
|
(iface-depth to-name from-class-ifaces type-recs))))))
|
|
((array-type? from)
|
|
(cond
|
|
((array-type? to)
|
|
(conversion-steps (array-type-type from) (array-type-type to) type-recs))
|
|
(else
|
|
(add1 (conversion-steps (array-type-type from) to type-recs)))))
|
|
(else
|
|
(case from
|
|
((byte) (depth to 1 '(short int long float double)))
|
|
((char) (depth to 1 '(byte short int long float double)))
|
|
((short) (depth to 1 '(int long float double)))
|
|
((int) (depth to 1 '(long float double)))
|
|
((long) (depth to 1 '(float double)))
|
|
(else 1))
|
|
)))
|
|
|
|
;number-assign-conversion: (list type) (list type) type-records -> int
|
|
(define (number-assign-conversions site-args method-args type-recs)
|
|
(cond
|
|
((null? site-args) 0)
|
|
((and (assignment-conversion (car method-args) (car site-args) type-recs)
|
|
(not (type=? (car site-args) (car method-args))))
|
|
(let ((step (conversion-steps (car site-args) (car method-args) type-recs)))
|
|
#;(printf "steps for ~a ~a~n" (car site-args) step)
|
|
(+ step (number-assign-conversions (cdr site-args) (cdr method-args) type-recs))))
|
|
(else (number-assign-conversions (cdr site-args) (cdr method-args) type-recs))))
|
|
|
|
;; resolve-overloading: (list method-record) (list type) (-> 'a) (-> 'a) (-> 'a) type-records-> method-record
|
|
(define (resolve-overloading methods arg-types arg-count-fail method-conflict-fail no-method-fail type-recs)
|
|
#;(print-struct #t)
|
|
(let* ((a (length arg-types))
|
|
(m-atypes method-record-atypes)
|
|
(a-convert? (lambda (t1 t2) (assignment-conversion t1 t2 type-recs)))
|
|
(methods (remove-dups (filter (lambda (mr) (= a (length (m-atypes mr)))) methods)))
|
|
(methods-same (filter (lambda (mr)
|
|
(andmap type=? (m-atypes mr) arg-types))
|
|
methods))
|
|
(assignable (filter (lambda (mr)
|
|
(andmap a-convert? (m-atypes mr) arg-types))
|
|
methods))
|
|
(sort (lambda (l p) (quicksort l p)))
|
|
(assignable-count (sort
|
|
(map (lambda (mr)
|
|
#;(printf "assigning conversions for ~a~n" (m-atypes mr))
|
|
(list (number-assign-conversions arg-types (m-atypes mr) type-recs)
|
|
mr))
|
|
assignable)
|
|
(lambda (i1 i2) (< (car i1) (car i2))))))
|
|
#;(printf "~a~n" assignable-count)
|
|
(cond
|
|
((null? methods) (arg-count-fail))
|
|
((= 1 (length methods-same)) (car methods-same))
|
|
((> (length methods-same) 1) (method-conflict-fail))
|
|
((null? assignable) (no-method-fail))
|
|
((= 1 (length assignable)) (car assignable))
|
|
((= (car (car assignable-count))
|
|
(car (cadr assignable-count))) (method-conflict-fail))
|
|
(else (cadr (car assignable-count))))))
|
|
|
|
;module-has-binding?: scheme-record string (-> void) -> void
|
|
;module-has-binding raises an exception when variable is not defined in mod-ref
|
|
(define (module-has-binding? mod-ref variable fail)
|
|
(let ((var (string->symbol (java-name->scheme variable))))
|
|
(or (memq var (scheme-record-provides mod-ref))
|
|
(let ((mod-syntax (datum->syntax-object #f
|
|
`(,#'module m mzscheme
|
|
(require ,(generate-require-spec (java-name->scheme (scheme-record-name mod-ref))
|
|
(scheme-record-path mod-ref)))
|
|
,var)
|
|
#f)))
|
|
(with-handlers ((exn? (lambda (e) (fail))))
|
|
(parameterize ([current-namespace (make-namespace)])
|
|
(expand mod-syntax)))
|
|
(set-scheme-record-provides! mod-ref (cons var (scheme-record-provides mod-ref)))))))
|
|
|
|
;generate-require-spec: string (list string) -> (U string (list symbol string+))
|
|
(define (generate-require-spec name path)
|
|
(let ((mod (string-append name ".ss")))
|
|
(cond
|
|
((null? path) mod)
|
|
((equal? (car path) "lib") `(lib ,mod ,@(cdr path)))
|
|
(else `(file ,(build-path (apply build-path path) mod))))))
|
|
|
|
;java-name->scheme: string -> string
|
|
(define (java-name->scheme name)
|
|
(cond
|
|
((regexp-match "[a-zA-Z0-9]*To[A-Z0-9]*" name)
|
|
(java-name->scheme (regexp-replace "To" name "->")))
|
|
((regexp-match "[a-zA-Z0-9]+P$" name)
|
|
(java-name->scheme (regexp-replace "P$" name "?")))
|
|
((regexp-match "[a-zA-Z0-9]+Set$" name)
|
|
(java-name->scheme (regexp-replace "Set$" name "!")))
|
|
((regexp-match "[a-zA-Z0-9]+Obj$" name)
|
|
(java-name->scheme (regexp-replace "Obj$" name "%")))
|
|
((regexp-match "[a-z0-9]+->[A-Z]" name) =>
|
|
(lambda (substring)
|
|
(let ((char (car (regexp-match "[A-Z]" (car substring)))))
|
|
(java-name->scheme (regexp-replace (string-append "->" char) name
|
|
(string-append "->" (string (char-downcase (car (string->list char))))))))))
|
|
((regexp-match "[a-z0-9]+[A-Z]" name) =>
|
|
(lambda (substring)
|
|
(let ((char (car (string->list (car (regexp-match "[A-Z]" (car substring))))))
|
|
(remainder (car (regexp-match "[a-z0-9]+" (car substring)))))
|
|
(java-name->scheme (regexp-replace (car substring) name
|
|
(string-append remainder "-" (string (char-downcase char))))))))
|
|
(else name)))
|
|
|
|
(define (inner-rec-member name inners)
|
|
(member name (map inner-record-name inners)))
|
|
|
|
;
|
|
; ; ;;
|
|
; ;
|
|
; ;;; ;
|
|
; ; ;
|
|
; ; ;;; ; ;;; ;;;;; ;;;;
|
|
; ; ; ;; ; ; ;; ;;
|
|
; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ; ; ;
|
|
; ; ; ; ; ; ; ;
|
|
; ; ;; ; ; ; ; ;; ;;
|
|
; ;;; ;;;;; ; ; ; ;;;;
|
|
;
|
|
|
|
|
|
(define type-version "version5")
|
|
(define type-length 11)
|
|
|
|
;; read-record: path -> (U class-record #f)
|
|
(define (read-record filename)
|
|
(letrec ((parse-class/iface
|
|
(lambda (input)
|
|
(and (= (length input) type-length)
|
|
(equal? type-version (list-ref input 9))
|
|
(or (equal? (version) (list-ref input 10))
|
|
(equal? "ignore" (list-ref input 10)))
|
|
(make-class-record (list-ref input 1)
|
|
(list-ref input 2)
|
|
(symbol=? 'class (car input))
|
|
(list-ref input 3)
|
|
(map parse-field (list-ref input 4))
|
|
(map parse-method (list-ref input 5))
|
|
(map parse-inner (list-ref input 6))
|
|
(list-ref input 7)
|
|
(list-ref input 8)))))
|
|
(parse-field
|
|
(lambda (input)
|
|
(make-field-record (car input)
|
|
(cadr input)
|
|
#f
|
|
(caddr input)
|
|
(parse-type (cadddr input)))))
|
|
(parse-method
|
|
(lambda (input)
|
|
(make-method-record (car input)
|
|
(cadr input)
|
|
(parse-type (caddr input))
|
|
(map parse-type (cadddr input))
|
|
(map parse-type (list-ref input 4))
|
|
#f
|
|
(list-ref input 5))))
|
|
(parse-inner
|
|
(lambda (input)
|
|
(make-inner-record (car input)
|
|
(cadr input)
|
|
(caddr input)
|
|
(symbol=? 'class (cadddr input)))))
|
|
(parse-type
|
|
(lambda (input)
|
|
(cond
|
|
((symbol? input) input)
|
|
((number? (car input))
|
|
(make-array-type (parse-type (cadr input)) (car input)))
|
|
(else
|
|
(make-ref-type (car input) (cdr input)))))))
|
|
(parse-class/iface (call-with-input-file filename read))))
|
|
|
|
;; write-record: class-record port->
|
|
(define (write-record rec port)
|
|
(letrec ((record->list
|
|
(lambda (r)
|
|
(list
|
|
(if (class-record-class? r)
|
|
'class
|
|
'interface)
|
|
(class-record-name r)
|
|
(class-record-modifiers r)
|
|
(class-record-object? r)
|
|
(map field->list (class-record-fields r))
|
|
(map method->list
|
|
(let* ((kept-overrides null)
|
|
(methods
|
|
(filter
|
|
(compose not
|
|
(lambda (meth-rec)
|
|
(and (method-record-override meth-rec)
|
|
(or (equal? (method-record-modifiers meth-rec)
|
|
(method-record-modifiers (method-record-override meth-rec)))
|
|
(not (set! kept-overrides (cons (method-record-override meth-rec) kept-overrides)))))))
|
|
(class-record-methods r))))
|
|
(filter (compose not (lambda (m) (memq m kept-overrides))) methods)))
|
|
(map inner->list (class-record-inners r))
|
|
(class-record-parents r)
|
|
(class-record-ifaces r)
|
|
type-version
|
|
(version))))
|
|
(field->list
|
|
(lambda (f)
|
|
(list
|
|
(field-record-name f)
|
|
(field-record-modifiers f)
|
|
(field-record-class f)
|
|
(type->list (field-record-type f)))))
|
|
(method->list
|
|
(lambda (m)
|
|
(list
|
|
(method-record-name m)
|
|
(method-record-modifiers m)
|
|
(type->list (method-record-rtype m))
|
|
(map type->list (method-record-atypes m))
|
|
(map type->list (method-record-throws m))
|
|
(method-record-class m))))
|
|
(inner->list
|
|
(lambda (i)
|
|
(list (inner-record-name i)
|
|
(inner-record-full-name i)
|
|
(inner-record-modifiers i)
|
|
(if (inner-record-class? i) 'class 'interface))))
|
|
(type->list
|
|
(lambda (t)
|
|
(cond
|
|
((symbol? t) t)
|
|
((ref-type? t) (cons (ref-type-class/iface t) (ref-type-path t)))
|
|
((array-type? t)
|
|
(list (array-type-dim t) (type->list (array-type-type t))))))))
|
|
(pretty-print (record->list rec) port)))
|
|
)
|