highly experimental quote and eval
This commit is contained in:
@@ -117,13 +117,26 @@ class FieldForms {
|
|||||||
|
|
||||||
var name = Helpers.varName(formName, args[0]);
|
var name = Helpers.varName(formName, args[0]);
|
||||||
var access = fieldAccess(formName, name, args[0]);
|
var access = fieldAccess(formName, name, args[0]);
|
||||||
|
var inStaticFunction = access.indexOf(AStatic) != -1;
|
||||||
var returnsValue = !isVoid(args[0]);
|
var returnsValue = !isVoid(args[0]);
|
||||||
|
|
||||||
return {
|
var wasInStatic = k.inStaticFunction;
|
||||||
|
|
||||||
|
var f = {
|
||||||
name: name,
|
name: name,
|
||||||
access: access,
|
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()
|
pos: wholeExp.macroPos()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
k = k.forStaticFunction(wasInStatic);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ typedef KissState = {
|
|||||||
loadingDirectory:String,
|
loadingDirectory:String,
|
||||||
hscript:Bool,
|
hscript:Bool,
|
||||||
macroVars:Map<String, Dynamic>,
|
macroVars:Map<String, Dynamic>,
|
||||||
collectedBlocks:Map<String, Array<ReaderExp>>
|
collectedBlocks:Map<String, Array<ReaderExp>>,
|
||||||
|
inStaticFunction:Bool
|
||||||
};
|
};
|
||||||
|
|
||||||
class Kiss {
|
class Kiss {
|
||||||
@@ -115,7 +116,8 @@ class Kiss {
|
|||||||
loadingDirectory: "",
|
loadingDirectory: "",
|
||||||
hscript: false,
|
hscript: false,
|
||||||
macroVars: new Map(),
|
macroVars: new Map(),
|
||||||
collectedBlocks: new Map()
|
collectedBlocks: new Map(),
|
||||||
|
inStaticFunction: false
|
||||||
};
|
};
|
||||||
|
|
||||||
return k;
|
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.
|
// 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 {
|
public static function forHScript(k:KissState):KissState {
|
||||||
var copy = new Cloner().clone(k);
|
var copy = new Cloner().clone(k);
|
||||||
@@ -416,6 +424,11 @@ class Kiss {
|
|||||||
setLocal(wholeExp, exps, copy);
|
setLocal(wholeExp, exps, copy);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO should this also be in forHScript()?
|
||||||
|
// In macro evaluation,
|
||||||
|
copy.macros.remove("eval");
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1089,6 +1089,58 @@ class Macros {
|
|||||||
macros["indexOf"] = indexOfMacro.bind(false);
|
macros["indexOf"] = indexOfMacro.bind(false);
|
||||||
macros["lastIndexOf"] = indexOfMacro.bind(true);
|
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;
|
return macros;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ class Reader {
|
|||||||
null;
|
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) -> RawHaxe(stream.expect("closing |#", () -> stream.takeUntilAndDrop("|#")));
|
||||||
|
|
||||||
readTable[":"] = (stream:Stream, k) -> TypedExp(nextToken(stream, "a type path"), assertRead(stream, k));
|
readTable[":"] = (stream:Stream, k) -> TypedExp(nextToken(stream, "a type path"), assertRead(stream, k));
|
||||||
|
|||||||
@@ -322,6 +322,11 @@ class BasicTestCase extends Test {
|
|||||||
function testExpComment() {
|
function testExpComment() {
|
||||||
_testExpComment();
|
_testExpComment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testEval() {
|
||||||
|
_testEvalStatic();
|
||||||
|
_testEval();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BasicObject {
|
class BasicObject {
|
||||||
|
|||||||
@@ -578,4 +578,15 @@
|
|||||||
(Assert.equals 2 (arrowSyntax))))
|
(Assert.equals 2 (arrowSyntax))))
|
||||||
|
|
||||||
(function _testExpComment []
|
(function _testExpComment []
|
||||||
(Assert.equals 15 (+ **6 5 **(- 5 11) 5 (+ 5 **(20 9)))))
|
(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