|
Defining quasisyntax in terms of syntax-case
Andre van Tonder
(30 Jun 2006 02:30 UTC)
|
|
Re: Defining quasisyntax in terms of syntax-case
Andre van Tonder
(30 Jun 2006 11:25 UTC)
|
|
Re: Defining quasisyntax in terms of syntax-case
David Feuer
(30 Jun 2006 11:43 UTC)
|
|
Re: Defining quasisyntax in terms of syntax-case
Andre van Tonder
(30 Jun 2006 13:56 UTC)
|
|
Re: Defining quasisyntax in terms of syntax-case Andre van Tonder (30 Jun 2006 19:06 UTC)
|
Apologies for the multiple postings. I have added a number of test cases and
fixed a few bugs. Here is the updated implementation. Any additional test
cases are welcome.
Andre
;;;=========================================================
;;;
;;; Quasisyntax in terms of SRFI-93 syntax-case.;;;
;;; Andre van Tonder
;;;
;;;=========================================================
;;;
;;; To make nested unquote-splicing behave in a useful way,
;;; the R5RS-compatible extension of quasiquote in appendix B
;;; of the following paper is here ported to quasisyntax:
;;;
;;; Alan Bawden - Quasiquotation in Lisp
;;; http://citeseer.ist.psu.edu/bawden99quasiquotation.html
;;;
;;; The algorithm converts a quasisyntax expression to an
;;; equivalent with-syntax expression.
;;; For example:
;;;
;;; (quasisyntax (set! ,a ,b))
;;; ==> (with-syntax ((t0 a)
;;; (t1 b))
;;; (syntax (set! t0 t1)))
;;;
;;; (quasisyntax (list ,@args))
;;; ==> (with-syntax (((t ...) args))
;;; (syntax (list t ...)))
;;;
;;; Note that quasisyntax is expanded first, before any
;;; ellipses act. For example:
;;;
;;; (quasisyntax (f ((b ,a) ...))
;;; ==> (with-syntax ((t a))
;;; (syntax (f ((b t) ...))))
;;;
;;; so that
;;;
;;; (let-syntax ((test-ellipses-over-unsyntax
;;; (lambda (e)
;;; (let ((a (syntax a)))
;;; (with-syntax (((b ...) (syntax (1 2 3))))
;;; (quasisyntax
;;; (quote ((b ,a) ...))))))))
;;; (test-ellipses-over-unsyntax))
;;;
;;; ==> ((1 a) (2 a) (3 a))
(define-syntax quasisyntax
(lambda (e)
(define (expand-quasisyntax x)
;; Expand returns a syntax object of the form
;; (template[t/e, ...] (replacement ...))
;; Here template[t/e ...] denotes the original template
;; with unquoted expressions e replaced by fresh
;; variables t, followed by the appropriate ellipses
;; if e is also spliced.
;; The second part of the return value is the list of
;; replacements, each of the form (t e) if e is just
;; unquoted, or ((t ...) e) if e is also spliced.
;; This will be the list of bindings of the resulting
;; with-syntax expression.
(define (expand x level)
(syntax-case x (quasisyntax unquote unquote-splicing)
((quasisyntax e)
(with-syntax (((k _) x) ; Original must be copied
((rest bs) (expand (syntax e) (+ level 1))))
(syntax
((k rest) bs))))
((unquote e)
(= level 0)
(with-syntax (((t) (generate-temporaries '(t))))
(syntax (t ((t e))))))
(((unquote e ...) . r)
(= level 0)
(with-syntax (((rest (b ...)) (expand (syntax r) 0))
((t ...) (generate-temporaries (syntax (e
...)))))
(syntax
((t ... . rest)
((t e) ... b ...)))))
(((unquote-splicing e ...) . r)
(= level 0)
(with-syntax (((rest (b ...)) (expand (syntax r) 0))
((t ...) (generate-temporaries (syntax (e
...)))))
(with-syntax ((((t ...) ...) (syntax ((t (... ...)) ...))))
(syntax
((t ... ... . rest)
(((t ...) e) ... b ...))))))
((k . r)
(and (> level 0)
(identifier? (syntax k))
(or (free-identifier=? (syntax k) (syntax unquote))
(free-identifier=? (syntax k) (syntax unquote-splicing))))
(with-syntax (((rest bs) (expand (syntax r) (- level 1))))
(syntax
((k . rest) bs))))
((h . t)
(with-syntax (((head (b1 ...)) (expand (syntax h) level))
((tail (b2 ...)) (expand (syntax t) level)))
(syntax
((head . tail)
(b1 ... b2 ...)))))
(#(e ...)
(with-syntax ((((e* ...) bs)
(expand (vector->list (syntax #(e ...))) level)))
(syntax
(#(e* ...) bs))))
(other
(syntax (other ())))))
(with-syntax (((template bindings) (expand x 0)))
(syntax
(with-syntax bindings (syntax template)))))
(syntax-case e ()
((k template)
(expand-quasisyntax (syntax template))))))
;;;==========================================================================
;;;
;;; Tests:
;;;
;;;==========================================================================
(define-syntax swap!
(lambda (e)
(syntax-case e ()
((_ a b)
(let ((a (syntax a))
(b (syntax b)))
(quasisyntax
(let ((temp ,a))
(set! ,a ,b)
(set! ,b temp))))))))
(let ((temp 1)
(set! 2))
(swap! set! temp)
(values temp set!)) ;==> 2 1
(define-syntax case
(lambda (x)
(syntax-case x ()
((_ e c1 c2 ...)
(quasisyntax
(let ((t e))
,(let f ((c1 (syntax c1)) (cmore (syntax (c2 ...))))
(if (null? cmore)
(syntax-case c1 (else)
((else e1 e2 ...) (syntax (begin e1 e2 ...)))
(((k ...) e1 e2 ...) (syntax (if (memv t '(k ...))
(begin e1 e2 ...)))))
(syntax-case c1 ()
(((k ...) e1 e2 ...)
(quasisyntax
(if (memv t '(k ...))
(begin e1 e2 ...)
,(f (car cmore) (cdr cmore))))))))))))))
(case 'a
((b c) 'no)
((d a) 'yes))
(define-syntax let-in-order
(lambda (form)
(syntax-case form ()
((_ ((i e) ...) e0 e1 ...)
(let f ((ies (syntax ((i e) ...)))
(its (syntax ())))
(syntax-case ies ()
(() (quasisyntax (let ,its e0 e1 ...)))
(((i e) . ies) (with-syntax (((t) (generate-temporaries '(t))))
(quasisyntax
(let ((t e))
,(f (syntax ies)
(quasisyntax
((i t) ,@its)))))))))))))
(let-in-order ((x 1)
(y 2))
(+ x y)) ;==> 3
(let-syntax ((test-ellipses-over-unsyntax
(lambda (e)
(let ((a (syntax a)))
(with-syntax (((b ...) (syntax (1 2 3))))
(quasisyntax
(quote ((b ,a) ...))))))))
(test-ellipses-over-unsyntax))
;==> ((1 a) (2 a) (3 a))
;; Some tests found online (Guile?)
(let-syntax ((test
(lambda (_)
(quasisyntax
'(list ,(+ 1 2) 4)))))
(test))
;==> (list 3 4)
(let-syntax ((test
(lambda (_)
(let ((name (syntax a)))
(quasisyntax '(list ,name ',name))))))
(test))
;==> (list a 'a)
(let-syntax ((test
(lambda (_)
(quasisyntax '(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)))))
(test))
;==> (a 3 4 5 6 b)
(let-syntax ((test
(lambda (_)
(quasisyntax '((foo ,(- 10 3)) ,@(cdr '(5)) . ,(car '(7)))))))
(test))
;==> ((foo 7) . 7)
(let-syntax ((test
(lambda (_)
(quasisyntax '#(10 5 ,(sqrt 4) ,@(map sqrt '(16 9)) 8)))))
(test))
;==> #6(10 5 2 4 3 8)
(let-syntax ((test
(lambda (_)
(quasisyntax ,(+ 2 3)))))
(test))
;==> 5
(let-syntax ((test
(lambda (_)
(quasisyntax
'(a (quasisyntax (b ,(+ 1 2) ,(foo ,(+ 1 3) d) e)) f)))))
(test))
;==> (a (quasisyntax (b ,(+ 1 2) ,(foo 4
d) e)) f)
(let-syntax ((test
(lambda (_)
(let ((name1 #'x) (name2 #'y))
(quasisyntax
'(a (quasisyntax (b ,,name1 ,#',name2 d)) e))))))
(test))
;==> (a (quasisyntax (b ,x ,#'y d)) e)
;; Bawden's extensions:
(let-syntax ((test
(lambda (_)
(quasisyntax '(a (unquote 1 2) b)))))
(test))
;==> (a 1 2 b)
(let-syntax ((test
(lambda (_)
(quasisyntax '(a (unquote-splicing '(1 2) '(3 4)) b)))))
(test))
;==> (a 1 2 3 4 b)
(let-syntax ((test
(lambda (_)
(let ((x #'(a b c)))
(quasisyntax '(quasisyntax (,,x ,@,x ,,@x ,@,@x)))))))
(test))
;==> (quasisyntax (,(a b c) ,@(a b c) (unquote a b c) (unquote-splicing
a b c)))
; which is equivalent to
; (quasisyntax (,(a b c) ,@(a b c) ,a ,b ,c ,@a ,@b ,@c)
; in the Bawden prescription