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:
2026-09-29 23:51:41 +03:00
parent 6f54bfbe08
commit 53f92727a5
3 changed files with 62 additions and 10 deletions

View File

@@ -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"