123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- #lang racket
- (require racket/match)
- (require (only-in srfi/1 every))
- (require "utilities.scm")
- (require "counter.scm")
- (require "box.scm")
- (require "sets.scm")
- (require "scope-set-env.scm")
- (require "syntax.scm")
- (provide resolve-program)
- ;;; resolve
- ;;
- ;; NOTE: Right now nothing actually adds to the 'set'
- ;; metadata of a syntax node
- ;;
- ;; once we have macros, they will need to do this
- (define specials
- '((lambda special)
- (begin special)
- (if special)
- (quote special)))
- (define (special? x)
- (and (pair? x) (eq? 'special (car x))))
- (define (resolve-program prg builtins)
- (let ((assoc-table
- (append (map (lambda (entry)
- (cons (car entry) (list 'variable (car entry))))
- builtins)
- specials)))
- (resolve-program^ prg assoc-table)))
- (define (resolve-program^ prg tbl)
- (if (null? prg)
- '()
- (let ((res (resolve-definition (car prg) tbl)))
- ;; TODO: need to add the new definition
- ;; the loop could move into resolve def to make that easy
- (cons res (resolve-program (cdr prg) tbl)))))
- (define (resolve-definition def builtins)
- ;; TODO: what if someone shadows DEFINE
- (let ((me-meta '((source . "resolve-definition"))))
- (match def
- ((list (syx 'id 'define meta-1) (syx 'id name meta-2) source)
- ;;
- ;; A simple definition (define <name> <single-form>)
- ;;
- ;; We make the definition as in scope before resolving it
- ;; this allows for recursive functions
- ;; TODO (update-env! env (make-dummy-binding name))
- (let ((ss-env (ss-env-make builtins)))
- (list (syx 'id 'define meta-1) (syx 'id name meta-2)
- (resolve-exp source ss-env))))
- ((list (syx 'id 'define meta-1) (list* header args) source)
- ;;
- ;; Handle implicit lambda definitions
- ;;
- ;; (define (f x y) y) ~> (define f (lambda (x y) y))
- ;;
- (resolve-definition
- (list (syx 'id 'define meta-1)
- header
- (list (syx 'id 'lambda me-meta) args source))
- builtins))
- ((list* (syx 'id 'define meta-1) header source source*)
- ;;
- ;; Handle implicit begin inside a definition
- ;;
- (resolve-definition
- (list (syx 'id 'define meta-1) header
- (list* (syx 'id 'begin me-meta) source source*))
- builtins))
- (else
- (error "resolve-definition: Bad definition!" def)))))
- (define (resolve-exp exp ss-env)
- (define (ss-lookup exp)
- (scope-set-lookup ss-env
- (syx-id exp)
- (union +
- (syx-set exp)
- (ss-env-current-scope-set ss-env))))
- (cond ((syx-atomic? exp) exp)
- ((syx-id? exp)
- (ss-lookup exp))
- ((null? exp)
- '())
- ((and (pair? exp)
- (syx-id? (car exp))
- (special? (ss-lookup (car exp))))
- ;; A special form or macro
- ;; TODO: do we have to actually do a resolution here?
- ;; I think we do because otherwise you could rename
- ;; one special as another and it'd get them mixed up
- ;; make sure to create a test for this
- (resolve-special exp ss-env))
- ((pair? exp)
- ;; A regular function application
- (cons (resolve-exp (car exp) ss-env)
- (resolve-exp (cdr exp) ss-env)))
- (else (error "resolve-exp: bad syntax!" exp))))
- ;; changes
- ;;
- ;; env stores a scope counter
- ;; env stores the current scope set (nested)
- ;; env stores a hash table mapping name * set --> value
- ;;
- ;; new function (add-scopeset-binding! env scop name value)
- ;; adds a scope set binding to the environment hash table
- ;; name * (current scope u {scop}) --> value
- ;;
- ;; changed function (extend-env env scop)
- ;; now this adds scop to the current scop set of our env
- (define (resolve-special exp env)
- (let ((me-meta '((source . "resolve-special"))))
- (match exp
- ((list (syx 'id 'lambda meta) params body)
- ;; We see a lambda special form
- ;; Validate that it is well formed
- (unless (every syx-id? params)
- (error "malformed lambda: parameters are not variables" exp))
- ;; create a new scope for this binder
- (let* ((sm ((ss-env-scope-counter env)))
- ;; making a mapping var,scope u {sm} -> gensym(var)
- (param-names (map syx-id params))
- (gen-params (map (lambda (p n)
- (syx (syx-type p)
- (gensym n)
- (syx-metadata p)))
- params
- param-names)))
- (for-each (lambda (nm gen)
- (add-scopeset-binding! env sm nm gen))
- param-names gen-params)
- (list (syx 'special 'lambda meta)
- gen-params
- (resolve-exp body (extend-env env sm)))))
- ((list* (syx 'id 'lambda meta) params body body*)
- ;; This case just handles the implicit begin of lambda
- (resolve-exp
- (list (syx 'id 'lambda meta) params
- (list* (syx 'id 'begin me-meta) body body*))
- env))
- ((list (syx 'id 'begin meta))
- (error "empty begin"))
- ((list* (syx 'id 'begin meta) exps)
- (list* (syx 'special 'begin meta) (map (lambda (exp) (resolve-exp exp env)) exps)))
- ((list (syx 'id 'if meta) t c a)
- (list (syx 'special 'if meta)
- (resolve-exp t env)
- (resolve-exp c env)
- (resolve-exp a env)))
- ((list (syx 'id 'if meta) t c)
- (error "illegal sussmans pirate" exp))
- ((list (syx 'id 'quote meta) m)
- (syx->datum m))
- (else (error "invalid special form syntax in resolve-special" exp)))))
|