Compare commits
33 Commits
no-reflect
...
build-cach
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc9f7460c2 | ||
|
|
043df0f47d | ||
|
|
7e2dfad607 | ||
|
|
94d02c91a8 | ||
|
|
99a1fc3fa6 | ||
|
|
a383f7692b | ||
|
|
5e5781e8bc | ||
|
|
63ce1853f5 | ||
|
|
f3ddaa6496 | ||
|
|
f46e49ce66 | ||
|
|
007c73d58e | ||
|
|
8c5903833c | ||
|
|
59d7407d1b | ||
|
|
0680220a77 | ||
|
|
acaedc170a | ||
|
|
8b9dbba624 | ||
|
|
f9348d4a46 | ||
|
|
6f4e6b9227 | ||
|
|
59135d5cea | ||
|
|
4accf55b41 | ||
|
|
fd3b01ef0f | ||
|
|
5825c2b617 | ||
|
|
328a8476a5 | ||
|
|
1589665652 | ||
|
|
08f15a6739 | ||
|
|
b5e992b820 | ||
|
|
8c50dcf04b | ||
|
|
4bc63c6eb0 | ||
|
|
acb7237ae4 | ||
|
|
e3bac681ce | ||
|
|
34516086c9 | ||
|
|
b1e7bf51cf | ||
|
|
d5186f2fa2 |
@@ -15,14 +15,14 @@ os:
|
|||||||
env:
|
env:
|
||||||
- HAXE_VERSION=3.4.7
|
- HAXE_VERSION=3.4.7
|
||||||
- HAXE_VERSION=edge
|
- HAXE_VERSION=edge
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- npm i -g lix@15.5.4
|
- npm i -g lix@15.5.4
|
||||||
- lix install haxe $HAXE_VERSION
|
- lix install haxe $HAXE_VERSION
|
||||||
- lix download
|
- lix download
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- lix run travix node -lib hx3compat
|
- lix run travix node
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
-D hx3compat=1.0.0
|
# @install: lix --silent download "haxelib:/hx3compat#1.0.3" into hx3compat/1.0.3/haxelib
|
||||||
# @install: lix --silent download "haxelib:/hx3compat#1.0.0" into hx3compat/1.0.0/haxelib
|
-cp ${HAXE_LIBCACHE}/hx3compat/1.0.3/haxelib/std
|
||||||
-cp ${HAXE_LIBCACHE}/hx3compat/1.0.0/haxelib/std
|
-D hx3compat=1.0.3
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# @install: lix --silent download "haxelib:hxnodejs#4.0.9" into hxnodejs/4.0.9/haxelib
|
# @install: lix --silent download "haxelib:/hxnodejs#12.0.0" into hxnodejs/12.0.0/haxelib
|
||||||
-D hxnodejs=4.0.9
|
-cp ${HAXE_LIBCACHE}/hxnodejs/12.0.0/haxelib/src
|
||||||
-cp ${HAXESHIM_LIBCACHE}/hxnodejs/4.0.9/haxelib/src
|
-D hxnodejs=12.0.0
|
||||||
-D nodejs
|
|
||||||
--macro allowPackage('sys')
|
--macro allowPackage('sys')
|
||||||
--macro _hxnodejs.VersionWarning.include()
|
# should behave like other target defines and not be defined in macro context
|
||||||
|
--macro define('nodejs')
|
||||||
|
|||||||
24
haxelib.json
24
haxelib.json
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "tink_macro",
|
"name": "tink_macro",
|
||||||
"license": "MIT",
|
"description": "The macro toolkit ;)",
|
||||||
|
"classPath": "src",
|
||||||
|
"dependencies": {
|
||||||
|
"tink_core": ""
|
||||||
|
},
|
||||||
|
"url": "https://github.com/haxetink/tink_macro",
|
||||||
|
"contributors": [
|
||||||
|
"back2dos"
|
||||||
|
],
|
||||||
|
"version": "0.22.0",
|
||||||
|
"releasenote": "Support Haxe 4.2",
|
||||||
"tags": [
|
"tags": [
|
||||||
"tink",
|
"tink",
|
||||||
"macro",
|
"macro",
|
||||||
"utility"
|
"utility"
|
||||||
],
|
],
|
||||||
"classPath": "src",
|
"license": "MIT"
|
||||||
"description": "The macro toolkit ;)",
|
|
||||||
"contributors": [
|
|
||||||
"back2dos"
|
|
||||||
],
|
|
||||||
"releasenote": "Compat with haxe 4 rc4 onward.",
|
|
||||||
"version": "0.18.0",
|
|
||||||
"url": "http://haxetink.org/tink_macro",
|
|
||||||
"dependencies": {
|
|
||||||
"tink_core": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ using StringTools;
|
|||||||
|
|
||||||
typedef Positions = tink.macro.Positions;
|
typedef Positions = tink.macro.Positions;
|
||||||
typedef ExprTools = haxe.macro.ExprTools;
|
typedef ExprTools = haxe.macro.ExprTools;
|
||||||
|
typedef TypedExprTools = haxe.macro.TypedExprTools;
|
||||||
|
typedef TypedExprs = tink.macro.TypedExprs;
|
||||||
typedef Exprs = tink.macro.Exprs;
|
typedef Exprs = tink.macro.Exprs;
|
||||||
typedef Functions = tink.macro.Functions;
|
typedef Functions = tink.macro.Functions;
|
||||||
typedef Metadatas = tink.macro.Metadatas;
|
typedef Metadatas = tink.macro.Metadatas;
|
||||||
@@ -28,11 +30,11 @@ typedef ClassBuilder = tink.macro.ClassBuilder;
|
|||||||
typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
|
typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
|
||||||
|
|
||||||
class MacroApi {
|
class MacroApi {
|
||||||
|
|
||||||
static var MAIN_CANDIDATES = ['-main', '-x', '--run'];
|
static var MAIN_CANDIDATES = ['-main', '-x', '--run'];
|
||||||
static public function getMainClass():Option<String> {
|
static public function getMainClass():Option<String> {
|
||||||
var args = Sys.args();
|
var args = Sys.args();
|
||||||
|
|
||||||
for (c in MAIN_CANDIDATES)
|
for (c in MAIN_CANDIDATES)
|
||||||
switch args.indexOf(c) {
|
switch args.indexOf(c) {
|
||||||
case -1:
|
case -1:
|
||||||
@@ -42,19 +44,26 @@ class MacroApi {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@:persistent static var idCounter = 0;
|
@:persistent static var idCounter = 0;
|
||||||
|
|
||||||
@:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
|
@:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
|
||||||
return '__tink_' + prefix + Std.string(idCounter++);
|
return '__tink_' + prefix + Std.string(idCounter++);
|
||||||
|
|
||||||
static public function pos()
|
static public function pos()
|
||||||
return haxe.macro.Context.currentPos();
|
return haxe.macro.Context.currentPos();
|
||||||
|
|
||||||
static public var completionPoint(default, null):Option<{
|
static public var completionPoint(default, null):Option<{
|
||||||
var file(default, never):String;
|
var file(default, never):String;
|
||||||
|
var content(default, never):Null<String>;
|
||||||
var pos(default, never):Int;
|
var pos(default, never):Int;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
static public function getBuildFields():Option<Array<haxe.macro.Expr.Field>>
|
||||||
|
return switch completionPoint {
|
||||||
|
case Some(v) if (v.content != null && (v.content.charAt(v.pos - 1) == '@' || (v.content.charAt(v.pos - 1) == ':' && v.content.charAt(v.pos - 2) == '@'))): None;
|
||||||
|
default: Some(haxe.macro.Context.getBuildFields());
|
||||||
|
}
|
||||||
|
|
||||||
static public var args(default, null):Iterable<String>;
|
static public var args(default, null):Iterable<String>;
|
||||||
static var initialized = initArgs();
|
static var initialized = initArgs();
|
||||||
|
|
||||||
@@ -63,7 +72,7 @@ class MacroApi {
|
|||||||
args = sysArgs;
|
args = sysArgs;
|
||||||
completionPoint = switch sysArgs.indexOf('--display') {
|
completionPoint = switch sysArgs.indexOf('--display') {
|
||||||
case -1: None;
|
case -1: None;
|
||||||
case sysArgs[_ + 1] => arg:
|
case sysArgs[_ + 1] => arg:
|
||||||
if (arg.startsWith('{"jsonrpc":')) {
|
if (arg.startsWith('{"jsonrpc":')) {
|
||||||
var payload:{
|
var payload:{
|
||||||
jsonrpc:String,
|
jsonrpc:String,
|
||||||
@@ -71,12 +80,15 @@ class MacroApi {
|
|||||||
params:{
|
params:{
|
||||||
file:String,
|
file:String,
|
||||||
offset:Int,
|
offset:Int,
|
||||||
|
contents:String,
|
||||||
}
|
}
|
||||||
} = haxe.Json.parse(arg);
|
} = haxe.Json.parse(arg);
|
||||||
|
|
||||||
switch payload {
|
switch payload {
|
||||||
case { jsonrpc: '2.0', method: 'display/completion' }:
|
case { jsonrpc: '2.0', method: 'display/completion' }:
|
||||||
Some({
|
Some({
|
||||||
file: payload.params.file,
|
file: payload.params.file,
|
||||||
|
content: payload.params.contents,
|
||||||
pos: payload.params.offset,
|
pos: payload.params.offset,
|
||||||
});
|
});
|
||||||
default: None;
|
default: None;
|
||||||
@@ -108,26 +120,26 @@ class MacroApi {
|
|||||||
@:forward
|
@:forward
|
||||||
abstract ObjectField(F) to F {
|
abstract ObjectField(F) to F {
|
||||||
|
|
||||||
static var QUOTED = "@$__hx__";
|
static var QUOTED = "@$__hx__";
|
||||||
|
|
||||||
inline function new(o) this = o;
|
inline function new(o) this = o;
|
||||||
|
|
||||||
public var field(get, never):String;
|
public var field(get, never):String;
|
||||||
|
|
||||||
function get_field()
|
function get_field()
|
||||||
return
|
return
|
||||||
if (quotes == Quoted)
|
if (quotes == Quoted)
|
||||||
this.field.substr(QUOTED.length);
|
this.field.substr(QUOTED.length);
|
||||||
else this.field;
|
else this.field;
|
||||||
|
|
||||||
public var quotes(get, never):QuoteStatus;
|
public var quotes(get, never):QuoteStatus;
|
||||||
|
|
||||||
function get_quotes()
|
function get_quotes()
|
||||||
return if (StringTools.startsWith(this.field, QUOTED)) Quoted else Unquoted;
|
return if (StringTools.startsWith(this.field, QUOTED)) Quoted else Unquoted;
|
||||||
|
|
||||||
@:from static function ofFull(o:{>F, quotes:QuoteStatus }):ObjectField
|
@:from static function ofFull(o:{>F, quotes:QuoteStatus }):ObjectField
|
||||||
return switch o.quotes {
|
return switch o.quotes {
|
||||||
case null | Unquoted:
|
case null | Unquoted:
|
||||||
new ObjectField({ field: o.field, expr: o.expr });
|
new ObjectField({ field: o.field, expr: o.expr });
|
||||||
default:
|
default:
|
||||||
new ObjectField({ field: QUOTED + o.field, expr: o.expr });
|
new ObjectField({ field: QUOTED + o.field, expr: o.expr });
|
||||||
|
|||||||
@@ -17,100 +17,78 @@ typedef BuildContextN = {
|
|||||||
|
|
||||||
|
|
||||||
typedef BuildContext = {
|
typedef BuildContext = {
|
||||||
pos:Position,
|
var pos(default, never):Position;
|
||||||
type:Type,
|
var type(default, never):Type;
|
||||||
usings:Array<TypePath>,
|
var usings(default, never):Array<TypePath>;
|
||||||
name:String,
|
var name(default, never):String;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef BuildContext2 = {>BuildContext,
|
typedef BuildContext2 = {>BuildContext,
|
||||||
type2:Type,
|
var type2(default, never):Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef BuildContext3 = {>BuildContext2,
|
typedef BuildContext3 = {>BuildContext2,
|
||||||
type3:Type,
|
var type3(default, never):Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BuildCache {
|
class BuildCache {
|
||||||
|
|
||||||
@:persistent static var cache = new Map();
|
@:persistent static var cache = new Map();
|
||||||
|
|
||||||
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition) {
|
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition, ?normalizer:Type->Type) {
|
||||||
if (types == null)
|
return _getTypeN(name, 3, switch types {
|
||||||
switch Context.getLocalType() {
|
case null: null;
|
||||||
case TInst(_.toString() == name => true, [t1, t2, t3]):
|
case v: [types.t1, types.t2, types.t3];
|
||||||
types = { t1: t1, t2: t2, t3: t3 };
|
}, pos, ctx -> build({
|
||||||
default:
|
|
||||||
throw 'assert';
|
|
||||||
}
|
|
||||||
|
|
||||||
var t1 = types.t1.toComplexType(),
|
|
||||||
t2 = types.t2.toComplexType(),
|
|
||||||
t3 = types.t2.toComplexType();
|
|
||||||
|
|
||||||
return getType(name, (macro : { t1: $t1, t2: $t2, t3: $t3 } ).toType(), pos, function (ctx) return build({
|
|
||||||
type: types.t1,
|
|
||||||
type2: types.t2,
|
|
||||||
type3: types.t3,
|
|
||||||
pos: ctx.pos,
|
pos: ctx.pos,
|
||||||
name: ctx.name,
|
type: ctx.types[0],
|
||||||
usings: ctx.usings
|
type2: ctx.types[1],
|
||||||
}));
|
type3: ctx.types[2],
|
||||||
|
usings: ctx.usings,
|
||||||
|
name: ctx.name
|
||||||
|
}), normalizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getTypeN(name, ?types, ?pos:Position, build:BuildContextN->TypeDefinition) {
|
static function _getTypeN(name, length, ?types, ?pos:Position, build:BuildContextN->TypeDefinition, ?normalizer:Type->Type) {
|
||||||
|
|
||||||
if (pos == null)
|
if (pos == null)
|
||||||
pos = Context.currentPos();
|
pos = Context.currentPos();
|
||||||
|
|
||||||
if (types == null)
|
if (types == null)
|
||||||
switch Context.getLocalType() {
|
switch Context.getLocalType() {
|
||||||
case TInst(_.toString() == name => true, params):
|
case TInst(_.toString() == name => true, params):
|
||||||
types = params;
|
types = params;
|
||||||
default:
|
case t:
|
||||||
throw 'assert';
|
pos.error('expected $name but found ${t.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
|
if (length != -1 && types.length != length)
|
||||||
name: 't$i',
|
pos.error('expected $length parameter${if (length == 1) '' else 's'}');
|
||||||
pos: pos,
|
|
||||||
kind: FVar(switch types[i] {
|
var forName =
|
||||||
case TInst(_.get().kind => KExpr(e), _):
|
switch cache[name] {
|
||||||
TPath('tink.macro.ConstParam'.asTypePath([TPExpr(e)]));
|
case null: cache[name] = new Group(name);
|
||||||
case t: t.toComplex();
|
case v: v;
|
||||||
}),
|
}
|
||||||
}]).toType();
|
|
||||||
|
var ret = forName.get(types, pos.sanitize(), build, normalizer);
|
||||||
return getType(name, compound, pos, function (ctx) return build({
|
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
|
||||||
types: types,
|
return ret;
|
||||||
pos: ctx.pos,
|
|
||||||
name: ctx.name,
|
|
||||||
usings: ctx.usings
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition) {
|
|
||||||
if (types == null)
|
|
||||||
switch Context.getLocalType() {
|
|
||||||
case TInst(_.toString() == name => true, [t1, t2]):
|
|
||||||
types = { t1: t1, t2: t2 };
|
|
||||||
default:
|
|
||||||
throw 'assert';
|
|
||||||
}
|
|
||||||
|
|
||||||
var t1 = types.t1.toComplexType(),
|
|
||||||
t2 = types.t2.toComplexType();
|
|
||||||
|
|
||||||
return getType(name, (macro : { t1: $t1, t2: $t2 } ).toType(), pos, function (ctx) return build({
|
|
||||||
type: types.t1,
|
|
||||||
type2: types.t2,
|
|
||||||
pos: ctx.pos,
|
|
||||||
name: ctx.name,
|
|
||||||
usings: ctx.usings
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getParams(name:String, ?pos:Position)
|
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition, ?normalizer:Type->Type)
|
||||||
|
return _getTypeN(name, 2, switch types {
|
||||||
|
case null: null;
|
||||||
|
case v: [types.t1, types.t2];
|
||||||
|
}, pos, ctx -> build({
|
||||||
|
pos: ctx.pos,
|
||||||
|
type: ctx.types[0],
|
||||||
|
type2: ctx.types[1],
|
||||||
|
usings: ctx.usings,
|
||||||
|
name: ctx.name
|
||||||
|
}), normalizer);
|
||||||
|
|
||||||
|
static public function getParams(name:String, ?pos:Position, ?count:Int)
|
||||||
return
|
return
|
||||||
switch Context.getLocalType() {
|
switch Context.getLocalType() {
|
||||||
case TInst(_.toString() == name => true, v):
|
case TInst(_.toString() == name => true, v):
|
||||||
@@ -119,9 +97,9 @@ class BuildCache {
|
|||||||
pos.makeFailure('Expected $name');
|
pos.makeFailure('Expected $name');
|
||||||
case v:
|
case v:
|
||||||
pos.makeFailure('$v should be a class');
|
pos.makeFailure('$v should be a class');
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getParam(name:String, ?pos:Position)
|
static public function getParam(name:String, ?pos:Position)
|
||||||
return
|
return
|
||||||
getParams(name, pos)
|
getParams(name, pos)
|
||||||
.flatMap(function (args:Array<Type>) return switch args {
|
.flatMap(function (args:Array<Type>) return switch args {
|
||||||
@@ -130,70 +108,51 @@ class BuildCache {
|
|||||||
default: pos.makeFailure('too many parameters');
|
default: pos.makeFailure('too many parameters');
|
||||||
});
|
});
|
||||||
|
|
||||||
static public function getType(name, ?type, ?pos:Position, build:BuildContext->TypeDefinition) {
|
static public function getType(name, ?type, ?pos:Position, build:BuildContext->TypeDefinition, ?normalizer:Type->Type) {
|
||||||
|
return _getTypeN(name, 1, switch type {
|
||||||
if (type == null)
|
case null: null;
|
||||||
type = getParam(name, pos).sure();
|
case v: [v];
|
||||||
|
}, pos, ctx -> build({
|
||||||
var forName =
|
pos: ctx.pos,
|
||||||
switch cache[name] {
|
type: ctx.types[0],
|
||||||
case null: cache[name] = new Group(name);
|
usings: ctx.usings,
|
||||||
case v: v;
|
name: ctx.name
|
||||||
}
|
}), normalizer);
|
||||||
|
|
||||||
var ret = forName.get(type, pos.sanitize(), build);
|
|
||||||
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private typedef Entry = {
|
private class Group {//TODO: this is somewhat obsolete
|
||||||
name:String,
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Group {
|
|
||||||
|
|
||||||
var name:String;
|
var name:String;
|
||||||
var counter = 0;
|
|
||||||
var entries = new TypeMap<Entry>();
|
|
||||||
|
|
||||||
public function new(name) {
|
public function new(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(type:Type, pos:Position, build:BuildContext->TypeDefinition):Type {
|
|
||||||
|
|
||||||
function make(path:String) {
|
|
||||||
var usings = [];
|
|
||||||
var def = build({
|
|
||||||
pos: pos,
|
|
||||||
type: type,
|
|
||||||
usings: usings,
|
|
||||||
name: path.split('.').pop()
|
|
||||||
});
|
|
||||||
|
|
||||||
entries.set(type, { name: path } );
|
public function get(types:Array<Type>, pos:Position, build:BuildContextN->TypeDefinition, ?normalizer):Type {
|
||||||
Context.defineModule(path, [def], usings);
|
|
||||||
return Context.getType(path);
|
types = types.map(switch normalizer {
|
||||||
|
case null: function (t) return Context.follow(t);
|
||||||
|
case f: f;
|
||||||
|
});
|
||||||
|
|
||||||
|
var retName = name + '_' + Context.signature(Sisyphus.exactParams(types));
|
||||||
|
|
||||||
|
return switch retName.definedType() {
|
||||||
|
case Some(v): v;
|
||||||
|
case None:
|
||||||
|
var usings = [];
|
||||||
|
var path = name.split('.');
|
||||||
|
|
||||||
|
var ret = build({
|
||||||
|
pos: pos,
|
||||||
|
types: types,
|
||||||
|
usings: usings,
|
||||||
|
name: retName.split('.').pop(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Context.defineModule(retName, [ret], usings);
|
||||||
|
Context.getType(retName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function doMake()
|
|
||||||
while (true)
|
|
||||||
switch '$name${counter++}' {
|
|
||||||
case _.definedType() => Some(_):
|
|
||||||
case v:
|
|
||||||
return make(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
switch entries.get(type) {
|
|
||||||
case null:
|
|
||||||
doMake();
|
|
||||||
case v:
|
|
||||||
switch v.name.definedType() {
|
|
||||||
case Some(v): v;
|
|
||||||
default: doMake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,10 @@ class ClassBuilder {
|
|||||||
target = Context.getLocalClass().get();
|
target = Context.getLocalClass().get();
|
||||||
|
|
||||||
if (fields == null)
|
if (fields == null)
|
||||||
fields = Context.getBuildFields();
|
fields = switch MacroApi.getBuildFields() {
|
||||||
|
case None: target.pos.error('Impossible to get builds fields now. Possible cause: https://github.com/HaxeFoundation/haxe/issues/9853');
|
||||||
|
case Some(v): v;
|
||||||
|
}
|
||||||
|
|
||||||
this.initializeFrom = fields;
|
this.initializeFrom = fields;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
@@ -192,10 +195,14 @@ class ClassBuilder {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose) {
|
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose)
|
||||||
var builder = new ClassBuilder();
|
return switch MacroApi.getBuildFields() {
|
||||||
for (p in plugins)
|
case None: null;
|
||||||
p(builder);
|
case Some(fields):
|
||||||
return builder.export(verbose);
|
var builder = new ClassBuilder(fields);
|
||||||
}
|
for (p in plugins)
|
||||||
|
p(builder);
|
||||||
|
return builder.export(verbose);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package tink.macro;
|
|||||||
|
|
||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
import haxe.macro.Type;
|
import haxe.macro.Type;
|
||||||
|
using haxe.macro.Tools;
|
||||||
|
using tink.MacroApi;
|
||||||
|
|
||||||
class Sisyphus {
|
class Sisyphus {
|
||||||
|
|
||||||
@@ -23,8 +25,8 @@ class Sisyphus {
|
|||||||
if (cf.params.length == 0) {
|
if (cf.params.length == 0) {
|
||||||
name: cf.name,
|
name: cf.name,
|
||||||
doc: cf.doc,
|
doc: cf.doc,
|
||||||
access:
|
access:
|
||||||
(cf.isPublic ? [ APublic ] : [ APrivate ])
|
(cf.isPublic ? [ APublic ] : [ APrivate ])
|
||||||
#if haxe4 .concat(if (cf.isFinal) [AFinal] else []) #end
|
#if haxe4 .concat(if (cf.isFinal) [AFinal] else []) #end
|
||||||
,
|
,
|
||||||
kind: switch([ cf.kind, cf.type ]) {
|
kind: switch([ cf.kind, cf.type ]) {
|
||||||
@@ -61,8 +63,7 @@ class Sisyphus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public function toComplexType(type : Null<Type>) : Null<ComplexType> return {
|
||||||
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return {
|
|
||||||
inline function direct()
|
inline function direct()
|
||||||
return Types.toComplex(type, { direct: true });
|
return Types.toComplex(type, { direct: true });
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -84,7 +85,7 @@ class Sisyphus {
|
|||||||
cl.kind.match(KTypeParameter(_))
|
cl.kind.match(KTypeParameter(_))
|
||||||
&& cl.module == classType.module
|
&& cl.module == classType.module
|
||||||
&& cl.pack.join('.') == classType.pack.join('.')
|
&& cl.pack.join('.') == classType.pack.join('.')
|
||||||
): ct;
|
): ct;
|
||||||
default:
|
default:
|
||||||
direct();
|
direct();
|
||||||
}
|
}
|
||||||
@@ -95,8 +96,12 @@ class Sisyphus {
|
|||||||
TPath(toTypePath(baseType, params));
|
TPath(toTypePath(baseType, params));
|
||||||
case TFun(args, ret):
|
case TFun(args, ret):
|
||||||
TFunction(
|
TFunction(
|
||||||
[ for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t) ],
|
[for (a in args) {
|
||||||
toComplexType(ret));
|
var t = #if haxe4 TNamed(a.name, #else ( #end toComplexType(a.t));
|
||||||
|
if (a.opt) TOptional(t) else t;
|
||||||
|
}],
|
||||||
|
toComplexType(ret)
|
||||||
|
);
|
||||||
case TAnonymous(_.get() => { fields: fields }):
|
case TAnonymous(_.get() => { fields: fields }):
|
||||||
TAnonymous([ for (cf in fields) toField(cf) ]);
|
TAnonymous([ for (cf in fields) toField(cf) ]);
|
||||||
case TDynamic(t):
|
case TDynamic(t):
|
||||||
@@ -121,7 +126,7 @@ class Sisyphus {
|
|||||||
case _ == name => true: null;
|
case _ == name => true: null;
|
||||||
case v: v;
|
case v: v;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
pack: baseType.pack,
|
pack: baseType.pack,
|
||||||
name: name,
|
name: name,
|
||||||
@@ -132,4 +137,91 @@ class Sisyphus {
|
|||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function exactBase<T:BaseType>(r:Ref<T>, params:Array<Type>) {
|
||||||
|
var t = r.get();
|
||||||
|
var isMain = !t.isPrivate && switch t.pack {
|
||||||
|
case []: t.module == t.name || t.module == 'StdTypes';
|
||||||
|
default: StringTools.endsWith(t.module, '.${t.name}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
if (isMain) t.pack.concat([t.name]).join('.')
|
||||||
|
else t.module + '.' + t.name
|
||||||
|
) + exactParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline function isFinal(c:ClassField)
|
||||||
|
return #if haxe4 c.isFinal #else false #end;
|
||||||
|
|
||||||
|
static function exactAnonField(c:ClassField) {
|
||||||
|
var kw =
|
||||||
|
switch c.kind {
|
||||||
|
case FMethod(_): 'function';
|
||||||
|
case FVar(_):
|
||||||
|
if (isFinal(c)) 'final' else 'var';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [for (m in c.meta.get()) m.toString() + ' '].join('') + '$kw ${c.name}' + (switch c.kind {
|
||||||
|
case FVar(read, write):
|
||||||
|
(
|
||||||
|
if (isFinal(c) || (read == AccNormal && write == AccNormal)) ''
|
||||||
|
else '(${read.accessToName()}, ${read.accessToName(false)})'
|
||||||
|
) + ':' + c.type.toExactString();
|
||||||
|
case FMethod(_):
|
||||||
|
switch haxe.macro.Context.follow(c.type) {
|
||||||
|
case TFun(arg, ret): exactSig(arg, ret, ':');
|
||||||
|
default: throw 'assert';
|
||||||
|
}
|
||||||
|
}) + ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
static function exactSig(args:Array<{name:String, opt:Bool, t:Type}>, ret:Type, sep:String)
|
||||||
|
return '(${[for (a in args) (if (a.opt) '?' else '') + a.name + ':' + toExactString(a.t)].join(', ')})$sep${toExactString(ret)}';
|
||||||
|
|
||||||
|
static public function exactParams(params:Array<Type>)
|
||||||
|
return switch params {
|
||||||
|
case []: '';
|
||||||
|
case params:
|
||||||
|
'<${params.map(toExactString).join(', ')}>';
|
||||||
|
}
|
||||||
|
static public function toExactString(t:Type)
|
||||||
|
return switch t {
|
||||||
|
case TMono(t): t.toString();
|
||||||
|
case TEnum(r, params): exactBase(r, params);
|
||||||
|
case TInst(r, params): exactBase(r, params);
|
||||||
|
case TType(r, params): exactBase(r, params);
|
||||||
|
case TAbstract(r, params): exactBase(r, params);
|
||||||
|
case TFun(args, ret): exactSig(args, ret, '->');
|
||||||
|
case TAnonymous(a): '{ ${[for (f in a.get().fields) exactAnonField(f)].join(' ')} }';
|
||||||
|
case TDynamic(null): 'Dynamic';
|
||||||
|
case TDynamic(t): 'Dynamic<${toExactString(t)}>';
|
||||||
|
case TLazy(f): toExactString(f());
|
||||||
|
}
|
||||||
|
|
||||||
|
static function eager(t:Type)
|
||||||
|
return switch t {
|
||||||
|
case TLazy(f): eager(f());
|
||||||
|
default: t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
|
||||||
|
if (follow) {
|
||||||
|
t1 = t1.reduce();
|
||||||
|
t2 = t2.reduce();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t1 = eager(t1);
|
||||||
|
t2 = eager(t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch t1.getIndex() - t2.getIndex() {
|
||||||
|
case 0:
|
||||||
|
switch Reflect.compare(t1.toString(), t2.toString()) {
|
||||||
|
case 0: Reflect.compare(t1.toExactString(), t2.toExactString());
|
||||||
|
case v: v;
|
||||||
|
}
|
||||||
|
case v: v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,33 @@ import haxe.macro.Type;
|
|||||||
using tink.MacroApi;
|
using tink.MacroApi;
|
||||||
|
|
||||||
class TypeMap<V> extends BalancedTree<Type, V> implements IMap<Type, V> {
|
class TypeMap<V> extends BalancedTree<Type, V> implements IMap<Type, V> {
|
||||||
var follow:Bool;
|
var normalize:Type->Type;
|
||||||
|
|
||||||
public function new(?noFollow:Bool) {
|
public function new(?normalize:Type->Type) {
|
||||||
this.follow = noFollow != true;
|
this.normalize = switch normalize {
|
||||||
|
case null: function (t) return t.reduce();
|
||||||
|
case fn: fn;
|
||||||
|
}
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
override function compare(k1:Type, k2:Type):Int
|
override function compare(k1:Type, k2:Type):Int
|
||||||
return k1.compare(k2, follow);
|
return normalize(k1).compare(normalize(k2), false);
|
||||||
|
|
||||||
|
static public function noFollow(t:Type)
|
||||||
|
return t;
|
||||||
|
|
||||||
|
static public function keepNull(t:Type):Type
|
||||||
|
return switch t {
|
||||||
|
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t])
|
||||||
|
#if !haxe4 | TType(_.get() => { name: 'Null', pack: []}, [t]) #end
|
||||||
|
:
|
||||||
|
var ct = keepNull(t).toComplex({ direct: true });
|
||||||
|
(macro : Null<$ct>).toType().sure();
|
||||||
|
|
||||||
|
case TLazy(f): keepNull(f());
|
||||||
|
case TType(_, _): keepNull(Context.follow(t, true));
|
||||||
|
default: t;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
55
src/tink/macro/TypedExprs.hx
Normal file
55
src/tink/macro/TypedExprs.hx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package tink.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Type;
|
||||||
|
import haxe.ds.Option;
|
||||||
|
using haxe.macro.Tools;
|
||||||
|
|
||||||
|
class TypedExprs {
|
||||||
|
|
||||||
|
static public function extractAll<T>(t:TypedExpr, f:TypedExpr->Option<T>):Array<T> {
|
||||||
|
var out = [];
|
||||||
|
function rec(t:TypedExpr)
|
||||||
|
if (t != null) {
|
||||||
|
switch f(t) {
|
||||||
|
case Some(v): out.push(v);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
t.iter(rec);
|
||||||
|
}
|
||||||
|
rec(t);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function extract<T>(t:TypedExpr, f:TypedExpr->Option<T>):Option<T> {
|
||||||
|
try extractAll(t, function (t) {
|
||||||
|
var ret = f(t);
|
||||||
|
if (ret != None)
|
||||||
|
throw ret;
|
||||||
|
return ret;
|
||||||
|
})
|
||||||
|
catch (e:Option<Dynamic>) return cast e;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function isThis(t:TypedExpr):Bool
|
||||||
|
return switch t {
|
||||||
|
case null: false;
|
||||||
|
case { expr: TConst(TThis) | TLocal({ name: '`this' })}: true;
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public inline function hasThis(t)
|
||||||
|
return contains(t, isThis);
|
||||||
|
|
||||||
|
static public function findAll(t:TypedExpr, f:TypedExpr->Bool):Array<TypedExpr>
|
||||||
|
return extractAll(t, collect(f));
|
||||||
|
|
||||||
|
static public function find(t:TypedExpr, f:TypedExpr->Bool):Option<TypedExpr>
|
||||||
|
return extract(t, collect(f));
|
||||||
|
|
||||||
|
static public function contains(t:TypedExpr, f:TypedExpr->Bool):Bool
|
||||||
|
return find(t, f) != None;
|
||||||
|
|
||||||
|
static inline function collect(f)
|
||||||
|
return function (t) return if (f(t)) Some(t) else None;
|
||||||
|
}
|
||||||
@@ -10,9 +10,11 @@ using haxe.macro.Tools;
|
|||||||
using tink.MacroApi;
|
using tink.MacroApi;
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
import haxe.macro.Type.Ref;
|
||||||
|
|
||||||
class Types {
|
class Types {
|
||||||
|
|
||||||
static public function definedType(typeName:String)
|
static public function definedType(typeName:String):Option<Type>
|
||||||
return
|
return
|
||||||
try {
|
try {
|
||||||
Some(Context.getType(typeName));
|
Some(Context.getType(typeName));
|
||||||
@@ -172,6 +174,9 @@ class Types {
|
|||||||
#if haxe4
|
#if haxe4
|
||||||
isExtern: field.isExtern,
|
isExtern: field.isExtern,
|
||||||
isFinal: field.isFinal,
|
isFinal: field.isFinal,
|
||||||
|
#if (haxe >= version("4.2.0-rc.1"))
|
||||||
|
isAbstract: field.isAbstract,
|
||||||
|
#end
|
||||||
#end
|
#end
|
||||||
}:ClassField)
|
}:ClassField)
|
||||||
]);
|
]);
|
||||||
@@ -205,7 +210,22 @@ class Types {
|
|||||||
default: Failure('type "$t" has no position');
|
default: Failure('type "$t" has no position');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public function deduceCommonType(types:Array<Type>):Outcome<Type, Error> {
|
||||||
|
var exprs = types.map(function(t) {
|
||||||
|
var ct = t.toComplex();
|
||||||
|
return macro (null:$ct);
|
||||||
|
});
|
||||||
|
|
||||||
|
return switch (macro $a{exprs}).typeof() {
|
||||||
|
case Success(TInst(_, [v])): Success(v);
|
||||||
|
case Success(_): throw 'unreachable';
|
||||||
|
case Failure(e): Failure(new Error('Unable to deduce common type among $types'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// like haxe.macro.TypeTools.toString, but not lossy
|
||||||
|
static public function toExactString(t:Type)
|
||||||
|
return Sisyphus.toExactString(t);
|
||||||
|
|
||||||
static public function toString(t:ComplexType)
|
static public function toString(t:ComplexType)
|
||||||
return new Printer().printComplexType(t);
|
return new Printer().printComplexType(t);
|
||||||
@@ -258,8 +278,7 @@ class Types {
|
|||||||
return if (once) t else reduce(t, false);
|
return if (once) t else reduce(t, false);
|
||||||
return switch type {
|
return switch type {
|
||||||
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
||||||
case TLazy(f): rec(f());
|
case TLazy(_) | TType(_): rec(Context.follow(type, once));
|
||||||
case TType(_, _): rec(Context.follow(type, once));
|
|
||||||
default: type;
|
default: type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,6 +313,9 @@ class Types {
|
|||||||
|
|
||||||
if (types.length == 1) return Success(types[1]);
|
if (types.length == 1) return Success(types[1]);
|
||||||
|
|
||||||
|
#if haxe4
|
||||||
|
return Success(TIntersection(types));
|
||||||
|
#end
|
||||||
var paths = [],
|
var paths = [],
|
||||||
fields = [];
|
fields = [];
|
||||||
|
|
||||||
@@ -335,18 +357,8 @@ class Types {
|
|||||||
throw 'assert';
|
throw 'assert';
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
|
static public function compare(t1:Type, t2:Type, ?follow:Bool = true)
|
||||||
if (follow) {
|
return Sisyphus.compare(t1, t2, follow);
|
||||||
t1 = t1.reduce();
|
|
||||||
t2 = t2.reduce();
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch t1.getIndex() - t2.getIndex() {
|
|
||||||
case 0:
|
|
||||||
Reflect.compare(t1.toString(), t2.toString());//much to my surprise, this actually seems to work (at least with 3.4)
|
|
||||||
case v: v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
|
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
-cp tests
|
-cp tests
|
||||||
|
-lib hx3compat
|
||||||
-main Run
|
-main Run
|
||||||
8
tests/Dummy.hx
Normal file
8
tests/Dummy.hx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Dummy {
|
||||||
|
public function new() {}
|
||||||
|
static public var p(default, never) = new Private();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Private {
|
||||||
|
public function new() {}
|
||||||
|
}
|
||||||
21
tests/ExactStrings.hx
Normal file
21
tests/ExactStrings.hx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import haxe.macro.Context.typeof;
|
||||||
|
using tink.MacroApi;
|
||||||
|
|
||||||
|
class ExactStrings extends Base {
|
||||||
|
function test() {
|
||||||
|
function expect(s:String, e, ?pos)
|
||||||
|
assertEquals(s, typeof(e).toExactString(), pos);
|
||||||
|
|
||||||
|
expect('Dummy', macro new Dummy());
|
||||||
|
expect('nested.Dummy', macro new nested.Dummy());
|
||||||
|
expect('Dummy.Private', macro Dummy.p);
|
||||||
|
expect('nested.Dummy.Private', macro nested.Dummy.p);
|
||||||
|
expect('{ @foo var x:Int; }', macro (null:{@foo var x:Int;}));
|
||||||
|
expect('{ @foo @bar var x:Int; }', macro (null:{@foo @bar var x:Int;}));
|
||||||
|
expect('{ @bar @foo var x:Int; }', macro (null:{@bar @foo var x:Int;}));// not 100% sure this is always the best choice, but let's roll with it
|
||||||
|
expect('{ @bar var x:Int; }', macro (null:{@bar var x:Int;}));
|
||||||
|
expect('{ var x:Int; var y:Int; }', macro (null:{x:Int,y:Int}));
|
||||||
|
expect('{ var x:Int; var y:Int; }', macro (null:{y:Int,x:Int}));
|
||||||
|
expect('{ function foo(x:Int, ?y:Int):Void; }', macro (null:{ function foo(x:Int, ?y:Int):Void; }));
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tests/MyString.hx
Normal file
1
tests/MyString.hx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
typedef MyString = String;
|
||||||
11
tests/Run.hx
11
tests/Run.hx
@@ -1,10 +1,13 @@
|
|||||||
package ;
|
package ;
|
||||||
|
|
||||||
import haxe.unit.*;
|
import haxe.unit.*;
|
||||||
|
#if macro
|
||||||
|
using haxe.macro.Tools;
|
||||||
|
#end
|
||||||
|
|
||||||
class Run {
|
class Run {
|
||||||
#if !macro
|
#if !macro
|
||||||
static function main()
|
static function main()
|
||||||
test();//It compiles ...
|
test();//It compiles ...
|
||||||
#else
|
#else
|
||||||
static var cases:Array<TestCase> = [
|
static var cases:Array<TestCase> = [
|
||||||
@@ -14,17 +17,19 @@ class Run {
|
|||||||
new TypeMapTest(),
|
new TypeMapTest(),
|
||||||
new Functions(),
|
new Functions(),
|
||||||
new Misc(),
|
new Misc(),
|
||||||
|
new ExactStrings(),
|
||||||
];
|
];
|
||||||
#end
|
#end
|
||||||
macro static function test() {
|
macro static function test() {
|
||||||
var runner = new TestRunner();
|
var runner = new TestRunner();
|
||||||
tink.macro.ClassBuilder;
|
tink.macro.ClassBuilder;
|
||||||
|
tink.macro.BuildCache;
|
||||||
for (c in cases)
|
for (c in cases)
|
||||||
runner.add(c);
|
runner.add(c);
|
||||||
runner.run();
|
runner.run();
|
||||||
if (!runner.result.success)
|
if (!runner.result.success)
|
||||||
haxe.macro.Context.error(runner.result.toString(), haxe.macro.Context.currentPos());
|
haxe.macro.Context.error(runner.result.toString(), haxe.macro.Context.currentPos());
|
||||||
|
|
||||||
return macro {
|
return macro {
|
||||||
trace('Let\'s ship it!');
|
trace('Let\'s ship it!');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
package;
|
package;
|
||||||
|
|
||||||
import haxe.unit.TestCase;
|
import haxe.unit.TestCase;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
|
||||||
import tink.macro.TypeMap;
|
using tink.MacroApi;
|
||||||
using haxe.macro.Context;
|
|
||||||
|
|
||||||
class TypeMapTest extends TestCase {
|
class TypeMapTest extends TestCase {
|
||||||
|
|
||||||
function testMap() {
|
function testMap() {
|
||||||
var t = new TypeMap();
|
var t = new TypeMap();
|
||||||
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof();
|
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof().sure();
|
||||||
var t2 = (macro [{ foo: [{ bar: 5 }]}]).typeof();
|
var t2 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
|
||||||
|
var t3 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
|
||||||
|
var t4 = (macro [{ foo: [({ bar: 5 }:{ @foo var bar:Int; })]}]).typeof().sure();
|
||||||
|
|
||||||
t.set(t1, 0);
|
t.set(t1, 0);
|
||||||
assertEquals(Lambda.count(t), 1);
|
assertEquals(Lambda.count(t), 1);
|
||||||
t.set(t2, 1);
|
t.set(t2, 1);
|
||||||
@@ -19,12 +21,33 @@ class TypeMapTest extends TestCase {
|
|||||||
t.set(t1, 2);
|
t.set(t1, 2);
|
||||||
assertEquals(Lambda.count(t), 2);
|
assertEquals(Lambda.count(t), 2);
|
||||||
t.set(t2, 3);
|
t.set(t2, 3);
|
||||||
|
t.set(t3, 3);
|
||||||
assertEquals(Lambda.count(t), 2);
|
assertEquals(Lambda.count(t), 2);
|
||||||
|
t.set(t4, 4);
|
||||||
|
assertEquals(Lambda.count(t), 3);
|
||||||
|
|
||||||
assertEquals(t.get(t1), 2);
|
assertEquals(t.get(t1), 2);
|
||||||
assertEquals(t.get(t2), 3);
|
assertEquals(t.get(t2), 3);
|
||||||
|
|
||||||
assertTrue(true);
|
assertTrue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testNormalization() {
|
||||||
|
for (settings in [
|
||||||
|
{ count: 4, map: new TypeMap(TypeMap.noFollow)},
|
||||||
|
{ count: 2, map: new TypeMap(TypeMap.keepNull)},
|
||||||
|
{ count: 1, map: new TypeMap()},
|
||||||
|
]) {
|
||||||
|
|
||||||
|
function set(ct:ComplexType)
|
||||||
|
settings.map.set(ct.toType().sure(), ct);
|
||||||
|
|
||||||
|
set(macro : String);
|
||||||
|
set(macro : MyString);
|
||||||
|
set(macro : Null<String>);
|
||||||
|
set(macro : Null<MyString>);
|
||||||
|
|
||||||
|
assertEquals(settings.count, Lambda.count(settings.map));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,36 +10,35 @@ using tink.MacroApi;
|
|||||||
class Types extends Base {
|
class Types extends Base {
|
||||||
function type(c:ComplexType)
|
function type(c:ComplexType)
|
||||||
return c.toType().sure();
|
return c.toType().sure();
|
||||||
|
|
||||||
function resolve(type:String)
|
function resolve(type:String)
|
||||||
return Context.getType(type);
|
return Context.getType(type);
|
||||||
|
|
||||||
inline function assertSuccess<S, F>(o:Outcome<S, F>)
|
inline function assertSuccess<S, F>(o:Outcome<S, F>)
|
||||||
assertTrue(o.isSuccess());
|
assertTrue(o.isSuccess());
|
||||||
|
|
||||||
inline function assertFailure<S, F>(o:Outcome<S, F>)
|
inline function assertFailure<S, F>(o:Outcome<S, F>)
|
||||||
assertFalse(o.isSuccess());
|
assertFalse(o.isSuccess());
|
||||||
|
|
||||||
function testIs() {
|
function testIs() {
|
||||||
|
|
||||||
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
|
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
|
||||||
assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
|
assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testFields() {
|
function testFields() {
|
||||||
var expected = type(macro : Void -> Iterator<Arrayish>),
|
var expected = type(macro : Void -> Iterator<Arrayish>),
|
||||||
iterator = type(macro : haxe.ds.StringMap<Arrayish>).getFields(true).sure().filter(function (c) return c.name == 'iterator')[0];
|
iterator = type(macro : haxe.ds.StringMap<Arrayish>).getFields(true).sure().filter(function (c) return c.name == 'iterator')[0];
|
||||||
|
|
||||||
assertSuccess(iterator.type.isSubTypeOf(expected));
|
assertSuccess(iterator.type.isSubTypeOf(expected));
|
||||||
assertSuccess(expected.isSubTypeOf(iterator.type));
|
assertSuccess(expected.isSubTypeOf(iterator.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testConvert() {
|
function testConvert() {
|
||||||
assertSuccess((macro : Int).toType());
|
assertSuccess((macro : Int).toType());
|
||||||
assertFailure((macro : Tni).toType());
|
assertFailure((macro : Tni).toType());
|
||||||
function blank()
|
function blank()
|
||||||
return type(MacroApi.pos().makeBlankType());
|
return type(MacroApi.pos().makeBlankType());
|
||||||
|
|
||||||
var bool = type(macro : Bool);
|
var bool = type(macro : Bool);
|
||||||
assertTrue(blank().isSubTypeOf(bool).isSuccess());
|
assertTrue(blank().isSubTypeOf(bool).isSuccess());
|
||||||
assertTrue(bool.isSubTypeOf(blank()).isSuccess());
|
assertTrue(bool.isSubTypeOf(blank()).isSuccess());
|
||||||
@@ -65,5 +64,20 @@ class Types extends Base {
|
|||||||
assertEquals('String', Context.getType('String').toComplex().toString());
|
assertEquals('String', Context.getType('String').toComplex().toString());
|
||||||
assertEquals('tink.CoreApi.Noise', Context.getType('tink.CoreApi.Noise').toComplex().toString());
|
assertEquals('tink.CoreApi.Noise', Context.getType('tink.CoreApi.Noise').toComplex().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testDeduceCommonType() {
|
||||||
|
function ct2t(ct:ComplexType) return ct.toType().sure();
|
||||||
|
assertEquals('StdTypes.Float', tink.macro.Types.deduceCommonType([(macro:Float), (macro:Int)].map(ct2t)).sure().toComplex().toString());
|
||||||
|
assertEquals('Types.CommonI1', tink.macro.Types.deduceCommonType([(macro:Types.CommonA), (macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
|
||||||
|
assertEquals('Types.CommonI2', tink.macro.Types.deduceCommonType([(macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
|
||||||
|
// assertEquals('Types.CommonI3', tink.macro.Types.deduceCommonType([(macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
interface CommonI1 {}
|
||||||
|
interface CommonI2 {}
|
||||||
|
interface CommonI3 {}
|
||||||
|
class CommonA implements CommonI1 {}
|
||||||
|
class CommonB implements CommonI2 implements CommonI1 {}
|
||||||
|
class CommonC implements CommonI3 implements CommonI2 implements CommonI1 {}
|
||||||
10
tests/nested/Dummy.hx
Normal file
10
tests/nested/Dummy.hx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package nested;
|
||||||
|
|
||||||
|
class Dummy {
|
||||||
|
public function new() {}
|
||||||
|
static public var p(default, never) = new Private();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Private {
|
||||||
|
public function new() {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user