From 5f1fe7434f2d245ff8ce9a90c7d50b284cc8ac81 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Mon, 13 Mar 2023 15:00:30 -0600 Subject: [PATCH] add haxe @meta support to kiss expressions --- kiss/src/kiss/Kiss.hx | 34 +++++++++++++++++++++++++++++- kiss/src/kiss/Reader.hx | 13 ++++++++++-- kiss/src/kiss/ReaderExp.hx | 1 + projects/kiss-vscode/src/Main.hx | 8 +------ projects/kiss-vscode/src/Main.kiss | 3 ++- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index 7937b274..800455a0 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -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 { + public static function macroExpandAndConvert(exp:ReaderExp, k:KissState, macroExpandOnly:Bool, ?metaNames, ?metaParams:Array>, ?metaPos:Array):Either { #if kissCache var str = Reader.toString(exp.def); if (!macroExpandOnly) { @@ -544,6 +544,21 @@ class Kiss { var expr:Either = 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; } diff --git a/kiss/src/kiss/Reader.hx b/kiss/src/kiss/Reader.hx index c6450f42..86b1ef4b 100644 --- a/kiss/src/kiss/Reader.hx +++ b/kiss/src/kiss/Reader.hx @@ -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... {} {}), + // 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()}'; } } } diff --git a/kiss/src/kiss/ReaderExp.hx b/kiss/src/kiss/ReaderExp.hx index 26c50db2..b74986a2 100644 --- a/kiss/src/kiss/ReaderExp.hx +++ b/kiss/src/kiss/ReaderExp.hx @@ -24,5 +24,6 @@ enum ReaderExpDef { ListEatingExp(exps:Array); // [::exp exp ...exps exp] ListRestExp(name:String); // ...exps or ... TypeParams(types:Array); // <>[T :Constraint U :Constraint1 :Constraint2 V] + HaxeMeta(name:String, params:Null>, exp:ReaderExp); // @meta or @(meta ) None; // not an expression, i.e. (#unless falseCondition exp) } diff --git a/projects/kiss-vscode/src/Main.hx b/projects/kiss-vscode/src/Main.hx index f2219821..772efb5e 100644 --- a/projects/kiss-vscode/src/Main.hx +++ b/projects/kiss-vscode/src/Main.hx @@ -14,10 +14,4 @@ using StringTools; using uuid.Uuid; @:build(kiss.Kiss.build()) -class Main { - // TODO support EMeta(s:MetadataEntry, e:Expr) via Kiss so this signature can be moved to Main.kiss - @:expose("activate") - static function activate(context:ExtensionContext) { - _activate(context); - } -} +class Main {} diff --git a/projects/kiss-vscode/src/Main.kiss b/projects/kiss-vscode/src/Main.kiss index bdc4aabf..c5340f0c 100644 --- a/projects/kiss-vscode/src/Main.kiss +++ b/projects/kiss-vscode/src/Main.kiss @@ -1,7 +1,8 @@ (loadFrom "kiss-vscode-api" "src/Util.kiss") (loadFrom "kiss-vscode-api" "src/KissUtil.kiss") -(function _activate [:ExtensionContext context] +@(:expose "activate") +(function activate [:ExtensionContext context] (printThroughInfoMessage) (load "commands/ExtensionTools.kiss") (load "commands/KissTools.kiss")