From 4b93292b3267d304bf5eb533e112aa586c2565e1 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Sat, 12 Jun 2021 21:26:01 -0600 Subject: [PATCH] Always return macro expansions as ReaderExp with pos --- src/kiss/Helpers.hx | 44 ++++++++++++++++++++++-------------------- src/kiss/Kiss.hx | 18 +++++++++++++++-- src/kiss/KissInterp.hx | 11 ++++++++++- src/kiss/Macros.hx | 5 ++--- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index b12c8f8..71cb3e4 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -10,6 +10,7 @@ import kiss.ReaderExp; import kiss.CompileError; import kiss.Kiss; import kiss.SpecialForms; +import kiss.Prelude; import uuid.Uuid; using uuid.Uuid; @@ -234,14 +235,14 @@ class Helpers { // When we ARE running at compiletime already, the pre-existing interp will be used static var interps:kiss.List = []; - public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map):Dynamic { - var code = k.convert(exp).toString(); // tink_macro to the rescue + public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map):ReaderExp { + var code = k.forHScript().convert(exp).toString(); // tink_macro to the rescue #if test Prelude.print("Compile-time hscript: " + code); #end var parser = new Parser(); if (interps.length == 0) { - var interp = new Interp(); + var interp = new KissInterp(); interp.variables.set("read", Reader.assertRead.bind(_, k)); interp.variables.set("readExpArray", Reader.readExpArray.bind(_, _, k)); interp.variables.set("ReaderExp", ReaderExpDef); @@ -254,12 +255,8 @@ class Helpers { fromDynamic: Operand.fromDynamic } }); - interp.variables.set("k", k.forCaseParsing()); + interp.variables.set("k", k.forHScript()); interp.variables.set("Helpers", Helpers); - interp.variables.set("Prelude", Prelude); - interp.variables.set("Lambda", Lambda); - interp.variables.set("Std", Std); - interps.push(interp); } else { interps.push(interps[-1]); @@ -283,20 +280,25 @@ class Helpers { if (value == null) { throw CompileError.fromExp(exp, "compile-time evaluation returned null"); } + var expResult = compileTimeValueToReaderExp(value, exp); #if test - var msg = "Compile-time value: "; - msg += try { - Reader.toString(value.def); - } catch (err:haxe.Exception) { - try { - Reader.toString(value); - } catch (err:haxe.Exception) { - Std.string(value); - } - } - Prelude.print(msg); + Prelude.print('Compile-time value: ${Reader.toString(expResult.def)}'); #end - return value; + return expResult; + } + + // The value could be either a ReaderExp, ReaderExpDef, Array of ReaderExp/ReaderExpDefs, or something else entirely, + // but it needs to be a ReaderExp for evalUnquotes() + static function compileTimeValueToReaderExp(e:Dynamic, source:ReaderExp):ReaderExp { + return if (Std.isOfType(e, Array)) { + var arr:Array = e; + var listExps = arr.map(compileTimeValueToReaderExp.bind(_, source)); + ListExp(listExps).withPosOf(source); + } else if (e.def == null) { + (e : ReaderExpDef).withPosOf(source); + } else { + (e : ReaderExp); + } } static function evalUnquoteLists(l:Array, k:KissState, ?args:Map):Array { @@ -360,7 +362,7 @@ class Helpers { public static function removeTypeAnnotations(exp:ReaderExp):ReaderExp { var def = switch (exp.def) { - case Symbol(_) | StrExp(_) | RawHaxe(_): + case Symbol(_) | StrExp(_) | RawHaxe(_) | Quasiquote(_): exp.def; case CallExp(func, callArgs): CallExp(removeTypeAnnotations(func), callArgs.map(removeTypeAnnotations)); diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index 78a9acc..8b0d670 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -202,7 +202,7 @@ class Kiss { convert(elementExp); } ]).withMacroPosOf(exp); - if (!isMap && k.wrapListExps) { + if (!isMap && k.wrapListExps && !k.hscript) { ENew({ pack: ["kiss"], name: "List" @@ -231,9 +231,23 @@ class Kiss { return expr; } - public static function forCaseParsing(k:KissState):KissState { + // 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); + copy.hscript = true; + return copy; + } + + // Return an identical Kiss State, but without wrapping list expressions as kiss.List constructor calls. + public static function withoutListWrapping(k:KissState) { var copy = new Cloner().clone(k); copy.wrapListExps = false; + return copy; + } + + // Return an identical Kiss State, but prepared for parsing a branch pattern of a (case...) expression + public static function forCaseParsing(k:KissState):KissState { + var copy = withoutListWrapping(k); copy.macros.remove("or"); copy.specialForms["or"] = SpecialForms.caseOr; return copy; diff --git a/src/kiss/KissInterp.hx b/src/kiss/KissInterp.hx index 78cc016..c31fc9b 100644 --- a/src/kiss/KissInterp.hx +++ b/src/kiss/KissInterp.hx @@ -1,14 +1,23 @@ package kiss; import hscript.Interp; +import kiss.Prelude; class KissInterp extends Interp { // TODO standardize this with KissConfig.prepareInterp - function new() { + public function new() { super(); variables.set("Prelude", Prelude); variables.set("Lambda", Lambda); variables.set("Std", Std); + variables.set("Keep", ExtraElementHandling.Keep); + variables.set("Drop", ExtraElementHandling.Drop); + variables.set("Throw", ExtraElementHandling.Throw); + } + + // the default exprReturn() contains a try-catch which hides very important callstacks sometimes + override function exprReturn(e):Dynamic { + return expr(e); } } diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index 271f979..382959e 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -365,8 +365,7 @@ class Macros { args[innerArgNames.shift()] = innerExps.slice(restIndex); // Return the macro expansion: - var expDef:ReaderExpDef = Helpers.runAtCompileTime(CallExp(Symbol("begin").withPosOf(wholeExp), exps.slice(2)).withPosOf(wholeExp), k, args); - expDef.withPosOf(wholeExp); + return Helpers.runAtCompileTime(CallExp(Symbol("begin").withPosOf(wholeExp), exps.slice(2)).withPosOf(wholeExp), k, args); }; null; @@ -396,7 +395,7 @@ class Macros { stream.putBackString(s); } var body = CallExp(Symbol("begin").withPos(stream.position()), exps.slice(2)).withPos(stream.position()); - Helpers.runAtCompileTime(body, k, [streamArgName => stream]); + Helpers.runAtCompileTime(body, k, [streamArgName => stream]).def; }; case CallExp(_, []): throw CompileError.fromExp(exps[1], 'expected an argument list. Change the parens () to brackets []');