diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index d0a66d56..871b2de5 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -416,15 +416,16 @@ class Macros { var maxArgs = 0; // Once the &opt meta appears, all following arguments are optional until &rest var optIndex = -1; - // Once the &rest meta appears, no other arguments can be declared + // Once the &rest or &body meta appears, no other arguments can be declared var restIndex = -1; + var requireRest = false; var argNames = []; var macroCallForm = '($name'; for (arg in argList) { if (restIndex != -1) { - throw CompileError.fromExp(arg, "macros cannot declare arguments after a &rest argument"); + throw CompileError.fromExp(arg, "macros cannot declare arguments after a &rest or &body argument"); } switch (arg.def) { case Symbol(name): @@ -442,10 +443,19 @@ class Macros { optIndex = maxArgs; ++maxArgs; case MetaExp("rest", {pos: _, def: Symbol(name)}): + if (name == "body") { + CompileError.warnFromExp(arg, "Consider using &body instead of &rest when writing macros with bodies."); + } argNames.push(name); macroCallForm += ' [$name...]'; restIndex = maxArgs; maxArgs = null; + case MetaExp("body", {pos: _, def: Symbol(name)}): + argNames.push(name); + macroCallForm += ' [$name...]'; + restIndex = maxArgs; + requireRest = true; + maxArgs = null; default: throw CompileError.fromExp(arg, "macro argument should be an untyped symbol or a symbol annotated with &opt or &rest"); } @@ -468,8 +478,13 @@ class Macros { for (idx in optIndex...restIndex) { args[innerArgNames.shift()] = if (exps.length > idx) innerExps[idx] else null; } - if (innerArgNames.length > 0) - args[innerArgNames.shift()] = innerExps.slice(restIndex); + if (innerArgNames.length > 0) { + var restArgs = innerExps.slice(restIndex); + if (requireRest && restArgs.length == 0) { + throw CompileError.fromExp(wholeExp, 'Macro $name requires one or more expression for &body'); + } + args[innerArgNames.shift()] = restArgs; + } // Return the macro expansion: return Helpers.runAtCompileTime(CallExp(Symbol("begin").withPosOf(wholeExp), exps.slice(2)).withPosOf(wholeExp), k, args); diff --git a/kiss/src/test/cases/MacroTestCase.kiss b/kiss/src/test/cases/MacroTestCase.kiss index f8989a19..bfc34acc 100644 --- a/kiss/src/test/cases/MacroTestCase.kiss +++ b/kiss/src/test/cases/MacroTestCase.kiss @@ -16,7 +16,7 @@ // You should be able to run list comprehensions on expressions // and put the pieces back together in a modular way -(defmacro altDefun [name args &rest body] +(defmacro altDefun [name args &body body] (let [argPairs (groups (expList args) 2) untypedArgs diff --git a/projects/aoc/src/UtilMacros.kiss b/projects/aoc/src/UtilMacros.kiss index 2bdca660..8a5c94ee 100644 --- a/projects/aoc/src/UtilMacros.kiss +++ b/projects/aoc/src/UtilMacros.kiss @@ -1,9 +1,9 @@ -(defmacro year [num &rest body] +(defmacro year [num &body body] `(#when ,(symbol (+ "year" (symbolNameValue num))) (print (+ "year " (Std.string ,num))) ,@body)) -(defmacro day [num &rest body] +(defmacro day [num &body body] `(#when ,(symbol (+ "day" (symbolNameValue num))) (print (+ "day " (Std.string ,num))) ,@body)) diff --git a/projects/asciilib2/src/asciilib/Colors.kiss b/projects/asciilib2/src/asciilib/Colors.kiss index df6a796d..d3cda1e5 100644 --- a/projects/asciilib2/src/asciilib/Colors.kiss +++ b/projects/asciilib2/src/asciilib/Colors.kiss @@ -16,7 +16,7 @@ (defmethod _index [x y] (+ x (* y width))) -(defmacro withIndex [idxName xName yName &rest body] +(defmacro withIndex [idxName xName yName &body body] `(let [,idxName (_index ,xName ,yName)] ,@body)) diff --git a/projects/kiss-vscode/config/KissConfig.kiss b/projects/kiss-vscode/config/KissConfig.kiss index d6e61853..696dc926 100644 --- a/projects/kiss-vscode/config/KissConfig.kiss +++ b/projects/kiss-vscode/config/KissConfig.kiss @@ -240,7 +240,7 @@ random Std.random int Std.int))) -(defmacro withValueOrInputBox [v &rest body] +(defmacro withValueOrInputBox [v &body body] `(if ,v {,@body} (awaitLet [,v (inputBox)] diff --git a/projects/nat-archive-tool/src/nat/ArchiveController.kiss b/projects/nat-archive-tool/src/nat/ArchiveController.kiss index 57ec771c..628b2661 100644 --- a/projects/nat-archive-tool/src/nat/ArchiveController.kiss +++ b/projects/nat-archive-tool/src/nat/ArchiveController.kiss @@ -107,7 +107,7 @@ (set lastCollector (_composeArgCollector collectedArgs arg lastCollector))) (lastCollector))) -(defmacro defcommand [name args &rest body] +(defmacro defcommand [name args &body body] (let [argPairs (groups (expList args) 2) methodArgs diff --git a/projects/nat-archive-tool/src/nat/Lib.kiss b/projects/nat-archive-tool/src/nat/Lib.kiss index 229cfecd..93b6db79 100644 --- a/projects/nat-archive-tool/src/nat/Lib.kiss +++ b/projects/nat-archive-tool/src/nat/Lib.kiss @@ -31,7 +31,7 @@ // Retrieve multiple components from an Entity with mutable access. // All components will be serialized after the block is done. -(defmacro withWritableComponents [archive e bindings &rest body] +(defmacro withWritableComponents [archive e bindings &body body] (let [bindingPairs (groups (expList bindings) 2 Throw) bindingList @@ -49,7 +49,7 @@ ,@saveList ,retValSymbol))) -(defmacro withWritableEntry [archive e &rest body] +(defmacro withWritableEntry [archive e &body body] (let [retValSymbol (symbol)] `(let [,retValSymbol {,@body}]