mark multi-form macro expansions with $
A bare list was read as several forms, so a macro returning `((make-adder 10) 5)' -- a call of what make-adder returns -- was spliced into two. Add a new $ char to denote splicing, so ($ form ...) splices.
This commit is contained in:
14
Readme.org
14
Readme.org
@@ -212,6 +212,20 @@ Sex has support for syntactic macros. Macro definitions look like
|
||||
functions: they have a name, an argument list and a body. Macro should
|
||||
return Sex code.
|
||||
|
||||
A macro returns *one* form. To return several --- a function beside the
|
||||
struct it works on, say --- return them under =$=, which splices them in
|
||||
where the macro was written:
|
||||
|
||||
#+begin_src scheme
|
||||
(defmacro (pair-of-fns a b)
|
||||
`($ (fn ,a () int (return 1))
|
||||
(fn ,b () int (return 2))))
|
||||
#+end_src
|
||||
|
||||
=($)= expands to nothing. Everything else is a single form, including
|
||||
one whose head is itself a form: =`((make-adder 10) 5)= calls what
|
||||
=make-adder= returned, and is not two forms.
|
||||
|
||||
*** Examples:
|
||||
**** Structure with templated value type
|
||||
#+begin_src scheme
|
||||
|
||||
27
semen.scm
27
semen.scm
@@ -52,22 +52,29 @@
|
||||
(cons (car forms) (take-until (cdr forms) tail))))
|
||||
|
||||
(define (macroexpand macro-form rest-forms)
|
||||
;; We want to replace macro with its expansion. The problem is,
|
||||
;; top-level macro can return either a single form, or a list of
|
||||
;; forms, when it for example generates some aux
|
||||
;; structures/functions/typedefs.
|
||||
;; `(defmacro (two) 2)' expands to `2', and `($ (fn a ...) (fn b ...))'
|
||||
;; to two forms, spliced where the macro was written. `($)' expands to
|
||||
;; nothing.
|
||||
;;
|
||||
;; Single form we just cons to the top of rest-forms, but multiple
|
||||
;; forms have to be appended to the rest-forms.
|
||||
;; Everything else is one form, a list whose head is itself a form
|
||||
;; included: `((make-adder 10) 5)' calls what `make-adder' returned,
|
||||
;; and without `$' to mark a splice there is no telling that from a
|
||||
;; list of the two forms `(make-adder 10)' and `5'.
|
||||
(let ((res (apply-macro macro-form))
|
||||
(src (form-source macro-form)))
|
||||
;; An expansion is fresh structure with no location of its own. Give
|
||||
;; it the call site's, the way cpp attributes a macro body to where
|
||||
;; the macro was used
|
||||
(if (list? (car res))
|
||||
(append (map (lambda (f) (stamp-form-source! f src)) res)
|
||||
rest-forms)
|
||||
(cons (stamp-form-source! res src) rest-forms))))
|
||||
(cond
|
||||
((splice-form? res)
|
||||
(append (map (lambda (f) (stamp-form-source! f src)) (cdr res))
|
||||
rest-forms))
|
||||
((null? res) rest-forms)
|
||||
(else
|
||||
(cons (stamp-form-source! res src) rest-forms)))))
|
||||
|
||||
(define (splice-form? form)
|
||||
(and (pair? form) (list? form) (eq? '$ (car form))))
|
||||
|
||||
(define (match-sex-form sex-form acc)
|
||||
(match sex-form
|
||||
|
||||
@@ -327,6 +327,37 @@ compiles."
|
||||
(fn f ((c (closure ((int)) int))) int (return (g c)))"
|
||||
"g(c)")))
|
||||
|
||||
;; `(car res)' on the expansion assumed it was a pair, so a macro
|
||||
;; computing a value rather than building a form crashed the compiler.
|
||||
(test-group "macro expanding to an atom"
|
||||
(test-assert "a number"
|
||||
(emits? "(defmacro (two) 2) (fn f () int (return (two)))"
|
||||
"return 2;"))
|
||||
(test-assert "a string"
|
||||
(emits? "(defmacro (who) \"sex\") (fn f () void (g (who)))"
|
||||
"g(\"sex\")"))
|
||||
;; a symbol expansion can stand where a type does, which is what
|
||||
;; makes a macro able to compute one
|
||||
(test-assert "a symbol, used as a type"
|
||||
(emits? "(defmacro (ty) 'int) (fn f () void (var x (ty) 0))"
|
||||
"int x = 0"))
|
||||
;; ...and nothing at all, for a macro that only registers something
|
||||
(test-assert "nothing, at toplevel"
|
||||
(emits? "(defmacro (quiet) (list)) (quiet) (fn f () int (return 1))"
|
||||
"return 1;"))
|
||||
(test-assert "nothing, in a body"
|
||||
(emits? "(defmacro (quiet) (list)) (fn f () int (quiet) (return 1))"
|
||||
"return 1;"))
|
||||
;; several forms need `$', which is what tells a splice from a call
|
||||
(test-assert "$ splices"
|
||||
(emits? "(defmacro (pair) (list '$ '(fn a () int (return 1))
|
||||
'(fn b () int (return 2))))
|
||||
(pair)"
|
||||
"b (void)"))
|
||||
(test-assert "and ($) is nothing at all"
|
||||
(emits? "(defmacro (quiet) (list '$)) (quiet) (fn f () int (return 1))"
|
||||
"return 1;")))
|
||||
|
||||
;; A unary expression parenthesised its operand rather than itself, so
|
||||
;; the parens landed inside: `*(p).x', which C reads as `*(p.x)'.
|
||||
(test-group "unary operand precedence"
|
||||
|
||||
Reference in New Issue
Block a user