;;; Copyright (c) 2000-2015 Dipanwita Sarkar, Andrew W. Keep, R. Kent Dybvig, Oscar Waddell ;;; See the accompanying file Copyright for details ;;; AWK - TODO - Once the meta-parser can handle language passes that match ;;; a single variable. ;;; FIXME - For Ikarus, I needed to use "dots" instead of the ".." ;;; because Ikarus sees .. as a syntax error, even when it is ;;; exported as an auxiliary keyword. ;;; Time-stamp: <2000-01-10 12:29:38 kemillik> ;;; (time-stamp generated by emacs: Type M-x time-stamp anywhere to update) ;;; syncase is a pattern matcher where patterns are quoted or ;;; quasiquoted expressions, or symbols. Unquoted symbols denote ;;; pattern variables. All quoted things must match precisely. ;;; Also, there is a symbol ".." that may be used to allow repetitions ;;; of the preceeding pattern. Any pattern variables within are bound ;;; to a list of matches. ".." may be nested. ;;; Below is the canonical example of "let" ;;; [`(let ([,var ,rhs] ..) ,body0 ,body1 ..) ;;; (guard (for-all symbol? var) (no-duplicates? var)) ;;; `((lambda ,var ,body0 ,@body1) ,@rhs)] ;;; For the pattern to match, the optional guard requires its ;;; arguments to be true. The guard also uses the pattern ;;; variables. ;;; We have added three obvious new forms: synlambda, synlet, and ;;; synlet*. Finally, we have added a very useful operation, ;;; make-double-collector-over-list, whose description follows from the ;;; very simple code below. ;;; Here are some descriptive examples of each of the new special forms. ;;;> (define foo ;;; (synlambda `((if ,exp0 ,exp1) ,env) ;;; (guard (number? exp1)) ;;; `(,env (if ,exp0 ,exp1 0)))) ;;;> (foo '(if 1 2) 'anenv) ;;;(anenv (if 1 2 0)) ;;;> (synlet ([`(if ,exp0 ,exp1) ;;; (guard (number? exp0)) ;;; '(if 0 1)]) ;;; `(if ,exp1, exp0)) ;;;(if 1 0) ;;;> (synlet ([`(if ,x ,y ,z) '(if 1 2 3)] ;;; [`(if ,a then ,b else ,c) '(if 1 then 2 else 3)] ;;; [`(when ,u ,w) (guard (number? u) (number? w) (= u w)) ;;; '(when 1 1)]) ;;; (list x y z a b c a b)) ;;; (1 2 3 1 2 3 1 2) ;;;> (synlet* ([`(if ,exp0 ,exp1) (guard (number? exp0)) '(if 0 1)] ;;; [`(if ,x ,y ,exp2) `(if ,exp0 ,exp1 5)]) ;;; `(if ,exp0 ,y ,exp2)) ;;;(if 0 1 5) (library (tests synforms) (export syncase) (import (rnrs)) (define-syntax syncase (syntax-rules () [(_ Exp (Clause ...) ...) (let ([x Exp]) (call/cc (lambda (succeed) (pm:c start x succeed Clause ...) ... (error 'syncase "No match for ~s" x))))])) (define-syntax pm:c (syntax-rules (guard start finish) [(pm:c start V Succ Pattern (guard Exp ...) Body0 Body ...) (pm:parse start Pattern (pm:c finish V (when (and Exp ...) (Succ (begin Body0 Body ...)))))] [(pm:c finish V Body Pattern UsedFormals) (pm:find-dup UsedFormals (cont (Dup) (pm:error "Duplicate patvar ~s in pattern ~s" Dup Pattern)) (cont () (pm V Pattern Body)))] [(_ start V Succ Pattern Body0 Body ...) (pm:c start V Succ Pattern (guard) Body0 Body ...)] [(_ start V Succ Pattern) (pm:error "Missing body for pattern ~s" Pattern)])) (define-syntax pm:parse ;; returns parsed thing + used formals (syntax-rules (dots quasiquote quote unquote start) [(pm:parse start () K) (pm:ak K (null) ())] [(pm:parse start (unquote X) K) (pm:ak K (formal X) (X))] [(pm:parse start (A . D) K) (pm:parseqq start (A . D) K)] [(pm:parse start X K) (pm:ak K (keyword X) ())])) (define-syntax pm:parseqq;; returns parsed thing + used formals (lambda (x) (syntax-case x (unquote start dothead dottail dottemps pairhead pairtail) [(pm:parseqq start (unquote ()) K) #'(pm:error "Bad variable: ~s" ())] [(pm:parseqq start (unquote (quasiquote X)) K) #'(pm:parseqq start X K)] [(pm:parseqq start (unquote (X . Y)) K) #'(pm:error "Bad variable: ~s" (X . Y))] [(pm:parseqq start (unquote #(X ...)) K) #'(pm:error "Bad variable: ~s" #(X ...))] [(pm:parseqq start (unquote X) K) #'(pm:ak K (formal X) (X))] [(pm:parseqq start (X dots . Y) K) (eq? (syntax->datum #'dots) '...) #'(pm:parseqq start X (pm:parseqq dothead Y K))] [(pm:parseqq dothead Y K Xpat Xformals) #'(pm:parseqq^ start Y () () (pm:parseqq dottail Xpat Xformals K))] [(pm:parseqq dottail Xpat Xformals K Yrevpat Yformals) #'(pm:gen-temps Xformals () (pm:parseqq dottemps Xpat Yrevpat Xformals Yformals K))] [(pm:parseqq dottemps Xpat Yrevpat (Xformal ...) (Yformal ...) K Xtemps) #'(pm:ak K (dots (Xformal ...) Xtemps Xpat Yrevpat) (Xformal ... Yformal ...))] [(pm:parseqq start (X . Y) K) #'(pm:parseqq start X (pm:parseqq pairhead Y K))] [(pm:parseqq pairhead Y K Xpat Xformals) #'(pm:parseqq start Y (pm:parseqq pairtail Xpat Xformals K))] [(pm:parseqq pairtail Xpat (Xformal ...) K Ypat (Yformal ...)) #'(pm:ak K (pair Xpat Ypat) (Xformal ... Yformal ...))] [(pm:parseqq start X K) #'(pm:ak K (keyword X) ())]))) (define-syntax pm:parseqq^;; returns list-of parsed thing + used formals (syntax-rules (dots start pairhead) [(pm:parseqq^ start () Acc Used K) (pm:ak K Acc ())] [(pm:parseqq^ start (dots . Y) Acc Used K) (pm:error "Illegal continuation of list pattern beyond dots: ~s" Y)] [(pm:parseqq^ start (X . Y) Acc Used K) (pm:parseqq start X (pm:parseqq^ pairhead Y Acc Used K))] [(pm:parseqq^ pairhead Y Acc (Used ...) K Xpat (Xformal ...)) (pm:parseqq^ start Y (Xpat . Acc) (Used ... Xformal ...) K)] [(pm:parseqq^ start X Acc Used K) (pm:error "Bad pattern ~s" X)])) (define-syntax pm (syntax-rules (keyword formal dots null pair) [(pm V (keyword K) Body) (when (eqv? V 'K) Body)] [(pm V (formal F) Body) (let ((F V)) Body)] [(pm V (dots Dformals DTemps DPat (PostPat ...)) Body) (when (list? V) (let ((rev (reverse V))) (pm:help rev (PostPat ...) Dformals DTemps DPat Body)))] [(pm V (null) Body) (when (null? V) Body)] [(pm V (pair P0 P1) Body) (when (pair? V) (let ((X (car V)) (Y (cdr V))) (pm X P0 (pm Y P1 Body))))])) (define-syntax pm:help (syntax-rules () [(pm:help V () (DFormal ...) (DTemp ...) DPat Body) (let f ((ls V) (DTemp '()) ...) (if (null? ls) (let ((DFormal DTemp) ...) Body) (let ((X (car ls)) (Y (cdr ls))) (pm X DPat (f Y (cons DFormal DTemp) ...)))))] [(pm:help V (Post0 PostPat ...) DFormals DTemps DPat Body) (when (pair? V) (let ((X (car V)) (Y (cdr V))) (pm X Post0 (pm:help Y (PostPat ...) DFormals DTemps DPat Body))))])) (define-syntax pm:error (syntax-rules () [(pm:error X ...) (error 'syncase 'X ...)])) (define-syntax pm:eq? (syntax-rules () [(_ A B SK FK) ; b should be an identifier (let-syntax ([f (syntax-rules (B) [(f B _SK _FK) (pm:ak _SK)] [(f nonB _SK _FK) (pm:ak _FK)])]) (f A SK FK))])) (define-syntax pm:member? (syntax-rules () [(pm:member? A () SK FK) (pm:ak FK)] [(pm:member? A (Id0 . Ids) SK FK) (pm:eq? A Id0 SK (cont () (pm:member? A Ids SK FK)))])) (define-syntax pm:find-dup (syntax-rules () [(pm:find-dup () SK FK) (pm:ak FK)] [(pm:find-dup (X . Y) SK FK) (pm:member? X Y (cont () (pm:ak SK X)) (cont () (pm:find-dup Y SK FK)))])) (define-syntax pm:gen-temps (syntax-rules () [(_ () Acc K) (pm:ak K Acc)] [(_ (X . Y) Acc K) (pm:gen-temps Y (temp . Acc) K)])) ;;; ------------------------------ ;;; Continuation representation and stuff (define-syntax cont ; broken for non-nullary case (syntax-rules () [(_ () Body) Body] [(_ (Var ...) Body Exp ...) (let-syntax ([f (syntax-rules () [(_ Var ...) Body])]) (f Exp ...))])) (define-syntax pm:ak (syntax-rules () [(_ (X Y ...) Z ...) (X Y ... Z ...)])) ;;; ------------------------------ ;;; tests ;(define exp0 ; '(syncase '((a) (b) (c d)) ; ((,zz ,ww) ((,zz .. ,ww) ..) ; zz))) ;(define test ; (lambda (x) ; (pretty-print x) ; (pretty-print (eval x)) ; (newline))) ; ;(define test0 (lambda () (test exp0))) ;;; There are three additional special forms, which should be obvious. (define-syntax synlambda (syntax-rules (guard) [(_ pat (guard g ...) body0 body1 ...) (lambda (x) (syncase x [pat (guard g ...) (begin body0 body1 ...)]))] [(_ pat body0 body1 ...) (lambda (x) (syncase x [pat (begin body0 body1 ...)]))])) (define-syntax synlet (syntax-rules (guard) [(_ ([pat (guard g) rhs] ...) body0 body1 ...) ((synlambda `(,pat ...) (guard (and g ...)) body0 body1 ...) `(,rhs ...))] [(_ ([pat rhs] ...) body0 body1 ...) ((synlambda `(,pat ...) body0 body1 ...) `(,rhs ...))] [(_ stuff ...) (synlet-all-guarded () stuff ...)])) (define-syntax synlet-all-guarded (syntax-rules (guard) [(_ (x ...) () body0 body1 ...) (synlet (x ...) body0 body1 ...)] [(_ (x ...) ([pat (guard g0 g1 g2 ...) rhs] decl ...) body0 body1 ...) (synlet-all-guarded (x ... [pat (guard (and g0 g1 g2 ...)) rhs]) (decl ...) body0 body1 ...)] [(_ (x ...) ([pat rhs] decl ...) body0 body1 ...) (synlet-all-guarded (x ... [pat (guard #t) rhs]) (decl ...) body0 body1 ...)] [(_ (x ...) ([pat] decl ...) body0 body1 ...) (pm:error "synlet missing right-hand-side for pattern: ~s" pat)] [(_ () (decl ...)) (pm:error "synlet missing body")])) (define-syntax synlet* (syntax-rules () [(_ (dec) body0 body1 ...) (synlet (dec) body0 body1 ...)] [(_ (dec0 decl ...) body0 body1 ...) (synlet (dec0) (synlet* (decl ...) body0 body1 ...))])) (define make-double-collector-over-list (lambda (constructor1 base1 constructor2 base2) (letrec ((loop42 (lambda args (if (not (= (length args) 2)) (error 'syncase "Invalid rhs expression")) (let ([f (car args)] [arg (cadr args)]) (cond [(null? arg) `(,base1 ,base2)] [else (synlet ([`(,x ,y) (f (car arg))] [`(,x* ,y*) (loop42 f (cdr arg))]) `(,(constructor1 x x*) ,(constructor2 y y*)))]))))) loop42))))