From e335b36cb99c0b9ba43785c8210ef17f755f6484 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Tue, 23 Nov 2021 22:17:59 -0700 Subject: [PATCH] highly experimental quote and eval --- kiss/src/kiss/FieldForms.hx | 17 ++++++++- kiss/src/kiss/Kiss.hx | 17 ++++++++- kiss/src/kiss/Macros.hx | 52 ++++++++++++++++++++++++++ kiss/src/kiss/Reader.hx | 4 ++ kiss/src/test/cases/BasicTestCase.hx | 5 +++ kiss/src/test/cases/BasicTestCase.kiss | 13 ++++++- 6 files changed, 103 insertions(+), 5 deletions(-) diff --git a/kiss/src/kiss/FieldForms.hx b/kiss/src/kiss/FieldForms.hx index 061e8c22..2bd2afff 100644 --- a/kiss/src/kiss/FieldForms.hx +++ b/kiss/src/kiss/FieldForms.hx @@ -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; } } diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index f9103940..cee9c8cb 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -41,7 +41,8 @@ typedef KissState = { loadingDirectory:String, hscript:Bool, macroVars:Map, - collectedBlocks:Map> + collectedBlocks:Map>, + 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; } diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index 578fd86f..abe87da1 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -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, k:KissState) -> { + wholeExp.checkNumArgs(1, 1, '(quote )'); + 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, k:KissState) -> { + wholeExp.checkNumArgs(1, 1, '(eval )'); + 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; } diff --git a/kiss/src/kiss/Reader.hx b/kiss/src/kiss/Reader.hx index 3ab5c8ec..6310a22d 100644 --- a/kiss/src/kiss/Reader.hx +++ b/kiss/src/kiss/Reader.hx @@ -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)); diff --git a/kiss/src/test/cases/BasicTestCase.hx b/kiss/src/test/cases/BasicTestCase.hx index 448dff0c..b150fabf 100644 --- a/kiss/src/test/cases/BasicTestCase.hx +++ b/kiss/src/test/cases/BasicTestCase.hx @@ -322,6 +322,11 @@ class BasicTestCase extends Test { function testExpComment() { _testExpComment(); } + + function testEval() { + _testEvalStatic(); + _testEval(); + } } class BasicObject { diff --git a/kiss/src/test/cases/BasicTestCase.kiss b/kiss/src/test/cases/BasicTestCase.kiss index 57310dd7..1c9eae0d 100644 --- a/kiss/src/test/cases/BasicTestCase.kiss +++ b/kiss/src/test/cases/BasicTestCase.kiss @@ -578,4 +578,15 @@ (Assert.equals 2 (arrowSyntax)))) (function _testExpComment [] - (Assert.equals 15 (+ **6 5 **(- 5 11) 5 (+ 5 **(20 9))))) \ No newline at end of file + (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))) \ No newline at end of file