Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
56fe8d7346 | ||
![]() |
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 | ||
![]() |
8890b62b46 | ||
![]() |
cce63becd6 | ||
![]() |
a101378a4c | ||
![]() |
b48b300cbc | ||
![]() |
27c9b20330 | ||
![]() |
b4fa5f4fe0 | ||
![]() |
e59fab499d | ||
![]() |
3356172b7f | ||
![]() |
4fd03ed667 | ||
![]() |
4cb5ee4196 | ||
![]() |
888481094a | ||
![]() |
7686fe3e35 | ||
![]() |
747d07bfc5 | ||
![]() |
00640d8328 | ||
![]() |
77973f6007 | ||
![]() |
0df9c28ace | ||
![]() |
d90fb966d2 |
2
.haxerc
2
.haxerc
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "4.0.0-rc.1",
|
||||
"version": "4.0.5",
|
||||
"resolveLibs": "scoped"
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
|
||||
stages:
|
||||
- test
|
||||
@@ -15,14 +15,14 @@ os:
|
||||
env:
|
||||
- HAXE_VERSION=3.4.7
|
||||
- HAXE_VERSION=edge
|
||||
|
||||
|
||||
install:
|
||||
- npm i -g lix
|
||||
- npm i -g lix@15.5.4
|
||||
- lix install haxe $HAXE_VERSION
|
||||
- lix download
|
||||
|
||||
script:
|
||||
- lix run travix node -lib hx3compat
|
||||
- lix run travix node
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
@@ -1,3 +1,3 @@
|
||||
-D hx3compat=1.0.0
|
||||
# @install: lix --silent download "haxelib:/hx3compat#1.0.0" into hx3compat/1.0.0/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/hx3compat/1.0.0/haxelib/std
|
||||
# @install: lix --silent download "haxelib:/hx3compat#1.0.3" into hx3compat/1.0.3/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/hx3compat/1.0.3/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
|
||||
-D hxnodejs=4.0.9
|
||||
-cp ${HAXESHIM_LIBCACHE}/hxnodejs/4.0.9/haxelib/src
|
||||
-D nodejs
|
||||
# @install: lix --silent download "haxelib:/hxnodejs#12.0.0" into hxnodejs/12.0.0/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/hxnodejs/12.0.0/haxelib/src
|
||||
-D hxnodejs=12.0.0
|
||||
--macro allowPackage('sys')
|
||||
--macro _hxnodejs.VersionWarning.include()
|
||||
# should behave like other target defines and not be defined in macro context
|
||||
--macro define('nodejs')
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# @install: lix --silent download "haxelib:tink_core#1.16.1" into tink_core/1.16.1/haxelib
|
||||
-D tink_core=1.16.1
|
||||
-cp ${HAXESHIM_LIBCACHE}/tink_core/1.16.1/haxelib/src
|
||||
-D tink_core=1.24.0
|
||||
# @install: lix --silent download "haxelib:/tink_core#1.24.0" into tink_core/1.24.0/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/tink_core/1.24.0/haxelib/src
|
||||
|
24
haxelib.json
24
haxelib.json
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"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.23.0",
|
||||
"releasenote": "Make TypeMap work exacly with anons.",
|
||||
"tags": [
|
||||
"tink",
|
||||
"macro",
|
||||
"utility"
|
||||
],
|
||||
"classPath": "src",
|
||||
"description": "The macro toolkit ;)",
|
||||
"contributors": [
|
||||
"back2dos"
|
||||
],
|
||||
"releasenote": "Various fixes",
|
||||
"version": "0.17.5",
|
||||
"url": "http://haxetink.org/tink_macro",
|
||||
"dependencies": {
|
||||
"tink_core": ""
|
||||
}
|
||||
"license": "MIT"
|
||||
}
|
@@ -8,6 +8,8 @@ using StringTools;
|
||||
|
||||
typedef Positions = tink.macro.Positions;
|
||||
typedef ExprTools = haxe.macro.ExprTools;
|
||||
typedef TypedExprTools = haxe.macro.TypedExprTools;
|
||||
typedef TypedExprs = tink.macro.TypedExprs;
|
||||
typedef Exprs = tink.macro.Exprs;
|
||||
typedef Functions = tink.macro.Functions;
|
||||
typedef Metadatas = tink.macro.Metadatas;
|
||||
@@ -28,20 +30,40 @@ typedef ClassBuilder = tink.macro.ClassBuilder;
|
||||
typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
|
||||
|
||||
class MacroApi {
|
||||
|
||||
static var idCounter = 0;
|
||||
|
||||
|
||||
static var MAIN_CANDIDATES = ['-main', '-x', '--run'];
|
||||
static public function getMainClass():Option<String> {
|
||||
var args = Sys.args();
|
||||
|
||||
for (c in MAIN_CANDIDATES)
|
||||
switch args.indexOf(c) {
|
||||
case -1:
|
||||
case v: return Some(args[v+1]);
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
@:persistent static var idCounter = 0;
|
||||
|
||||
@:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
|
||||
return '__tink_' + prefix + Std.string(idCounter++);
|
||||
|
||||
static public function pos()
|
||||
|
||||
static public function pos()
|
||||
return haxe.macro.Context.currentPos();
|
||||
|
||||
static public var completionPoint(default, null):Option<{
|
||||
var file(default, never):String;
|
||||
var content(default, never):Null<String>;
|
||||
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 var initialized = initArgs();
|
||||
|
||||
@@ -50,7 +72,7 @@ class MacroApi {
|
||||
args = sysArgs;
|
||||
completionPoint = switch sysArgs.indexOf('--display') {
|
||||
case -1: None;
|
||||
case sysArgs[_ + 1] => arg:
|
||||
case sysArgs[_ + 1] => arg:
|
||||
if (arg.startsWith('{"jsonrpc":')) {
|
||||
var payload:{
|
||||
jsonrpc:String,
|
||||
@@ -58,12 +80,15 @@ class MacroApi {
|
||||
params:{
|
||||
file:String,
|
||||
offset:Int,
|
||||
contents:String,
|
||||
}
|
||||
} = haxe.Json.parse(arg);
|
||||
|
||||
switch payload {
|
||||
case { jsonrpc: '2.0', method: 'display/completion' }:
|
||||
Some({
|
||||
file: payload.params.file,
|
||||
content: payload.params.contents,
|
||||
pos: payload.params.offset,
|
||||
});
|
||||
default: None;
|
||||
@@ -95,26 +120,26 @@ class MacroApi {
|
||||
@:forward
|
||||
abstract ObjectField(F) to F {
|
||||
|
||||
static var QUOTED = "@$__hx__";
|
||||
static var QUOTED = "@$__hx__";
|
||||
|
||||
inline function new(o) this = o;
|
||||
|
||||
public var field(get, never):String;
|
||||
|
||||
|
||||
function get_field()
|
||||
return
|
||||
if (quotes == Quoted)
|
||||
return
|
||||
if (quotes == Quoted)
|
||||
this.field.substr(QUOTED.length);
|
||||
else this.field;
|
||||
|
||||
public var quotes(get, never):QuoteStatus;
|
||||
|
||||
|
||||
function get_quotes()
|
||||
return if (StringTools.startsWith(this.field, QUOTED)) Quoted else Unquoted;
|
||||
|
||||
@:from static function ofFull(o:{>F, quotes:QuoteStatus }):ObjectField
|
||||
return switch o.quotes {
|
||||
case null | Unquoted:
|
||||
case null | Unquoted:
|
||||
new ObjectField({ field: o.field, expr: o.expr });
|
||||
default:
|
||||
new ObjectField({ field: QUOTED + o.field, expr: o.expr });
|
||||
|
@@ -31,23 +31,23 @@ typedef BuildContext3 = {>BuildContext2,
|
||||
type3:Type,
|
||||
}
|
||||
|
||||
class BuildCache {
|
||||
|
||||
static var cache = new Map();
|
||||
|
||||
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition) {
|
||||
class BuildCache {
|
||||
|
||||
@:persistent static var cache = new Map();
|
||||
|
||||
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition, ?normalizer:Type->Type) {
|
||||
if (types == null)
|
||||
switch Context.getLocalType() {
|
||||
case TInst(_.toString() == name => true, [t1, t2, t3]):
|
||||
types = { t1: t1, t2: t2, t3: t3 };
|
||||
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,
|
||||
@@ -55,62 +55,62 @@ class BuildCache {
|
||||
pos: ctx.pos,
|
||||
name: ctx.name,
|
||||
usings: ctx.usings
|
||||
}));
|
||||
}), normalizer);
|
||||
}
|
||||
|
||||
static public function getTypeN(name, ?types, ?pos:Position, build:BuildContextN->TypeDefinition) {
|
||||
|
||||
|
||||
static public function getTypeN(name, ?types, ?pos:Position, build:BuildContextN->TypeDefinition, ?normalizer:Type->Type) {
|
||||
|
||||
if (pos == null)
|
||||
pos = Context.currentPos();
|
||||
|
||||
|
||||
if (types == null)
|
||||
switch Context.getLocalType() {
|
||||
case TInst(_.toString() == name => true, params):
|
||||
types = params;
|
||||
default:
|
||||
throw 'assert';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
|
||||
name: 't$i',
|
||||
pos: pos,
|
||||
kind: FVar(switch types[i] {
|
||||
case TInst(_.get().kind => KExpr(e), _):
|
||||
case TInst(_.get().kind => KExpr(e), _):
|
||||
TPath('tink.macro.ConstParam'.asTypePath([TPExpr(e)]));
|
||||
case t: t.toComplex();
|
||||
}),
|
||||
}]).toType();
|
||||
|
||||
|
||||
return getType(name, compound, pos, function (ctx) return build({
|
||||
types: types,
|
||||
pos: ctx.pos,
|
||||
name: ctx.name,
|
||||
usings: ctx.usings
|
||||
}));
|
||||
}
|
||||
|
||||
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition) {
|
||||
}), normalizer);
|
||||
}
|
||||
|
||||
static public function getType2(name, ?types, ?pos:Position, build:BuildContext2->TypeDefinition, ?normalizer:Type->Type) {
|
||||
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
|
||||
}));
|
||||
}), normalizer);
|
||||
}
|
||||
|
||||
static public function getParams(name:String, ?pos:Position)
|
||||
static public function getParams(name:String, ?pos:Position)
|
||||
return
|
||||
switch Context.getLocalType() {
|
||||
case TInst(_.toString() == name => true, v):
|
||||
@@ -119,9 +119,9 @@ class BuildCache {
|
||||
pos.makeFailure('Expected $name');
|
||||
case v:
|
||||
pos.makeFailure('$v should be a class');
|
||||
}
|
||||
}
|
||||
|
||||
static public function getParam(name:String, ?pos:Position)
|
||||
static public function getParam(name:String, ?pos:Position)
|
||||
return
|
||||
getParams(name, pos)
|
||||
.flatMap(function (args:Array<Type>) return switch args {
|
||||
@@ -130,18 +130,18 @@ class BuildCache {
|
||||
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) {
|
||||
|
||||
if (type == null)
|
||||
type = getParam(name, pos).sure();
|
||||
|
||||
var forName =
|
||||
|
||||
var forName =
|
||||
switch cache[name] {
|
||||
case null: cache[name] = new Group(name);
|
||||
case null: cache[name] = new Group(name, normalizer);
|
||||
case v: v;
|
||||
}
|
||||
|
||||
var ret = forName.get(type, pos.sanitize(), build);
|
||||
|
||||
var ret = forName.get(type, pos.sanitize(), build);
|
||||
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
|
||||
return ret;
|
||||
}
|
||||
@@ -152,23 +152,24 @@ private typedef Entry = {
|
||||
}
|
||||
|
||||
private class Group {
|
||||
|
||||
|
||||
var name:String;
|
||||
var counter = 0;
|
||||
var entries = new TypeMap<Entry>();
|
||||
|
||||
public function new(name) {
|
||||
var entries:TypeMap<Entry>;
|
||||
|
||||
public function new(name, ?normalizer) {
|
||||
this.name = name;
|
||||
this.entries = new TypeMap(normalizer);
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
pos: pos,
|
||||
type: type,
|
||||
usings: usings,
|
||||
name: path.split('.').pop()
|
||||
});
|
||||
|
||||
@@ -177,15 +178,15 @@ private class Group {
|
||||
return Context.getType(path);
|
||||
}
|
||||
|
||||
function doMake()
|
||||
while (true)
|
||||
function doMake()
|
||||
while (true)
|
||||
switch '$name${counter++}' {
|
||||
case _.definedType() => Some(_):
|
||||
case v:
|
||||
return make(v);
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
switch entries.get(type) {
|
||||
case null:
|
||||
doMake();
|
||||
|
@@ -23,7 +23,10 @@ class ClassBuilder {
|
||||
target = Context.getLocalClass().get();
|
||||
|
||||
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.target = target;
|
||||
@@ -192,10 +195,14 @@ class ClassBuilder {
|
||||
return m;
|
||||
}
|
||||
|
||||
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose) {
|
||||
var builder = new ClassBuilder();
|
||||
for (p in plugins)
|
||||
p(builder);
|
||||
return builder.export(verbose);
|
||||
}
|
||||
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose)
|
||||
return switch MacroApi.getBuildFields() {
|
||||
case None: null;
|
||||
case Some(fields):
|
||||
var builder = new ClassBuilder(fields);
|
||||
for (p in plugins)
|
||||
p(builder);
|
||||
return builder.export(verbose);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -25,20 +25,20 @@ class Constructor {
|
||||
var owner:ClassBuilder;
|
||||
var meta:Metadata;
|
||||
public var isPublic:Null<Bool>;
|
||||
|
||||
|
||||
public function new(owner:ClassBuilder, f:Function, ?isPublic:Null<Bool> = null, ?pos:Position, ?meta:Metadata) {
|
||||
this.nuStatements = [];
|
||||
this.owner = owner;
|
||||
this.isPublic = isPublic;
|
||||
this.pos = pos.sanitize();
|
||||
|
||||
|
||||
this.onGenerateHooks = [];
|
||||
this.args = [];
|
||||
this.beforeArgs = [];
|
||||
this.afterArgs = [];
|
||||
this.meta = meta;
|
||||
|
||||
this.oldStatements =
|
||||
|
||||
this.oldStatements =
|
||||
if (f == null) [];
|
||||
else {
|
||||
for (i in 0...f.args.length) {
|
||||
@@ -49,15 +49,15 @@ class Constructor {
|
||||
}
|
||||
beforeArgs.push(a);
|
||||
}
|
||||
|
||||
|
||||
if (f.expr == null) [];
|
||||
else
|
||||
switch (f.expr.expr) {
|
||||
case EBlock(exprs): exprs;
|
||||
default: oldStatements = [f.expr];
|
||||
default: oldStatements = [f.expr];
|
||||
}
|
||||
}
|
||||
superCall =
|
||||
superCall =
|
||||
if (oldStatements.length == 0) [].toBlock();
|
||||
else switch oldStatements[0] {
|
||||
case macro super($a{_}): oldStatements.shift();
|
||||
@@ -68,43 +68,66 @@ class Constructor {
|
||||
public function getArgList():Array<FunctionArg>
|
||||
return beforeArgs.concat(args).concat(afterArgs);
|
||||
|
||||
public function addStatement(e:Expr, ?prepend)
|
||||
public function addStatement(e:Expr, ?prepend)
|
||||
if (prepend)
|
||||
this.nuStatements.unshift(e)
|
||||
else
|
||||
this.nuStatements.push(e);
|
||||
|
||||
public function addArg(name:String, ?t:ComplexType, ?e:Expr, ?opt = false)
|
||||
|
||||
public function addArg(name:String, ?t:ComplexType, ?e:Expr, ?opt = false)
|
||||
args.push( { name : name, opt : opt || e != null, type : t, value: e } );
|
||||
|
||||
|
||||
public function init(name:String, pos:Position, with:FieldInit, ?options:{ ?prepend:Bool, ?bypass:Bool }) {
|
||||
if (options == null)
|
||||
if (options == null)
|
||||
options = {};
|
||||
var e =
|
||||
switch with {
|
||||
case Arg(t, noPublish):
|
||||
if (noPublish != true)
|
||||
if (noPublish != true)
|
||||
publish();
|
||||
args.push( { name : name, opt : false, type : t } );
|
||||
name.resolve(pos);
|
||||
case OptArg(e, t, noPublish):
|
||||
if (noPublish != true)
|
||||
if (noPublish != true)
|
||||
publish();
|
||||
args.push( { name : name, opt : true, type : t, value: e } );
|
||||
name.resolve(pos);
|
||||
case Value(e): e;
|
||||
}
|
||||
|
||||
var tmp = MacroApi.tempName();
|
||||
var member = owner.memberByName(name).sure();
|
||||
|
||||
if (options.bypass && member.kind.match(FProp(_, 'never' | 'set', _, _))) {
|
||||
|
||||
|
||||
var member = owner.memberByName(name).sure(),
|
||||
initStatement = macro @:pos(pos) this.$name = $e;
|
||||
|
||||
var bypass = switch options.bypass {
|
||||
case null:
|
||||
switch member.kind {
|
||||
case FProp(_, 'default' | 'null', _):
|
||||
false;
|
||||
#if haxe4
|
||||
case FProp('default', 'never', _):
|
||||
member.isFinal = true;
|
||||
false;
|
||||
case FProp(_, 'set', _):
|
||||
member.addMeta(':isVar');
|
||||
initStatement = macro @:pos(pos) @:bypassAccessor this.$name = $e;
|
||||
false;
|
||||
#end
|
||||
case FProp(_):
|
||||
true;
|
||||
case FFun(_):
|
||||
pos.error('cannot rebind function');
|
||||
default: false;
|
||||
}
|
||||
case v: v;
|
||||
}
|
||||
|
||||
if (bypass && member.kind.match(FProp(_, 'never' | 'set', _, _))) {
|
||||
|
||||
member.addMeta(':isVar');
|
||||
|
||||
addStatement((function () {
|
||||
var fields = [for (f in (macro this).typeof().sure().getClass().fields.get()) f.name => f];
|
||||
|
||||
|
||||
function setDirectly(t:TypedExpr) {
|
||||
var direct = null;
|
||||
function seek(t:TypedExpr) {
|
||||
@@ -112,15 +135,15 @@ class Constructor {
|
||||
case TField({ expr: TConst(TThis) }, FInstance(_, _, f)) if (f.get().name == name): direct = t;
|
||||
default: t.iter(seek);
|
||||
}
|
||||
}
|
||||
}
|
||||
seek(t);
|
||||
if (direct == null) pos.error('nope');
|
||||
var direct = Context.storeTypedExpr(direct);
|
||||
return macro @:pos(pos) $direct = $e;
|
||||
}
|
||||
|
||||
|
||||
return switch fields[name] {
|
||||
case null:
|
||||
case null:
|
||||
pos.error('this direct initialization causes the compiler to do really weird things');
|
||||
case f:
|
||||
switch f.kind {
|
||||
@@ -130,32 +153,41 @@ class Constructor {
|
||||
macro @:pos(pos) this.$name = $e;
|
||||
case FVar(AccNo | AccNormal, AccNever):
|
||||
setDirectly(Context.typeExpr(macro @:pos(pos) this.$name));
|
||||
#if haxe4
|
||||
case FVar(AccCall, _):
|
||||
var target = Context.storeTypedExpr(Context.typeExpr(macro @:pos(pos) @:bypassAccessor this.$name));
|
||||
macro @:pos(pos) $target = $e;
|
||||
#else
|
||||
case FVar(AccCall, AccNever):
|
||||
setDirectly(fields['get_$name'].expr());
|
||||
#end
|
||||
case FVar(_, AccCall):
|
||||
setDirectly(fields['set_$name'].expr());
|
||||
default:
|
||||
pos.error('not implemented');
|
||||
}
|
||||
}
|
||||
}).bounce(), options.prepend);
|
||||
}).bounce(), options.prepend);
|
||||
}
|
||||
else
|
||||
addStatement(macro @:pos(pos) this.$name = $e, options.prepend);
|
||||
else
|
||||
addStatement(
|
||||
initStatement,
|
||||
options.prepend
|
||||
);
|
||||
}
|
||||
public inline function publish()
|
||||
if (isPublic == null)
|
||||
public inline function publish()
|
||||
if (isPublic == null)
|
||||
isPublic = true;
|
||||
|
||||
function toBlock()
|
||||
|
||||
function toBlock()
|
||||
return [superCall]
|
||||
.concat(nuStatements)
|
||||
.concat(oldStatements)
|
||||
.toBlock(pos);
|
||||
|
||||
public function onGenerate(hook)
|
||||
|
||||
public function onGenerate(hook)
|
||||
this.onGenerateHooks.push(hook);
|
||||
|
||||
|
||||
public function toHaxe():Field {
|
||||
var f:Function = {
|
||||
args: this.beforeArgs.concat(this.args).concat(this.afterArgs),
|
||||
|
@@ -1,12 +1,11 @@
|
||||
package tink.macro;
|
||||
|
||||
import Type in Inspect;
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
import haxe.PosInfos;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.DynamicAccess as Dict;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
@@ -20,6 +19,7 @@ typedef VarDecl = { name : String, type : ComplexType, expr : Null<Expr> };
|
||||
typedef ParamSubst = {
|
||||
var exists(default, null):String->Bool;
|
||||
var get(default, null):String->ComplexType;
|
||||
function iterator():Iterator<ComplexType>;
|
||||
}
|
||||
|
||||
private class Heureka { public function new() {} }
|
||||
@@ -34,7 +34,7 @@ class Exprs {
|
||||
case _ if (condition(e)): throw new Heureka();
|
||||
default: haxe.macro.ExprTools.iter(e, seek);
|
||||
}
|
||||
|
||||
|
||||
return try {
|
||||
haxe.macro.ExprTools.iter(e, seek);
|
||||
false;
|
||||
@@ -48,22 +48,23 @@ class Exprs {
|
||||
static public inline function as(e:Expr, c:ComplexType)
|
||||
return ECheckType(e, c).at(e.pos);
|
||||
|
||||
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dynamic<String>, ?skipFields = false, ?callPos:PosInfos) {
|
||||
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dict<String>, ?skipFields = false, ?callPos:PosInfos) {
|
||||
if (nuPos == null)
|
||||
nuPos = Context.currentPos();
|
||||
if (rules == null)
|
||||
rules = { };
|
||||
function replace(s:String)
|
||||
return {
|
||||
if (Reflect.hasField(rules, s))
|
||||
Reflect.field(rules, s)
|
||||
else if (s.startsWith('tmp')) {
|
||||
Reflect.setField(rules, s, MacroApi.tempName(s.substr(3)));
|
||||
replace(s);
|
||||
}
|
||||
else s;
|
||||
|
||||
function replace(s:String):String
|
||||
return switch rules[s] {
|
||||
case null:
|
||||
if (s.startsWith('tmp')) {
|
||||
rules[s] = MacroApi.tempName(s.substr(3));
|
||||
replace(s);
|
||||
}
|
||||
else s;
|
||||
case v: v;
|
||||
}
|
||||
|
||||
|
||||
return e.transform(function (e:Expr) {
|
||||
return
|
||||
if (Context.getPosInfos(e.pos).file != callPos.fileName) e;
|
||||
@@ -71,7 +72,7 @@ class Exprs {
|
||||
e.pos = nuPos;
|
||||
switch (e.expr) {
|
||||
case EVars(vars):
|
||||
for (v in vars)
|
||||
for (v in vars)
|
||||
v.name = replace(v.name);
|
||||
e;
|
||||
case EField(owner, field):
|
||||
@@ -86,7 +87,7 @@ class Exprs {
|
||||
for (f in fields)
|
||||
f.field = replace(f.field);
|
||||
e;
|
||||
default:
|
||||
default:
|
||||
switch (e.getIdent()) {
|
||||
case Success(s): replace(s).resolve(e.pos);
|
||||
default: e;
|
||||
@@ -95,10 +96,10 @@ class Exprs {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
static public function withPrivateAccess(e:Expr)
|
||||
return
|
||||
e.transform(function (e:Expr)
|
||||
return
|
||||
e.transform(function (e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EField(owner, field):
|
||||
@@ -106,72 +107,95 @@ class Exprs {
|
||||
default: e;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
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<Expr>, ?pos)
|
||||
return
|
||||
static public function substitute(source:Expr, vars:Dict<Expr>, ?pos)
|
||||
return
|
||||
transform(source, function (e:Expr) {
|
||||
return
|
||||
switch (e.getIdent()) {
|
||||
case Success(name):
|
||||
if (Reflect.hasField(vars, name))
|
||||
Reflect.field(vars, name);
|
||||
else
|
||||
e;
|
||||
default: e;
|
||||
}
|
||||
return switch e {
|
||||
case macro $i{name}:
|
||||
switch vars[name] {
|
||||
case null: e;
|
||||
case v: v;
|
||||
}
|
||||
default: e;
|
||||
}
|
||||
}, pos);
|
||||
|
||||
static public inline function ifNull(e:Expr, fallback:Expr)
|
||||
|
||||
static public inline function ifNull(e:Expr, fallback:Expr)
|
||||
return
|
||||
switch e {
|
||||
case macro null: fallback;
|
||||
default: e;
|
||||
}
|
||||
|
||||
static public function substParams(source:Expr, subst:ParamSubst, ?pos):Expr
|
||||
return crawl(
|
||||
source,
|
||||
function (e)
|
||||
return switch e.expr {
|
||||
case ENew({ pack: [], name: name }, args) if (subst.exists(name)):
|
||||
switch subst.get(name) {
|
||||
case TPath(p):
|
||||
ENew(p, args).at(e.pos);
|
||||
default: e;//TODO: report an error?
|
||||
}
|
||||
case EConst(CIdent(name)) if (subst.exists(name)):
|
||||
|
||||
static public function substParams(source:Expr, subst:ParamSubst, ?pos:Position):Expr {
|
||||
if (!subst.iterator().hasNext())
|
||||
return source;
|
||||
|
||||
function replace(ct:ComplexType)
|
||||
return switch ct {
|
||||
case TPath({ pack: [], name: name }) if (subst.exists(name)):
|
||||
subst.get(name);
|
||||
default: ct;
|
||||
}
|
||||
|
||||
return source
|
||||
.transform(
|
||||
function (e) return switch e {
|
||||
case macro $i{name} if (subst.exists(name)):
|
||||
switch subst.get(name) {
|
||||
case TPath({ pack: pack, name: name }):
|
||||
pack.concat([name]).drill(e.pos);
|
||||
default: e;//TODO: report an error?
|
||||
}
|
||||
default: e;
|
||||
},
|
||||
function (c:ComplexType)
|
||||
return
|
||||
switch (c) {
|
||||
case TPath({ pack: [], name: name }) if (subst.exists(name)):
|
||||
subst.get(name);
|
||||
default: c;
|
||||
})
|
||||
.mapTypes(replace);
|
||||
}
|
||||
|
||||
static public function mapTypes(e:Expr, transformer:ComplexType->ComplexType, ?pos:Position) {
|
||||
return e.transform(
|
||||
function (e) return switch e {
|
||||
case { expr: ENew(p, args) }:
|
||||
switch TPath(p).map(transformer) {
|
||||
case TPath(nu):
|
||||
ENew(nu, args).at(e.pos);
|
||||
case v:
|
||||
pos.error(v.toString() + ' cannot be instantiated in ${e.pos}');
|
||||
}
|
||||
, pos);
|
||||
|
||||
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<Dynamic>, transformer:Expr->Expr, retyper:ComplexType-> ComplexType, pos:Position):Array<Dynamic>
|
||||
case macro cast($v, $ct):
|
||||
ct = ct.map(transformer);
|
||||
(macro @:pos(e.pos) cast($v, $ct));
|
||||
case { expr: ECheckType(v, ct) }: // cannot use reification, because that's `EParentheses(ECheckType)` and macros can (and apparently do) generate a non-wrapped one
|
||||
ECheckType(v, ct.map(transformer)).at(e.pos);
|
||||
case { expr: EVars(vars) }:
|
||||
EVars([for (v in vars) {
|
||||
name: v.name,
|
||||
type: v.type.map(transformer),
|
||||
expr: v.expr,
|
||||
}]).at(e.pos);
|
||||
case { expr: EFunction(kind, f) }:
|
||||
EFunction(kind, Functions.mapSignature(f, transformer)).at(e.pos);
|
||||
default: e;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static public function transform(source:Expr, transformer:Expr->Expr, ?pos):Expr {
|
||||
function apply(e:Expr)
|
||||
return switch e {
|
||||
case null | { expr: null }: e;
|
||||
default: transformer(haxe.macro.ExprTools.map(e, apply));
|
||||
}
|
||||
|
||||
return apply(source);
|
||||
}
|
||||
|
||||
static public function getIterType(target:Expr)
|
||||
return
|
||||
if (a == null) a;
|
||||
else
|
||||
[for (v in a)
|
||||
crawl(v, transformer, retyper, pos)
|
||||
];
|
||||
|
||||
static public function getIterType(target:Expr)
|
||||
return
|
||||
(macro @:pos(target.pos) {
|
||||
var t = null,
|
||||
target = $target;
|
||||
@@ -179,21 +203,21 @@ class Exprs {
|
||||
t = i;
|
||||
t;
|
||||
}).typeof();
|
||||
|
||||
|
||||
static public function yield(e:Expr, yielder:Expr->Expr, ?options: { ?leaveLoops: Bool }):Expr {
|
||||
inline function rec(e)
|
||||
inline function rec(e)
|
||||
return yield(e, yielder, options);
|
||||
|
||||
|
||||
if (options == null)
|
||||
options = { };
|
||||
|
||||
|
||||
var loops = options.leaveLoops != true;
|
||||
return
|
||||
if (e == null || e.expr == null) e;
|
||||
else switch (e.expr) {
|
||||
case EVars(_):
|
||||
e.pos.error('Variable declaration not supported here');
|
||||
case EBlock(exprs) if (exprs.length > 0):
|
||||
case EBlock(exprs) if (exprs.length > 0):
|
||||
exprs = exprs.copy();
|
||||
exprs.push(rec(exprs.pop()));
|
||||
EBlock(exprs).at(e.pos);
|
||||
@@ -201,10 +225,11 @@ class Exprs {
|
||||
,ETernary(econd, eif, eelse):
|
||||
EIf(econd, rec(eif), rec(eelse)).at(e.pos);
|
||||
case ESwitch(e, cases, edef):
|
||||
cases = Reflect.copy(cases);//not exactly pretty, but does the job
|
||||
for (c in cases)
|
||||
c.expr = rec(c.expr);
|
||||
ESwitch(e, cases, rec(edef)).at(e.pos);
|
||||
ESwitch(e, [for (c in cases) {
|
||||
expr: rec(c.expr),
|
||||
guard: c.guard,
|
||||
values: c.values,
|
||||
}], rec(edef)).at(e.pos);
|
||||
case EFor(it, expr) if (loops):
|
||||
EFor(it, rec(expr)).at(e.pos);
|
||||
case EWhile(cond, body, normal) if (loops):
|
||||
@@ -218,101 +243,77 @@ class Exprs {
|
||||
default: yielder(e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
else
|
||||
switch (Inspect.typeof(target)) {
|
||||
case TNull, TInt, TFloat, TBool, TFunction, TUnknown, TClass(_): target;
|
||||
case TEnum(e):
|
||||
var ret:Dynamic = Inspect.createEnumIndex(e, Inspect.enumIndex(target), crawlArray(Inspect.enumParameters(target), transformer, retyper, pos));
|
||||
if (Inspect.getEnum(ret) == ComplexType)
|
||||
retyper(ret);
|
||||
else
|
||||
ret;
|
||||
case TObject:
|
||||
var ret:Dynamic = { };
|
||||
for (field in Reflect.fields(target))
|
||||
Reflect.setField(ret, field, crawl(Reflect.field(target, field), transformer, retyper, pos));
|
||||
if (Std.is(ret.expr, ExprDef)) {
|
||||
ret = transformer(ret);
|
||||
if (pos != null) ret.pos = pos;
|
||||
}
|
||||
ret;
|
||||
}
|
||||
|
||||
static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position)
|
||||
|
||||
static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position)
|
||||
return macro @:pos(pos.sanitize()) for ($i{loopVar} in $target) $body;
|
||||
|
||||
static public function toFields(object:Dynamic<Expr>, ?pos:Position)
|
||||
return EObjectDecl([for (field in Reflect.fields(object))
|
||||
{ field:field, expr: untyped Reflect.field(object, field) }
|
||||
|
||||
static public function toFields(object:Dict<Expr>, ?pos:Position)
|
||||
return EObjectDecl([for (field in object.keys())
|
||||
{ field:field, expr: object[field] }
|
||||
]).at(pos);
|
||||
|
||||
static public inline function log(e:Expr, ?pos:PosInfos):Expr {
|
||||
haxe.Log.trace(e.toString(), pos);
|
||||
return e;
|
||||
}
|
||||
|
||||
static public inline function reject(e:Expr, ?reason:String = 'cannot handle expression'):Dynamic
|
||||
|
||||
static public inline function reject(e:Expr, ?reason:String = 'cannot handle expression'):Dynamic
|
||||
return e.pos.error(reason);
|
||||
|
||||
static public inline function toString(e:Expr):String
|
||||
|
||||
static public inline function toString(e:Expr):String
|
||||
return new haxe.macro.Printer().printExpr(e);
|
||||
|
||||
static public inline function at(e:ExprDef, ?pos:Position)
|
||||
|
||||
static public inline function at(e:ExprDef, ?pos:Position)
|
||||
return {
|
||||
expr: e,
|
||||
pos: pos.sanitize()
|
||||
};
|
||||
|
||||
static public inline function instantiate(s:String, ?args:Array<Expr>, ?params:Array<TypeParam>, ?pos:Position)
|
||||
|
||||
static public inline function instantiate(s:String, ?args:Array<Expr>, ?params:Array<TypeParam>, ?pos:Position)
|
||||
return s.asTypePath(params).instantiate(args, pos);
|
||||
|
||||
static public inline function assign(target:Expr, value:Expr, ?op:Binop, ?pos:Position)
|
||||
|
||||
static public inline function assign(target:Expr, value:Expr, ?op:Binop, ?pos:Position)
|
||||
return binOp(target, value, op == null ? OpAssign : OpAssignOp(op), pos);
|
||||
|
||||
static public inline function define(name:String, ?init:Expr, ?typ:ComplexType, ?pos:Position)
|
||||
|
||||
static public inline function define(name:String, ?init:Expr, ?typ:ComplexType, ?pos:Position)
|
||||
return at(EVars([ { name:name, type: typ, expr: init } ]), pos);
|
||||
|
||||
static public inline function add(e1:Expr, e2, ?pos)
|
||||
|
||||
static public inline function add(e1:Expr, e2, ?pos)
|
||||
return binOp(e1, e2, OpAdd, pos);
|
||||
|
||||
static public inline function unOp(e:Expr, op, ?postFix = false, ?pos)
|
||||
|
||||
static public inline function unOp(e:Expr, op, ?postFix = false, ?pos)
|
||||
return EUnop(op, postFix, e).at(pos);
|
||||
|
||||
static public inline function binOp(e1:Expr, e2, op, ?pos)
|
||||
|
||||
static public inline function binOp(e1:Expr, e2, op, ?pos)
|
||||
return EBinop(op, e1, e2).at(pos);
|
||||
|
||||
static public inline function field(e:Expr, field, ?pos)
|
||||
|
||||
static public inline function field(e:Expr, field, ?pos)
|
||||
return EField(e, field).at(pos);
|
||||
|
||||
static public inline function call(e:Expr, ?params, ?pos)
|
||||
|
||||
static public inline function call(e:Expr, ?params, ?pos)
|
||||
return ECall(e, params == null ? [] : params).at(pos);
|
||||
|
||||
static public inline function toExpr(v:Dynamic, ?pos:Position)
|
||||
|
||||
static public inline function toExpr(v:Dynamic, ?pos:Position)
|
||||
return Context.makeExpr(v, pos.sanitize());
|
||||
|
||||
static public inline function toArray(exprs:Iterable<Expr>, ?pos)
|
||||
|
||||
static public inline function toArray(exprs:Iterable<Expr>, ?pos)
|
||||
return EArrayDecl(exprs.array()).at(pos);
|
||||
|
||||
static public inline function toMBlock(exprs:Array<Expr>, ?pos)
|
||||
|
||||
static public inline function toMBlock(exprs:Array<Expr>, ?pos)
|
||||
return EBlock(exprs).at(pos);
|
||||
|
||||
static public inline function toBlock(exprs:Iterable<Expr>, ?pos)
|
||||
return toMBlock(Lambda.array(exprs), pos);
|
||||
|
||||
|
||||
static public inline function toBlock(exprs:Iterable<Expr>, ?pos)
|
||||
return toMBlock(Lambda.array(exprs), pos);
|
||||
|
||||
static public function drill(parts:Array<String>, ?pos:Position, ?target:Expr) {
|
||||
if (target == null)
|
||||
if (target == null)
|
||||
target = at(EConst(CIdent(parts.shift())), pos);
|
||||
for (part in parts)
|
||||
target = field(target, part, pos);
|
||||
return target;
|
||||
return target;
|
||||
}
|
||||
|
||||
static public inline function resolve(s:String, ?pos)
|
||||
|
||||
static public inline function resolve(s:String, ?pos)
|
||||
return drill(s.split('.'), pos);
|
||||
|
||||
static var scopes = new Array<Array<Var>>();
|
||||
@@ -320,7 +321,7 @@ class Exprs {
|
||||
static function inScope<T>(a:Array<Var>, f:Void->T) {
|
||||
scopes.push(a);
|
||||
|
||||
inline function leave()
|
||||
inline function leave()
|
||||
scopes.pop();
|
||||
try {
|
||||
var ret = f();
|
||||
@@ -330,24 +331,24 @@ class Exprs {
|
||||
catch (e:Dynamic) {
|
||||
leave();
|
||||
return Error.rethrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function scoped<T>(f:Void->T)
|
||||
static public function scoped<T>(f:Void->T)
|
||||
return inScope([], f);
|
||||
|
||||
static public function inSubScope<T>(f:Void->T, a:Array<Var>)
|
||||
static public function inSubScope<T>(f:Void->T, a:Array<Var>)
|
||||
return inScope(switch scopes[scopes.length - 1] {
|
||||
case null: a;
|
||||
case v: v.concat(a);
|
||||
}, f);
|
||||
|
||||
|
||||
static public function typeof(expr:Expr, ?locals)
|
||||
return
|
||||
try {
|
||||
if (locals == null)
|
||||
locals = scopes[scopes.length - 1];
|
||||
if (locals != null)
|
||||
if (locals != null)
|
||||
expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos);
|
||||
Success(Context.typeof(expr));
|
||||
}
|
||||
@@ -356,20 +357,20 @@ class Exprs {
|
||||
}
|
||||
catch (e:Dynamic) {
|
||||
expr.pos.makeFailure(e);
|
||||
}
|
||||
|
||||
static public inline function cond(cond:Expr, cons:Expr, ?alt:Expr, ?pos)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
static public function isWildcard(e:Expr)
|
||||
return
|
||||
switch e {
|
||||
case macro _: true;
|
||||
default: false;
|
||||
}
|
||||
|
||||
static public function getString(e:Expr)
|
||||
return
|
||||
|
||||
static public function getString(e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
@@ -377,10 +378,10 @@ class Exprs {
|
||||
default: e.pos.makeFailure(NOT_A_STRING);
|
||||
}
|
||||
default: e.pos.makeFailure(NOT_A_STRING);
|
||||
}
|
||||
|
||||
static public function getInt(e:Expr)
|
||||
return
|
||||
}
|
||||
|
||||
static public function getInt(e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
@@ -388,22 +389,22 @@ class Exprs {
|
||||
default: e.pos.makeFailure(NOT_AN_INT);
|
||||
}
|
||||
default: e.pos.makeFailure(NOT_AN_INT);
|
||||
}
|
||||
|
||||
static public function getIdent(e:Expr)
|
||||
return
|
||||
}
|
||||
|
||||
static public function getIdent(e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
case CIdent(id): Success(id);
|
||||
default: e.pos.makeFailure(NOT_AN_IDENT);
|
||||
}
|
||||
default:
|
||||
default:
|
||||
e.pos.makeFailure(NOT_AN_IDENT);
|
||||
}
|
||||
|
||||
static public function getName(e:Expr)
|
||||
return
|
||||
}
|
||||
|
||||
static public function getName(e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
@@ -411,15 +412,15 @@ class Exprs {
|
||||
default: e.pos.makeFailure(NOT_A_NAME);
|
||||
}
|
||||
default: e.pos.makeFailure(NOT_A_NAME);
|
||||
}
|
||||
|
||||
static public function getFunction(e:Expr)
|
||||
}
|
||||
|
||||
static public function getFunction(e:Expr)
|
||||
return
|
||||
switch (e.expr) {
|
||||
case EFunction(_, f): Success(f);
|
||||
default: e.pos.makeFailure(NOT_A_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
static public function concat(e:Expr, with:Expr, ?pos) {
|
||||
if(pos == null) pos = e.pos;
|
||||
return
|
||||
@@ -430,7 +431,7 @@ class Exprs {
|
||||
default: EBlock([e, with]).at(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static var FIRST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
static var LATER = FIRST + '0123456789';
|
||||
|
||||
@@ -438,7 +439,7 @@ class Exprs {
|
||||
var ret = FIRST.charAt(i % FIRST.length);
|
||||
|
||||
i = Std.int(i / FIRST.length);
|
||||
|
||||
|
||||
while (i > 0) {
|
||||
ret += LATER.charAt(i % LATER.length);
|
||||
i = Std.int(i / LATER.length);
|
||||
@@ -446,7 +447,7 @@ class Exprs {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline var NOT_AN_INT = "integer constant expected";
|
||||
static inline var NOT_AN_IDENT = "identifier expected";
|
||||
static inline var NOT_A_STRING = "string constant expected";
|
||||
|
@@ -4,10 +4,19 @@ import haxe.macro.Expr;
|
||||
|
||||
using tink.macro.Exprs;
|
||||
|
||||
#if haxe4
|
||||
private abstract Kind(FunctionKind) from FunctionKind to FunctionKind {
|
||||
@:from static function ofString(s:String):Kind
|
||||
return FNamed(s);
|
||||
}
|
||||
#else
|
||||
private typedef Kind = String;
|
||||
#end
|
||||
|
||||
class Functions {
|
||||
static public inline function asExpr(f:Function, ?name, ?pos)
|
||||
return EFunction(name, f).at(pos);
|
||||
|
||||
static public inline function asExpr(f:Function, ?kind:Kind, ?pos)
|
||||
return EFunction(kind, f).at(pos);
|
||||
|
||||
static public inline function toArg(name:String, ?t, ?opt = false, ?value = null):FunctionArg {
|
||||
return {
|
||||
name: name,
|
||||
@@ -22,8 +31,24 @@ class Functions {
|
||||
ret: ret,
|
||||
params: params == null ? [] : params,
|
||||
expr: if (makeReturn) EReturn(e).at(e.pos) else e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function mapSignature(f:Function, transform):Function
|
||||
return {
|
||||
ret: Types.map(f.ret, transform),
|
||||
args: [for (a in f.args) {
|
||||
name: a.name,
|
||||
value: a.value,
|
||||
type: Types.map(a.type, transform),
|
||||
#if haxe4
|
||||
meta: a.meta,
|
||||
#end
|
||||
}],
|
||||
expr: f.expr,
|
||||
params: Types.mapTypeParamDecls(f.params, transform),
|
||||
}
|
||||
|
||||
static public function getArgIdents(f:Function):Array<Expr> {
|
||||
var ret = [];
|
||||
for (arg in f.args)
|
||||
|
@@ -3,6 +3,7 @@ package tink.macro;
|
||||
import haxe.macro.Expr;
|
||||
|
||||
class Metadatas {
|
||||
static var printer = new haxe.macro.Printer();
|
||||
static public function toMap(m:Metadata) {
|
||||
var ret = new Map<String,Array<Array<Expr>>>();
|
||||
if (m != null)
|
||||
@@ -18,4 +19,8 @@ class Metadatas {
|
||||
return
|
||||
if (m == null) [];
|
||||
else [for (meta in m) if (meta.name == name) meta.params];
|
||||
|
||||
static public inline function toString(m:MetadataEntry) {
|
||||
return printer.printMetadata(m);
|
||||
}
|
||||
}
|
@@ -2,6 +2,8 @@ package tink.macro;
|
||||
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
using haxe.macro.Tools;
|
||||
using tink.MacroApi;
|
||||
|
||||
class Sisyphus {
|
||||
|
||||
@@ -23,8 +25,8 @@ class Sisyphus {
|
||||
if (cf.params.length == 0) {
|
||||
name: cf.name,
|
||||
doc: cf.doc,
|
||||
access:
|
||||
(cf.isPublic ? [ APublic ] : [ APrivate ])
|
||||
access:
|
||||
(cf.isPublic ? [ APublic ] : [ APrivate ])
|
||||
#if haxe4 .concat(if (cf.isFinal) [AFinal] else []) #end
|
||||
,
|
||||
kind: switch([ cf.kind, cf.type ]) {
|
||||
@@ -61,8 +63,7 @@ class Sisyphus {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return {
|
||||
static public function toComplexType(type : Null<Type>) : Null<ComplexType> return {
|
||||
inline function direct()
|
||||
return Types.toComplex(type, { direct: true });
|
||||
switch (type) {
|
||||
@@ -84,7 +85,7 @@ class Sisyphus {
|
||||
cl.kind.match(KTypeParameter(_))
|
||||
&& cl.module == classType.module
|
||||
&& cl.pack.join('.') == classType.pack.join('.')
|
||||
): ct;
|
||||
): ct;
|
||||
default:
|
||||
direct();
|
||||
}
|
||||
@@ -95,8 +96,12 @@ class Sisyphus {
|
||||
TPath(toTypePath(baseType, params));
|
||||
case TFun(args, ret):
|
||||
TFunction(
|
||||
[ for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t) ],
|
||||
toComplexType(ret));
|
||||
[for (a in args) {
|
||||
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 }):
|
||||
TAnonymous([ for (cf in fields) toField(cf) ]);
|
||||
case TDynamic(t):
|
||||
@@ -121,7 +126,7 @@ class Sisyphus {
|
||||
case _ == name => true: null;
|
||||
case v: v;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
pack: baseType.pack,
|
||||
name: name,
|
||||
@@ -132,4 +137,89 @@ 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
|
||||
) + switch params {
|
||||
case []: '';
|
||||
case params:
|
||||
'<${params.map(toExactString).join(', ')}>';
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
class TypeMap<V> extends BalancedTree<Type, V> implements IMap<Type, V> {
|
||||
var follow:Bool;
|
||||
|
||||
public function new(?noFollow:Bool) {
|
||||
this.follow = noFollow != true;
|
||||
var normalize:Type->Type;
|
||||
|
||||
public function new(?normalize:Type->Type) {
|
||||
this.normalize = switch normalize {
|
||||
case null: function (t) return t.reduce();
|
||||
case fn: fn;
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
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,6 +10,8 @@ using haxe.macro.Tools;
|
||||
using tink.MacroApi;
|
||||
using tink.CoreApi;
|
||||
|
||||
import haxe.macro.Type.Ref;
|
||||
|
||||
class Types {
|
||||
|
||||
static public function definedType(typeName:String)
|
||||
@@ -70,6 +72,79 @@ class Types {
|
||||
getDeclaredFields(t.superClass.t.get(), out, marker);
|
||||
}
|
||||
|
||||
static public function mapTypeParams(a:Array<TypeParam>, f)
|
||||
return
|
||||
if (a == null) null;
|
||||
else [for (p in a) switch p {
|
||||
case TPType(t): TPType(t.map(f));
|
||||
default: p;
|
||||
}];
|
||||
|
||||
static public function mapTypeParamDecls(a:Array<TypeParamDecl>, f:ComplexType->ComplexType):Array<TypeParamDecl>
|
||||
return
|
||||
if (a == null) null;
|
||||
else [for (p in a) {
|
||||
name: p.name,
|
||||
meta: p.meta,
|
||||
params: mapTypeParamDecls(p.params, f),
|
||||
constraints: switch p.constraints {
|
||||
case null: null;
|
||||
case a: [for (t in a) map(t, f)];
|
||||
}
|
||||
}];
|
||||
|
||||
static public function mapTypePath(p:TypePath, transform):TypePath
|
||||
return {
|
||||
name: p.name,
|
||||
pack: p.pack,
|
||||
sub: p.sub,
|
||||
params: mapTypeParams(p.params, transform),
|
||||
}
|
||||
|
||||
static public function map(ct:ComplexType, transform:ComplexType->ComplexType) {
|
||||
if (ct == null)
|
||||
return null;
|
||||
inline function rec(ct)
|
||||
return map(transform(ct), transform);
|
||||
|
||||
function mapFields(fields:Array<Field>):Array<Field>
|
||||
return [for (f in fields) {
|
||||
name: f.name,
|
||||
pos: f.pos,
|
||||
kind: switch f.kind {
|
||||
case FVar(t, e): FVar(rec(t), e);
|
||||
case FProp(get, set, t, e): FProp(get, set, rec(t), e);
|
||||
case FFun(f): FFun(Functions.mapSignature(f, transform));
|
||||
},
|
||||
access: f.access,
|
||||
meta: switch f.meta {
|
||||
case null: null;
|
||||
case a: [for (m in a) {
|
||||
name: m.name,
|
||||
pos: m.pos,
|
||||
params: [for (e in m.params) e.mapTypes(transform)],
|
||||
}];
|
||||
},
|
||||
doc: f.doc,
|
||||
}];
|
||||
return transform(switch ct {
|
||||
case TParent(t): TParent(rec(t));
|
||||
#if haxe4
|
||||
case TNamed(n, t): TNamed(n, rec(t));
|
||||
case TIntersection(tl): TIntersection([for (t in tl) rec(t)]);
|
||||
#end
|
||||
case TOptional(t): TOptional(rec(t));
|
||||
case TAnonymous(fields):
|
||||
TAnonymous(mapFields(fields));
|
||||
case TPath(p):
|
||||
TPath(mapTypePath(p, transform));
|
||||
case TExtend(paths, fields):
|
||||
TExtend([for (p in paths) mapTypePath(p, transform)], mapFields(fields));
|
||||
case TFunction(args, ret):
|
||||
TFunction([for (a in args) rec(a)], rec(ret));
|
||||
});
|
||||
}
|
||||
|
||||
static var fieldsCache = new Map();
|
||||
static public function getFields(t:Type, ?substituteParams = true)
|
||||
return
|
||||
@@ -84,12 +159,31 @@ class Types {
|
||||
}
|
||||
var ret = fieldsCache.get(id);
|
||||
if (substituteParams && ret.isSuccess()) {
|
||||
var fields = Reflect.copy(ret.sure());
|
||||
|
||||
for (field in fields)
|
||||
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params);
|
||||
ret = Success([
|
||||
for (field in ret.sure()) ({
|
||||
name: field.name,
|
||||
type: haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params),
|
||||
pos: field.pos,
|
||||
meta: field.meta,
|
||||
doc: field.doc,
|
||||
expr: function () return field.expr(),
|
||||
isPublic: field.isPublic,
|
||||
params: field.params,
|
||||
kind: field.kind,
|
||||
overloads: field.overloads,
|
||||
#if haxe4
|
||||
isExtern: field.isExtern,
|
||||
isFinal: field.isFinal,
|
||||
#if (haxe >= version("4.2.0-rc.1"))
|
||||
isAbstract: field.isAbstract,
|
||||
#end
|
||||
#end
|
||||
}:ClassField)
|
||||
]);
|
||||
}
|
||||
#if !haxe4
|
||||
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
|
||||
#end
|
||||
ret;
|
||||
case TAnonymous(anon): Success(anon.get().fields);
|
||||
default: Context.currentPos().makeFailure('type $t has no fields');
|
||||
@@ -101,8 +195,8 @@ class Types {
|
||||
case TInst(t, _): Success(t.get().statics.get());
|
||||
default: Failure('type has no statics');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static public function getPosition(t:Type)
|
||||
return
|
||||
switch t {
|
||||
@@ -115,8 +209,23 @@ class Types {
|
||||
case TDynamic(v) if(v != null): getPosition(v);
|
||||
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)
|
||||
return new Printer().printComplexType(t);
|
||||
@@ -169,8 +278,7 @@ class Types {
|
||||
return if (once) t else reduce(t, false);
|
||||
return switch type {
|
||||
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
||||
case TLazy(f): rec(f());
|
||||
case TType(_, _): rec(Context.follow(type, once));
|
||||
case TLazy(_) | TType(_): rec(Context.follow(type, once));
|
||||
default: type;
|
||||
}
|
||||
}
|
||||
@@ -202,26 +310,29 @@ class Types {
|
||||
}
|
||||
|
||||
static public function intersect(types:Array<ComplexType>, ?pos:Position):Outcome<ComplexType, Error> {
|
||||
|
||||
|
||||
if (types.length == 1) return Success(types[1]);
|
||||
|
||||
#if haxe4
|
||||
return Success(TIntersection(types));
|
||||
#end
|
||||
var paths = [],
|
||||
fields = [];
|
||||
|
||||
for (t in types)
|
||||
switch t {
|
||||
case TPath(p): paths.push(p);
|
||||
case TAnonymous(f):
|
||||
|
||||
case TAnonymous(f):
|
||||
|
||||
for (f in f) fields.push(f);
|
||||
|
||||
case TExtend(p, f):
|
||||
|
||||
case TExtend(p, f):
|
||||
|
||||
for (f in f) fields.push(f);
|
||||
for (p in p) paths.push(p);
|
||||
|
||||
default:
|
||||
|
||||
|
||||
return Failure(new Error(t.toString() + ' cannot be interesected', pos));
|
||||
}
|
||||
|
||||
@@ -246,28 +357,18 @@ class Types {
|
||||
throw 'assert';
|
||||
}
|
||||
|
||||
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
|
||||
if (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 public function compare(t1:Type, t2:Type, ?follow:Bool = true)
|
||||
return Sisyphus.compare(t1, t2, follow);
|
||||
|
||||
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
|
||||
|
||||
static public function getFieldSuggestions(type:ComplexType, name:String):String
|
||||
static public function getFieldSuggestions(type:ComplexType, name:String):String
|
||||
return switch (macro (null : $type).$name).typeof() {
|
||||
case Failure(SUGGESTIONS.match(_.message) => true): SUGGESTIONS.matched(0);
|
||||
default: '';
|
||||
}
|
||||
|
||||
static public function toDecl(p:TypeParameter):TypeParamDecl
|
||||
static public function toDecl(p:TypeParameter):TypeParamDecl
|
||||
return {
|
||||
name: p.name,
|
||||
constraints: switch p.t {
|
||||
@@ -279,4 +380,4 @@ class Types {
|
||||
static public function toTypeParam(p:TypeParameter):TypeParam
|
||||
return TPType(p.t.toComplex());
|
||||
|
||||
}
|
||||
}
|
@@ -1,2 +1,3 @@
|
||||
-cp tests
|
||||
-lib hx3compat
|
||||
-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; }));
|
||||
}
|
||||
}
|
11
tests/Functions.hx
Normal file
11
tests/Functions.hx
Normal file
@@ -0,0 +1,11 @@
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
using tink.MacroApi;
|
||||
|
||||
class Functions extends Base {
|
||||
function test() {
|
||||
var f:Function = (macro function () {}).getFunction().sure();
|
||||
f.asExpr('foo');
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
10
tests/Misc.hx
Normal file
10
tests/Misc.hx
Normal file
@@ -0,0 +1,10 @@
|
||||
import tink.MacroApi;
|
||||
import haxe.unit.TestCase;
|
||||
|
||||
using tink.CoreApi;
|
||||
|
||||
class Misc extends TestCase {
|
||||
function testMain() {
|
||||
assertEquals('Run', MacroApi.getMainClass().force());
|
||||
}
|
||||
}
|
1
tests/MyString.hx
Normal file
1
tests/MyString.hx
Normal file
@@ -0,0 +1 @@
|
||||
typedef MyString = String;
|
13
tests/Run.hx
13
tests/Run.hx
@@ -1,10 +1,13 @@
|
||||
package ;
|
||||
|
||||
import haxe.unit.*;
|
||||
#if macro
|
||||
using haxe.macro.Tools;
|
||||
#end
|
||||
|
||||
class Run {
|
||||
#if !macro
|
||||
static function main()
|
||||
#if !macro
|
||||
static function main()
|
||||
test();//It compiles ...
|
||||
#else
|
||||
static var cases:Array<TestCase> = [
|
||||
@@ -12,17 +15,21 @@ class Run {
|
||||
new Types(),
|
||||
new Positions(),
|
||||
new TypeMapTest(),
|
||||
new Functions(),
|
||||
new Misc(),
|
||||
new ExactStrings(),
|
||||
];
|
||||
#end
|
||||
macro static function test() {
|
||||
var runner = new TestRunner();
|
||||
tink.macro.ClassBuilder;
|
||||
tink.macro.BuildCache;
|
||||
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!');
|
||||
}
|
||||
|
@@ -1,17 +1,19 @@
|
||||
package;
|
||||
|
||||
import haxe.unit.TestCase;
|
||||
import haxe.macro.Expr;
|
||||
|
||||
import tink.macro.TypeMap;
|
||||
using haxe.macro.Context;
|
||||
using tink.MacroApi;
|
||||
|
||||
class TypeMapTest extends TestCase {
|
||||
|
||||
function testMap() {
|
||||
var t = new TypeMap();
|
||||
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof();
|
||||
var t2 = (macro [{ foo: [{ bar: 5 }]}]).typeof();
|
||||
|
||||
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof().sure();
|
||||
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);
|
||||
assertEquals(Lambda.count(t), 1);
|
||||
t.set(t2, 1);
|
||||
@@ -19,12 +21,33 @@ class TypeMapTest extends TestCase {
|
||||
t.set(t1, 2);
|
||||
assertEquals(Lambda.count(t), 2);
|
||||
t.set(t2, 3);
|
||||
t.set(t3, 3);
|
||||
assertEquals(Lambda.count(t), 2);
|
||||
|
||||
t.set(t4, 4);
|
||||
assertEquals(Lambda.count(t), 3);
|
||||
|
||||
assertEquals(t.get(t1), 2);
|
||||
assertEquals(t.get(t2), 3);
|
||||
|
||||
|
||||
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 {
|
||||
function type(c:ComplexType)
|
||||
return c.toType().sure();
|
||||
|
||||
|
||||
function resolve(type:String)
|
||||
return Context.getType(type);
|
||||
|
||||
|
||||
inline function assertSuccess<S, F>(o:Outcome<S, F>)
|
||||
assertTrue(o.isSuccess());
|
||||
|
||||
|
||||
inline function assertFailure<S, F>(o:Outcome<S, F>)
|
||||
assertFalse(o.isSuccess());
|
||||
|
||||
|
||||
function testIs() {
|
||||
|
||||
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
|
||||
assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function testFields() {
|
||||
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];
|
||||
|
||||
|
||||
assertSuccess(iterator.type.isSubTypeOf(expected));
|
||||
assertSuccess(expected.isSubTypeOf(iterator.type));
|
||||
}
|
||||
|
||||
|
||||
function testConvert() {
|
||||
assertSuccess((macro : Int).toType());
|
||||
assertFailure((macro : Tni).toType());
|
||||
function blank()
|
||||
return type(MacroApi.pos().makeBlankType());
|
||||
|
||||
|
||||
var bool = type(macro : Bool);
|
||||
assertTrue(blank().isSubTypeOf(bool).isSuccess());
|
||||
assertTrue(bool.isSubTypeOf(blank()).isSuccess());
|
||||
@@ -65,5 +64,20 @@ class Types extends Base {
|
||||
assertEquals('String', Context.getType('String').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