highly experimental quote and eval

This commit is contained in:
2021-11-23 22:17:59 -07:00
parent 855fa06a87
commit e335b36cb9
6 changed files with 103 additions and 5 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -322,6 +322,11 @@ class BasicTestCase extends Test {
function testExpComment() {
_testExpComment();
}
function testEval() {
_testEvalStatic();
_testEval();
}
}
class BasicObject {

View File

@@ -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)))