racket/collects/mzlib/private/match/coupling-and-binding.scm
Sam Tobin-Hochstadt d1fe9f9645 Rewrite emit and assem to use better style.
Remove pointless optional arguments in getbindings.
Don't create unneccessary match-lambda*.
Implement keyword arguments to define-match-expander.
Lots of refactoring of gen-match for general clarity.
Use combinators instead of writing our own loops.
Simplify struct info accessor.
Add timing printer.
Refactor coupling/binding for general clarity.
Rewrite logical-equal not to use the expansion of match.
General replacement of () with [].

svn: r4192
2006-08-30 19:41:47 +00:00

185 lines
8.5 KiB
Scheme

(module coupling-and-binding mzscheme
;; This library is used by match.ss
(provide couple-tests meta-couple subst-bindings)
(require "test-structure.scm"
"match-helper.ss"
(lib "pretty.ss")
(lib "list.ss"))
(require-for-template mzscheme)
;; a structure representing bindings of portions of the matched data
;; exp: the expression that is bound in s-exp form
;; exp-stx: the expression that is bound in syntax form
;; new-exp: the new symbol that will represent the expression
(define-struct binding (exp exp-stx new-exp))
;;!(function couple-tests
;; (form (couple-tests test-list ks-func kf-func let-bound)
;; ->
;; ((list list) -> syntax))
;; (contract (list
;; ((((list list) -> syntax) list) ->
;; ((list list) -> syntax))
;; (list -> ((list list) -> syntax))
;; list)
;; ->
;; ((list list) -> syntax)))
;; This is a major function of the compiler. This function
;; couples a list of tests together. Here is where state is
;; passed around to the various partially compiled tests so that
;; compilation can be completed. This returns a function that takes a
;; list of tests so far and a list of bound pattern variables.
(define (couple-tests test-list ks-func kf-func let-bound)
;(print-time "entering couple-tests")
;(printf "test-list: ~a~n" (map test-tst test-list))
;(printf "test-list size: ~a~n" (length test-list))
(if (null? test-list)
(ks-func (kf-func let-bound) let-bound)
(let* ([cur-test (car test-list)]
[rest-tests (cdr test-list)]
;; this couples together the rest of the test
;; it is passed a list of the already bound expressions
;; only used in test/rest
[couple-rest (lambda (let-bound)
(couple-tests rest-tests
ks-func
(if (negate-test? cur-test)
(lambda (let-bound)
(lambda (sf bv)
#`(match-failure)))
kf-func)
let-bound))]
;; this generates the current test as well as the rest of the match expression
;; it is passed a list of the already bound expressions
[test/rest (lambda (let-bound)
((test-comp cur-test)
(couple-rest let-bound)
(kf-func let-bound)
let-bound))])
(if (and
;; the expression is referenced twice
(>= (test-bind-count cur-test) 2)
;; and it's not already bound to some variable
(not (exp-already-bound?
(test-bind-exp cur-test)
let-bound)))
;; then generate a new binding for this expression
(let* ([new-exp (get-exp-var)]
[binding (make-binding (test-bind-exp cur-test)
(test-bind-exp-stx cur-test)
new-exp)]
[let-bound (cons binding let-bound)])
(with-syntax (;; the new variable
[v new-exp]
;; the expression being bound
;; with appropriate substitutions for the already bound portions
[expr (sub-expr-subst (binding-exp-stx binding) let-bound)])
(lambda (sf bv)
#`(let ([v expr])
;; the new body, using the new binding (through let-bound)
#,((test/rest let-bound) sf bv)))))
;; otherwise it doesn't need a binding, and we can just do the test
(test/rest let-bound)))))
;;!(function subst-bindings
;; (form (subst-bindings exp-stx let-bound) -> syntax)
;; (contract (syntax list) -> syntax)
;; (example (subst-bindings (syntax (car (cdr x)))
;; (list (list '(cdr x)
;; (syntax (cdr x))
;; 'exp5)))
;; -> (syntax (car 'exp5))))
;; This function substitutes let bound variables names for the
;; expressions that they represent.
(define (subst-bindings exp-stx let-bound)
(cond [(get-bind exp-stx let-bound) => binding-new-exp]
[else (sub-expr-subst exp-stx let-bound)]))
;;!(function sub-exp-subst
;; (form (sub-exp-subst exp-stx let-bound) -> syntax)
;; (contract (syntax list) -> syntax)
;; (example (subst-bindings (syntax (car (cdr x)))
;; (list (list '(cdr x)
;; (syntax (cdr x))
;; 'exp5)))
;; -> (syntax (car 'exp5))))
;; This function substitutes let bound variables names for the
;; expressions that they represent. This only works if a
;; subexpression of exp-stx is bound in the let-bound list.
;; This function assumes that all accessors are of the form
;; (acc obj other-args ...) (such as list-ref)
(define (sub-expr-subst exp-stx let-bound)
(syntax-case exp-stx ()
[(access sub-exp rest ...)
(let ([binding (get-bind #'sub-exp let-bound)])
(if binding
#`(access #,(binding-new-exp binding) rest ...)
#`(access #,(sub-expr-subst #'sub-exp let-bound) rest ...)))]
[_ exp-stx]))
; helper for the following functions
(define ((equal-bind-get exp) e)
(equal? exp (binding-exp e)))
;;!(function get-bind
;; (form (get-bind exp let-bound) -> binding)
;; (contract (any list) -> list))
;; This function looks up the binding for a given expression exp
;; in the binding list let-bound. If the binding is found then the
;; binding is returned if not then #f is returned.
(define (get-bind exp let-bound)
(cond [(memf (equal-bind-get (syntax-object->datum exp)) let-bound) => car]
[else #f]))
;;!(function exp-already-bound?
;; (form (exp-already-bound? exp let-bound) -> binding)
;; (contract (any list) -> boolean))
;; This function looks up the binding for a given expression exp
;; in the binding list let-bound. If the binding is found then #t
;; binding is returned if not then #f is returned.
(define (exp-already-bound? exp let-bound)
(ormap (equal-bind-get exp) let-bound))
;;!(function meta-couple
;; (form (meta-couple rendered-list failure-func
;; let-bound bvsf)
;; ->
;; ((list list) -> syntax))
;; (contract (list ((list list) -> syntax) list list)
;; ->
;; ((list list) -> syntax)))
;; This function takes a list of rendered clauses which also have
;; success functions attached and couples the whole lot together
;; yeilding one function that when invoked will compile the whole
;; original match expression.
(define (meta-couple rendered-list failure-func let-bound bvsf)
#;(print-time "entering meta-couple")
;(printf "rendered-list ~n")
;(pretty-print (map (lambda (x) (map test-tst (car x))) rendered-list))
(if (null? rendered-list)
failure-func
;; here we erase the previously bound variables
(let* ([failed
(lambda (let-bound)
(lambda (sf bv)
((meta-couple (cdr rendered-list)
failure-func
let-bound
bvsf)
sf bvsf)))])
(couple-tests (caar rendered-list)
(cdar rendered-list) ;; successfunc needs
;; failure method
failed ;; needs let-bound
let-bound ;; initial-let bindings
)))) ;; fail-func
(require (lib "trace.ss"))
;(trace meta-couple)
;(trace couple-tests)
)