Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
;;; type inference for Scheme programs
;;; AUTHOR: Gary T. Leavens, using code originally by Mary Feeley
;------------------------------------------------------------------------------
;
; Error handling.
(define fail ; TYPE: datum -> poof
(lambda msg
; EFFECT: print the error message
(error "error:" msg)))
;-----------------------------------------------------------------------------
;
; The unifier.
(define *failure* 'unification-failure)
(load-quietly-from-lib "unify.ss")
;------------------------------------------------------------------------------
;
; An environment is an object that associates variables to values.
; They are represented by association lists.
(define env-empty ; TYPE: -> env
(lambda ()
; ENSURES: result is an empty environment
'()))
(define env-update ; TYPE: variable, datum, env -> env
(lambda (var val env)
; ENSURES: result is a copy of env but with var associated to val
(cons (cons var val) env)))
(define env-join ; TYPE: env, env -> env
(lambda (env1 env2)
; ENSURES: result is a new environment containing the bindings
; in env1 and env2 (env1 has precedence)
(append env1 env2)))
(define env-bound? ; TYPE: variable, env -> boolean
(lambda (var env)
; ENSURES: result is true iff the variable is bound in env
(assq var env)))
(define env-value ; TYPE: variable, env -> datum
(lambda (var env)
; REQUIRES: var has an association in env
; ENSURES: result is the value associated to var in env
(cdr (assq var env))))
;------------------------------------------------------------------------------
;
; Variables are represented by symbols starting with a "?" (e.g. ?a)
(define variable? ; TYPE: datum -> boolean
(lambda (x)
(and (symbol? x) (char=? (string-ref (symbol->string x) 0) #\?))))
(define new-variable ; TYPE: -> variable
(let ((variable-count 0))
(lambda ()
(set! variable-count (+ variable-count 1))
(string->symbol (string-append "?" (number->string variable-count))))))
;------------------------------------------------------------------------------
(define instance ; TYPE: type expr, generic variable list -> type expr
(lambda (x prefix)
; ENSURES: result is an instance of x with new variables
; in place of the generic variables in prefix
(letrec ((new ; TYPE: type expr, env, (type expr, env -> type expr)
; -> type expr
(lambda (x env success)
(cond ((and (variable? x) (generic? x prefix))
(if (env-bound? x env)
(success (env-value x env) env)
(let ((var (new-variable)))
(success var (env-update x var env)))))
((pair? x)
(new (car x) env
(lambda (a env)
(new (cdr x) env
(lambda (b env)
(success (cons a b) env))))))
(else
(success x env))))))
(new x (env-empty) (lambda (a env) a)))))
(define generic? ; TYPE: variable, generic variables list -> boolean
(lambda (var prefix)
; ENSURES: result is true iff the variable var is generic in prefix
(cond ((null? prefix)
#t)
((and (eq? (cadar prefix) var) (not (eq? (caar prefix) 'let)))
#f)
(else
(generic? var (cdr prefix))))))
;------------------------------------------------------------------------------
(define global-var-types
'(
(+ . (-> number number number))
(- . (-> number number number))
(* . (-> number number number))
(/ . (-> number number number))
(< . (-> number number boolean))
(> . (-> number number boolean))
(<= . (-> number number boolean))
(= . (-> number number boolean))
(>= . (-> number number boolean))
(add1 . (-> number number))
(sub1 . (-> number number))
(zero? . (-> number boolean))
(eq? . (-> symbol symbol boolean))
(eqv? . (-> boolean boolean boolean)) ; too conservative...
(equal? . (-> ?a ?b boolean)) ; not quite right...
(cons . (-> ?a (list ?a) (list ?a)))
(car . (-> (list ?a) ?a))
(cdr . (-> (list ?a) (list ?a)))
(caar . (-> (list (list ?a)) ?a))
(cadr . (-> (list ?a) ?a))
(cdar . (-> (list (list ?a)) (list ?a)))
(cddr . (-> (list ?a) (list ?a)))
(set-car! . (-> (list ?a) ?a void))
(set-cdr! . (-> (list ?a) (list ?a) void))
(length . (-> (list ?a) number))
(list-ref . (-> (list ?a) number ?a))
(append . (-> (list ?a) (list ?a) (list ?a)))
(reverse . (-> (list ?a) (list ?a)))
(map . (-> (-> ?a ?b) (list ?a) (list ?b)))
(for-each . (-> (-> ?a void) (list ?a) void))
(null? . (-> ?a boolean))
(pair? . (-> ?a boolean))
(boolean? . (-> ?a boolean))
(symbol? . (-> ?a boolean))
(procedure? . (-> ?a boolean))
(number? . (-> ?a boolean))
(integer? . (-> ?a boolean))
(real? . (-> ?a boolean))
(string? . (-> ?a boolean))
(vector? . (-> ?a boolean))
(not . (-> boolean boolean))
(or . (-> boolean boolean boolean))
(and . (-> boolean boolean boolean))
(call/cc . (-> (-> (-> ?a ?b) ?c) ?a))
(apply . (-> (-> ?a ?b) (list ?a) ?b)) ; works for monadic procs only
(write . (-> ?a void))
(writeln . (-> ?a void)) ; works for one argument only
(display . (-> ?a void))
(newline . (-> void))
(read . (-> ?a))
(string-append . (-> string string string))
(string=? . (-> string string boolean))
(string-ci=? . (-> string string boolean))
(string-length . (-> string string number))
(substring . (-> string number number))
(symbol->string . (-> symbol string))
(list->vector . (-> (list ?a) (vector ?a)))
(make-vector . (-> number ?a (vector ?a)))
(vector-generator . (-> (-> number ?a) (-> number (vector ?a))))
(vector-length . (-> (vector ?a) number ?a))
(vector-ref . (-> (vector ?a) number ?a))
(vector-set! . (-> (vector ?a) number ?a void))
(caaar . (-> (list (list (list ?a))) ?a))
(caadr . (-> (list (list ?a)) ?a))
(cadar . (-> (list (list ?a)) ?a))
(caddr . (-> (list ?a) ?a))
(cdaar . (-> (list (list (list ?a))) (list ?a)))
(cdadr . (-> (list (list ?a)) (list ?a)))
(cddar . (-> (list (list ?a)) (list ?a)))
(cdddr . (-> (list ?a) (list ?a)))
(caaaar . (-> (list (list (list (list ?a)))) ?a))
(caaadr . (-> (list (list (list ?a))) ?a))
(caadar . (-> (list (list (list ?a))) ?a))
(caaddr . (-> (list (list ?a)) ?a))
(cadaar . (-> (list (list (list ?a))) ?a))
(cadadr . (-> (list (list ?a)) ?a))
(caddar . (-> (list (list ?a)) ?a))
(cadddr . (-> (list ?a) ?a))
(cdaaar . (-> (list (list (list (list ?a)))) (list ?a)))
(cdaadr . (-> (list (list (list ?a))) (list ?a)))
(cdadar . (-> (list (list (list ?a))) (list ?a)))
(cdaddr . (-> (list (list ?a)) (list ?a)))
(cddaar . (-> (list (list (list ?a))) (list ?a)))
(cddadr . (-> (list (list ?a)) (list ?a)))
(cdddar . (-> (list (list ?a)) (list ?a)))
(cddddr . (-> (list ?a) (list ?a)))
(negative? . (-> number boolean))
(positive? . (-> number boolean))
(abs . (-> number number))
(ceiling . (-> number number))
(floor . (-> number number))
(truncate . (-> number number))
(round . (-> number number))
(expt . (-> number number number))
(sqrt . (-> number number))
(max . (-> number number number))
(min . (-> number number number))
(exp . (-> number number))
(log . (-> number number))
(sin . (-> number number))
(cos . (-> number number))
(asin . (-> number number))
(acos . (-> number number))
(tan . (-> number number))
(atan . (-> number number))
(quotient . (-> number number number))
(remainder . (-> number number number))
(modulo . (-> number number number))
(char>? . (-> char char boolean))
(char . (-> char char boolean))
(char<=? . (-> char char boolean))
(char=? . (-> char char boolean))
(char>=? . (-> char char boolean))
(char>? . (-> char char boolean))
)
)
(define constant-type ; TYPE: datum -> type expr
(lambda (x)
; ENSURES: result is the type of the Scheme constant x
(cond ((number? x) 'number)
((char? x) 'char)
((string? x) 'string)
((boolean? x) 'boolean)
((symbol? x) 'symbol)
((null? x) '(list ?a))
((pair? x)
; the following only deals properly with flat lists
(let ((element-type (constant-type (car x))))
(for-each (lambda (y)
(if (not (equal? (constant-type y) element-type))
(set! element-type 'datum)))
(cdr x))
(list 'list element-type)))
((vector? x)
(if (= (vector-length x) 0)
'(vector ?a)
(let ((element-type (constant-type (vector-ref x 0))))
(vector-for-each
(lambda (y)
(if (not (equal? (constant-type y) element-type))
(set! element-type 'datum)))
x 1)
(list 'vector element-type))))
(else (fail "unknown constant type")))))
(define vector-for-each
; TYPE: all T . (T -> void), (vector of T), number -> void
(lambda (f v start)
; EFFECT: apply f to each element of v, starting at index start
; and going towards larger indexes
(let ((size (vector-length v)))
(letrec ((helper ; TYPE: integer -> void
(lambda (i)
(if (not (= i size))
(begin
(f (vector-ref v i))
(helper (+ 1 i)))))))
(helper start)))))
(define quoted-constant-type ; TYPE: datum -> type expr
(lambda (x)
(if (eq? x '())
'(list ?a) ; patch because #f = ()
(constant-type x))))
(define type ; TYPE: s-expression -> type expr
(lambda (f)
; ENSURES: result is the type of expression f
(let ((e (env-empty)))
(letrec
((j ; TYPE: generic variables list, s-expression -> type expr
(lambda (p f)
; ENSURES: result is the type of f, once variables in e
; are substituted for the variables in result
; This is algorithm 'j' from Robin Milner's paper
(cond
((symbol? f)
(if (env-bound? f p)
(let ((x (env-value f p)))
(let ((kind (car x))
(type (cadr x)))
(if (eq? kind 'let) (instance type p) type)))
(let ((glbl-binding (assq f global-var-types)))
(if (null? glbl-binding)
(fail (string-append
"unknown global name: "
(symbol->string f)))
(instance (cdr glbl-binding) (env-empty))))))
((not (pair? f))
(instance (constant-type f) (env-empty)))
((eq? (car f) 'quote)
(instance (quoted-constant-type (cadr f)) (env-empty)))
((eq? (car f) 'if)
(let ((pre (j p (cadr f)))
(con (j p (caddr f)))
(alt (j p (cadddr f))))
(set! e (unify con alt (unify pre 'boolean e)))
con))
((eq? (car f) 'lambda)
(let ((parms (map (lambda (x) (new-variable)) (cadr f))))
(let ((body (j (env-join
(map2 (lambda (x y) (list x 'lambda y))
(cadr f)
parms)
p)
(caddr f))))
(cons '-> (append parms (list body))))))
((eq? (car f) 'let)
(let ((p-let (env-join (map (lambda (x)
(list (car x)
'let
(j p (cadr x))))
(cadr f))
p)))
(j p-let (caddr f))))
((eq? (car f) 'letrec)
(let* ((p-rec (map (lambda (x)
(list (car x)
'lambda (new-variable)))
(cadr f)))
(p* (env-join p-rec p)))
(for-each
(lambda (x)
(let ((val (j p* (cadr x))))
(set! e
(unify (cadr (env-value (car x) p*))
val
e))))
(cadr f))
(let ((p-rec2 ; env with letrec vars let-bound to types
(map
(lambda (x)
(list (car x)
'let
(env-value (caddr x) e)))
p-rec)))
(j (env-join p-rec2 p)
(caddr f)))))
((and (pair? (car f)) (eq? (caar f) 'lambda))
(j p (list 'let (map2 list (cadar f) (cdr f)) (caddar f))))
(else
(let ((result (new-variable))
(oper (j p (car f)))
(args (map (lambda (x) (j p x)) (cdr f))))
(set! e (unify oper
(cons '-> (append args (list result)))
e))
result))))))
(let ((t (j (env-empty) f)))
(let ((expanded-type (subst-bindings e t)))
(if (eq? *failure* expanded-type)
(fail "type error (or your expression is too hard)")
expanded-type)))))))
(define map2 ; TYPE: (-> ((-> (S T) U) (list S) (list T)) (list U))
(lambda (proc ls1 ls2)
; REQUIRES: ls1 and ls2 have same length.
; ENSURES: the result is a list, its items come from the result of
; proc operating on corresponding items in ls1 and ls2.
(if (null? ls1) '()
(cons (proc (car ls1) (car ls2))
(map2 proc (cdr ls1) (cdr ls2))))))