Always return macro expansions as ReaderExp with pos

This commit is contained in:
2021-06-12 21:26:01 -06:00
parent 51536132eb
commit e3543cfafb
4 changed files with 51 additions and 27 deletions

View File

@@ -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<Interp> = [];
public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map<String, Dynamic>):Dynamic {
var code = k.convert(exp).toString(); // tink_macro to the rescue
public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map<String, Dynamic>):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<Dynamic> = 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<ReaderExp>, k:KissState, ?args:Map<String, Dynamic>):Array<ReaderExp> {
@@ -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));

View File

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

View File

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

View File

@@ -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 []');