4 Commits

Author SHA1 Message Date
6aead2330a Prelude.splitRecursive
Some checks failed
CI / test (push) Failing after 54s
CI / test-core (14, ubuntu-latest, 3.x, js) (push) Failing after 1m44s
CI / test-core (14, ubuntu-latest, 3.x, interp) (push) Failing after 1m40s
CI / test-core (14, ubuntu-latest, 3.x, nodejs) (push) Failing after 2m6s
CI / test-core (14, ubuntu-latest, 3.x, py) (push) Failing after 2m19s
CI / test-core (14, ubuntu-latest, 3.x, cpp) (push) Failing after 2m2s
2025-11-14 14:33:15 -06:00
57b61fea7e add when, unless to portable macros 2025-11-14 13:54:56 -06:00
e481120950 add macros to KissInterp2 2025-11-14 13:48:45 -06:00
d291037059 Start making macros portable 2025-11-14 13:38:36 -06:00
6 changed files with 89 additions and 26 deletions

View File

@@ -14,6 +14,7 @@
(prop &mut :ReadTable endOfFileReadTable (new Map))
(prop &mut :Map<String,ReaderExpDef> identAliases (new Map))
(prop &mut :Map<String,ReaderExpDef> callAliases (new Map))
(prop &mut :Map<String,kiss.PortableMacros.PortableMacroFunction> macros (kiss.PortableMacros.builtins))
(prop :Map<String,Dynamic> globals [
=>"false" false
@@ -256,6 +257,9 @@
// Special form call
((when (specialForms.exists form) (CallExp (object def (Symbol form)) args))
((dictGet specialForms form) args cc))
// Macro call
((when (macros.exists form) (CallExp (object def (Symbol form)) args))
(evalCC ((dictGet macros form) (makeExp def) args) cc))
// Method/function call with call alias
((when (callAliases.exists name) (CallExp (object def (Symbol name)) args))
(evalCC (dictGet callAliases name) ->f

View File

@@ -8,6 +8,7 @@ import kiss.ReaderExp;
import kiss.Kiss;
import kiss.KissError;
import kiss.Helpers;
import kiss.PortableMacros;
import uuid.Uuid;
import hscript.Parser;
import haxe.EnumTools;
@@ -62,18 +63,15 @@ class Macros {
Kiss.load(otherKissFile, k, libPath, false, wholeExp);
};
function wrapPortable(m:PortableMacroFunction) {
return (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> {
m(wholeExp, exps);
};
}
function destructiveVersion(op:String, assignOp:String) {
k.doc(assignOp, 2, null, '($assignOp <var> <v1> <values...>)');
macros[assignOp] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k) -> {
var b = wholeExp.expBuilder();
b.call(
b.symbol("set"), [
exps[0],
b.call(
b.symbol(op),
exps)
]);
};
macros[assignOp] = wrapPortable(PortableMacros.destructiveVersion(op));
}
destructiveVersion("%", "%=");
@@ -243,21 +241,7 @@ class Macros {
function addBodyIf(keywordName:String, underlyingIf:String, negated:Bool) {
k.doc(keywordName, 2, null, '($keywordName <condition> <body...>)');
macros[keywordName] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k) -> {
var b = wholeExp.expBuilder();
var condition = if (negated) {
b.call(
b.symbol("not"), [
args[0]
]);
} else {
args[0];
}
return b.call(b.symbol(underlyingIf), [
condition,
b.begin(args.slice(1))
]);
};
macros[keywordName] = wrapPortable(PortableMacros.bodyIf(underlyingIf, negated));
}
addBodyIf("when", "if", false);
addBodyIf("unless", "if", true);

View File

@@ -0,0 +1,51 @@
package kiss;
using kiss.ExpBuilder;
typedef PortableMacroFunction = (ReaderExp, Array<ReaderExp>)->ReaderExp;
class PortableMacros {
public static function builtins():Map<String,PortableMacroFunction> {
var m = new Map<String,PortableMacroFunction>();
m["%="] = destructiveVersion("%");
m["^="] = destructiveVersion("^");
m["+="] = destructiveVersion("+");
m["-="] = destructiveVersion("-");
m["*="] = destructiveVersion("*");
m["/="] = destructiveVersion("/");
m["when"] = bodyIf("if", false);
m["unless"] = bodyIf("if", true);
return m;
}
public static function destructiveVersion(op:String) {
return (wholeExp:ReaderExp, exps:Array<ReaderExp>) -> {
var b = wholeExp.expBuilder();
b.call(
b.symbol("set"), [
exps[0],
b.call(
b.symbol(op),
exps)
]);
};
}
public static function bodyIf(underlyingIf:String, negated:Bool) {
return (wholeExp:ReaderExp, args:Array<ReaderExp>) -> {
var b = wholeExp.expBuilder();
var condition = if (negated) {
b.call(
b.symbol("not"), [
args[0]
]);
} else {
args[0];
}
return b.call(b.symbol(underlyingIf), [
condition,
b.begin(args.slice(1))
]);
};
}
}

View File

@@ -57,6 +57,7 @@ typedef HashableString = {
hashCode:()->Int
};
@:keep
class Prelude {
public static function hashableString(s:String):HashableString {
return {
@@ -404,6 +405,22 @@ class Prelude {
return filter(_splitByAll([s], delimiters));
}
/**
* Split a string by the first delimiter, then recursively split the substrings by
* the following delimiters in order.
* i.e. (splitRecursive "a:b::c:d" ["::" ":"]) returns [["a" "b"] ["c" "d"]]
*/
public static function splitRecursive(s:String, delimiters:Array<String>):Array<Dynamic> {
var parts = s.split(delimiters[0]);
return if (delimiters.length == 1) {
parts;
} else {
[for (part in parts) {
splitRecursive(part, delimiters.slice(1));
}];
}
}
public static function reverse<T>(l:kiss.List<T>):kiss.List<T> {
var c = l.copy();
c.reverse();

View File

@@ -46,5 +46,8 @@ class KissInterp2TestCase extends Test {
function testFor() {
_testFor();
}
function testDestructive() {
_testDestructive();
}
}

View File

@@ -58,4 +58,8 @@
(function _testFor []
(let [interp (new Interp)]
(assertEval [1 2 3] "(for i (range 3) (+ i 1))")))
(assertEval [1 2 3] "(for i (range 3) (+ i 1))")))
(function _testDestructive []
(let [interp (new Interp)]
(assertEval 5 "(let [i 2] (+= i 3) i)")))