diff --git a/src/tink/Macro.hx b/src/tink/Macro.hx new file mode 100644 index 0000000..83a2c07 --- /dev/null +++ b/src/tink/Macro.hx @@ -0,0 +1,21 @@ +package tink; + +typedef Positions = tink.macro.Positions; +typedef ExprTools = haxe.macro.ExprTools; +typedef Exprs = tink.macro.Exprs; +typedef Functions = tink.macro.Functions; +typedef Metadatas = tink.macro.Metadatas; +typedef Bouncer = tink.macro.Bouncer; +typedef Types = tink.macro.Types; +typedef Binops = tink.macro.Ops.Binary; +typedef Unops = tink.macro.Ops.Unary; +typedef Outcome = tink.core.Outcome; +typedef OutcomeTools = tink.core.Outcome.OutcomeTools; +//TODO: consider adding stuff from haxe.macro.Expr here + +class Macro { + static var idCounter = 0; + static public inline function tempName(c:Class, ?prefix = '__tinkTmp'):String + return prefix + Std.string(idCounter++); + static public function pos() return haxe.macro.Context.currentPos(); +} \ No newline at end of file diff --git a/src/tink/macro/ClassBuilder.hx b/src/tink/macro/ClassBuilder.hx index d8269a3..00b939d 100644 --- a/src/tink/macro/ClassBuilder.hx +++ b/src/tink/macro/ClassBuilder.hx @@ -5,7 +5,7 @@ import haxe.macro.Type; import haxe.macro.Context; import haxe.macro.Printer; -using tink.macro.Tools; +using tink.Macro; using Lambda; class ClassBuilder { diff --git a/src/tink/macro/Constructor.hx b/src/tink/macro/Constructor.hx index e482877..a77a457 100644 --- a/src/tink/macro/Constructor.hx +++ b/src/tink/macro/Constructor.hx @@ -2,7 +2,7 @@ package tink.macro; import haxe.macro.Expr; -using tink.macro.Tools; +using tink.Macro; class Constructor { var oldStatements:Array; @@ -12,6 +12,7 @@ class Constructor { var afterArgs:Array; var pos:Position; var onGenerateHooks:ArrayVoid>; + var superCall:Expr; public var isPublic:Null; public function new(f:Function, ?isPublic:Null = null, ?pos:Position) { @@ -43,6 +44,12 @@ class Constructor { default: oldStatements = [f.expr]; } } + superCall = + if (oldStatements.length == 0) [].toBlock(); + else switch oldStatements[0] { + case macro super($a{_}): oldStatements.shift(); + default: [].toBlock(); + } } public function addStatement(e:Expr, ?prepend) if (prepend) @@ -68,7 +75,7 @@ class Constructor { isPublic = true; function toBlock() - return nuStatements.concat(oldStatements).toBlock(pos); + return [superCall].concat(nuStatements).concat(oldStatements).toBlock(pos); public function onGenerate(hook) this.onGenerateHooks.push(hook); diff --git a/src/tink/macro/Exprs.hx b/src/tink/macro/Exprs.hx index 2fe26be..a932c9e 100644 --- a/src/tink/macro/Exprs.hx +++ b/src/tink/macro/Exprs.hx @@ -24,15 +24,9 @@ typedef ParamSubst = { class Exprs { - static public inline function is(e:Expr, c:ComplexType) { + static public inline function is(e:Expr, c:ComplexType) return ECheckType(e, c).at(e.pos).typeof().isSuccess(); - } - static var annotCounter = 0; - static var annotations = new Map(); - static public function tag(e:Expr, data:D) { - annotations.set(annotCounter, data); - return [(annotCounter++).toExpr(e.pos), e].toBlock(e.pos); - } + static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dynamic, ?skipFields = false, ?callPos:PosInfos) { if (nuPos == null) nuPos = Context.currentPos(); @@ -43,7 +37,7 @@ class Exprs { if (Reflect.hasField(rules, s)) Reflect.field(rules, s) else if (s.startsWith('tmp')) { - Reflect.setField(rules, s, Tools.tempName(String, '__tink' + s.substr(3))); + Reflect.setField(rules, s, Macro.tempName(String, '__tink' + s.substr(3))); replace(s); } else s; @@ -80,14 +74,8 @@ class Exprs { } }); } - static public function untag(e:Expr):{data:D, e:Expr } { - return - switch (e.expr) { - case EBlock(exprs): { e: exprs[1], data: annotations.get(exprs[0].getInt().sure()) }; - default: e.reject(); - } - } - static public function withPrivateAccess(e:Expr) { + + static public function withPrivateAccess(e:Expr) return e.transform(function (e:Expr) return @@ -97,13 +85,10 @@ class Exprs { default: e; } ); - } - static public function getPrivate(e:Expr, field:String, ?pos) { - return EMeta( { name: ':privateAccess', params: [], pos: pos }, e.field(field, pos)).at(pos); - } - static public function partial(c:ComplexType, data:D, ?pos) - return ECheckType(macro null, c).at(pos).tag(data); - + + static public function getPrivate(e:Expr, field:String, ?pos:Position) + return macro @:pos(pos.sanitize()) @:privateAccess $e.$field; + static public function substitute(source:Expr, vars:Dynamic, ?pos) return transform(source, function (e:Expr) { @@ -120,8 +105,10 @@ class Exprs { static public inline function ifNull(e:Expr, fallback:Expr) return - if (e.getIdent().equals('null')) fallback; - else e; + switch e { + case macro null: fallback; + default: e; + } static public function substParams(source:Expr, subst:ParamSubst, ?pos):Expr return crawl( @@ -130,10 +117,8 @@ class Exprs { function (c:ComplexType) return switch (c) { - case TPath(p): - if (p.pack.length == 0 && subst.exists(p.name)) - subst.get(p.name); - else c; + case TPath({ pack: [], name: name }) if (subst.exists(name)): + subst.get(name); default: c; } , pos); @@ -141,35 +126,32 @@ class Exprs { static public function transform(source:Expr, transformer:Expr->Expr, ?pos):Expr return crawl(source, transformer, function (t) return t, pos); - static function crawlArray(a:Array, transformer:Expr->Expr, retyper:ComplexType-> ComplexType, pos:Position):Array { - if (a == null) return a; - var ret = []; - for (v in a) - ret.push(crawl(v, transformer, retyper, pos)); - return ret; - } + static function crawlArray(a:Array, transformer:Expr->Expr, retyper:ComplexType-> ComplexType, pos:Position):Array + return + if (a == null) a; + else + [for (v in a) + crawl(v, transformer, retyper, pos) + ]; + static public function getIterType(target:Expr) return - (macro { + (macro @:pos(target.pos) { var t = null, target = $target; for (i in target) t = i; t; - }).finalize(target.pos).typeof(); + }).typeof(); static public function yield(e:Expr, yielder:Expr->Expr):Expr { inline function rec(e) return yield(e, yielder); return - if (e == null) null; - else if (e.expr == null) e; + if (e == null || e.expr == null) e; else switch (e.expr) { case EVars(_): - (macro @:pos(e.pos) var x = { var x = 5; } ).typeof().sure(); - throw 'unreachable';//the above should cause an error - case EParenthesis(e): - EParenthesis(rec(e)).at(e.pos); + e.pos.error('Variable declaration not supported here'); case EBlock(exprs) if (exprs.length > 0): exprs = exprs.copy(); exprs.push(rec(exprs.pop())); @@ -186,12 +168,6 @@ class Exprs { EFor(it, rec(expr)).at(e.pos); case EWhile(cond, body, normal): EWhile(cond, rec(body), normal).at(e.pos); - case ECheckType(e, t): - ECheckType(rec(e), t).at(e.pos); - case EMeta(s, e): - EMeta(s, rec(e)).at(e.pos); - case EUntyped(e): - EUntyped(rec(e)).at(e.pos); case EBreak, EContinue: e; case EBinop(OpArrow, value, jump) if (jump.expr == EContinue || jump.expr == EBreak): macro @:pos(e.pos) { @@ -202,7 +178,7 @@ class Exprs { } } - static function crawl(target:Dynamic, transformer:Expr->Expr, retyper:ComplexType->ComplexType, pos:Position):Dynamic { + static function crawl(target:Dynamic, transformer:Expr->Expr, retyper:ComplexType->ComplexType, pos:Position):Dynamic return if (Std.is(target, Array)) crawlArray(target, transformer, retyper, pos); @@ -210,13 +186,13 @@ class Exprs { switch (Inspect.typeof(target)) { case TNull, TInt, TFloat, TBool, TFunction, TUnknown, TClass(_): target; case TEnum(e): - if (Inspect.getEnum(target) == ComplexType) return retyper(target); - else { + // if (Inspect.getEnum(target) == ComplexType) return retyper(target); + // else { var ret:Dynamic = Inspect.createEnumIndex(e, Inspect.enumIndex(target), crawlArray(Inspect.enumParameters(target), transformer, retyper, pos)); return if (Inspect.getEnum(ret) == ComplexType) retyper(ret); else ret; - } + // } case TObject: var ret:Dynamic = { }; for (field in Reflect.fields(target)) @@ -227,13 +203,17 @@ class Exprs { } ret; } - } - + + //TODO: this whole thing needs an overhaul static public function typedMap(source:Expr, f:Expr->Array->Expr, ctx:Array, ?pos:Position):Expr { if (ctx == null) ctx = []; function rec(e, ?inner) return typedMap(e, f, inner == null ? ctx : inner, pos); + + function def(name:String, ?e:Expr, ?t:ComplexType) + return { name: name, expr: e, type: t }; + if (source == null || source.expr == null) return source; var mappedSource = f(source, ctx); if (mappedSource != source) return mappedSource; @@ -260,10 +240,10 @@ class Exprs { case ENew(t, params): ENew(t, params.typedMapArray(f, ctx, pos)); case EBinop(op, e1, e2): EBinop(op, rec(e1), rec(e2)); case EObjectDecl(fields): - var newFields = []; - for (field in fields) - newFields.push( { field:field.field, expr:rec(field.expr) } ); - EObjectDecl(newFields); + EObjectDecl([for (field in fields) { + field: field.field, + expr: rec(field.expr) + }]); case ESwitch(expr, cases, def): var newCases = cases; newCases = []; @@ -309,12 +289,11 @@ class Exprs { newCases.push( { expr: rec(c.expr, innerCtx), values: caseValues } ); } case _: - for (c in cases) { - var caseValues = []; - for (v in c.values) - caseValues.push(rec(v)); - newCases.push( { expr: rec(c.expr), values: caseValues } ); - } + for (c in cases) + newCases.push({ + expr: rec(c.expr), + values: [for (v in c.values) rec(v)] + }); } ESwitch(expr, newCases, rec(def)); case EFor(it, expr): @@ -336,24 +315,21 @@ class Exprs { Context.error("Internal error in " + mappedSource.toString(), mappedSource.pos); } case ETry(e, catches): - var newCatches = []; - for (c in catches) - { - var innerCtx = ctx.copy(); - innerCtx.push({ name:c.name, expr: null, type:c.type }); - newCatches.push({name:c.name, expr:rec(c.expr, innerCtx), type:c.type}); - } - ETry(rec(e), newCatches); + ETry( + rec(e), + [for (c in catches) { + name: c.name, + expr: rec(c.expr, ctx.concat([def(c.name, c.type)])), + type: c.type + }] + ); case EFunction(name, func): - var innerCtx = ctx.copy(); - for (arg in func.args) - innerCtx.push( { name:arg.name, type:arg.type, expr:null } ); - func.expr = rec(func.expr, innerCtx); + func.expr = rec(func.expr, ctx.concat([for (arg in func.args) def(arg.name, arg.type)])); EFunction(name, func); case EVars(vars): + //This is incorrect. When declaring multiple variables, they are declared in the previous context "at once" var ret = []; - for (v in vars) - { + for (v in vars) { var vExpr = v.expr == null ? null : typedMap(v.expr, f, ctx); if (v.type == null && vExpr != null) v.type = vExpr.typeof(ctx).sure().toComplex(); @@ -367,22 +343,16 @@ class Exprs { return ret.at(pos == null ? source.pos : pos); } - static public function typedMapArray(source:Array, f:Expr->Array->Expr, ctx:Array, ?pos) { - var ret = []; - for (e in source) - ret.push(typedMap(e, f, ctx, pos)); - return ret; - } + static public function typedMapArray(source:Array, f:Expr->Array->Expr, ctx:Array, ?pos) + return [for (e in source) typedMap(e, f, ctx, pos)]; static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position) return EFor(EIn(loopVar.resolve(pos), target).at(pos), body).at(pos); - static public function toFields(object:Dynamic, ?pos:Position) { - var args = []; - for (field in Reflect.fields(object)) - args.push( { field:field, expr: untyped Reflect.field(object, field) } ); - return EObjectDecl(args).at(pos); - } + static public function toFields(object:Dynamic, ?pos:Position) + return EObjectDecl([for (field in Reflect.fields(object)) + { field:field, expr: untyped Reflect.field(object, field) } + ]).at(pos); static public inline function log(e:Expr, ?pos:PosInfos):Expr { haxe.Log.trace(e.toString(), pos); @@ -393,13 +363,12 @@ class Exprs { return e.pos.error(reason); static public inline function toString(e:Expr):String - // return tink.macro.tools.Printer.printExpr('', e); return new haxe.macro.Printer().printExpr(e); static public inline function at(e:ExprDef, ?pos:Position) return { expr: e, - pos: pos.getPos() + pos: pos.sanitize() }; static public inline function instantiate(s:String, ?args:Array, ?params:Array, ?pos:Position) @@ -427,7 +396,7 @@ class Exprs { return ECall(e, params == null ? [] : params).at(pos); static public inline function toExpr(v:Dynamic, ?pos:Position) - return Context.makeExpr(v, pos.getPos()); + return Context.makeExpr(v, pos.sanitize()); static public inline function toArray(exprs:Iterable, ?pos) return EArrayDecl(exprs.array()).at(pos); @@ -452,7 +421,7 @@ class Exprs { static public inline function resolve(s:String, ?pos) return drill(s.split('.'), pos); - static public function typeof(expr:Expr, ?locals) { + static public function typeof(expr:Expr, ?locals) return try { if (locals != null) @@ -466,18 +435,14 @@ class Exprs { catch (e:Dynamic) { expr.pos.makeFailure(e); } - } + static public inline function cond(cond:Expr, cons:Expr, ?alt:Expr, ?pos) return EIf(cond, cons, alt).at(pos); static public function isWildcard(e:Expr) return - switch(e.expr) { - case EConst(c): - switch (c) { - case CIdent(s): s == '_'; - default: false; - } + switch e { + case macro _: true; default: false; } @@ -538,86 +503,4 @@ class Exprs { static inline var NOT_A_STRING = "string constant expected"; static inline var NOT_A_NAME = "name expected"; static inline var NOT_A_FUNCTION = "function expected"; - static inline var EMPTY_EXPRESSION = "expression expected"; - - static public function match(expr:Expr, pattern:Expr) - return new Matcher().match(expr, pattern); - -} - -private class Matcher { - var exprs:Dynamic; - var strings:Dynamic; - public function new() { - this.exprs = {}; - this.strings = {}; - } - public function match(expr:Expr, pattern:Expr) { - return - try { - recurse(expr, pattern); - Success({ - exprs: exprs, - strings: strings,//TODO: deprecate - names: strings, - pos: expr.pos - }); - } - catch (e:String) Failure(e); - } - function matchObject(x1:Dynamic, x2:Dynamic) { - if (x2 == null) throw Std.string(x2) + ' expected but found ' + Std.string(x1); - for (f in Reflect.fields(x1)) - matchAny(Reflect.field(x1, f), Reflect.field(x2, f)); - } - function matchString(s1:String, s2:String) { - if (s2 == null) - equal(s1, s2); - else if (s2.startsWith('eval__') || s2.startsWith('NAME__')) - Reflect.setField(strings, s2.substr(6), s1); - else - equal(s1, s2); - } - function equal(x1:Dynamic, x2:Dynamic) { - if (x1 != x2) throw Std.string(x2) + ' expected but found ' + Std.string(x1); - } - function matchAny(x1:Dynamic, x2:Dynamic) { - switch (Inspect.typeof(x1)) { - case TNull, TInt, TFloat, TBool: equal(x1, x2); - case TObject: - if (Std.is(x1.expr, ExprDef)) recurse(x1, x2); - else matchObject(x1, x2); - case TFunction: - throw 'unexpected'; - case TClass(c): - if (c == Array) matchArray(x1, x2); - else if (c == String) matchString(x1, x2); - else throw 'unexpected'; - case TEnum(_): matchEnum(x1, x2); - case TUnknown: - } - } - function matchArray(a1:Array, a2:Array) { - equal(a1.length, a2.length); - for (i in 0...a1.length) - matchAny(a1[i], a2[i]); - } - function matchEnum(e1:Dynamic, e2:Dynamic) { - equal(Inspect.enumConstructor(e1), Inspect.enumConstructor(e2)); - matchArray(Inspect.enumParameters(e1), Inspect.enumParameters(e2)); - } - function recurse(expr:Expr, pattern:Expr) { - if (pattern == null) throw 'nothing expected but found ' + expr.toString(); - switch (pattern.getIdent()) { - case Success(s): - if (s.startsWith('$')) - Reflect.setField(exprs, s.substr(1), expr); - else if (s.startsWith('EXPR__')) - Reflect.setField(exprs, s.substr(6), expr); - else - matchEnum(expr.expr, pattern.expr); - default: - matchEnum(expr.expr, pattern.expr); - } - } } \ No newline at end of file diff --git a/src/tink/macro/Member.hx b/src/tink/macro/Member.hx index 772b677..7bf0c4f 100644 --- a/src/tink/macro/Member.hx +++ b/src/tink/macro/Member.hx @@ -1,8 +1,7 @@ package tink.macro; import haxe.macro.Expr; -using tink.core.Outcome; -using tink.macro.Tools; +using tink.Macro; abstract Member(Field) from Field to Field { static public function prop(name:String, t:ComplexType, pos, ?noread = false, ?nowrite = false):Member { diff --git a/src/tink/macro/Ops.hx b/src/tink/macro/Ops.hx index 8d43f3a..605084a 100644 --- a/src/tink/macro/Ops.hx +++ b/src/tink/macro/Ops.hx @@ -2,8 +2,7 @@ package tink.macro; import haxe.macro.Expr; -using tink.core.Outcome; -using tink.macro.Tools; +using tink.Macro; class Binary { static public function get(o:Binop, e:Expr) @@ -29,11 +28,10 @@ class Binary { static public inline function make(op:Binop, e1:Expr, e2:Expr, ?pos) return Exprs.binOp(e1, e2, op, pos); - } - +} class Unary { - static public function get(o:Unop, e:Expr, postfix:Bool = false) { + static public function get(o:Unop, e:Expr, postfix:Bool = false) return switch e.expr { case EUnop(op, postFix, arg): @@ -46,8 +44,8 @@ class Unary { default: e.pos.makeFailure('expected unary operation ' + o); } - } - static public function getUnop(e:Expr) { + + static public function getUnop(e:Expr) return switch e.expr { case EUnop(op, postFix, arg): @@ -55,8 +53,7 @@ class Unary { default: e.pos.makeFailure('expected unary operation but found ' + Type.enumConstructor(e.expr)); } - } - static public function make(op:Unop, e:Expr, ?postFix = false, ?pos) { + + static public function make(op:Unop, e:Expr, ?postFix = false, ?pos) return EUnop(op, postFix, e).at(pos); - } } \ No newline at end of file diff --git a/src/tink/macro/Positions.hx b/src/tink/macro/Positions.hx index eb8f0a6..969d7b6 100644 --- a/src/tink/macro/Positions.hx +++ b/src/tink/macro/Positions.hx @@ -2,62 +2,41 @@ package tink.macro; import haxe.macro.Context; import haxe.macro.Expr; - +import tink.core.Error; using tink.macro.Positions; using tink.core.Outcome; class Positions { - - static public function getOutcome(pos:Position, outcome:Outcome):D { + static public function getOutcome(pos:Position, outcome:Outcome):D return switch (outcome) { case Success(d): d; case Failure(f): pos.error(f); } - } + static public function makeBlankType(pos:Position):ComplexType - return Types.toComplex(Context.typeof(macro null)); + return Types.toComplex(Context.typeof(macro @:pos(pos.sanitize()) null)); - static public inline function getPos(pos:Position) { + static public inline function sanitize(pos:Position) return if (pos == null) Context.currentPos(); else pos; - } - static public function errorExpr(pos:Position, error:Dynamic) { + + static public function errorExpr(pos:Position, error:Dynamic) return Bouncer.bounce(function ():Expr { return Positions.error(pos, error); }, pos); - } - static public inline function error(pos:Position, error:Dynamic):Dynamic { - return Context.error(Std.string(error), pos); - } + + static public inline function error(pos:Position, error:Dynamic):Dynamic + return Context.error(Std.string(error), sanitize(pos)); + static public inline function warning(pos:Position, warning:Dynamic, ?ret:A):A { Context.warning(Std.string(warning), pos); return ret; } - ///used to easily construct failed outcomes - static public function makeFailure(pos:Position, reason:Reason):Outcome> { - return Failure(new MacroError(reason, pos)); - } -} -private class MacroError implements ThrowableFailure { - public var data(default, null):Data; - public var pos(default, null):Position; - public function new(data:Data, ?pos:Position) { - this.data = data; - this.pos = - if (pos == null) - Context.currentPos(); - else - pos; - } - public function toString() { - return 'Error@' + Std.string(pos) + ': ' + Std.string(data); - } - public function throwSelf():Dynamic { - return Context.error(Std.string(data), pos); - } + static public function makeFailure(pos:Position, reason:String):Outcome + return Failure(new Error(reason, pos)); } \ No newline at end of file diff --git a/src/tink/macro/Tools.hx b/src/tink/macro/Tools.hx deleted file mode 100644 index d5db61c..0000000 --- a/src/tink/macro/Tools.hx +++ /dev/null @@ -1,52 +0,0 @@ -package tink.macro; - -#if macro - import haxe.macro.Context; - typedef _Positions = Positions; - typedef _ExprTools = haxe.macro.ExprTools; - typedef _Exprs = Exprs; - typedef _Functions = Functions; - typedef _Metadatas = Metadatas; - typedef _Bouncer = Bouncer; - typedef _Types = Types; - typedef _Binops = Ops.Binary; - typedef _Unops = Ops.Unary; - typedef _Outcomes = tink.core.Outcome.OutcomeTools; -#end -class Tools { - static var idCounter = 0; - static public inline function tempName(c:Class, ?prefix = '__tinkTmp'):String { - return prefix + Std.string(idCounter++); - } - #if macro - static public function deprecate(at:haxe.PosInfos, useInstead:String, ret:A, ?p:haxe.PosInfos) { - try { - var lines = sys.io.File.getContent(at.fileName).split('\n'), - min = 0, - line = lines[at.lineNumber - 1]; - - for (i in 0...at.lineNumber-1) - min += lines[i].length + 1; - - var max = min + line.length; - - function count(haystack:String, needle) - return haystack.split(needle).length - 1; - - if (count(line, p.methodName) == 1) { - min += line.indexOf(p.methodName); - max = min + p.methodName.length; - } - Context.warning(p.className + '::' + p.methodName + ' is deprecated. Use $useInstead instead', Context.makePosition( { - min: min, - max: max, - file: at.fileName - })); - } - catch (e:Dynamic) - haxe.Log.trace('This function is deprecated, use $useInstead instead. Call site: ${p.className}@${p.lineNumber}'); - - return ret; - } - #end -} \ No newline at end of file diff --git a/src/tink/macro/Types.hx b/src/tink/macro/Types.hx index 0d93ac0..964d57b 100644 --- a/src/tink/macro/Types.hx +++ b/src/tink/macro/Types.hx @@ -15,22 +15,24 @@ using tink.core.Outcome; class Types { static var types = new MapType>(); static var idCounter = 0; - macro static public function getType(id:Int):Type { + + macro static public function getType(id:Int):Type return types.get(id)(); - } - static public function getID(t:Type, ?reduced = true) { - if (reduced) - t = reduce(t); - return - switch (t) { - case TAbstract(t, _): t.toString(); - case TInst(t, _): t.toString(); - case TEnum(t, _): t.toString(); - case TType(t, _): t.toString(); - default: null; - } - } - static public function accessToName(v:VarAccess, ?read = true) { + + static public function getID(t:Type, ?reduced = true) + return + if (reduced) + getID(reduce(t), false); + else + switch (t) { + case TAbstract(t, _): t.toString(); + case TInst(t, _): t.toString(); + case TEnum(t, _): t.toString(); + case TType(t, _): t.toString(); + default: null; + } + + static public function accessToName(v:VarAccess, ?read = true) return switch (v) { case AccNormal, AccInline: 'default'; @@ -40,7 +42,7 @@ class Types { default: throw 'not implemented'; } - } + static function getDeclaredFields(t:ClassType, out:Array, marker:Map) { for (field in t.fields.get()) if (!marker.exists(field.name)) { @@ -50,9 +52,8 @@ class Types { if (t.isInterface) for (t in t.interfaces) getDeclaredFields(t.t.get(), out, marker); - else - if (t.superClass != null) - getDeclaredFields(t.superClass.t.get(), out, marker); + else if (t.superClass != null) + getDeclaredFields(t.superClass.t.get(), out, marker); } static var fieldsCache = new Map(); @@ -80,7 +81,7 @@ class Types { switch (member.typeof()) { case Success(t): t; case Failure(f): - switch (reduce(field.type)) { + switch reduce(field.type) { case TFun(args, _): var fArgs = [], fParams = []; @@ -144,23 +145,23 @@ class Types { static public function isSubTypeOf(t:Type, of:Type, ?pos) return - ECheckType(ECheckType('null'.resolve(), toComplex(t)).at(pos), toComplex(of)).at(pos).typeof(); + ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof(); static public function isDynamic(t:Type) - return switch(reduce(t)) { + return switch reduce(t) { case TDynamic(_): true; default: false; } - static public function toType(t:ComplexType, ?pos) - return [ - '_'.define(t, pos), - '_'.resolve(pos) - ].toBlock(pos).typeof(); + static public function toType(t:ComplexType, ?pos:Position) + return (macro @:pos(pos.sanitize()) { + var v:$t = null; + v; + }).typeof(); - static public inline function instantiate(t:TypePath, ?args, ?pos) { + static public inline function instantiate(t:TypePath, ?args, ?pos) return ENew(t, args == null ? [] : args).at(pos); - } + static public function asTypePath(s:String, ?params):TypePath { var parts = s.split('.'); var name = parts.pop(), @@ -177,39 +178,38 @@ class Types { sub: sub }; } + static public inline function asComplexType(s:String, ?params) return TPath(asTypePath(s, params)); static public inline function reduce(type:Type, ?once) return Context.follow(type, once); - static public function isVar(field:ClassField) { + static public function isVar(field:ClassField) return switch (field.kind) { case FVar(_, _): true; default: false; } - } + static public function register(type:Void->Type):Int { - var id = idCounter++; - types.set(id, type); - return id; - } - static function paramsToComplex(params:Array):Array { - var ret = []; - for (p in params) - ret.push(TPType(toComplex(p, true))); - return ret; + types.set(idCounter, type); + return idCounter++; } + + static function paramsToComplex(params:Array):Array + return [for (p in params) TPType(toComplex(p))]; + static function baseToComplex(t:BaseType, params:Array) return asComplexType(t.module + '.' + t.name, paramsToComplex(params)); - static public function toComplex(type:Type, ?pretty = false):ComplexType { + static public function toComplex(type:Type):ComplexType { var ret = haxe.macro.TypeTools.toComplexType(type); if (ret == null) ret = lazyComplex(function () return type); return ret; } - static public function lazyComplex(f:Void->Type) { + + static public function lazyComplex(f:Void->Type) return TPath({ pack : ['haxe','macro'], @@ -217,5 +217,4 @@ class Types { params : [TPExpr('tink.macro.Types.getType'.resolve().call([register(f).toExpr()]))], sub : null, }); - } } \ No newline at end of file diff --git a/tests/Arrayish.hx b/tests/Arrayish.hx new file mode 100644 index 0000000..c64180a --- /dev/null +++ b/tests/Arrayish.hx @@ -0,0 +1,5 @@ +package ; + +class Arrayish implements ArrayAccess { + public var length:Int; +} \ No newline at end of file diff --git a/tests/Base.hx b/tests/Base.hx index c22f8f2..6bf003c 100644 --- a/tests/Base.hx +++ b/tests/Base.hx @@ -13,8 +13,8 @@ abstract PhysicalType(Either, Enum>) { public function toString() return switch this { - case Left(c): return Type.getClassName(c); - case Right(e): return Type.getEnumName(e); + case Left(c): Type.getClassName(c); + case Right(e): Type.getEnumName(e); } public function check(v:T) @@ -37,10 +37,10 @@ class Base extends TestCase { currentTest.posInfos = c; throw currentTest; } - function throws(f:Void->Void, t:PhysicalType, ?check:A->Bool, ?pos:PosInfos):Void { + function throws(f:Void->Void, ?t:PhysicalType, ?check:A->Bool, ?pos:PosInfos):Void { try f() catch (e:Dynamic) { - if (!t.check(e)) fail('Exception $e not of type $t', pos); + if (t != null && !t.check(e)) fail('Exception $e not of type $t', pos); if (check != null && !check(e)) fail('Exception $e does not satisfy condition', pos); assertTrue(true); return; diff --git a/tests/Exprs.hx b/tests/Exprs.hx new file mode 100644 index 0000000..365298e --- /dev/null +++ b/tests/Exprs.hx @@ -0,0 +1,70 @@ +package ; + +import haxe.macro.Expr; +using tink.Macro; + +class Exprs extends Base { + function exprEq(e1:Expr, e2:Expr) { + assertEquals(e1.toString(), e2.toString()); + } + function testGet() { + assertEquals('foo', (macro foo).getIdent().sure()); + assertEquals('foo', (macro "foo").getString().sure()); + assertEquals('foo', (macro foo).getName().sure()); + assertEquals('foo', (macro "foo").getName().sure()); + assertEquals(5, (macro 5).getInt().sure()); + + exprEq(macro [a, b, c], (macro function (a, b, c) [a, b, c]).getFunction().sure().expr); + assertEquals('a,b,c', [for (arg in (macro function (a, b, c) [a, b, c]).getFunction().sure().args) arg.name].join(',')); + + assertFalse((macro 'foo').getIdent().isSuccess()); + assertFalse((macro foo).getString().isSuccess()); + assertFalse((macro 5).getName().isSuccess()); + assertFalse((macro 5.1).getInt().isSuccess()); + assertFalse((macro foo).getFunction().isSuccess()); + } + + function testShortcuts() { + assertTrue(true); + } + + function testIterType() { + assertEquals('Int', (macro [1, 2]).getIterType().sure().getID()); + assertEquals('Int', (macro [1, 2].iterator()).getIterType().sure().getID()); + assertEquals('Int', ECheckType(macro null, macro: Arrayish).at().getIterType().sure().getID()); + } + + function testYield() { + function yielder(e) return macro @yield $e; + function test(x:Expr, e:Expr) + exprEq(x, e.yield(yielder)); + + test(macro @yield foo, macro foo); + test(macro @yield (foo), macro (foo)); + test(macro for (_) @yield foo, macro for (_) foo); + test(macro while (_) @yield foo, macro while (_) foo); + test(macro @yield [while (_) foo], macro [while (_) foo]); + } + + function testSubstitute() { + exprEq( + macro foo.call(arg1, arg2), + (macro bar.call(x, y)).substitute({ x: macro arg1, y: macro arg2, bar: macro foo }) + ); + + exprEq( + macro { + var x:Map = new Map(), + y:Array = []; + }, + (macro { + var x:Map = new Map(), + y:C = []; + }).substParams([ + 'A' => macro : Int, + 'B' => macro : String, + 'C' => macro : Array + ]) + ); + } +} \ No newline at end of file diff --git a/tests/Positions.hx b/tests/Positions.hx new file mode 100644 index 0000000..6e4e5d4 --- /dev/null +++ b/tests/Positions.hx @@ -0,0 +1,23 @@ +package ; + +import haxe.macro.Context; +import haxe.macro.Expr; +using tink.Macro; + +class Positions extends Base { + function stringCompare(v1:A, v2:A) + assertEquals(Std.string(v1), Std.string(v2)); + + function testSanitize() { + var p:Position = null; + stringCompare(Context.currentPos(), p.sanitize()); + p = Context.makePosition({ min: 0, max: 10, file: 'foo.txt' }); + stringCompare(p, p); + } + + function testBlank() { + var p:Position = null; + var t = p.makeBlankType(); + stringCompare('TMono()', cast t.toType().sure()); + } +} \ No newline at end of file diff --git a/tests/Run.hx b/tests/Run.hx index dbeb64d..7e28878 100644 --- a/tests/Run.hx +++ b/tests/Run.hx @@ -1,24 +1,28 @@ package ; -#if !macro - import haxe.unit.TestCase; - import haxe.unit.TestRunner; - import neko.Lib; -#else - import tink.macro.Member; - import tink.macro.Constructor; - import tink.macro.ClassBuilder; - using tink.macro.Tools; -#end +import haxe.unit.*; class Run { #if !macro - static var tests:Array = []; - static function main() { - test();//it compiles!!! - } + static function main() + test();//It compiles ... + #else + static var cases:Array = [ + new Exprs(), + new Types(), + new Positions(), + ]; #end macro static function test() { - return macro null; + var runner = new TestRunner(); + for (c in cases) + runner.add(c); + runner.run(); + if (!runner.result.success) + haxe.macro.Context.error(runner.result.toString(), haxe.macro.Context.currentPos()); + + return macro { + trace('Let\'s ship it!'); + } } } \ No newline at end of file diff --git a/tests/Types.hx b/tests/Types.hx new file mode 100644 index 0000000..dd3b777 --- /dev/null +++ b/tests/Types.hx @@ -0,0 +1,46 @@ +package ; + +import haxe.macro.Expr; +import haxe.macro.Context; +using tink.Macro; + +class Types extends Base { + function type(c:ComplexType) + return c.toType().sure(); + + function resolve(type:String) + return Context.getType(type); + + inline function assertSuccess(o:Outcome) + assertTrue(o.isSuccess()); + + inline function assertFailure(o:Outcome) + assertFalse(o.isSuccess()); + + function testIs() { + assertSuccess(resolve('Int').isSubTypeOf(resolve('Float'))); + assertFailure(resolve('Float').isSubTypeOf(resolve('Int'))); + } + + function testFields() { + var iterator = type(macro : haxe.ds.StringMap).getFields(true).sure().filter(function (c) return c.name == 'iterator')[0]; + var expected = type(macro : Void -> Iterator); + + assertSuccess(iterator.type.isSubTypeOf(expected)); + assertSuccess(expected.isSubTypeOf(iterator.type)); + } + + function testConvert() { + assertSuccess((macro : Int).toType()); + assertFailure((macro : Tni).toType()); + function blank() + return type(Macro.pos().makeBlankType()); + + var bool = type(macro : Bool); + assertTrue(blank().isSubTypeOf(bool).isSuccess()); + assertTrue(bool.isSubTypeOf(blank()).isSuccess()); + + Macro.pos().makeBlankType().toString(); + } +} +