Allow case guards, |

This commit is contained in:
2021-01-03 17:51:31 -07:00
parent 2e2e862f8a
commit f21b4378f6
3 changed files with 44 additions and 16 deletions

View File

@@ -153,6 +153,37 @@ class Helpers {
}
}
public static function makeSwitchCase(caseExp:ReaderExp, k:KissState, ?guard:Expr):Case {
var guard:Expr = null;
function makeSwitchPattern(patternExp:ReaderExp):Array<Expr> {
return switch (patternExp.def) {
case CallExp({pos: _, def: Symbol("when")}, whenExps):
patternExp.checkNumArgs(2, 2, "(when [guard] [pattern])");
if (guard != null)
throw CompileError.fromExp(caseExp, "case expression can only have one `when` guard");
guard = macro Prelude.truthy(${k.convert(whenExps[0])});
makeSwitchPattern(whenExps[1]);
case CallExp({pos: _, def: Symbol("or")}, orExps):
patternExp.checkNumArgs(2, null, "(or [pattern1] [pattern2] [patterns...])");
orExps.map(k.forCaseParsing().convert);
default:
[k.forCaseParsing().convert(patternExp)];
}
}
return switch (caseExp.def) {
case CallExp(patternExp, caseBodyExps):
{
values: makeSwitchPattern(patternExp),
expr: k.convert(CallExp(Symbol("begin").withPosOf(caseExp), caseBodyExps).withPosOf(caseExp)),
guard: guard
};
default:
throw CompileError.fromExp(caseExp, "case expressions for (case...) must take the form ([pattern] [body...])");
}
}
/**
Throw a CompileError if the given expression has the wrong number of arguments
**/

View File

@@ -252,20 +252,7 @@ class SpecialForms {
default:
null;
};
ESwitch(k.forCaseParsing().convert(args[0]), [
for (caseExp in args.slice(1))
switch (caseExp.def) {
// TODO support CallExp(Symbol("or")) to generate more than one case value
// TODO support guards
case CallExp(patternExp, caseBodyExps):
{
values: [k.forCaseParsing().convert(patternExp)],
expr: k.convert(CallExp(Symbol("begin").withPosOf(caseExp), caseBodyExps).withPosOf(caseExp))
};
default:
throw CompileError.fromExp(caseExp, "case expressions for (case...) must take the form ([pattern] [body...])");
}
], defaultExpr).withMacroPosOf(wholeExp);
ESwitch(k.forCaseParsing().convert(args[0]), args.slice(1).map(Helpers.makeSwitchCase.bind(_, k)), defaultExpr).withMacroPosOf(wholeExp);
};
// TODO macros for ifLet, expectLet, which extract from enums

View File

@@ -330,7 +330,17 @@
((Some "hey") (Assert.pass))
(otherwise (Assert.fail)))
(Assert.equals 5 (case (toOption 0)
(otherwise 5))))
(otherwise 5)))
// Test case with guards and multiple values
(case 5
((or 5 6) (Assert.pass))
(otherwise (Assert.fail)))
(case [2 3]
((or [_ 3] [1 1]) (Assert.pass))
(otherwise (Assert.fail)))
(case 5
((when false (or 5 6)) (Assert.fail))
(otherwise (Assert.pass))))
(defun _testMaps []
(deflocal :Map<String,String> myMap [=>"hey" "you"
@@ -400,4 +410,4 @@
// Make sure built-in call aliases don't override user-defined variables
(defun _testCallAlias []
(let [map [=>"hey" "you"]]
(Assert.equals "you" (dictGet map "hey"))))
(Assert.equals "you" (dictGet map "hey"))))