From 66a6a4d3961592be9c33282285d05b907f2bd1a6 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Thu, 29 Jun 2023 14:07:10 -0600 Subject: [PATCH] experimental type aliases --- src/kiss/EmbeddedScript.hx | 6 ++--- src/kiss/FieldForms.hx | 2 +- src/kiss/Helpers.hx | 47 +++++++++++++++++++++++--------------- src/kiss/Kiss.hx | 2 ++ src/kiss/Macros.hx | 26 +++++++++++++++++---- src/kiss/Main.hx | 2 +- src/kiss/SpecialForms.hx | 10 ++++---- 7 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/kiss/EmbeddedScript.hx b/src/kiss/EmbeddedScript.hx index 78c28a5..3fc1095 100644 --- a/src/kiss/EmbeddedScript.hx +++ b/src/kiss/EmbeddedScript.hx @@ -186,7 +186,7 @@ class EmbeddedScript { ret: null, args: [ { - type: Helpers.parseComplexType("(()->Void)->Void", null), + type: Helpers.parseComplexType("(()->Void)->Void", k, null), name: "c" } ], @@ -207,10 +207,10 @@ class EmbeddedScript { name: "fork", access: [APublic], kind: FFun({ - ret: Helpers.parseComplexType("Array", null), + ret: Helpers.parseComplexType("Array", k, null), args: [ { - type: Helpers.parseComplexType("Array", null), + type: Helpers.parseComplexType("Array", k, null), name: "commands" } ], diff --git a/src/kiss/FieldForms.hx b/src/kiss/FieldForms.hx index 23bcc21..0f00938 100644 --- a/src/kiss/FieldForms.hx +++ b/src/kiss/FieldForms.hx @@ -103,7 +103,7 @@ class FieldForms { checkPrintFieldsCalledWarning(name, wholeExp, k); var access = fieldAccess(formName, name, args[0]); - var type = Helpers.explicitType(args[0]); + var type = Helpers.explicitType(args[0], k); k.addVarInScope( {name: name, type: type}, false, diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index 4f2788a..bf6ad8f 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -49,8 +49,10 @@ class Helpers { return s.charAt(0) == s.charAt(0).toUpperCase(); } - public static function parseTypePath(path:String, ?from:ReaderExp):TypePath { - return switch (parseComplexType(path, from)) { + public static function parseTypePath(path:String, k:KissState, ?from:ReaderExp):TypePath { + if (k.typeAliases.exists(path)) + path = k.typeAliases[path]; + return switch (parseComplexType(path, k, from)) { case TPath(path): path; default: @@ -63,7 +65,10 @@ class Helpers { }; } - public static function parseComplexType(path:String, ?from:ReaderExp, mustResolve=false):ComplexType { + public static function parseComplexType(path:String, k:KissState, ?from:ReaderExp, mustResolve=false):ComplexType { + if (k.typeAliases.exists(path)) + path = k.typeAliases[path]; + // Trick Haxe into parsing it for us: var typeCheckStr = 'var thing:$path;'; var errorMessage = 'Haxe could not parse a complex type from `$path` in `${typeCheckStr}`'; @@ -102,20 +107,22 @@ class Helpers { } } - public static function explicitTypeString(nameExp:ReaderExp):String { + public static function explicitTypeString(nameExp:ReaderExp, k:KissState):String { return switch (nameExp.def) { case MetaExp(_, innerExp): - explicitTypeString(innerExp); + explicitTypeString(innerExp, k); + case TypedExp(type, _) if (k.typeAliases.exists(type)): + k.typeAliases[type]; case TypedExp(type, _): type; default: null; }; } - public static function explicitType(nameExp:ReaderExp):ComplexType { - var string = explicitTypeString(nameExp); + public static function explicitType(nameExp:ReaderExp, k:KissState):ComplexType { + var string = explicitTypeString(nameExp, k); if (string == null) return null; - return Helpers.parseComplexType(string, nameExp); + return Helpers.parseComplexType(string, k, nameExp); } public static function varName(formName:String, nameExp:ReaderExp, nameType = "variable") { @@ -129,17 +136,19 @@ class Helpers { }; } - public static function makeTypeParam(param:ReaderExp, ?constraints:Array = null):TypeParamDecl { + public static function makeTypeParam(param:ReaderExp, k:KissState, ?constraints:Array = null):TypeParamDecl { if (constraints == null) constraints = []; switch (param.def) { case Symbol(name): + if (k.typeAliases.exists(name)) + name = k.typeAliases[name]; return { name: name, constraints: constraints }; case TypedExp(type, param): - constraints.push(parseComplexType(type)); - return makeTypeParam(param, constraints); + constraints.push(parseComplexType(type, k)); + return makeTypeParam(param, k, constraints); default: throw KissError.fromExp(param, "expected or : "); } @@ -152,7 +161,7 @@ class Helpers { ""; }; - var params = [for (p in typeParams) makeTypeParam(p)]; + var params = [for (p in typeParams) makeTypeParam(p, k)]; var numArgs = 0; // Once the &opt meta appears, all following arguments are optional until &rest @@ -171,7 +180,7 @@ class Helpers { throw KissError.fromExp(funcArg, "lambda does not support &rest arguments"); } - var typeOfRestArg = explicitTypeString(funcArg); + var typeOfRestArg = explicitTypeString(funcArg, k); var isDynamicArray = switch (typeOfRestArg) { case "Array" | "kiss.List" | "List": true; @@ -219,7 +228,7 @@ class Helpers { }, type: switch (funcArg.def) { case TypedExp(type, _): - Helpers.parseComplexType(type, funcArg); + Helpers.parseComplexType(type, k, funcArg); default: null; }, opt: opt @@ -269,7 +278,7 @@ class Helpers { // But setting null arguments to default values is so common, and arguments are not settable references, // so function args are not immutable. return { - ret: if (name != null) Helpers.explicitType(name) else null, + ret: if (name != null) Helpers.explicitType(name, k) else null, args: args, expr: expr, params: params @@ -470,14 +479,14 @@ class Helpers { } // hscript.Interp is very finicky about some edge cases. // This function handles them - private static function mapForInterp(expr:Expr):Expr { + private static function mapForInterp(expr:Expr, k:KissState):Expr { return expr.map(subExp -> { switch (subExp.expr) { case ETry(e, catches): catches = [for (c in catches) { // hscript.Parser expects :Dynamic after the catch varname { - type: Helpers.parseComplexType("Dynamic"), + type: Helpers.parseComplexType("Dynamic", k), name: c.name, expr: c.expr }; @@ -486,14 +495,14 @@ class Helpers { pos: subExp.pos, expr: ETry(e, catches) }; - default: mapForInterp(subExp); + default: mapForInterp(subExp, k); } }); } static var parser = new Parser(); static function compileTimeHScript(exp:ReaderExp, k:KissState) { - var hscriptExp = mapForInterp(k.forMacroEval().convert(exp)); + var hscriptExp = mapForInterp(k.forMacroEval().convert(exp), k); var code = hscriptExp.toString(); // tink_macro to the rescue #if macrotest Prelude.print("Compile-time hscript: " + code); diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index fa7f7d0..eba2660 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -51,6 +51,7 @@ typedef KissState = { loadedFiles:Map>, callAliases:Map, identAliases:Map, + typeAliases:Map, fieldList:Array, // TODO This map was originally created to track whether the programmer wrote their own main function, but could also // be used to allow macros to edit fields that were already defined (for instance, to decorate a function or add something @@ -182,6 +183,7 @@ class Kiss { /* concat used to live here as an alias but now it is in a macro that also applies (the Array) to the result */ ], + typeAliases: new Map(), fieldList: [], fieldDict: new Map(), loadingDirectory: "", diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index 74f0095..bf7f11f 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -654,17 +654,24 @@ class Macros { // Having this floating out here is sketchy, but should work out fine because the variable is always re-set // through the next function before being used in defalias or undefalias - var aliasMap:Map = null; + var aliasMap:Map = null; + var extractString = false; function getAliasName(k:KissState, nameExpWithMeta:ReaderExp, formName:String):String { var error = KissError.fromExp(nameExpWithMeta, 'first argument to $formName should be &call [alias] or &ident [alias]'); var nameExp = switch (nameExpWithMeta.def) { case MetaExp("call", nameExp): + extractString = false; aliasMap = k.callAliases; nameExp; case MetaExp("ident", nameExp): + extractString = false; aliasMap = k.identAliases; nameExp; + case MetaExp("type", nameExp): + aliasMap = k.typeAliases; + extractString = true; + nameExp; default: throw error; }; @@ -676,12 +683,21 @@ class Macros { }; } - k.doc("defalias", 2, 2, "(defAlias <<&call or &ident> whenItsThis> )"); + k.doc("defalias", 2, 2, "(defAlias <<&call or &ident or &type> whenItsThis> )"); macros["defalias"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { k.stateChanged = true; var name = getAliasName(k, exps[0], "defAlias"); - aliasMap[name] = exps[1].def; + var into = exps[1].def; + if (extractString) + aliasMap[name] = switch (into) { + case Symbol(typeName): + typeName; + default: + throw KissError.fromExp(wholeExp, "type alias must be of a plain symbol"); + } + else + aliasMap[name] = into; return null; }; renameAndDeprecate("defalias", "defAlias"); @@ -774,7 +790,7 @@ class Macros { var firstValue = bindingList.shift(); var b = wholeExp.expBuilder(); var firstNameSymbol = b.symbol(firstNameString); - var firstNameType = Helpers.explicitTypeString(firstName); + var firstNameType = Helpers.explicitTypeString(firstName, k); var rejectionHandlerArgsAndBody = []; var usingDefaultHandler = false; @@ -1340,7 +1356,7 @@ class Macros { var b = wholeExp.expBuilder(); var name = exps[0]; var nameString = Prelude.symbolNameValue(name, true, false); - var type = Helpers.explicitTypeString(name); + var type = Helpers.explicitTypeString(name, k); var initialValue = exps[1]; var filename = if (savedVarFilename != null) { if (!savedVarFilename.endsWith(".json")) diff --git a/src/kiss/Main.hx b/src/kiss/Main.hx index 4aa780b..97aa3cf 100644 --- a/src/kiss/Main.hx +++ b/src/kiss/Main.hx @@ -359,7 +359,7 @@ class Main { v; } #if macro - var type = Context.resolveType(Helpers.parseComplexType(theInterface), Context.currentPos()); + var type = Context.resolveType(Helpers.parseComplexType(theInterface, Kiss.defaultKissState()), Context.currentPos()); switch (type) { case TInst(classTypeRef, params): var classType = classTypeRef.get(); diff --git a/src/kiss/SpecialForms.hx b/src/kiss/SpecialForms.hx index 927afc3..66e0317 100644 --- a/src/kiss/SpecialForms.hx +++ b/src/kiss/SpecialForms.hx @@ -116,7 +116,7 @@ class SpecialForms { default: k.convert(args[0]).toString(); }; - ENew(Helpers.parseTypePath(classType, args[0]), args.slice(1).map(k.convert)).withMacroPosOf(wholeExp); + ENew(Helpers.parseTypePath(classType, k, args[0]), args.slice(1).map(k.convert)).withMacroPosOf(wholeExp); }; k.doc("set", 2, 2, "(set )"); @@ -162,7 +162,7 @@ class SpecialForms { name: varName(nameExp), type: switch (nameExp.def) { case TypedExp(type, _): - Helpers.parseComplexType(type, nameExp); + Helpers.parseComplexType(type, k, nameExp); default: null; }, isFinal: isFinal && !k.hscript, @@ -456,7 +456,7 @@ class SpecialForms { }; if (pkg.length > 0) type = pkg + "." + type; - ECheckType(k.convert(args[1]), Helpers.parseComplexType(type, wholeExp, !type.contains("<"))).withMacroPosOf(wholeExp); + ECheckType(k.convert(args[1]), Helpers.parseComplexType(type, k, wholeExp, !type.contains("<"))).withMacroPosOf(wholeExp); }; k.doc("try", 1, null, "(try )"); @@ -479,7 +479,7 @@ class SpecialForms { }, type: switch (catchArgs[0].def) { case ListExp([{pos: _, def: TypedExp(type, _)}]): - Helpers.parseComplexType(type, catchArgs[0]); + Helpers.parseComplexType(type, k, catchArgs[0]); default: null; }, expr: k.convert(CallExp(Symbol("begin").withPos(catchArgs[1].pos), catchArgs.slice(1)).withPos(catchArgs[1].pos)) @@ -539,7 +539,7 @@ class SpecialForms { if (args.length > 1) { switch (args[1].def) { case Symbol(typePath): - t = Helpers.parseComplexType(typePath, wholeExp, !typePath.contains("<")); + t = Helpers.parseComplexType(typePath, k, wholeExp, !typePath.contains("<")); default: throw KissError.fromExp(wholeExp, 'second argument to cast should be a type path symbol'); }