Refactor variadic handling so (apply [operator] [args]) will be possible

This commit is contained in:
2020-12-04 10:49:43 -07:00
parent 24c1b5a604
commit 1b07c2ddf9
4 changed files with 64 additions and 63 deletions

View File

@@ -20,13 +20,13 @@ class Macros {
public static function builtins() {
var macros:Map<String, MacroFunction> = [];
macros["+"] = foldMacro("Prelude.add");
macros["+"] = variadicMacro("Prelude.add");
macros["-"] = foldMacro("Prelude.subtract");
macros["-"] = variadicMacro("Prelude.subtract");
macros["*"] = foldMacro("Prelude.multiply");
macros["*"] = variadicMacro("Prelude.multiply");
macros["/"] = foldMacro("Prelude.divide");
macros["/"] = variadicMacro("Prelude.divide");
macros["%"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k) -> {
wholeExp.checkNumArgs(2, 2, '(% [divisor] [dividend])');
@@ -38,15 +38,15 @@ class Macros {
CallExp(Symbol("Prelude.pow").withPosOf(wholeExp), [exps[1], exps[0]]).withPosOf(wholeExp);
};
macros["min"] = foldMacro("Prelude.min");
macros["max"] = foldMacro("Prelude.max");
macros["min"] = variadicMacro("Prelude.min");
macros["max"] = variadicMacro("Prelude.max");
macros["_greaterThan"] = foldMacro("Prelude.greaterThan");
macros["_greaterEqual"] = foldMacro("Prelude.greaterEqual");
macros["_lessThan"] = foldMacro("Prelude.lessThan");
macros["_lesserEqual"] = foldMacro("Prelude.lesserEqual");
macros[">"] = variadicMacro("Prelude.greaterThan");
macros[">="] = variadicMacro("Prelude.greaterEqual");
macros["<"] = variadicMacro("Prelude.lessThan");
macros["<="] = variadicMacro("Prelude.lesserEqual");
macros["_eq"] = foldMacro("Prelude.areEqual");
macros["="] = variadicMacro("Prelude.areEqual");
function bodyIf(formName:String, negated:Bool, wholeExp:ReaderExp, args:Array<ReaderExp>, k) {
wholeExp.checkNumArgs(2, null, '($formName [condition] [body...])');
@@ -246,33 +246,14 @@ class Macros {
};
}
static function foldMacro(func:String):MacroFunction {
static function variadicMacro(func:String):MacroFunction {
return (wholeExp:ReaderExp, exps:Array<ReaderExp>, k) -> {
// Lambda.fold calls need at least 1 argument
wholeExp.checkNumArgs(1, null);
var uniqueVarExps = [];
var bindingList = [];
for (exp in exps) {
var uniqueVarName = "_" + Uuid.v4().toShort();
var uniqueVarSymbol = Symbol(uniqueVarName).withPosOf(wholeExp);
uniqueVarExps.push(uniqueVarSymbol);
bindingList = bindingList.concat([
TypedExp("kiss.Operand", uniqueVarSymbol).withPosOf(wholeExp),
CallExp(Symbol("kiss.Operand.fromDynamic").withPosOf(wholeExp), [exp]).withPosOf(wholeExp)
]);
};
CallExp(Symbol("let").withPosOf(wholeExp), [
ListExp(bindingList).withPosOf(wholeExp),
CallExp(Symbol("kiss.Operand.toDynamic").withPosOf(wholeExp), [
CallExp(Symbol("Lambda.fold").withPosOf(wholeExp), [
ListExp(uniqueVarExps.slice(1)).withPosOf(wholeExp),
Symbol(func).withPosOf(wholeExp),
uniqueVarExps[0]
]).withPosOf(wholeExp)
]).withPosOf(wholeExp),
CallExp(Symbol(func).withPosOf(wholeExp), [
ListExp([
for (exp in exps) {
CallExp(Symbol("kiss.Operand.fromDynamic").withPosOf(wholeExp), [exp]).withPosOf(wholeExp);
}
]).withPosOf(wholeExp)
]).withPosOf(wholeExp);
};
}

View File

@@ -6,8 +6,19 @@ import kiss.Operand;
import haxe.ds.Either;
class Prelude {
static function variadic(op:(Operand, Operand) -> Null<Operand>, comparison = false):(Array<Operand>) -> Dynamic {
return (l:kiss.List<Operand>) -> switch (Lambda.fold(l.slice(1), op, l[0])) {
case null:
false;
case somethingElse if (comparison):
true;
case somethingElse:
Operand.toDynamic(somethingElse);
};
}
// Kiss arithmetic will incur overhead because of these switch statements, but the results will not be platform-dependent
public static function add(a:Operand, b:Operand):Operand {
static function _add(a:Operand, b:Operand):Operand {
return switch ([a, b]) {
case [Left(str), Left(str2)]:
Left(str2 + str);
@@ -18,7 +29,9 @@ class Prelude {
};
}
public static function subtract(val:Operand, from:Operand):Operand {
public static var add = variadic(_add);
static function _subtract(val:Operand, from:Operand):Operand {
return switch ([from, val]) {
case [Right(from), Right(val)]:
Right(from - val);
@@ -27,7 +40,9 @@ class Prelude {
}
}
public static function multiply(a:Operand, b:Operand):Operand {
public static var subtract = variadic(_subtract);
static function _multiply(a:Operand, b:Operand):Operand {
return switch ([a, b]) {
case [Right(f), Right(f2)]:
Right(f * f2);
@@ -44,7 +59,9 @@ class Prelude {
};
}
public static function divide(bottom:Operand, top:Operand):Operand {
public static var multiply = variadic(_multiply);
static function _divide(bottom:Operand, top:Operand):Operand {
return switch ([top, bottom]) {
case [Right(f), Right(f2)]:
Right(f / f2);
@@ -53,6 +70,8 @@ class Prelude {
};
}
public static var divide = variadic(_divide);
public static function mod(bottom:Float, top:Float):Float {
return top % bottom;
}
@@ -61,34 +80,48 @@ class Prelude {
return Math.pow(base, exponent);
}
public static function min(a:Operand, b:Operand):Operand {
static function _min(a:Operand, b:Operand):Operand {
return Right(Math.min(a.toFloat(), b.toFloat()));
}
public static function max(a:Operand, b:Operand):Operand {
public static var min = variadic(_min);
static function _max(a:Operand, b:Operand):Operand {
return Right(Math.max(a.toFloat(), b.toFloat()));
}
public static function greaterThan(b:Operand, a:Operand):Null<Operand> {
public static var max = variadic(_max);
static function _greaterThan(b:Operand, a:Operand):Null<Operand> {
return if (a == null || b == null) null else if (a.toFloat() > b.toFloat()) b else null;
}
public static function greaterEqual(b:Operand, a:Operand):Null<Operand> {
public static var greaterThan = variadic(_greaterThan, true);
static function _greaterEqual(b:Operand, a:Operand):Null<Operand> {
return if (a == null || b == null) null else if (a.toFloat() >= b.toFloat()) b else null;
}
public static function lessThan(b:Operand, a:Operand):Null<Operand> {
public static var greaterEqual = variadic(_greaterEqual, true);
static function _lessThan(b:Operand, a:Operand):Null<Operand> {
return if (a == null || b == null) null else if (a.toFloat() < b.toFloat()) b else null;
}
public static function lesserEqual(b:Operand, a:Operand):Null<Operand> {
public static var lessThan = variadic(_lessThan, true);
static function _lesserEqual(b:Operand, a:Operand):Null<Operand> {
return if (a == null || b == null) null else if (a.toFloat() <= b.toFloat()) b else null;
}
public static function areEqual(a:Operand, b:Operand):Operand {
public static var lesserEqual = variadic(_lesserEqual, true);
static function _areEqual(a:Operand, b:Operand):Operand {
return if (Operand.toDynamic(a) == Operand.toDynamic(b)) a else null;
}
public static var areEqual = variadic(_areEqual, true);
public static function groups<T>(a:Array<T>, size, keepRemainder = false) {
var numFullGroups = Math.floor(a.length / size);
var fullGroups = [

View File

@@ -247,12 +247,6 @@ class SpecialForms {
EThrow(k.convert(args[0])).withMacroPosOf(wholeExp);
};
map[">"] = foldComparison("_greaterThan");
map[">="] = foldComparison("_greaterEqual");
map["<"] = foldComparison("_lessThan");
map["<="] = foldComparison("_lesserEqual");
map["="] = foldComparison("_eq");
map["if"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(2, 3, '(if [cond] [then] [?else])');
@@ -282,12 +276,4 @@ class SpecialForms {
return map;
}
static function foldComparison(func:String) {
return (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
var callFoldMacroExpr = k.convert(CallExp(Symbol(func).withPosOf(wholeExp), args).withPosOf(wholeExp));
wholeExp.checkNumArgs(1, null);
EBinop(OpNotEq, macro null, macro kiss.Operand.toDynamic($callFoldMacroExpr)).withMacroPosOf(wholeExp);
};
}
}

View File

@@ -31,7 +31,8 @@
(defun _testMultiplication []
(Assert.equals 60 (* 2 5 6))
(Assert.equals 5522401584 (* 84 289 89 71 36)))
(Assert.equals 5522401584 (* 84 289 89 71 36))
(Assert.equals "heyheyhey" (* "hey" 3)))
// All math operations return floats, none truncate by default
(defvar myQuotient (/ 6 3 2 2))