experimental type aliases

This commit is contained in:
2023-06-29 14:07:10 -06:00
parent f6027cc00c
commit 66a6a4d396
7 changed files with 61 additions and 34 deletions

View File

@@ -186,7 +186,7 @@ class EmbeddedScript {
ret: null, ret: null,
args: [ args: [
{ {
type: Helpers.parseComplexType("(()->Void)->Void", null), type: Helpers.parseComplexType("(()->Void)->Void", k, null),
name: "c" name: "c"
} }
], ],
@@ -207,10 +207,10 @@ class EmbeddedScript {
name: "fork", name: "fork",
access: [APublic], access: [APublic],
kind: FFun({ kind: FFun({
ret: Helpers.parseComplexType("Array<EmbeddedScript>", null), ret: Helpers.parseComplexType("Array<EmbeddedScript>", k, null),
args: [ args: [
{ {
type: Helpers.parseComplexType("Array<Command>", null), type: Helpers.parseComplexType("Array<Command>", k, null),
name: "commands" name: "commands"
} }
], ],

View File

@@ -103,7 +103,7 @@ class FieldForms {
checkPrintFieldsCalledWarning(name, wholeExp, k); checkPrintFieldsCalledWarning(name, wholeExp, k);
var access = fieldAccess(formName, name, args[0]); var access = fieldAccess(formName, name, args[0]);
var type = Helpers.explicitType(args[0]); var type = Helpers.explicitType(args[0], k);
k.addVarInScope( k.addVarInScope(
{name: name, type: type}, {name: name, type: type},
false, false,

View File

@@ -49,8 +49,10 @@ class Helpers {
return s.charAt(0) == s.charAt(0).toUpperCase(); return s.charAt(0) == s.charAt(0).toUpperCase();
} }
public static function parseTypePath(path:String, ?from:ReaderExp):TypePath { public static function parseTypePath(path:String, k:KissState, ?from:ReaderExp):TypePath {
return switch (parseComplexType(path, from)) { if (k.typeAliases.exists(path))
path = k.typeAliases[path];
return switch (parseComplexType(path, k, from)) {
case TPath(path): case TPath(path):
path; path;
default: 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: // Trick Haxe into parsing it for us:
var typeCheckStr = 'var thing:$path;'; var typeCheckStr = 'var thing:$path;';
var errorMessage = 'Haxe could not parse a complex type from `$path` in `${typeCheckStr}`'; 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) { return switch (nameExp.def) {
case MetaExp(_, innerExp): case MetaExp(_, innerExp):
explicitTypeString(innerExp); explicitTypeString(innerExp, k);
case TypedExp(type, _) if (k.typeAliases.exists(type)):
k.typeAliases[type];
case TypedExp(type, _): case TypedExp(type, _):
type; type;
default: null; default: null;
}; };
} }
public static function explicitType(nameExp:ReaderExp):ComplexType { public static function explicitType(nameExp:ReaderExp, k:KissState):ComplexType {
var string = explicitTypeString(nameExp); var string = explicitTypeString(nameExp, k);
if (string == null) return null; 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") { public static function varName(formName:String, nameExp:ReaderExp, nameType = "variable") {
@@ -129,17 +136,19 @@ class Helpers {
}; };
} }
public static function makeTypeParam(param:ReaderExp, ?constraints:Array<ComplexType> = null):TypeParamDecl { public static function makeTypeParam(param:ReaderExp, k:KissState, ?constraints:Array<ComplexType> = null):TypeParamDecl {
if (constraints == null) constraints = []; if (constraints == null) constraints = [];
switch (param.def) { switch (param.def) {
case Symbol(name): case Symbol(name):
if (k.typeAliases.exists(name))
name = k.typeAliases[name];
return { return {
name: name, name: name,
constraints: constraints constraints: constraints
}; };
case TypedExp(type, param): case TypedExp(type, param):
constraints.push(parseComplexType(type)); constraints.push(parseComplexType(type, k));
return makeTypeParam(param, constraints); return makeTypeParam(param, k, constraints);
default: default:
throw KissError.fromExp(param, "expected <GenericTypeName> or :<Constraint> <GenericTypeName>"); throw KissError.fromExp(param, "expected <GenericTypeName> or :<Constraint> <GenericTypeName>");
} }
@@ -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; var numArgs = 0;
// Once the &opt meta appears, all following arguments are optional until &rest // 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"); throw KissError.fromExp(funcArg, "lambda does not support &rest arguments");
} }
var typeOfRestArg = explicitTypeString(funcArg); var typeOfRestArg = explicitTypeString(funcArg, k);
var isDynamicArray = switch (typeOfRestArg) { var isDynamicArray = switch (typeOfRestArg) {
case "Array<Dynamic>" | "kiss.List<Dynamic>" | "List<Dynamic>": case "Array<Dynamic>" | "kiss.List<Dynamic>" | "List<Dynamic>":
true; true;
@@ -219,7 +228,7 @@ class Helpers {
}, },
type: switch (funcArg.def) { type: switch (funcArg.def) {
case TypedExp(type, _): case TypedExp(type, _):
Helpers.parseComplexType(type, funcArg); Helpers.parseComplexType(type, k, funcArg);
default: null; default: null;
}, },
opt: opt opt: opt
@@ -269,7 +278,7 @@ class Helpers {
// But setting null arguments to default values is so common, and arguments are not settable references, // But setting null arguments to default values is so common, and arguments are not settable references,
// so function args are not immutable. // so function args are not immutable.
return { return {
ret: if (name != null) Helpers.explicitType(name) else null, ret: if (name != null) Helpers.explicitType(name, k) else null,
args: args, args: args,
expr: expr, expr: expr,
params: params params: params
@@ -470,14 +479,14 @@ class Helpers {
} }
// hscript.Interp is very finicky about some edge cases. // hscript.Interp is very finicky about some edge cases.
// This function handles them // This function handles them
private static function mapForInterp(expr:Expr):Expr { private static function mapForInterp(expr:Expr, k:KissState):Expr {
return expr.map(subExp -> { return expr.map(subExp -> {
switch (subExp.expr) { switch (subExp.expr) {
case ETry(e, catches): case ETry(e, catches):
catches = [for (c in catches) { catches = [for (c in catches) {
// hscript.Parser expects :Dynamic after the catch varname // hscript.Parser expects :Dynamic after the catch varname
{ {
type: Helpers.parseComplexType("Dynamic"), type: Helpers.parseComplexType("Dynamic", k),
name: c.name, name: c.name,
expr: c.expr expr: c.expr
}; };
@@ -486,14 +495,14 @@ class Helpers {
pos: subExp.pos, pos: subExp.pos,
expr: ETry(e, catches) expr: ETry(e, catches)
}; };
default: mapForInterp(subExp); default: mapForInterp(subExp, k);
} }
}); });
} }
static var parser = new Parser(); static var parser = new Parser();
static function compileTimeHScript(exp:ReaderExp, k:KissState) { 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 var code = hscriptExp.toString(); // tink_macro to the rescue
#if macrotest #if macrotest
Prelude.print("Compile-time hscript: " + code); Prelude.print("Compile-time hscript: " + code);

View File

@@ -51,6 +51,7 @@ typedef KissState = {
loadedFiles:Map<String, Null<ReaderExp>>, loadedFiles:Map<String, Null<ReaderExp>>,
callAliases:Map<String, ReaderExpDef>, callAliases:Map<String, ReaderExpDef>,
identAliases:Map<String, ReaderExpDef>, identAliases:Map<String, ReaderExpDef>,
typeAliases:Map<String, String>,
fieldList:Array<Field>, fieldList:Array<Field>,
// TODO This map was originally created to track whether the programmer wrote their own main function, but could also // 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 // 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 /* concat used to live here as an alias but now it is in a macro that also
applies (the Array<Dynamic>) to the result */ applies (the Array<Dynamic>) to the result */
], ],
typeAliases: new Map(),
fieldList: [], fieldList: [],
fieldDict: new Map(), fieldDict: new Map(),
loadingDirectory: "", loadingDirectory: "",

View File

@@ -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 // 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 // through the next function before being used in defalias or undefalias
var aliasMap:Map<String, ReaderExpDef> = null; var aliasMap:Map<String, Dynamic> = null;
var extractString = false;
function getAliasName(k:KissState, nameExpWithMeta:ReaderExp, formName:String):String { 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 error = KissError.fromExp(nameExpWithMeta, 'first argument to $formName should be &call [alias] or &ident [alias]');
var nameExp = switch (nameExpWithMeta.def) { var nameExp = switch (nameExpWithMeta.def) {
case MetaExp("call", nameExp): case MetaExp("call", nameExp):
extractString = false;
aliasMap = k.callAliases; aliasMap = k.callAliases;
nameExp; nameExp;
case MetaExp("ident", nameExp): case MetaExp("ident", nameExp):
extractString = false;
aliasMap = k.identAliases; aliasMap = k.identAliases;
nameExp; nameExp;
case MetaExp("type", nameExp):
aliasMap = k.typeAliases;
extractString = true;
nameExp;
default: default:
throw error; throw error;
}; };
@@ -676,12 +683,21 @@ class Macros {
}; };
} }
k.doc("defalias", 2, 2, "(defAlias <<&call or &ident> whenItsThis> <makeItThis>)"); k.doc("defalias", 2, 2, "(defAlias <<&call or &ident or &type> whenItsThis> <makeItThis>)");
macros["defalias"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> { macros["defalias"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> {
k.stateChanged = true; k.stateChanged = true;
var name = getAliasName(k, exps[0], "defAlias"); 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; return null;
}; };
renameAndDeprecate("defalias", "defAlias"); renameAndDeprecate("defalias", "defAlias");
@@ -774,7 +790,7 @@ class Macros {
var firstValue = bindingList.shift(); var firstValue = bindingList.shift();
var b = wholeExp.expBuilder(); var b = wholeExp.expBuilder();
var firstNameSymbol = b.symbol(firstNameString); var firstNameSymbol = b.symbol(firstNameString);
var firstNameType = Helpers.explicitTypeString(firstName); var firstNameType = Helpers.explicitTypeString(firstName, k);
var rejectionHandlerArgsAndBody = []; var rejectionHandlerArgsAndBody = [];
var usingDefaultHandler = false; var usingDefaultHandler = false;
@@ -1340,7 +1356,7 @@ class Macros {
var b = wholeExp.expBuilder(); var b = wholeExp.expBuilder();
var name = exps[0]; var name = exps[0];
var nameString = Prelude.symbolNameValue(name, true, false); var nameString = Prelude.symbolNameValue(name, true, false);
var type = Helpers.explicitTypeString(name); var type = Helpers.explicitTypeString(name, k);
var initialValue = exps[1]; var initialValue = exps[1];
var filename = if (savedVarFilename != null) { var filename = if (savedVarFilename != null) {
if (!savedVarFilename.endsWith(".json")) if (!savedVarFilename.endsWith(".json"))

View File

@@ -359,7 +359,7 @@ class Main {
v; v;
} }
#if macro #if macro
var type = Context.resolveType(Helpers.parseComplexType(theInterface), Context.currentPos()); var type = Context.resolveType(Helpers.parseComplexType(theInterface, Kiss.defaultKissState()), Context.currentPos());
switch (type) { switch (type) {
case TInst(classTypeRef, params): case TInst(classTypeRef, params):
var classType = classTypeRef.get(); var classType = classTypeRef.get();

View File

@@ -116,7 +116,7 @@ class SpecialForms {
default: k.convert(args[0]).toString(); 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 <variable> <value>)"); k.doc("set", 2, 2, "(set <variable> <value>)");
@@ -162,7 +162,7 @@ class SpecialForms {
name: varName(nameExp), name: varName(nameExp),
type: switch (nameExp.def) { type: switch (nameExp.def) {
case TypedExp(type, _): case TypedExp(type, _):
Helpers.parseComplexType(type, nameExp); Helpers.parseComplexType(type, k, nameExp);
default: null; default: null;
}, },
isFinal: isFinal && !k.hscript, isFinal: isFinal && !k.hscript,
@@ -456,7 +456,7 @@ class SpecialForms {
}; };
if (pkg.length > 0) if (pkg.length > 0)
type = pkg + "." + type; 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 <thing> <catches...>)"); k.doc("try", 1, null, "(try <thing> <catches...>)");
@@ -479,7 +479,7 @@ class SpecialForms {
}, },
type: switch (catchArgs[0].def) { type: switch (catchArgs[0].def) {
case ListExp([{pos: _, def: TypedExp(type, _)}]): case ListExp([{pos: _, def: TypedExp(type, _)}]):
Helpers.parseComplexType(type, catchArgs[0]); Helpers.parseComplexType(type, k, catchArgs[0]);
default: null; default: null;
}, },
expr: k.convert(CallExp(Symbol("begin").withPos(catchArgs[1].pos), catchArgs.slice(1)).withPos(catchArgs[1].pos)) 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) { if (args.length > 1) {
switch (args[1].def) { switch (args[1].def) {
case Symbol(typePath): case Symbol(typePath):
t = Helpers.parseComplexType(typePath, wholeExp, !typePath.contains("<")); t = Helpers.parseComplexType(typePath, k, wholeExp, !typePath.contains("<"));
default: default:
throw KissError.fromExp(wholeExp, 'second argument to cast should be a type path symbol'); throw KissError.fromExp(wholeExp, 'second argument to cast should be a type path symbol');
} }