#lang racket/base (require (for-syntax racket/base syntax/parse syntax/parse/experimental/contract) racket/contract syntax/stx "deriv-util.rkt" "stx-util.rkt" "context.rkt" "steps.rkt" "reductions-config.rkt") (provide (all-from-out "steps.rkt") (all-from-out "reductions-config.rkt") DEBUG R !) (define-syntax ! (syntax-rules ())) (define-syntax-rule (with-syntax1 ([pattern rhs]) . body) (syntax-case rhs () [pattern (let () . body)] [x (raise-syntax-error 'with-syntax1 (format "failed pattern match against ~s" 'pattern) #'x)])) (define-syntax-rule (DEBUG form ...) (when #f form ... (void))) (define-syntax-rule (STRICT-CHECKS form ...) (when #f form ... (void))) (define RST/c (syntaxish? syntaxish? state/c list? . -> . RS/c)) ;; (R R-clause ...) : RST ;; An R-clause is one of ;; [! expr] ;; [#:set-syntax expr] ;; [#:expect-syntax expr] ;; [#:pattern pattern] ;; [#:do expr ...] ;; [#:let var expr] ;; [#:left-foot] ;; [#:walk term2 description] ;; [#:rename pattern rename [description]] ;; [#:rename/no-step pattern stx stx] ;; [#:reductions expr] ;; [#:learn ids] ;; [#:if test R-clause ...] ;; [#:when test R-clause ...] ;; [#:hide-check ids] ;; [#:seek-check] ;; [generator hole fill] (define-syntax R (syntax-parser [(R . clauses) #'(lambda (f v s ws) (R** f v _ s ws . clauses))])) (define-syntax RP (syntax-parser [(RP p . clauses) #'(lambda (f v s ws) (R** f v p s ws . clauses))])) ;; (R** form virtual-form pattern . clauses) (define-syntax R** (syntax-parser #:literals (! =>) ;; (R** f v p s ws . clauses) ;; f is the "real" form ;; v is the "virtual" form (used for steps) ;; - vis=#t: starts as f ;; - vis=#f: starts as last visible term ;; s is the last marked state ;; ws is the list of steps, reversed ;; Base: done [(R** f v p s ws) #'(RSunit ws f v s)] [(R** f v p s ws => k . more) #:declare k (expr/c #'RST/c) #'(RSbind (k f v s ws) (RP p . more))] ;; Error-point case [(R** f v p s ws [! maybe-exn] . more) #:declare maybe-exn (expr/c #'(or/c exn? false/c)) #'(let ([x maybe-exn]) (if x ;; FIXME (RSfail (cons (stumble v x) ws) x) (R** f v p s ws . more)))] ;; Change patterns [(R** f v p s ws [#:pattern p2] . more) #'(R** f v p2 s ws . more)] ;; Execute expressions for effect [(R** f v p s ws [#:do expr ...] . more) #'(begin (with-syntax1 ([p f]) expr ... (void)) (R** f v p s ws . more))] [(R** f v p s ws [#:let var expr] . more) #'(let ([var (with-syntax1 ([p f]) expr)]) (R** f v p s ws . more))] [(R** f v p s ws [#:parameterize ((param expr) ...) . clauses] . more) #:declare param (expr/c #'parameter?) #'(RSbind (parameterize ((param expr) ...) (R** f v p s ws . clauses)) (RP p . more))] ;; Change syntax [(R** f v p s ws [#:set-syntax form] . more) #:declare form (expr/c #'syntaxish?) #'(let ([f2 (with-syntax1 ([p f]) form)]) ;; FIXME: should (current-pass-hides?) be relevant? (let ([v2 (if (visibility) f2 v)]) (R** f2 v2 p s ws . more)))] [(R** f v p s ws [#:expect-syntax expr ds] . more) #:declare expr (expr/c #'syntax?) #'(let ([expected (with-syntax1 ([p f]) expr)]) (STRICT-CHECKS (check-same-stx 'expect-syntax f expected ds)) (R** f v p s ws . more))] [(R** f v p s ws [#:left-foot] . more) #'(R** f v p s ws [#:step #f v] . more)] [(R** f v p s ws [#:left-foot fs] . more) #'(R** f v p s ws [#:step #f fs] . more)] [(R** f v p s ws [#:step type] . more) #'(R** f v p s ws [#:step type v] . more)] [(R** f v p s ws [#:step type fs] . more) #:declare fs (expr/c #'syntaxish?) #:declare type (expr/c #'(or/c step-type? false/c)) #'(let ([s2 (and (visibility) (current-state-with v (with-syntax1 ([p f]) fs)))] [type-var type]) (DEBUG (printf "visibility = ~s\n" (if (visibility) 'VISIBLE 'HIDDEN)) (printf "step: s1 = ~s\n" s) (printf "step: s2 = ~s\n\n" s2)) (let ([ws2 (if (and (visibility) type-var) (cons (make step type-var s s2) ws) ws)]) (R** f v p s2 ws2 . more)))] [(R** f v p s ws [#:walk form2 description] . more) #:declare form2 (expr/c #'syntaxish?) #'(let ([wfv (with-syntax1 ([p f]) form2)]) (R** f v p s ws [#:left-foot] [#:set-syntax wfv] [#:step description] . more))] [(R** f v p s ws [#:reductions rs] . more) #:declare rs (expr/c #'(listof step?)) #'(let ([ws2 (if (visibility) (revappend (with-syntax1 ([p f]) rs) ws) ws)]) (R** f v p s ws2 . more))] [(R** f v p s ws [#:in-hole hole . clauses] . more) #'(let ([k (RP p . more)] [reducer (lambda (_) (R . clauses))]) (Run reducer f v p s ws hole #f k))] ;; Rename [(R** f v p s ws [#:rename pattern renames] . more) #'(R** f v p s ws [#:rename pattern renames #f] . more)] [(R** f v p s ws [#:rename pattern renames description] . more) #'(R** f v p s ws [#:rename* pattern renames description #f]. more)] [(R** f v p s ws [#:rename* pattern renames description mark-flag] . more) #'(let-values ([(renames-var description-var) (with-syntax1 ([p f]) (values renames description))]) (let* ([pre-renames-var (with-syntax1 ([p f]) (syntax pattern))] [f2 ((CC pattern f p) renames)] [whole-form-rename? (eq? f pre-renames-var)] [renames-mapping (make-renames-mapping pre-renames-var renames-var)] [v2 (cond [(or (visibility) (eq? mark-flag #f)) (apply-renames-mapping renames-mapping v)] [(eq? mark-flag 'mark) v] [(eq? mark-flag 'unmark) (apply-renames-mapping (compose-renames-mappings (table->renames-mapping (marking-table)) renames-mapping) v)])] [ws2 (if (and description-var (visibility)) (cons (walk v v2 description-var #:foci1 pre-renames-var #:foci2 renames-var) ws) ws)]) (parameterize ((subterms-table (table-apply-renames-mapping (subterms-table) renames-mapping whole-form-rename?))) (R** f2 v2 p s ws2 . more))))] [(R** f v p s ws [#:rename/mark pvar from to] . more) #:declare from (expr/c #'syntaxish?) #:declare to (expr/c #'syntaxish?) #'(let ([real-from (with-syntax1 ([p f]) #'pvar)]) (STRICT-CHECKS (check-same-stx 'rename/mark real-from from)) (when (marking-table) (add-to-renames-table (marking-table) from to)) (R** f v p s ws [#:rename* pvar to #f 'mark] . more))] [(R** f v p s ws [#:rename/unmark pvar from to] . more) #:declare from (expr/c #'syntaxish?) #:declare to (expr/c #'syntaxish?) #'(let ([real-from (with-syntax1 ([p f]) #'pvar)]) (STRICT-CHECKS (check-same-stx 'rename/mark real-from from)) (R** f v p s ws [#:rename* pvar to #f 'unmark] . more))] ;; Change syntax with rename (but no step) [(R** f v p s ws [#:rename/no-step pvar from to] . more) #:declare from (expr/c #'syntaxish?) #:declare to (expr/c #'syntaxish?) #'(let ([real-from (with-syntax1 ([p f]) #'pvar)]) (STRICT-CHECKS (check-same-stx 'rename/no-step real-from from)) (R** f v p s ws [#:rename pvar to] . more))] ;; Add to definite binders [(R** f v p s ws [#:binders ids] . more) #:declare ids (expr/c #'(listof identifier)) #'(begin (learn-binders (flatten-identifiers (with-syntax1 ([p f]) ids))) (R** f v p s ws . more))] ;; Add to definite uses [(R** f v p s ws [#:learn ids] . more) #:declare ids (expr/c #'(listof identifier?)) #'(begin (learn-definites (with-syntax1 ([p f]) ids)) (R** f v p s ws . more))] ;; Conditional (pattern changes lost afterwards ...) [(R** f v p s ws [#:if test [consequent ...] [alternate ...]] . more) #'(let ([continue (RP p . more)]) (if (with-syntax1 ([p f]) test) (R** f v p s ws consequent ... => continue) (R** f v p s ws alternate ... => continue)))] ;; Conditional (pattern changes lost afterwards ...) [(R** f v p s ws [#:when test consequent ...] . more) #'(let ([continue (RP p . more)]) (if (with-syntax1 ([p f]) test) (R** f v p s ws consequent ... => continue) (continue f v s ws)))] ;; HIDING DIRECTIVES [(R** f v p s ws [#:hide-check ids] . more) #:declare ids (expr/c #'(listof identifier?)) #'(visibility-off (andmap (macro-policy) ids) v (lambda () (R** f v p s ws . more)))] [(R** f v p s ws [#:seek-check] . more) #'(seek-point f v (lambda (v2) (R** f v2 p s ws . more)))] [(R** f v p s ws [#:print-state msg] . more) #'(begin (printf "** ~s\n" msg) (printf "f = ~.s\n" (stx->datum f)) (printf "v = ~.s\n" (stx->datum v)) (printf "s = ~.s\n" (stx->datum s)) (R** f v p s ws . more))] ;; ** Multi-pass reductions ** ;; Pass1 does expansion. ;; If something should happen regardless of whether hiding occurred ;; in pass1 (eg, lifting), put it before the Pass2 marker. ;; Use #:unsafe-bind-visible to access 'v' ;; Warning: don't do anything that relies on real 'f' before pass2 ;; If something should be hidden if any hiding occurred in pass1, ;; put it after the Pass2 marker (eg, splice, block->letrec). [(R** f v p s ws [#:pass1] . more) #'(parameterize ((hides-flags (cons (box (not (visibility))) (hides-flags)))) (DEBUG (printf "** pass1\n")) (R** f v p s ws . more))] [(R** f v p s ws [#:pass2 clause ...] . more) #'(let* ([previous-pass-hides? (current-pass-hides?)] [k (lambda (f2 v2 s2 ws2) (parameterize ((hides-flags (cdr (hides-flags)))) (when previous-pass-hides? (current-pass-hides? #t)) (R** f2 v2 p s2 ws2 . more)))]) (DEBUG (printf "** pass2\n")) ;; FIXME: maybe refresh subterms table from v? (visibility-off (not previous-pass-hides?) v (lambda () (when #f (print-viable-subterms v)) (R** f v p s ws clause ... => k)) #t))] [(R** f v p s ws [#:with-visible-form clause ...] . more) #'(let ([k (RP p #| [#:set-syntax f] |# . more)]) (if (visibility) (R** v v p s ws clause ... => k) (k f v s ws)))] [(R** f v p s ws [#:new-local-context clause ...] . more) ;; If vis = #t, then (clause ...) do not affect local config ;; If vis = #f, then proceed normally ;; *except* must save & restore real term #'(let* ([vis (visibility)] [process-clauses (lambda () (R** #f (if vis #f v) _ #f ws clause ...))]) (RSbind (if vis (with-new-local-context v (process-clauses)) (process-clauses)) (lambda (f2 v2 s2 ws2) (let ([v2 (if vis v v2)] [s2 (if vis s s2)]) (R** f v2 p s2 ws2 . more)))))] ;; Subterm handling [(R** f v p s ws [reducer hole fill] . more) #:declare reducer (expr/c #'(any/c . -> . RST/c)) #'(let ([k (RP p . more)] [reducer-var reducer]) (Run reducer-var f v p s ws hole fill k))])) (define-syntax (Run stx) (syntax-case stx () ;; Implementation of subterm handling for (hole ...) sequences [(Run reducer f v p s ws (hole :::) fills-e k) (and (identifier? #':::) (free-identifier=? #'::: (quote-syntax ...))) #'(let* ([fctx (CC (hole :::) f p)] [init-e1s (with-syntax1 ([p f]) (syntax->list #'(hole :::)))] [fills fills-e]) (DEBUG (printf "Run (multi, vis=~s)\n" (visibility)) (printf " f: ~.s\n" (stx->datum f)) (printf " v: ~.s\n" (stx->datum v)) (printf " p: ~.s\n" 'p) (printf " hole: ~.s\n" '(hole :::)) (print-viable-subterms v)) (if (visibility) (let ([vctx (CC (hole :::) v p)] [vsubs (with-syntax1 ([p v]) (syntax->list #'(hole :::)))]) (run-multiple/visible reducer init-e1s fctx vsubs vctx fills s ws k)) (run-multiple/nonvisible reducer init-e1s fctx v fills s ws k)))] ;; Implementation of subterm handling [(Run reducer f v p s ws hole fill k) #'(let* ([init-e (with-syntax1 ([p f]) #'hole)] [fctx (CC hole f p)]) (DEBUG (printf "Run (single, vis=~s)\n" (visibility)) (printf " f: ~.s\n" (stx->datum f)) (printf " v: ~.s\n" (stx->datum v)) (printf " p: ~.s\n" 'p) (printf " hole: ~.s\n" 'hole) (print-viable-subterms v)) (if (visibility) (let ([vctx (CC hole v p)] [vsub (with-syntax1 ([p v]) #'hole)]) (run-one reducer init-e fctx vsub vctx fill s ws k)) (run-one reducer init-e fctx v values fill s ws k)))])) ;; run-one (define (run-one reducer init-e fctx vsub vctx fill s ws k) (DEBUG (printf "run-one\n") (printf " fctx: ~.s\n" (stx->datum (fctx #'HOLE))) (printf " vctx: ~.s\n" (stx->datum (vctx #'HOLE)))) (RSbind (with-context vctx ((reducer fill) init-e vsub s ws)) (lambda (f2 v2 s2 ws2) (k (fctx f2) (vctx v2) s2 ws2)))) ;; run-multiple/visible (define (run-multiple/visible reducer init-e1s fctx vsubs vctx fills s ws k) (DEBUG (printf "run-multiple/visible\n") (printf " fctx: ~.s\n" (stx->datum (fctx (for/list ([dummy init-e1s]) #'HOLE)))) (printf " vctx: ~.s\n" (stx->datum (vctx (for/list ([dummy init-e1s]) #'HOLE)))) (unless (= (length fills) (length init-e1s)) (printf " fills(~s): ~.s\n" (length fills) fills) (printf " init-e1s: ~.s\n" (stx->datum init-e1s)) (printf " vsubs: ~.s\n" (stx->datum vsubs)))) (let loop ([fills fills] [prefix null] [vprefix null] [suffix init-e1s] [vsuffix vsubs] [s s] [ws ws]) (cond [(pair? fills) (RSbind (with-context (lambda (x) (vctx (revappend vprefix (cons x (cdr vsuffix))))) ((reducer (car fills)) (car suffix) (car vsuffix) s ws)) (lambda (f2 v2 s2 ws2) (loop (cdr fills) (cons f2 prefix) (cons v2 vprefix) (cdr suffix) (cdr vsuffix) s2 ws2)))] [(null? fills) (k (fctx (reverse prefix)) (vctx (reverse vprefix)) s ws)]))) ;; run-multiple/nonvisible (define (run-multiple/nonvisible reducer init-e1s fctx v fills s ws k) (DEBUG (printf "run-multiple/nonvisible\n") (printf " fctx: ~.s\n" (stx->datum (fctx (for/list ([dummy init-e1s]) #'HOLE))))) (let loop ([fills fills] [prefix null] [suffix init-e1s] [v v] [s s] [ws ws]) (DEBUG (printf " v: ~.s\n" (stx->datum (datum->syntax #f v)))) (cond [(pair? fills) (RSbind ((reducer (car fills)) (car suffix) v s ws) (lambda (f2 v2 s2 ws2) (loop (cdr fills) (cons f2 prefix) (cdr suffix) v2 s2 ws2)))] [(null? fills) (k (fctx (reverse prefix)) v s ws)]))) ;; ------------------------------------ ;; CC ;; the context constructor (define-syntax (CC stx) (syntax-case stx () [(CC HOLE expr pattern) #'(syntax-copier HOLE expr pattern)])) (define (revappend a b) (cond [(pair? a) (revappend (cdr a) (cons (car a) b))] [(null? a) b])) ;; visibility-off : boolean stx stx (-> a) -> a (define (visibility-off new-visible? stx k [reset-subterms? #f]) (cond [(and (not new-visible?) (or (visibility) reset-subterms?)) (begin (DEBUG (printf "hide => seek: ~.s\n" (stx->datum stx))) (current-pass-hides? #t) (let* ([subterms (gather-proper-subterms stx)] [marking (marking-table)] [subterms (if marking (table-apply-renames-mapping subterms (table->renames-mapping marking) #f) subterms)]) (parameterize ((visibility #f) (subterms-table subterms) (marking-table (or marking (make-hasheq)))) (k))))] [else (k)])) ;; Seek (provide/contract [seek-point (syntaxish? syntaxish? (syntaxish? . -> . RS/c) . -> . RS/c)]) ;; seek-point : stx (-> RS/c) -> RS/c (define (seek-point stx vstx k) (if (visibility) (k vstx) (begin (DEBUG (printf "Seek point\n") (print-viable-subterms stx)) (let ([paths (table-get (subterms-table) stx)]) (cond [(null? paths) (DEBUG (printf "seek-point: failed on ~.s\n" (stx->datum stx))) (k vstx)] [(null? (cdr paths)) (let ([path (car paths)]) (DEBUG (printf "seek => hide: ~.s\n" (stx->datum stx))) (let ([ctx (lambda (x) (path-replace vstx path x))]) (RScase (parameterize ((visibility #t) (subterms-table #f) (marking-table #f)) ;; Found stx within vstx (with-context ctx (k stx))) (lambda (ws2 stx2 vstx2 s2) (let ([vstx2 (ctx vstx2)]) (RSunit ws2 stx2 vstx2 s2))) (lambda (ws exn) (RSfail ws exn)))))] [else (raise (make nonlinearity stx paths))]))))) (provide print-viable-subterms) (define (print-viable-subterms stx) (DEBUG (let ([t (subterms-table)]) (when t (printf "viable subterms:\n") (let loop ([stx stx]) (cond [(syntax? stx) (let ([paths (table-get t stx)]) (if (pair? paths) (printf " ~s\n" (stx->datum stx)) (loop (syntax-e stx))))] [(pair? stx) (loop (car stx)) (loop (cdr stx))])))))) (define (check-same-stx function actual expected [derivs null]) (unless (eq? actual expected) (let* ([actual-datum (stx->datum actual)] [expected-datum (stx->datum expected)] [same-form? (equal? actual-datum expected-datum)]) (if same-form? (fprintf (current-error-port) "same form but wrong wrappings:\n~.s\nwrongness:\n~.s\n" actual-datum (wrongness actual expected)) (fprintf (current-error-port) "got:\n~.s\n\nexpected:\n~.s\n" actual-datum expected-datum)) (for ([d derivs]) (fprintf (current-error-port) "\n~.s\n" d)) (error function (if same-form? "wrong starting point (wraps)!" "wrong starting point (form)!"))))) (define (wrongness a b) (cond [(eq? a b) '---] [(stx-list? a) (map wrongness (stx->list a) (stx->list b))] [(stx-pair? a) (cons (wrongness (stx-car a) (stx-car b)) (wrongness (stx-cdr a) (stx-cdr b)))] [else (stx->datum a)])) ;; flatten-identifiers : syntaxlike -> (list-of identifier) (define (flatten-identifiers stx) (syntax-case stx () [id (identifier? #'id) (list #'id)] [() null] [(x . y) (append (flatten-identifiers #'x) (flatten-identifiers #'y))] [else (error 'flatten-identifers "neither syntax list nor identifier: ~s" (if (syntax? stx) (syntax->datum stx) stx))]))