experimental type aliases
This commit is contained in:
@@ -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<EmbeddedScript>", null),
|
||||
ret: Helpers.parseComplexType("Array<EmbeddedScript>", k, null),
|
||||
args: [
|
||||
{
|
||||
type: Helpers.parseComplexType("Array<Command>", null),
|
||||
type: Helpers.parseComplexType("Array<Command>", k, null),
|
||||
name: "commands"
|
||||
}
|
||||
],
|
||||
|
@@ -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,
|
||||
|
@@ -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<ComplexType> = null):TypeParamDecl {
|
||||
public static function makeTypeParam(param:ReaderExp, k:KissState, ?constraints:Array<ComplexType> = 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 <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;
|
||||
// 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<Dynamic>" | "kiss.List<Dynamic>" | "List<Dynamic>":
|
||||
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);
|
||||
|
@@ -51,6 +51,7 @@ typedef KissState = {
|
||||
loadedFiles:Map<String, Null<ReaderExp>>,
|
||||
callAliases:Map<String, ReaderExpDef>,
|
||||
identAliases:Map<String, ReaderExpDef>,
|
||||
typeAliases:Map<String, String>,
|
||||
fieldList:Array<Field>,
|
||||
// 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<Dynamic>) to the result */
|
||||
],
|
||||
typeAliases: new Map(),
|
||||
fieldList: [],
|
||||
fieldDict: new Map(),
|
||||
loadingDirectory: "",
|
||||
|
@@ -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<String, ReaderExpDef> = null;
|
||||
var aliasMap:Map<String, Dynamic> = 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> <makeItThis>)");
|
||||
k.doc("defalias", 2, 2, "(defAlias <<&call or &ident or &type> whenItsThis> <makeItThis>)");
|
||||
macros["defalias"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, 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"))
|
||||
|
@@ -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();
|
||||
|
@@ -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 <variable> <value>)");
|
||||
@@ -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 <thing> <catches...>)");
|
||||
@@ -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');
|
||||
}
|
||||
|
Reference in New Issue
Block a user