;;;; File: er-macros.scm ;;;; Author: Juergen Lorenz ;;;; ju (at) jugilo (dot) de ;;;; Date: Oct 24, 2010 ;;;; Oct 28, 2010 ;;;; Nov 02, 2010 ;;;; Nov 04, 2010 ;;;; Nov 09, 2010 ;;;; Nov 26, 2010 ;;;; Nov 28, 2010 ;;;; Jan 19, 2011 ;;;; Jan 27, 2011 ;;;; Feb 02, 2011 ;;;; Feb 16, 2011 ;;;; Feb 18, 2011 ;;;; Jun 20, 2011 ;;;; Jun 22, 2011 ;;;; Jun 26, 2011 ;;;; Jul 07, 2011 ;The macros in this module have been renamed for consistency with the ;ir-macros module. The old names are deprecated, but still implemented ;as aliases, so that code relying on them is not broken. ;In fact, the ir-macros module is much more convenient. (module er-macros * (import scheme (only chicken gensym assert condition-case)) (import-for-syntax (only matchable match)) ;As an application of the match macro we'll define a macro, ;er-macro-rules, that will simplify the implementation of low-level ;macros considerably. Note, that the transformer of such a macro is a ;routine of three parameters, usually called form, rename, compare?, and ;that within that routine form has to be destructured by hand, and the ;symbols have to be renamed by hand as well. In our enhancement, we want ;both to be done (almost) automatically. Moreover, we want our macro to ;be of the same syntax as syntax-rules, i.e. it should look like this ; ; (er-macro-rules (compare ?%sym ...) (pat xpr) (pat1 xpr1) ...) ; ;where %sym explicitly renames the symbol sym (another one letter prefix ;would do as well), pat, pat1, ... represent admissible macro calls and ;xpr, xpr1 ... corresponding expressions, evaluationg to the ;macro-expansion. Moreover, er-macro-rules ;should expand into a (lambda (form rename compare?) ...) as does ;syntax-rules. Since match consumes the form argument and %sym ... the ;rename argument, only the remaining compare? needs to be listed in the ;first argument of er-macro-rules. ;Note that the list (%sym ...) is in general rather long, it will be ;transformed into a long let of the form (let ((%sym (rename (sym-cdr ;'%sym))) ...) in the expansion, where sym-cdr is a local routine, which ;strips the prefix. ; ;As a non-trivial example consider the following low-level ;implementation of or. Note the similarity to the high-level one. ; ; (define-syntax my-or ; ok ; (er-macro-rules (compare? %if %my-or) ; ((_) #f) ; ((_ arg . args) ; `(,%if ,arg ,arg (,%my-or ,@args))))) ;A high-level implementation of er-macro-rules would work, but it ;would expose the local routine, sym-cdr, and delay the stripping of ;prefixes to run-time, although it can be done at compile-time. So we ;prefer a low-level implementation. ;;; (er-macro-rules (compare? %sym ...) (pat0 xpr0) (pat1 xpr1) ...) ;;; ---------------------------------------------------------------- ;;; where xpr0, xpr1, ... are procedures of one argument, a compare? ;;; procedure. ;;; Checks the macro's use against a series of patterns, pat0, pat1 ... ;;; and returns a syntax-transformer, i.e. a three argument lambda ;;; expression with body of the matching xpr0, xpr1, ... ;;; This macro will mostly be imported with import-for-syntax (define-syntax er-macro-rules ;; what can be done at compile-time is done at compile-time (lambda (f r c?) (let ( (compare? (caadr f)) (syms (cdadr f)) (rules (cddr f)) ) (let ( (pats (map car rules)) (xprs (map cadr rules)) (sym-cdr (lambda (%sym) (string->symbol (substring (symbol->string %sym) 1)))) (%let (r 'let)) (%lambda (r 'lambda)) (%match (r 'match)) (%er-macro-transformer (r 'er-macro-transformer)) (gform (gensym 'form)) (grename (gensym 'rename)) ;(gcompare? (gensym 'compare?)) ) `(,%er-macro-transformer (,%lambda (,gform ,grename ,compare?) (,%let ,(map (lambda (sym) `(,sym (,grename ',(sym-cdr sym)))) syms) (,%match ,gform ,@(map (lambda (pat xpr) `(,pat ,xpr));(,xpr ,compare?))) pats xprs))))))))) ;;; (explicit-renaming (compare? %sym ...) (code0 xpr0) (code1 xpr1) ...) ;;; --------------------------------------------------------------------- ;;; alias of er-macro-rules ;;; (for reasons of compatibility with earlier versions) (define-syntax explicit-renaming (syntax-rules () ((_ syms . pairs) (er-macro-rules syms . pairs)))) ;With er-macro-rules under our belt, it's easy to implement three ;macros, er-macro-define, er-macro-let and er-macro-letrec, which ;facilitate the implementation of low-level macros even more: We simply ;match one pattern, the macro code, against a literal lambda-expression ;with arguments compare? %sym .... This time, my-or would look like this ; ; (er-macro-define (my-or . args) ; (lambda (compare? %if %my-or) ; (if (null? args) ; #f ; (let ((tmp (car args))) ; `(,%if ,tmp ,tmp (,%my-or ,@(cdr args))))))) ;;; (er-macro-define code proc) ;;; ------------------------ ;;; where code is the complete macro-code (name . args), i.e. the pattern ;;; of a macro call, and proc is a macro-transformer, now a literal ;;; lambda-expression with parameters compare? %sym .... ;;; Returns a low-level define-syntax expression of the form ;;; (define-syntax name (lambda (form rename compare?) ...)) by means of ;;; er-macro-rules. (define-syntax er-macro-define (syntax-rules (lambda) ((_ (name . args) (lambda (compare? . syms) xpr . xprs)) (define-syntax name (er-macro-rules (compare? . syms) ((_ . args) (begin xpr . xprs))))))) ;er-macro-let and er-macro-letrec are local versions of er-macro-define, where ;the local macros are evaluated in parallel or recursively. For example ; (let ((f (lambda (n) (+ n 10)))) ; (er-macro-let ( ; ((f n) (lambda (compare?) n)) ; ((g n) (lambda (compare? %f) `(,%f ,n))) ; ) ; (display (list (f 1) (g 1))) (newline))) ; ;will result in (1 11) while ; ; (let ((f (lambda (n) (+ n 10)))) ; (er-macro-letrec ( ; ((f n) (lambda (compare?) n)) ; ((g n) (lambda (compare? %f) `(,%f ,n))) ; ) ; (display (list (f 1) (g 1))) (newline))) ; ;returns (1 1). ;;; (er-macro-let ((code proc) ...) . body) ;;; ------------------------------------ ;;; where code and proc are as in er-macro-define. This is ;;; a local version of er-macro-define, allowing a list of ;;; (code proc) lists to be processed in body in parallel. (define-syntax er-macro-let (syntax-rules (lambda) ((_ ( ((name0 . args0) (lambda (compare? . syms0) xpr0 . xprs0)) ... ) xpr . xprs) (let-syntax ( (name0 (er-macro-rules (compare? . syms0) ((_ . args0) (begin xpr0 . xprs0)))) ... ) xpr . xprs)))) ;;; (er-macro-letrec ((code proc) ...) . body) ;;; --------------------------------------- ;;; where code and proc are as in er-macro-define. ;;; Local version of er-macro-define, allowing a list of ;;; (code proc) lists to be processed in body recursively. (define-syntax er-macro-letrec (syntax-rules (lambda) ((_ ( ((name0 . args0) (lambda (compare? . syms0) xpr0 . xprs0)) ... ) xpr . xprs) (letrec-syntax ( (name0 (er-macro-rules (compare? . syms0) ((_ . args0) (begin xpr0 . xprs0)))) ... ) xpr . xprs)))) ;The following macro can be used to simplify renaming. ;Its syntax is similar to Graham's with-gensyms but more flexible ;because of the extra rename argument. ; ;;; (with-aliases (rename %sym0 %sym1 ...) . body) ;;; ---------------------------------------------- ;;; executes body with aliases of sym0 sym1 ... ;;; (to be used in macros with import-for-syntax) (define-syntax with-aliases (er-macro-transformer (lambda (f r c?) (let ( (rename (caadr f)) (%syms (cdadr f)) (body (cddr f)) (%let (r 'let)) ) (let ((syms (map (lambda (%sym) (string->symbol (substring (symbol->string %sym) 1))) %syms))) `(,%let ,(map (lambda (%sym sym) `(,%sym (,rename ',sym))) %syms syms) ,@body)))))) ) ; module er-macros