add haxe @meta support to kiss expressions

This commit is contained in:
2023-03-13 15:00:30 -06:00
parent 565969ac58
commit 5f1fe7434f
5 changed files with 48 additions and 11 deletions

View File

@@ -475,7 +475,7 @@ class Kiss {
}
// Core functionality of Kiss: returns ReaderExp when macroExpandOnly is true, and haxe.macro.Expr otherwise
public static function macroExpandAndConvert(exp:ReaderExp, k:KissState, macroExpandOnly:Bool):Either<ReaderExp,Expr> {
public static function macroExpandAndConvert(exp:ReaderExp, k:KissState, macroExpandOnly:Bool, ?metaNames, ?metaParams:Array<Array<Expr>>, ?metaPos:Array<haxe.macro.Position>):Either<ReaderExp,Expr> {
#if kissCache
var str = Reader.toString(exp.def);
if (!macroExpandOnly) {
@@ -544,6 +544,21 @@ class Kiss {
var expr:Either<ReaderExp,Expr> = switch (exp.def) {
case None:
if (macroExpandOnly) Left(exp) else Right(none);
case HaxeMeta(name, params, exp):
if (macroExpandOnly) {
Left(HaxeMeta(name, params, left(macroExpandAndConvert(exp, k, true))).withPosOf(exp));
} else {
if (metaNames == null) metaNames = [];
if (metaParams == null) metaParams = [];
if (metaPos == null) metaPos = [];
metaNames.push(name);
if (params == null)
metaParams.push(null);
else
metaParams.push([for (param in params) right(macroExpandAndConvert(param, k, false))]);
metaPos.push(Helpers.macroPos(exp));
Right(right(macroExpandAndConvert(exp, k, false, metaNames, metaParams, metaPos)));
}
case Symbol(alias) if (k.identAliases.exists(alias)):
var substitution = k.identAliases[alias].withPosOf(exp);
if (macroExpandOnly) Left(substitution) else macroExpandAndConvert(substitution, k, false);
@@ -558,6 +573,16 @@ class Kiss {
case CallExp({pos: _, def: Symbol(ff)}, args) if (fieldForms.exists(ff) && !macroExpandOnly):
checkNumArgs(ff);
var field = fieldForms[ff](exp, args.copy(), k);
if (metaNames != null) {
field.meta = [];
while (metaNames.length > 0) {
field.meta.push({
name: metaNames.shift(),
params: metaParams.shift(),
pos: metaPos.shift()
});
}
}
k.fieldList.push(field);
k.fieldDict[field.name] = field;
k.stateChanged = true;
@@ -665,6 +690,13 @@ class Kiss {
}
}
#end
if (metaNames != null && !macroExpandOnly) {
expr = Right(EMeta({
name: metaNames.pop(),
params: metaParams.pop(),
pos: metaPos.pop()
}, right(expr)).withMacroPosOf(exp));
}
return expr;
}

View File

@@ -32,11 +32,16 @@ class Reader {
var pos = stream.position();
CallExp(assertRead(stream, k), readExpArray(stream, ")", k, pos));
};
readTable["@"] = (stream, k) -> HaxeMeta(nextToken(stream, "a haxe metadata entry name"), null, assertRead(stream, k));
readTable["@("] = (stream, k) -> HaxeMeta(nextToken(stream, "a haxe metadata entry name"), readExpArray(stream, ")", k), assertRead(stream, k));
readTable["["] = (stream, k) -> ListExp(readExpArray(stream, "]", k));
readTable["[::"] = (stream, k) -> ListEatingExp(readExpArray(stream, "]", k));
readTable["..."] = (stream, k) -> ListRestExp(nextToken(stream, "name for list-eating rest exp", true));
// Provides a nice syntactic sugar for (if... {[then block]} {[else block]}),
// and also handles string interpolation cases like "${exp}moreString"
// Provides a nice syntactic sugar for (if... {<then block...>} {<else block...>}),
// and also handles string interpolation cases like "${exp}moreString":
readTable["{"] = (stream:Stream, k) -> CallExp(Symbol("begin").withPos(stream.position()), readExpArray(stream, "}", k));
readTable["<>["] = (stream, k) -> TypeParams(readExpArray(stream, "]", k));
@@ -567,6 +572,10 @@ class Reader {
'...${name}';
case None:
'';
case HaxeMeta(name, null, exp):
'@${name} ${exp.def.toString()}';
case HaxeMeta(name, params, exp):
'@(${name} ${[for (param in params) param.def.toString()].join(" ")}) ${exp.def.toString()}';
}
}
}

View File

@@ -24,5 +24,6 @@ enum ReaderExpDef {
ListEatingExp(exps:Array<ReaderExp>); // [::exp exp ...exps exp]
ListRestExp(name:String); // ...exps or ...
TypeParams(types:Array<ReaderExp>); // <>[T :Constraint U :Constraint1 :Constraint2 V]
HaxeMeta(name:String, params:Null<Array<ReaderExp>>, exp:ReaderExp); // @meta <exp> or @(meta <params...>) <exp>
None; // not an expression, i.e. (#unless falseCondition exp)
}