From 678959708df480de6c50275c578582ef6a062870 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Mon, 29 Nov 2021 13:28:18 -0700 Subject: [PATCH] case handle null. close #14 --- kiss/src/kiss/Macros.hx | 41 ++++++++++---------------- kiss/src/kiss/SpecialForms.hx | 36 +++++++++++++++++++++- kiss/src/test/cases/BasicTestCase.hx | 4 +++ kiss/src/test/cases/BasicTestCase.kiss | 9 ++++-- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index 95dd1479..8b13380c 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -645,7 +645,6 @@ class Macros { var bindingList = exps[0].bindingList(funcName); var firstPattern = bindingList.shift(); var firstValue = bindingList.shift(); - var firstValueSymbol = b.symbol(); var thenExp = if (assertLet) b.begin(exps.slice(1)) else exps[1]; var elseExp = if (!assertLet && exps.length > 2) { @@ -656,29 +655,20 @@ class Macros { b.symbol("null"); }; - return b.callSymbol("let", [ - b.list([firstValueSymbol, firstValue]), - b.callSymbol("if", [ - firstValueSymbol, - b.call( - b.symbol("case"), [ - firstValueSymbol, - b.call( - firstPattern, [ - if (bindingList.length == 0) { - thenExp; - } else { - ifLet(assertLet, wholeExp, [ - b.list(bindingList) - ].concat(exps.slice(1)), k); - } - ]), - b.call( - b.symbol("otherwise"), [ - elseExp - ]) - ]), - elseExp + return b.callSymbol("case", [ + firstValue, + b.call( + firstPattern, [ + if (bindingList.length == 0) { + thenExp; + } else { + ifLet(assertLet, wholeExp, [ + b.list(bindingList) + ].concat(exps.slice(1)), k); + } + ]), + b.callSymbol("otherwise", [ + elseExp ]) ]); } @@ -1109,7 +1099,8 @@ class Macros { return b.callSymbol("case", [ b.callField(funcName, exps.shift(), exps), b.callSymbol("-1", [b.symbol("haxe.ds.Option.None")]), - b.callSymbol("other", [b.callSymbol("haxe.ds.Option.Some", [b.symbol("other")])]) + b.callSymbol("other", [b.callSymbol("haxe.ds.Option.Some", [b.symbol("other")])]), + b.callSymbol("null", [b.callSymbol("throw", [b.str("Haxe indexOf is broken")])]) ]); } macros["indexOf"] = indexOfMacro.bind(false); diff --git a/kiss/src/kiss/SpecialForms.hx b/kiss/src/kiss/SpecialForms.hx index 2278c58f..2924420e 100644 --- a/kiss/src/kiss/SpecialForms.hx +++ b/kiss/src/kiss/SpecialForms.hx @@ -12,6 +12,7 @@ using kiss.Reader; using kiss.Helpers; using kiss.Prelude; using kiss.Kiss; +using tink.MacroApi; // Special forms convert Kiss reader expressions into Haxe macro expressions typedef SpecialFormFunction = (wholeExp:ReaderExp, args:Array, k:KissState) -> Expr; @@ -285,6 +286,14 @@ class SpecialForms { // Therefore only one case is required in a case statement, because one case could be enough // to cover all patterns. wholeExp.checkNumArgs(2, null, '(case [expression] [cases...] [optional: (otherwise [default])])'); + + var isTupleCase = switch (args[0].def) { + case ListExp(_): + true; + default: + false; + } + var b = wholeExp.expBuilder(); var defaultExpr = switch (args[-1].def) { case CallExp({pos: _, def: Symbol("otherwise")}, otherwiseExps): @@ -293,7 +302,32 @@ class SpecialForms { default: null; }; - ESwitch(k.withoutListWrapping().convert(args[0]), args.slice(1).map(Helpers.makeSwitchCase.bind(_, k)), defaultExpr).withMacroPosOf(wholeExp); + + var cases = args.slice(1); + // case also override's haxe's switch() behavior by refusing to match null values against patterns. + if (!isTupleCase) { + var nullExpr = defaultExpr; + var idx = 0; + for (arg in cases) { + switch (arg.def) { + case CallExp({pos: _, def: Symbol("null")}, nullExps): + cases.splice(idx, 1); + nullExpr = k.convert(b.begin(nullExps)); + break; + default: + } + ++idx; + } + + if (nullExpr == null) { + throw CompileError.fromExp(wholeExp, "Unmatched pattern: null"); + } + + var nullCase = b.callSymbol("null", [b.raw(nullExpr.toString())]); + cases.insert(0, nullCase); + } + + ESwitch(k.withoutListWrapping().convert(args[0]), cases.map(Helpers.makeSwitchCase.bind(_, k)), defaultExpr).withMacroPosOf(wholeExp); }; // Type check syntax: diff --git a/kiss/src/test/cases/BasicTestCase.hx b/kiss/src/test/cases/BasicTestCase.hx index b150fabf..9f278c82 100644 --- a/kiss/src/test/cases/BasicTestCase.hx +++ b/kiss/src/test/cases/BasicTestCase.hx @@ -327,6 +327,10 @@ class BasicTestCase extends Test { _testEvalStatic(); _testEval(); } + + function testCaseOnNull() { + _testCaseOnNull(); + } } class BasicObject { diff --git a/kiss/src/test/cases/BasicTestCase.kiss b/kiss/src/test/cases/BasicTestCase.kiss index 1c9eae0d..3ddcdd67 100644 --- a/kiss/src/test/cases/BasicTestCase.kiss +++ b/kiss/src/test/cases/BasicTestCase.kiss @@ -351,7 +351,7 @@ (function _testCase [] (case (toOption []) (None (Assert.pass)) - ((Some value) (Assert.fail))) + (otherwise (Assert.fail))) (case (toOption "hey") (None (Assert.fail)) ((Some "hey") (Assert.pass)) @@ -589,4 +589,9 @@ (function _testEvalStatic [] (Assert.equals 9 (eval 'staticValue)) - (assertThrows (eval 'value))) \ No newline at end of file + (assertThrows (eval 'value))) + +(function _testCaseOnNull [] + (Assert.equals 5 (case null (v 10) (null 5))) + (Assert.equals 5 (case null (v 10) (null 5) (otherwise 6))) + (Assert.equals 5 (case null (v 10) (otherwise 5)))) \ No newline at end of file