highly experimental quote and eval
This commit is contained in:
@@ -117,13 +117,26 @@ class FieldForms {
|
||||
|
||||
var name = Helpers.varName(formName, args[0]);
|
||||
var access = fieldAccess(formName, name, args[0]);
|
||||
var inStaticFunction = access.indexOf(AStatic) != -1;
|
||||
var returnsValue = !isVoid(args[0]);
|
||||
|
||||
return {
|
||||
var wasInStatic = k.inStaticFunction;
|
||||
|
||||
var f = {
|
||||
name: name,
|
||||
access: access,
|
||||
kind: FFun(Helpers.makeFunction(args[0], returnsValue, args[1], args.slice(2), k, formName)),
|
||||
kind: FFun(
|
||||
Helpers.makeFunction(
|
||||
args[0],
|
||||
returnsValue,
|
||||
args[1],
|
||||
args.slice(2),
|
||||
k.forStaticFunction(inStaticFunction),
|
||||
formName)),
|
||||
pos: wholeExp.macroPos()
|
||||
};
|
||||
|
||||
k = k.forStaticFunction(wasInStatic);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,8 @@ typedef KissState = {
|
||||
loadingDirectory:String,
|
||||
hscript:Bool,
|
||||
macroVars:Map<String, Dynamic>,
|
||||
collectedBlocks:Map<String, Array<ReaderExp>>
|
||||
collectedBlocks:Map<String, Array<ReaderExp>>,
|
||||
inStaticFunction:Bool
|
||||
};
|
||||
|
||||
class Kiss {
|
||||
@@ -115,7 +116,8 @@ class Kiss {
|
||||
loadingDirectory: "",
|
||||
hscript: false,
|
||||
macroVars: new Map(),
|
||||
collectedBlocks: new Map()
|
||||
collectedBlocks: new Map(),
|
||||
inStaticFunction: false
|
||||
};
|
||||
|
||||
return k;
|
||||
@@ -385,6 +387,12 @@ class Kiss {
|
||||
};
|
||||
}
|
||||
|
||||
// This doesn't clone k because k might be modified in important ways :(
|
||||
public static function forStaticFunction(k:KissState, inStaticFunction:Bool) {
|
||||
k.inStaticFunction = inStaticFunction;
|
||||
return k;
|
||||
}
|
||||
|
||||
// Return an identical Kiss State, but without type annotations or wrapping list expressions as kiss.List constructor calls.
|
||||
public static function forHScript(k:KissState):KissState {
|
||||
var copy = new Cloner().clone(k);
|
||||
@@ -416,6 +424,11 @@ class Kiss {
|
||||
setLocal(wholeExp, exps, copy);
|
||||
};
|
||||
};
|
||||
|
||||
// TODO should this also be in forHScript()?
|
||||
// In macro evaluation,
|
||||
copy.macros.remove("eval");
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@@ -1089,6 +1089,58 @@ class Macros {
|
||||
macros["indexOf"] = indexOfMacro.bind(false);
|
||||
macros["lastIndexOf"] = indexOfMacro.bind(true);
|
||||
|
||||
// Under the hood, quoted expressions are just Kiss strings for a KissInterp
|
||||
macros["quote"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, 1, '(quote <exp>)');
|
||||
var b = wholeExp.expBuilder();
|
||||
return b.str(Reader.toString(exps[0].def));
|
||||
};
|
||||
|
||||
// Under the hood, (eval) uses Type.getClassFields, Type.getInstanceFields, and Reflect.field, to mishmash
|
||||
// a bunch of fields ONLY from (var) and (prop) in the class that called Kiss.build(), AT RUNTIME, into a KissInterp that evaluates a string of Kiss code.
|
||||
// This is all complicated, and language- and platform-dependent. And slow, because it converts Kiss to HScript at runtime.
|
||||
// When (eval) is used in a static function, it cannot access instance variables.
|
||||
// (eval) should not be used for serious purposes.
|
||||
macros["eval"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, 1, '(eval <exp>)');
|
||||
var b = wholeExp.expBuilder();
|
||||
var className = Context.getLocalClass().toString();
|
||||
var classSymbol = b.symbol(className);
|
||||
|
||||
// TODO this might be reusable for passing things to #extern
|
||||
|
||||
var classFieldsSymbol = b.symbol();
|
||||
var instanceFieldsSymbol = b.symbol();
|
||||
var interpSymbol = b.symbol();
|
||||
var bindings = [
|
||||
interpSymbol, b.callSymbol("new", [b.symbol("kiss.KissInterp")]),
|
||||
classFieldsSymbol, b.callSymbol("Type.getClassFields", [classSymbol]),
|
||||
];
|
||||
if (!k.inStaticFunction) {
|
||||
bindings = bindings.concat([instanceFieldsSymbol, b.callSymbol("Type.getInstanceFields", [classSymbol])]);
|
||||
}
|
||||
var body = [
|
||||
b.callSymbol("doFor", [
|
||||
b.symbol("staticField"), classFieldsSymbol,
|
||||
b.callField("set", b.field("variables", interpSymbol), [
|
||||
b.symbol("staticField"), b.callSymbol("Reflect.field", [classSymbol, b.symbol("staticField")])
|
||||
])
|
||||
])
|
||||
];
|
||||
if (!k.inStaticFunction) {
|
||||
body.push(
|
||||
b.callSymbol("doFor", [
|
||||
b.symbol("instanceField"), instanceFieldsSymbol,
|
||||
b.callField("set", b.field("variables", interpSymbol), [
|
||||
b.symbol("instanceField"), b.callSymbol("Reflect.field", [b.symbol("this"), b.symbol("instanceField")])
|
||||
])
|
||||
])
|
||||
);
|
||||
}
|
||||
body.push(b.callField("evalKiss", interpSymbol, [exps[0]]));
|
||||
b.let(bindings, body);
|
||||
};
|
||||
|
||||
return macros;
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,10 @@ class Reader {
|
||||
null;
|
||||
};
|
||||
|
||||
readTable["'"] = (stream:Stream, k) -> {
|
||||
CallExp(Symbol("quote").withPos(stream.position()), [assertRead(stream, k)]);
|
||||
};
|
||||
|
||||
readTable["#|"] = (stream:Stream, k) -> RawHaxe(stream.expect("closing |#", () -> stream.takeUntilAndDrop("|#")));
|
||||
|
||||
readTable[":"] = (stream:Stream, k) -> TypedExp(nextToken(stream, "a type path"), assertRead(stream, k));
|
||||
|
@@ -322,6 +322,11 @@ class BasicTestCase extends Test {
|
||||
function testExpComment() {
|
||||
_testExpComment();
|
||||
}
|
||||
|
||||
function testEval() {
|
||||
_testEvalStatic();
|
||||
_testEval();
|
||||
}
|
||||
}
|
||||
|
||||
class BasicObject {
|
||||
|
@@ -579,3 +579,14 @@
|
||||
|
||||
(function _testExpComment []
|
||||
(Assert.equals 15 (+ **6 5 **(- 5 11) 5 (+ 5 **(20 9)))))
|
||||
|
||||
(var staticValue 9)
|
||||
(prop value 2)
|
||||
(method _testEval []
|
||||
(Assert.equals 9 (eval 'staticValue))
|
||||
(Assert.equals 2 (eval 'value))
|
||||
(Assert.equals 11 (eval '(+ staticValue value))))
|
||||
|
||||
(function _testEvalStatic []
|
||||
(Assert.equals 9 (eval 'staticValue))
|
||||
(assertThrows (eval 'value)))
|
Reference in New Issue
Block a user