Compare commits
4 Commits
050a5fbf66
...
6aead2330a
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aead2330a | |||
| 57b61fea7e | |||
| e481120950 | |||
| d291037059 |
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
51
src/kiss/PortableMacros.hx
Normal file
51
src/kiss/PortableMacros.hx
Normal 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))
|
||||
]);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -46,5 +46,8 @@ class KissInterp2TestCase extends Test {
|
||||
function testFor() {
|
||||
_testFor();
|
||||
}
|
||||
function testDestructive() {
|
||||
_testDestructive();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)")))
|
||||
|
||||
Reference in New Issue
Block a user