diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index d0a66d5..871b2de 100644 --- a/src/kiss/Macros.hx +++ b/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/src/test/cases/MacroTestCase.kiss b/src/test/cases/MacroTestCase.kiss index f8989a1..bfc34ac 100644 --- a/src/test/cases/MacroTestCase.kiss +++ b/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