diff --git a/kiss/src/kiss/Helpers.hx b/kiss/src/kiss/Helpers.hx index d23abebd..7544323e 100644 --- a/kiss/src/kiss/Helpers.hx +++ b/kiss/src/kiss/Helpers.hx @@ -642,7 +642,13 @@ class Helpers { function let(bindings:Array, body:Array) { return callSymbol("let", [list(bindings)].concat(body)); } - + function throwAssertOrNeverError(messageExp:ReaderExp) { + var failureError = KissError.fromExp(posRef, "").toString(AssertionFail); + var colonsInPrefix = if (Sys.systemName() == "Windows") 5 else 4; + return callSymbol("throw", [ + callSymbol("kiss.Prelude.runtimeInsertAssertionMessage", [messageExp, str(failureError), int(colonsInPrefix)]) + ]); + } return { call: call, callSymbol: callSymbol, @@ -666,22 +672,30 @@ class Helpers { objectWith: objectWith, // Only use within assertion macros throwAssertionError: () -> { - var failureError = KissError.fromExp(posRef, "").toString(AssertionFail); - var colonsInPrefix = if (Sys.systemName() == "Windows") 5 else 4; + var usage = "throwAssertionError can only be used in a builder of an assertion macro"; var exps = switch (posRef.def) { case CallExp(_, exps): exps; default: - throw KissError.fromExp(_symbol("throwAssertionError"), "throwAssertionError can only be used in a builder of an assertion macro"); + throw KissError.fromExp(_symbol("throwAssertionError"), usage); } var messageExp = if (exps.length > 1) { exps[1]; } else { str(""); }; - callSymbol("throw", [ - callSymbol("kiss.Prelude.runtimeInsertAssertionMessage", [messageExp, str(failureError), int(colonsInPrefix)]) - ]); + throwAssertOrNeverError(messageExp); + }, + neverCase: () -> { + switch (posRef.def) { + case CallExp({pos: _, def: Symbol("never")}, neverExps): + posRef.checkNumArgs(1, 1, '(never )'); + call(neverExps[0], [ + throwAssertOrNeverError(str('case should never match pattern ${Reader.toString(neverExps[0].def)}')) + ]); + default: + posRef; + } }, // Compile-time only! throwKissError: (reason:String) -> { @@ -705,6 +719,16 @@ class Helpers { }; } + public static function checkNoEarlyOtherwise(cases:kiss.List) { + for (i in 0...cases.length) { + switch (cases[i].def) { + case CallExp({pos: _, def: Symbol("otherwise")}, _) if (i != cases.length - 1): + throw KissError.fromExp(cases[i], "(otherwise ) branch must come last in a (case <...>) expression"); + default: + } + } + } + public static function argList(exp:ReaderExp, forThis:String, allowEmpty = true):Array { return switch (exp.def) { case ListExp([]) if (allowEmpty): diff --git a/kiss/src/kiss/SpecialForms.hx b/kiss/src/kiss/SpecialForms.hx index 87007320..920bf396 100644 --- a/kiss/src/kiss/SpecialForms.hx +++ b/kiss/src/kiss/SpecialForms.hx @@ -333,27 +333,11 @@ class SpecialForms { // to cover all patterns. var args:kiss.List = args.copy(); - var cases:kiss.List = args.slice(1); - for (i in 0...cases.length) { - switch (cases[i].def) { - case CallExp({pos: _, def: Symbol("never")}, neverExps): - cases[i].checkNumArgs(1, 1, '(never )'); - var b = cases[i].expBuilder(); - var failureError = KissError.fromExp(cases[i], '').toString(AssertionFail); - var colonsInPrefix = if (Sys.systemName() == "Windows") 5 else 4; - cases[i] = b.call(neverExps[0], [ - b.callSymbol('throw', [ - b.callSymbol('kiss.Prelude.runtimeInsertAssertionMessage', [b.str('${Reader.toString(neverExps[0].def)} should never match pattern ${Reader.toString(neverExps[0].def)}'), b.str(failureError), b.int(colonsInPrefix)])])]); - default: - } - } - for (i in 0...cases.length) { - switch (cases[i].def) { - case CallExp({pos: _, def: Symbol("otherwise")}, _) if (i != cases.length - 1): - throw KissError.fromExp(cases[i], "(otherwise ) branch must come last in a (case <...>) expression"); - default: - } - } + var cases:kiss.List = [for (c in args.slice(1)) { + c.expBuilder().neverCase(); + }]; + + Helpers.checkNoEarlyOtherwise(cases); var isTupleCase = switch (args[0].def) { case ListExp(_):