Compare commits

..

49 Commits

Author SHA1 Message Date
Juraj Kirchheim
8890b62b46 Now we have it, I think ... 2020-01-06 14:53:06 +01:00
Juraj Kirchheim
cce63becd6 Whoops. 2020-01-06 14:40:38 +01:00
Juraj Kirchheim
a101378a4c Try to be smarter about when bouncing initialization. 2020-01-06 14:35:26 +01:00
Juraj Kirchheim
b48b300cbc Add some missing cases in type mapping. 2020-01-04 15:45:29 +01:00
Juraj Kirchheim
27c9b20330 Get rid of all reflection. 2020-01-04 14:20:40 +01:00
Juraj Kirchheim
b4fa5f4fe0 Update lix version to download correct nightly. 2019-09-13 09:45:24 +02:00
Juraj Kirchheim
e59fab499d Release 0.18.0 2019-09-13 09:40:22 +02:00
Juraj Kirchheim
3356172b7f Update to newest haxe version. 2019-09-13 09:39:24 +02:00
Juraj Kirchheim
4fd03ed667 Release 0.17.7 2019-06-29 11:27:39 +02:00
Juraj Kirchheim
4cb5ee4196 Use @:persistent as necessary. 2019-06-22 15:16:04 +02:00
Kevin Leung
888481094a Pin lix version 2019-05-14 11:33:28 +08:00
Kevin Leung
7686fe3e35 Use xenial 2019-05-14 11:10:52 +08:00
Kevin Leung
747d07bfc5 Revert "Better type comparison (closes #25)"
This reverts commit 00640d8328.
2019-05-13 18:53:42 +08:00
Kevin Leung
00640d8328 Better type comparison (closes #25) 2019-05-13 17:01:54 +08:00
Kevin Leung
77973f6007 print metadata 2019-05-13 17:00:54 +08:00
Juraj Kirchheim
0df9c28ace Release 0.17.6 2019-05-07 22:43:21 +02:00
Juraj Kirchheim
d90fb966d2 Add call to find main class. 2019-05-07 22:42:39 +02:00
Kevin Leung
9018b06279 Release 0.17.5 2019-04-27 20:54:09 +08:00
Kevin Leung
6ed0424093 Convert TypeParameter to TypeParam 2019-04-23 18:21:03 +08:00
Juraj Kirchheim
53868cc6f3 Some fumbling with isSubTypeOf and add test for final. 2019-03-03 17:13:04 +01:00
Juraj Kirchheim
296cc6c861 Support final access on haxe4. Resolves #17. 2019-03-03 12:24:22 +01:00
Juraj Kirchheim
56c2ed9642 Don't do setter bypass if it's not really necessary. 2019-03-03 12:18:40 +01:00
Juraj Kirchheim
ac908bc82d Deal with somewhat obscure haxe 4 issue. 2019-03-03 12:18:07 +01:00
Juraj Kirchheim
1604a135ae Expose compiler args and completion point. 2019-02-26 16:01:39 +01:00
Juraj Kirchheim
5e65f5354f Add unification helper. 2019-02-24 08:25:04 +01:00
Juraj Kirchheim
8f81256731 Deal with null monos. 2019-02-24 08:24:50 +01:00
Kevin Leung
1b717919fa Release 0.17.4 2019-02-24 09:28:59 +08:00
Kevin Leung
3d9aec835e Merge pull request #24 from kevinresol/type_get_pos
Get Position from a Type
2019-02-24 00:26:07 +08:00
Kevin Leung
86fa47eb0d Get Position from a Type 2019-02-23 22:37:17 +08:00
Juraj Kirchheim
b5605e48ac Use nightly rather than latest. 2019-02-01 17:44:53 +01:00
Juraj Kirchheim
6f5eba983e Update travix. 2019-01-02 17:27:10 +01:00
Juraj Kirchheim
d5a44a9268 Release 0.17.3 2019-01-02 17:08:03 +01:00
Juraj Kirchheim
a11804c3ab Merge branch 'master' of https://github.com/haxetink/tink_macro 2019-01-02 16:59:06 +01:00
Juraj Kirchheim
380ceb39ea Add helper for transforming TypeParameter to TypeParamDecl. 2019-01-02 16:58:42 +01:00
Juraj Kirchheim
c48d445a54 Don't use get_field/set_field for field access anymore. 2018-11-08 11:27:37 +01:00
Juraj Kirchheim
b7e413d839 Release 0.17.2 2018-10-05 09:23:32 +02:00
Juraj Kirchheim
e1e487079b Add Exprs.as for ECheckType. 2018-10-03 11:50:21 +02:00
Juraj Kirchheim
5f959f788e Release 0.17.1 2018-10-02 09:12:28 +02:00
Juraj Kirchheim
4f84570bb7 Improve type parameter treatment. 2018-10-01 16:10:45 +02:00
Juraj Kirchheim
e398d0e694 Add helper for getting field suggestions. 2018-10-01 16:10:34 +02:00
Juraj Kirchheim
13d11f4f66 Make type comparator available. 2018-09-22 10:12:02 +02:00
Kevin Leung
d6142847eb Update ci script 2018-09-19 16:40:12 +08:00
Kevin Leung
e6a0304016 Release 0.17.0 2018-09-08 14:14:22 +08:00
Kevin Leung
69368937a4 let lix manage travix 2018-09-08 13:59:47 +08:00
Kevin Leung
df2389d2c8 Merge branch 'master' of https://github.com/haxetink/tink_macro 2018-09-08 13:53:37 +08:00
Kevin Leung
aad80afec2 Standardize build script 2018-09-08 13:53:27 +08:00
Juraj Kirchheim
b68c6ac4a1 Fix complex type intersection. 2018-09-03 18:49:08 +02:00
Juraj Kirchheim
682aeacce8 Add ComplexType::intersect. 2018-09-01 15:09:05 +02:00
Juraj Kirchheim
9bcec770eb Update travix. 2018-09-01 15:08:53 +02:00
26 changed files with 884 additions and 498 deletions

View File

@@ -1 +1,4 @@
{"version":"3.4.4","resolveLibs":"scoped"} {
"version": "4.0.5",
"resolveLibs": "scoped"
}

View File

@@ -1,28 +1,41 @@
sudo: required sudo: required
dist: trusty dist: xenial
language: haxe stages:
haxe: - test
- 3.2.1 - deploy
- development
matrix: language: node_js
allow_failures: node_js: 8
- haxe: development
install: os:
- haxelib install travix - linux
- haxelib run travix install # - osx
- haxelib install hx3compat
script:
- haxelib run travix node -lib hx3compat
env: env:
secure: T4SCtY5qmEsK1ARWPevJmqLm23tv4CobLrbPOQV3FsoQno7FCP1S/+9GmuoJKzeTjWMzdTeDsp8TVwZ6AyGjvhl2nZNjhU+QTsir4tfbYYRyvsz/QK6pveFbPQVv7OsnnaB4wbZtqGZ8mzFeQf7Ol4tsNe7iUFJb/iVc+4/lUxo= - HAXE_VERSION=3.4.7
- HAXE_VERSION=edge
install:
- npm i -g lix@15.5.4
- lix install haxe $HAXE_VERSION
- lix download
deploy: script:
provider: script - lix run travix node -lib hx3compat
script: haxe && haxelib run travix release
on: jobs:
tags: true include:
# - stage: test # should uncomment this when there is no matrix above (e.g. only one os, one env, etc)
- stage: deploy
language: haxe
haxe: "3.4.7"
os: linux
install: skip
script: skip
env:
secure: T4SCtY5qmEsK1ARWPevJmqLm23tv4CobLrbPOQV3FsoQno7FCP1S/+9GmuoJKzeTjWMzdTeDsp8TVwZ6AyGjvhl2nZNjhU+QTsir4tfbYYRyvsz/QK6pveFbPQVv7OsnnaB4wbZtqGZ8mzFeQf7Ol4tsNe7iUFJb/iVc+4/lUxo=
after_success:
- haxelib install travix
- haxelib run travix install
- haxelib run travix release

View File

@@ -1,5 +0,0 @@
{
"haxe.displayConfigurations": [
["tests.hxml"]
]
}

6
dev.hxml Normal file
View File

@@ -0,0 +1,6 @@
tests.hxml
-lib travix
-lib tink_macro
-lib hx3compat
-js whatever.js
--no-output

View File

@@ -0,0 +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

View File

@@ -0,0 +1,8 @@
-D tink_cli=0.3.1
# @install: lix --silent download "haxelib:/tink_cli#0.3.1" into tink_cli/0.3.1/haxelib
-lib tink_io
-lib tink_stringly
-lib tink_macro
-cp ${HAXE_LIBCACHE}/tink_cli/0.3.1/haxelib/src
# Make sure docs are generated
-D use-rtti-doc

View File

@@ -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.24.0
-D tink_core=1.16.1 # @install: lix --silent download "haxelib:/tink_core#1.24.0" into tink_core/1.24.0/haxelib
-cp ${HAXESHIM_LIBCACHE}/tink_core/1.16.1/haxelib/src -cp ${HAXE_LIBCACHE}/tink_core/1.24.0/haxelib/src

View File

@@ -0,0 +1,5 @@
-D tink_io=0.5.0
# @install: lix --silent download "haxelib:/tink_io#0.5.0" into tink_io/0.5.0/haxelib
-lib tink_streams
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_io/0.5.0/haxelib/src

View File

@@ -0,0 +1,4 @@
-D tink_streams=0.2.1
# @install: lix --silent download "haxelib:/tink_streams#0.2.1" into tink_streams/0.2.1/haxelib
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_streams/0.2.1/haxelib/src

View File

@@ -0,0 +1,4 @@
-D tink_stringly=0.2.0
# @install: lix --silent download "haxelib:/tink_stringly#0.2.0" into tink_stringly/0.2.0/haxelib
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_stringly/0.2.0/haxelib/src

View File

@@ -1,4 +1,6 @@
# @run: haxelib run-dir travix ${HAXESHIM_LIBCACHE}/travix/0.10.3/haxelib -D travix=0.12.2
# @install: lix --silent download "haxelib:travix#0.10.3" into travix/0.10.3/haxelib # @install: lix --silent download "gh://github.com/back2dos/travix#7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5" into travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5
-D travix=0.10.3 # @post-install: cd ${HAXE_LIBCACHE}/travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5 && haxe -cp src --run travix.PostDownload
-cp ${HAXESHIM_LIBCACHE}/travix/0.10.3/haxelib/src # @run: haxelib run-dir travix ${HAXE_LIBCACHE}/travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5
-lib tink_cli
-cp ${HAXE_LIBCACHE}/travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5/src

View File

@@ -11,8 +11,8 @@
"contributors": [ "contributors": [
"back2dos" "back2dos"
], ],
"releasenote": "Fix haxe 4 compat.", "releasenote": "Compat with haxe 4 rc4 onward.",
"version": "0.16.7", "version": "0.18.0",
"url": "http://haxetink.org/tink_macro", "url": "http://haxetink.org/tink_macro",
"dependencies": { "dependencies": {
"tink_core": "" "tink_core": ""

View File

@@ -4,6 +4,7 @@ import haxe.macro.Expr.TypeDefinition;
using tink.CoreApi; using tink.CoreApi;
using tink.macro.Positions; using tink.macro.Positions;
using StringTools;
typedef Positions = tink.macro.Positions; typedef Positions = tink.macro.Positions;
typedef ExprTools = haxe.macro.ExprTools; typedef ExprTools = haxe.macro.ExprTools;
@@ -28,7 +29,20 @@ typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
class MacroApi { 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 @:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
return '__tink_' + prefix + Std.string(idCounter++); return '__tink_' + prefix + Std.string(idCounter++);
@@ -36,6 +50,45 @@ class MacroApi {
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<{
var file(default, never):String;
var pos(default, never):Int;
}>;
static public var args(default, null):Iterable<String>;
static var initialized = initArgs();
static function initArgs() {
var sysArgs = Sys.args();
args = sysArgs;
completionPoint = switch sysArgs.indexOf('--display') {
case -1: None;
case sysArgs[_ + 1] => arg:
if (arg.startsWith('{"jsonrpc":')) {
var payload:{
jsonrpc:String,
method:String,
params:{
file:String,
offset:Int,
}
} = haxe.Json.parse(arg);
switch payload {
case { jsonrpc: '2.0', method: 'display/completion' }:
Some({
file: payload.params.file,
pos: payload.params.offset,
});
default: None;
}
}
else None;
}
try haxe.macro.Context.onMacroContextReused(initArgs)
catch (all:Dynamic) {}
return true;
}
} }

View File

@@ -33,7 +33,7 @@ typedef BuildContext3 = {>BuildContext2,
class BuildCache { class BuildCache {
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) {
if (types == null) if (types == null)
@@ -141,7 +141,9 @@ class BuildCache {
case v: v; case v: v;
} }
return 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;
} }
} }

View File

@@ -25,20 +25,20 @@ class Constructor {
var owner:ClassBuilder; var owner:ClassBuilder;
var meta:Metadata; var meta:Metadata;
public var isPublic:Null<Bool>; public var isPublic:Null<Bool>;
public function new(owner:ClassBuilder, f:Function, ?isPublic:Null<Bool> = null, ?pos:Position, ?meta:Metadata) { public function new(owner:ClassBuilder, f:Function, ?isPublic:Null<Bool> = null, ?pos:Position, ?meta:Metadata) {
this.nuStatements = []; this.nuStatements = [];
this.owner = owner; this.owner = owner;
this.isPublic = isPublic; this.isPublic = isPublic;
this.pos = pos.sanitize(); this.pos = pos.sanitize();
this.onGenerateHooks = []; this.onGenerateHooks = [];
this.args = []; this.args = [];
this.beforeArgs = []; this.beforeArgs = [];
this.afterArgs = []; this.afterArgs = [];
this.meta = meta; this.meta = meta;
this.oldStatements = this.oldStatements =
if (f == null) []; if (f == null) [];
else { else {
for (i in 0...f.args.length) { for (i in 0...f.args.length) {
@@ -49,15 +49,15 @@ class Constructor {
} }
beforeArgs.push(a); beforeArgs.push(a);
} }
if (f.expr == null) []; if (f.expr == null) [];
else else
switch (f.expr.expr) { switch (f.expr.expr) {
case EBlock(exprs): exprs; case EBlock(exprs): exprs;
default: oldStatements = [f.expr]; default: oldStatements = [f.expr];
} }
} }
superCall = superCall =
if (oldStatements.length == 0) [].toBlock(); if (oldStatements.length == 0) [].toBlock();
else switch oldStatements[0] { else switch oldStatements[0] {
case macro super($a{_}): oldStatements.shift(); case macro super($a{_}): oldStatements.shift();
@@ -68,43 +68,66 @@ class Constructor {
public function getArgList():Array<FunctionArg> public function getArgList():Array<FunctionArg>
return beforeArgs.concat(args).concat(afterArgs); return beforeArgs.concat(args).concat(afterArgs);
public function addStatement(e:Expr, ?prepend) public function addStatement(e:Expr, ?prepend)
if (prepend) if (prepend)
this.nuStatements.unshift(e) this.nuStatements.unshift(e)
else else
this.nuStatements.push(e); 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 } ); 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 }) { public function init(name:String, pos:Position, with:FieldInit, ?options:{ ?prepend:Bool, ?bypass:Bool }) {
if (options == null) if (options == null)
options = {}; options = {};
var e = var e =
switch with { switch with {
case Arg(t, noPublish): case Arg(t, noPublish):
if (noPublish != true) if (noPublish != true)
publish(); publish();
args.push( { name : name, opt : false, type : t } ); args.push( { name : name, opt : false, type : t } );
name.resolve(pos); name.resolve(pos);
case OptArg(e, t, noPublish): case OptArg(e, t, noPublish):
if (noPublish != true) if (noPublish != true)
publish(); publish();
args.push( { name : name, opt : true, type : t, value: e } ); args.push( { name : name, opt : true, type : t, value: e } );
name.resolve(pos); name.resolve(pos);
case Value(e): e; case Value(e): e;
} }
var tmp = MacroApi.tempName(); var member = owner.memberByName(name).sure(),
initStatement = macro @:pos(pos) this.$name = $e;
if (options.bypass) {
switch owner.memberByName(name) { var bypass = switch options.bypass {
case Success(member): member.addMeta(':isVar'); case null:
default: 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 () { addStatement((function () {
var fields = [for (f in (macro this).typeof().sure().getClass().fields.get()) f.name => f]; var fields = [for (f in (macro this).typeof().sure().getClass().fields.get()) f.name => f];
function setDirectly(t:TypedExpr) { function setDirectly(t:TypedExpr) {
var direct = null; var direct = null;
function seek(t:TypedExpr) { function seek(t:TypedExpr) {
@@ -112,15 +135,15 @@ class Constructor {
case TField({ expr: TConst(TThis) }, FInstance(_, _, f)) if (f.get().name == name): direct = t; case TField({ expr: TConst(TThis) }, FInstance(_, _, f)) if (f.get().name == name): direct = t;
default: t.iter(seek); default: t.iter(seek);
} }
} }
seek(t); seek(t);
if (direct == null) pos.error('nope'); if (direct == null) pos.error('nope');
var direct = Context.storeTypedExpr(direct); var direct = Context.storeTypedExpr(direct);
return macro @:pos(pos) $direct = $e; return macro @:pos(pos) $direct = $e;
} }
return switch fields[name] { return switch fields[name] {
case null: case null:
pos.error('this direct initialization causes the compiler to do really weird things'); pos.error('this direct initialization causes the compiler to do really weird things');
case f: case f:
switch f.kind { switch f.kind {
@@ -130,32 +153,41 @@ class Constructor {
macro @:pos(pos) this.$name = $e; macro @:pos(pos) this.$name = $e;
case FVar(AccNo | AccNormal, AccNever): case FVar(AccNo | AccNormal, AccNever):
setDirectly(Context.typeExpr(macro @:pos(pos) this.$name)); 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): case FVar(AccCall, AccNever):
setDirectly(fields['get_$name'].expr()); setDirectly(fields['get_$name'].expr());
#end
case FVar(_, AccCall): case FVar(_, AccCall):
setDirectly(fields['set_$name'].expr()); setDirectly(fields['set_$name'].expr());
default: default:
pos.error('not implemented'); pos.error('not implemented');
} }
} }
}).bounce(), options.prepend); }).bounce(), options.prepend);
} }
else else
addStatement(macro @:pos(pos) this.$name = $e, options.prepend); addStatement(
initStatement,
options.prepend
);
} }
public inline function publish() public inline function publish()
if (isPublic == null) if (isPublic == null)
isPublic = true; isPublic = true;
function toBlock() function toBlock()
return [superCall] return [superCall]
.concat(nuStatements) .concat(nuStatements)
.concat(oldStatements) .concat(oldStatements)
.toBlock(pos); .toBlock(pos);
public function onGenerate(hook) public function onGenerate(hook)
this.onGenerateHooks.push(hook); this.onGenerateHooks.push(hook);
public function toHaxe():Field { public function toHaxe():Field {
var f:Function = { var f:Function = {
args: this.beforeArgs.concat(this.args).concat(this.afterArgs), args: this.beforeArgs.concat(this.args).concat(this.afterArgs),

View File

@@ -1,12 +1,11 @@
package tink.macro; package tink.macro;
import Type in Inspect;
import haxe.macro.Context; import haxe.macro.Context;
import haxe.macro.Expr; import haxe.macro.Expr;
import haxe.macro.Type; import haxe.macro.Type;
import haxe.PosInfos; import haxe.PosInfos;
import haxe.macro.Printer; import haxe.macro.Printer;
import haxe.DynamicAccess as Dict;
using Lambda; using Lambda;
using StringTools; using StringTools;
@@ -20,6 +19,7 @@ typedef VarDecl = { name : String, type : ComplexType, expr : Null<Expr> };
typedef ParamSubst = { typedef ParamSubst = {
var exists(default, null):String->Bool; var exists(default, null):String->Bool;
var get(default, null):String->ComplexType; var get(default, null):String->ComplexType;
function iterator():Iterator<ComplexType>;
} }
private class Heureka { public function new() {} } private class Heureka { public function new() {} }
@@ -34,7 +34,7 @@ class Exprs {
case _ if (condition(e)): throw new Heureka(); case _ if (condition(e)): throw new Heureka();
default: haxe.macro.ExprTools.iter(e, seek); default: haxe.macro.ExprTools.iter(e, seek);
} }
return try { return try {
haxe.macro.ExprTools.iter(e, seek); haxe.macro.ExprTools.iter(e, seek);
false; false;
@@ -43,24 +43,28 @@ class Exprs {
} }
static public inline function is(e:Expr, c:ComplexType) static public inline function is(e:Expr, c:ComplexType)
return ECheckType(e, c).at(e.pos).typeof().isSuccess(); return e.as(c).typeof().isSuccess();
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dynamic<String>, ?skipFields = false, ?callPos:PosInfos) { 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:Dict<String>, ?skipFields = false, ?callPos:PosInfos) {
if (nuPos == null) if (nuPos == null)
nuPos = Context.currentPos(); nuPos = Context.currentPos();
if (rules == null) if (rules == null)
rules = { }; rules = { };
function replace(s:String)
return { function replace(s:String):String
if (Reflect.hasField(rules, s)) return switch rules[s] {
Reflect.field(rules, s) case null:
else if (s.startsWith('tmp')) { if (s.startsWith('tmp')) {
Reflect.setField(rules, s, MacroApi.tempName(s.substr(3))); rules[s] = MacroApi.tempName(s.substr(3));
replace(s); replace(s);
} }
else s; else s;
case v: v;
} }
return e.transform(function (e:Expr) { return e.transform(function (e:Expr) {
return return
if (Context.getPosInfos(e.pos).file != callPos.fileName) e; if (Context.getPosInfos(e.pos).file != callPos.fileName) e;
@@ -68,7 +72,7 @@ class Exprs {
e.pos = nuPos; e.pos = nuPos;
switch (e.expr) { switch (e.expr) {
case EVars(vars): case EVars(vars):
for (v in vars) for (v in vars)
v.name = replace(v.name); v.name = replace(v.name);
e; e;
case EField(owner, field): case EField(owner, field):
@@ -83,7 +87,7 @@ class Exprs {
for (f in fields) for (f in fields)
f.field = replace(f.field); f.field = replace(f.field);
e; e;
default: default:
switch (e.getIdent()) { switch (e.getIdent()) {
case Success(s): replace(s).resolve(e.pos); case Success(s): replace(s).resolve(e.pos);
default: e; default: e;
@@ -92,10 +96,10 @@ class Exprs {
} }
}); });
} }
static public function withPrivateAccess(e:Expr) static public function withPrivateAccess(e:Expr)
return return
e.transform(function (e:Expr) e.transform(function (e:Expr)
return return
switch (e.expr) { switch (e.expr) {
case EField(owner, field): case EField(owner, field):
@@ -103,72 +107,95 @@ class Exprs {
default: e; default: e;
} }
); );
static public function getPrivate(e:Expr, field:String, ?pos:Position) static public function getPrivate(e:Expr, field:String, ?pos:Position)
return macro @:pos(pos.sanitize()) @:privateAccess $e.$field; return macro @:pos(pos.sanitize()) @:privateAccess $e.$field;
static public function substitute(source:Expr, vars:Dynamic<Expr>, ?pos) static public function substitute(source:Expr, vars:Dict<Expr>, ?pos)
return return
transform(source, function (e:Expr) { transform(source, function (e:Expr) {
return return switch e {
switch (e.getIdent()) { case macro $i{name}:
case Success(name): switch vars[name] {
if (Reflect.hasField(vars, name)) case null: e;
Reflect.field(vars, name); case v: v;
else }
e; default: e;
default: e; }
}
}, pos); }, pos);
static public inline function ifNull(e:Expr, fallback:Expr) static public inline function ifNull(e:Expr, fallback:Expr)
return return
switch e { switch e {
case macro null: fallback; case macro null: fallback;
default: e; default: e;
} }
static public function substParams(source:Expr, subst:ParamSubst, ?pos):Expr static public function substParams(source:Expr, subst:ParamSubst, ?pos:Position):Expr {
return crawl( if (!subst.iterator().hasNext())
source, return source;
function (e)
return switch e.expr { function replace(ct:ComplexType)
case ENew({ pack: [], name: name }, args) if (subst.exists(name)): return switch ct {
switch subst.get(name) { case TPath({ pack: [], name: name }) if (subst.exists(name)):
case TPath(p): subst.get(name);
ENew(p, args).at(e.pos); default: ct;
default: e;//TODO: report an error? }
}
case EConst(CIdent(name)) if (subst.exists(name)): return source
.transform(
function (e) return switch e {
case macro $i{name} if (subst.exists(name)):
switch subst.get(name) { switch subst.get(name) {
case TPath({ pack: pack, name: name }): case TPath({ pack: pack, name: name }):
pack.concat([name]).drill(e.pos); pack.concat([name]).drill(e.pos);
default: e;//TODO: report an error? default: e;//TODO: report an error?
} }
default: e; default: e;
}, })
function (c:ComplexType) .mapTypes(replace);
return }
switch (c) {
case TPath({ pack: [], name: name }) if (subst.exists(name)): static public function mapTypes(e:Expr, transformer:ComplexType->ComplexType, ?pos:Position) {
subst.get(name); return e.transform(
default: c; 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); case macro cast($v, $ct):
ct = ct.map(transformer);
static public function transform(source:Expr, transformer:Expr->Expr, ?pos):Expr (macro @:pos(e.pos) cast($v, $ct));
return crawl(source, transformer, function (t) return t, pos); 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);
static function crawlArray(a:Array<Dynamic>, transformer:Expr->Expr, retyper:ComplexType-> ComplexType, pos:Position):Array<Dynamic> 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 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) { (macro @:pos(target.pos) {
var t = null, var t = null,
target = $target; target = $target;
@@ -176,21 +203,21 @@ class Exprs {
t = i; t = i;
t; t;
}).typeof(); }).typeof();
static public function yield(e:Expr, yielder:Expr->Expr, ?options: { ?leaveLoops: Bool }):Expr { 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); return yield(e, yielder, options);
if (options == null) if (options == null)
options = { }; options = { };
var loops = options.leaveLoops != true; var loops = options.leaveLoops != true;
return return
if (e == null || e.expr == null) e; if (e == null || e.expr == null) e;
else switch (e.expr) { else switch (e.expr) {
case EVars(_): case EVars(_):
e.pos.error('Variable declaration not supported here'); 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 = exprs.copy();
exprs.push(rec(exprs.pop())); exprs.push(rec(exprs.pop()));
EBlock(exprs).at(e.pos); EBlock(exprs).at(e.pos);
@@ -198,10 +225,11 @@ class Exprs {
,ETernary(econd, eif, eelse): ,ETernary(econd, eif, eelse):
EIf(econd, rec(eif), rec(eelse)).at(e.pos); EIf(econd, rec(eif), rec(eelse)).at(e.pos);
case ESwitch(e, cases, edef): case ESwitch(e, cases, edef):
cases = Reflect.copy(cases);//not exactly pretty, but does the job ESwitch(e, [for (c in cases) {
for (c in cases) expr: rec(c.expr),
c.expr = rec(c.expr); guard: c.guard,
ESwitch(e, cases, rec(edef)).at(e.pos); values: c.values,
}], rec(edef)).at(e.pos);
case EFor(it, expr) if (loops): case EFor(it, expr) if (loops):
EFor(it, rec(expr)).at(e.pos); EFor(it, rec(expr)).at(e.pos);
case EWhile(cond, body, normal) if (loops): case EWhile(cond, body, normal) if (loops):
@@ -215,101 +243,77 @@ class Exprs {
default: yielder(e); default: yielder(e);
} }
} }
static function crawl(target:Dynamic, transformer:Expr->Expr, retyper:ComplexType->ComplexType, pos:Position):Dynamic static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position)
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)
return macro @:pos(pos.sanitize()) for ($i{loopVar} in $target) $body; return macro @:pos(pos.sanitize()) for ($i{loopVar} in $target) $body;
static public function toFields(object:Dynamic<Expr>, ?pos:Position) static public function toFields(object:Dict<Expr>, ?pos:Position)
return EObjectDecl([for (field in Reflect.fields(object)) return EObjectDecl([for (field in object.keys())
{ field:field, expr: untyped Reflect.field(object, field) } { field:field, expr: object[field] }
]).at(pos); ]).at(pos);
static public inline function log(e:Expr, ?pos:PosInfos):Expr { static public inline function log(e:Expr, ?pos:PosInfos):Expr {
haxe.Log.trace(e.toString(), pos); haxe.Log.trace(e.toString(), pos);
return e; 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); 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); 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 { return {
expr: e, expr: e,
pos: pos.sanitize() 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); 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); 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); 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); 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); 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); 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); 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); 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()); 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); 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); return EBlock(exprs).at(pos);
static public inline function toBlock(exprs:Iterable<Expr>, ?pos) static public inline function toBlock(exprs:Iterable<Expr>, ?pos)
return toMBlock(Lambda.array(exprs), pos); return toMBlock(Lambda.array(exprs), pos);
static public function drill(parts:Array<String>, ?pos:Position, ?target:Expr) { static public function drill(parts:Array<String>, ?pos:Position, ?target:Expr) {
if (target == null) if (target == null)
target = at(EConst(CIdent(parts.shift())), pos); target = at(EConst(CIdent(parts.shift())), pos);
for (part in parts) for (part in parts)
target = field(target, part, pos); 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); return drill(s.split('.'), pos);
static var scopes = new Array<Array<Var>>(); static var scopes = new Array<Array<Var>>();
@@ -317,7 +321,7 @@ class Exprs {
static function inScope<T>(a:Array<Var>, f:Void->T) { static function inScope<T>(a:Array<Var>, f:Void->T) {
scopes.push(a); scopes.push(a);
inline function leave() inline function leave()
scopes.pop(); scopes.pop();
try { try {
var ret = f(); var ret = f();
@@ -327,24 +331,24 @@ class Exprs {
catch (e:Dynamic) { catch (e:Dynamic) {
leave(); leave();
return Error.rethrow(e); return Error.rethrow(e);
} }
} }
static public function scoped<T>(f:Void->T) static public function scoped<T>(f:Void->T)
return inScope([], f); 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] { return inScope(switch scopes[scopes.length - 1] {
case null: a; case null: a;
case v: v.concat(a); case v: v.concat(a);
}, f); }, f);
static public function typeof(expr:Expr, ?locals) static public function typeof(expr:Expr, ?locals)
return return
try { try {
if (locals == null) if (locals == null)
locals = scopes[scopes.length - 1]; locals = scopes[scopes.length - 1];
if (locals != null) if (locals != null)
expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos); expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos);
Success(Context.typeof(expr)); Success(Context.typeof(expr));
} }
@@ -353,20 +357,20 @@ class Exprs {
} }
catch (e:Dynamic) { catch (e:Dynamic) {
expr.pos.makeFailure(e); 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); return EIf(cond, cons, alt).at(pos);
static public function isWildcard(e:Expr) static public function isWildcard(e:Expr)
return return
switch e { switch e {
case macro _: true; case macro _: true;
default: false; default: false;
} }
static public function getString(e:Expr) static public function getString(e:Expr)
return return
switch (e.expr) { switch (e.expr) {
case EConst(c): case EConst(c):
switch (c) { switch (c) {
@@ -374,10 +378,10 @@ class Exprs {
default: e.pos.makeFailure(NOT_A_STRING); default: e.pos.makeFailure(NOT_A_STRING);
} }
default: e.pos.makeFailure(NOT_A_STRING); default: e.pos.makeFailure(NOT_A_STRING);
} }
static public function getInt(e:Expr) static public function getInt(e:Expr)
return return
switch (e.expr) { switch (e.expr) {
case EConst(c): case EConst(c):
switch (c) { switch (c) {
@@ -385,22 +389,22 @@ class Exprs {
default: e.pos.makeFailure(NOT_AN_INT); default: e.pos.makeFailure(NOT_AN_INT);
} }
default: e.pos.makeFailure(NOT_AN_INT); default: e.pos.makeFailure(NOT_AN_INT);
} }
static public function getIdent(e:Expr) static public function getIdent(e:Expr)
return return
switch (e.expr) { switch (e.expr) {
case EConst(c): case EConst(c):
switch (c) { switch (c) {
case CIdent(id): Success(id); case CIdent(id): Success(id);
default: e.pos.makeFailure(NOT_AN_IDENT); default: e.pos.makeFailure(NOT_AN_IDENT);
} }
default: default:
e.pos.makeFailure(NOT_AN_IDENT); e.pos.makeFailure(NOT_AN_IDENT);
} }
static public function getName(e:Expr) static public function getName(e:Expr)
return return
switch (e.expr) { switch (e.expr) {
case EConst(c): case EConst(c):
switch (c) { switch (c) {
@@ -408,15 +412,15 @@ class Exprs {
default: e.pos.makeFailure(NOT_A_NAME); default: e.pos.makeFailure(NOT_A_NAME);
} }
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 return
switch (e.expr) { switch (e.expr) {
case EFunction(_, f): Success(f); case EFunction(_, f): Success(f);
default: e.pos.makeFailure(NOT_A_FUNCTION); default: e.pos.makeFailure(NOT_A_FUNCTION);
} }
static public function concat(e:Expr, with:Expr, ?pos) { static public function concat(e:Expr, with:Expr, ?pos) {
if(pos == null) pos = e.pos; if(pos == null) pos = e.pos;
return return
@@ -427,7 +431,7 @@ class Exprs {
default: EBlock([e, with]).at(pos); default: EBlock([e, with]).at(pos);
} }
} }
static var FIRST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; static var FIRST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
static var LATER = FIRST + '0123456789'; static var LATER = FIRST + '0123456789';
@@ -435,7 +439,7 @@ class Exprs {
var ret = FIRST.charAt(i % FIRST.length); var ret = FIRST.charAt(i % FIRST.length);
i = Std.int(i / FIRST.length); i = Std.int(i / FIRST.length);
while (i > 0) { while (i > 0) {
ret += LATER.charAt(i % LATER.length); ret += LATER.charAt(i % LATER.length);
i = Std.int(i / LATER.length); i = Std.int(i / LATER.length);
@@ -443,7 +447,7 @@ class Exprs {
return ret; return ret;
} }
static inline var NOT_AN_INT = "integer constant expected"; static inline var NOT_AN_INT = "integer constant expected";
static inline var NOT_AN_IDENT = "identifier expected"; static inline var NOT_AN_IDENT = "identifier expected";
static inline var NOT_A_STRING = "string constant expected"; static inline var NOT_A_STRING = "string constant expected";

View File

@@ -4,10 +4,19 @@ import haxe.macro.Expr;
using tink.macro.Exprs; 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 { class Functions {
static public inline function asExpr(f:Function, ?name, ?pos) static public inline function asExpr(f:Function, ?kind:Kind, ?pos)
return EFunction(name, f).at(pos); return EFunction(kind, f).at(pos);
static public inline function toArg(name:String, ?t, ?opt = false, ?value = null):FunctionArg { static public inline function toArg(name:String, ?t, ?opt = false, ?value = null):FunctionArg {
return { return {
name: name, name: name,
@@ -22,8 +31,24 @@ class Functions {
ret: ret, ret: ret,
params: params == null ? [] : params, params: params == null ? [] : params,
expr: if (makeReturn) EReturn(e).at(e.pos) else e 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> { static public function getArgIdents(f:Function):Array<Expr> {
var ret = []; var ret = [];
for (arg in f.args) for (arg in f.args)

View File

@@ -9,7 +9,7 @@ abstract Member(Field) from Field to Field {
name: name, name: name,
pos: pos, pos: pos,
access: [APublic], access: [APublic],
kind: FProp(noread ? 'null' : 'get_' + name, nowrite ? 'null' : ('set_' + name), t), kind: FProp(noread ? 'null' : 'get', nowrite ? 'null' : 'set', t),
} }
return ret; return ret;
} }
@@ -40,6 +40,22 @@ abstract Member(Field) from Field to Field {
public var isStatic(get, set):Bool; public var isStatic(get, set):Bool;
public var isPublic(get, set):Null<Bool>; public var isPublic(get, set):Null<Bool>;
public var isBound(get, set):Null<Bool>; public var isBound(get, set):Null<Bool>;
#if haxe4
/**
* Setting this to true will erase any getters/setters.
*/
public var isFinal(get, set):Bool;
function get_isFinal() return hasAccess(AFinal);
function set_isFinal(param) {
if (setAccess(AFinal, param)) //TODO: perhaps also do something about AInline
switch kind {
case FProp(_, _, t, e):
kind = FVar(t, e);
default:
}
return param;
}
#end
public function getFunction() public function getFunction()
return return
@@ -111,21 +127,10 @@ abstract Member(Field) from Field to Field {
inline function set_pos(param) return this.pos = param; inline function set_pos(param) return this.pos = param;
inline function get_overrides() return hasAccess(AOverride); inline function get_overrides() return hasAccess(AOverride);
inline function set_overrides(param) { inline function set_overrides(param) return setAccess(AOverride, param);
changeAccess(
param ? AOverride : null,
param ? null : AOverride
);
return param;
}
inline function get_isStatic() return hasAccess(AStatic); inline function get_isStatic() return hasAccess(AStatic);
function set_isStatic(param) { inline function set_isStatic(param) return setAccess(AStatic, param);
changeAccess(
param ? AStatic : null,
param ? null : AStatic
);
return param;
}
function get_isPublic() { function get_isPublic() {
if (this.access != null) if (this.access != null)
@@ -179,6 +184,15 @@ abstract Member(Field) from Field to Field {
if (x == a) return true; if (x == a) return true;
return false; return false;
} }
function setAccess(a:Access, isset:Bool) {
changeAccess(
isset ? a : null,
isset ? null : a
);
return isset;
}
function changeAccess(add:Access, remove:Access) { function changeAccess(add:Access, remove:Access) {
var i = 0; var i = 0;
@@ -202,4 +216,4 @@ abstract Member(Field) from Field to Field {
if (add != null) if (add != null)
this.access.push(add); this.access.push(add);
} }
} }

View File

@@ -3,6 +3,7 @@ package tink.macro;
import haxe.macro.Expr; import haxe.macro.Expr;
class Metadatas { class Metadatas {
static var printer = new haxe.macro.Printer();
static public function toMap(m:Metadata) { static public function toMap(m:Metadata) {
var ret = new Map<String,Array<Array<Expr>>>(); var ret = new Map<String,Array<Array<Expr>>>();
if (m != null) if (m != null)
@@ -18,4 +19,8 @@ class Metadatas {
return return
if (m == null) []; if (m == null) [];
else [for (meta in m) if (meta.name == name) meta.params]; else [for (meta in m) if (meta.name == name) meta.params];
static public inline function toString(m:MetadataEntry) {
return printer.printMetadata(m);
}
} }

View File

@@ -23,14 +23,22 @@ 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: cf.isPublic ? [ APublic ] : [ APrivate ], access:
(cf.isPublic ? [ APublic ] : [ APrivate ])
#if haxe4 .concat(if (cf.isFinal) [AFinal] else []) #end
,
kind: switch([ cf.kind, cf.type ]) { kind: switch([ cf.kind, cf.type ]) {
#if haxe4
case [ FVar(_, _), ret ] if (cf.isFinal):
FVar(toComplexType(ret), null);
#end
case [ FVar(read, write), ret ]: case [ FVar(read, write), ret ]:
FProp( FProp(
varAccessToString(read, "get"), varAccessToString(read, "get"),
varAccessToString(write, "set"), varAccessToString(write, "set"),
toComplexType(ret), toComplexType(ret),
null); null
);
case [ FMethod(_), TFun(args, ret) ]: case [ FMethod(_), TFun(args, ret) ]:
FFun({ FFun({
args: [ args: [
@@ -70,7 +78,16 @@ class Sisyphus {
case TInst(_.get() => classType, params): case TInst(_.get() => classType, params):
switch (classType.kind) { switch (classType.kind) {
case KTypeParameter(_): case KTypeParameter(_):
direct();//TODO: check if the parameter is in scope, in which case the name can simply be used var ct = Types.asComplexType(classType.name);
switch Types.toType(ct) {
case Success(TInst(_.get() => cl, _)) if (
cl.kind.match(KTypeParameter(_))
&& cl.module == classType.module
&& cl.pack.join('.') == classType.pack.join('.')
): ct;
default:
direct();
}
default: default:
TPath(toTypePath(classType, params)); TPath(toTypePath(classType, params));
} }

View File

@@ -5,7 +5,6 @@ import haxe.ds.BalancedTree;
import haxe.macro.Context; import haxe.macro.Context;
import haxe.macro.Type; import haxe.macro.Type;
using haxe.macro.Tools;
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> {
@@ -16,18 +15,7 @@ class TypeMap<V> extends BalancedTree<Type, V> implements IMap<Type, V> {
super(); super();
} }
override function compare(k1:Type, k2:Type):Int { override function compare(k1:Type, k2:Type):Int
return k1.compare(k2, follow);
if (follow) {
k1 = k1.reduce();
k2 = k2.reduce();
}
return switch k1.getIndex() - k2.getIndex() {
case 0:
Reflect.compare(k1.toString(), k2.toString());//much to my surprise, this actually seems to work (at least with 3.4)
case v: v;
}
}
} }

View File

@@ -1,204 +1,371 @@
package tink.macro; package tink.macro;
import haxe.macro.Printer; import haxe.macro.Printer;
import Type in Enums;
import haxe.macro.Context;
import haxe.macro.Context; import haxe.macro.Expr;
import haxe.macro.Expr; import haxe.macro.Type;
import haxe.macro.Type;
using haxe.macro.Tools;
using tink.macro.Exprs; using tink.MacroApi;
using tink.macro.Positions; using tink.CoreApi;
using tink.macro.Functions;
using tink.CoreApi; class Types {
class Types { static public function definedType(typeName:String)
return
static public function definedType(typeName:String) try {
return Some(Context.getType(typeName));
try { }
Some(Context.getType(typeName)); catch (e:Dynamic)
} if (Std.string(e) == 'Type not found \'$typeName\'') None;
catch (e:Dynamic) else tink.core.Error.rethrow(e);
if (Std.string(e) == 'Type not found \'$typeName\'') None;
else tink.core.Error.rethrow(e); static var types = new Map<Int,Void->Type>();
static var idCounter = 0;
static var types = new Map<Int,Void->Type>(); static public function getID(t:Type, ?reduced = true)
static var idCounter = 0; return
static public function getID(t:Type, ?reduced = true) if (reduced)
return getID(reduce(t), false);
if (reduced) else
getID(reduce(t), false); switch (t) {
else case TAbstract(t, _): t.toString();
switch (t) { case TInst(t, _): t.toString();
case TAbstract(t, _): t.toString(); case TEnum(t, _): t.toString();
case TInst(t, _): t.toString(); case TType(t, _): t.toString();
case TEnum(t, _): t.toString(); default: null;
case TType(t, _): t.toString(); }
default: null;
} static public function accessToName(v:VarAccess, ?read = true)
return
static public function accessToName(v:VarAccess, ?read = true) switch (v) {
return case AccNormal, AccInline: 'default';
switch (v) { case AccNo: 'null';
case AccNormal, AccInline: 'default'; case AccNever: 'never';
case AccNo: 'null'; case AccCall: if (read) 'get' else 'set';
case AccNever: 'never'; default:
case AccCall: if (read) 'get' else 'set'; throw 'not implemented';
default: }
throw 'not implemented';
} static public function getMeta(type:Type)
return switch type {
static public function getMeta(type:Type) case TInst(_.get().meta => m, _): [m];
return switch type { case TEnum(_.get().meta => m, _): [m];
case TInst(_.get().meta => m, _): [m]; case TAbstract(_.get().meta => m, _): [m];
case TEnum(_.get().meta => m, _): [m]; case TType(_.get() => t, _): [t.meta].concat(getMeta(t.type));
case TAbstract(_.get().meta => m, _): [m]; case TLazy(f): getMeta(f());
case TType(_.get() => t, _): [t.meta].concat(getMeta(t.type)); default: [];
case TLazy(f): getMeta(f()); }
default: [];
} static function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) {
for (field in t.fields.get())
static function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) { if (!marker.exists(field.name)) {
for (field in t.fields.get()) marker.set(field.name, true);
if (!marker.exists(field.name)) { out.push(field);
marker.set(field.name, true); }
out.push(field); if (t.isInterface)
} for (t in t.interfaces)
if (t.isInterface) getDeclaredFields(t.t.get(), out, marker);
for (t in t.interfaces) else if (t.superClass != null)
getDeclaredFields(t.t.get(), out, marker); getDeclaredFields(t.superClass.t.get(), out, marker);
else if (t.superClass != null) }
getDeclaredFields(t.superClass.t.get(), out, marker);
} static public function mapTypeParams(a:Array<TypeParam>, f)
return
static var fieldsCache = new Map(); if (a == null) null;
static public function getFields(t:Type, ?substituteParams = true) else [for (p in a) switch p {
return case TPType(t): TPType(t.map(f));
switch (reduce(t)) { default: p;
case TInst(c, params): }];
var id = c.toString(),
c = c.get(); static public function mapTypeParamDecls(a:Array<TypeParamDecl>, f:ComplexType->ComplexType):Array<TypeParamDecl>
if (!fieldsCache.exists(id)) { return
var fields = []; if (a == null) null;
getDeclaredFields(c, fields, new Map()); else [for (p in a) {
fieldsCache.set(id, Success(fields)); name: p.name,
} meta: p.meta,
var ret = fieldsCache.get(id); params: mapTypeParamDecls(p.params, f),
if (substituteParams && ret.isSuccess()) { constraints: switch p.constraints {
var fields = Reflect.copy(ret.sure()); case null: null;
case a: [for (t in a) map(t, f)];
for (field in fields) }
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params); }];
}
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache static public function mapTypePath(p:TypePath, transform):TypePath
ret; return {
case TAnonymous(anon): Success(anon.get().fields); name: p.name,
default: Context.currentPos().makeFailure('type $t has no fields'); pack: p.pack,
} sub: p.sub,
params: mapTypeParams(p.params, transform),
static public function getStatics(t:Type) }
return
switch (reduce(t)) { static public function map(ct:ComplexType, transform:ComplexType->ComplexType) {
case TInst(t, _): Success(t.get().statics.get()); if (ct == null)
default: Failure('type has no statics'); return null;
} inline function rec(ct)
return map(transform(ct), transform);
static public function toString(t:ComplexType)
return new Printer().printComplexType(t); function mapFields(fields:Array<Field>):Array<Field>
return [for (f in fields) {
static public function isSubTypeOf(t:Type, of:Type, ?pos) name: f.name,
return pos: f.pos,
ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof(); kind: switch f.kind {
case FVar(t, e): FVar(rec(t), e);
static public function isDynamic(t:Type) case FProp(get, set, t, e): FProp(get, set, rec(t), e);
return switch reduce(t) { case FFun(f): FFun(Functions.mapSignature(f, transform));
case TDynamic(_): true; },
default: false; access: f.access,
} meta: switch f.meta {
case null: null;
static public function toType(t:ComplexType, ?pos:Position) case a: [for (m in a) {
return (macro @:pos(pos.sanitize()) { name: m.name,
var v:$t = null; pos: m.pos,
v; params: [for (e in m.params) e.mapTypes(transform)],
}).typeof(); }];
},
static public inline function instantiate(t:TypePath, ?args, ?pos) doc: f.doc,
return ENew(t, args == null ? [] : args).at(pos); }];
return transform(switch ct {
static public function asTypePath(s:String, ?params):TypePath { case TParent(t): TParent(rec(t));
var parts = s.split('.'); #if haxe4
var name = parts.pop(), case TNamed(n, t): TNamed(n, rec(t));
sub = null; case TIntersection(tl): TIntersection([for (t in tl) rec(t)]);
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) { #end
sub = name; case TOptional(t): TOptional(rec(t));
name = parts.pop(); case TAnonymous(fields):
if(sub == name) sub = null; TAnonymous(mapFields(fields));
} case TPath(p):
return { TPath(mapTypePath(p, transform));
name: name, case TExtend(paths, fields):
pack: parts, TExtend([for (p in paths) mapTypePath(p, transform)], mapFields(fields));
params: params == null ? [] : params, case TFunction(args, ret):
sub: sub TFunction([for (a in args) rec(a)], rec(ret));
}; });
} }
static public inline function asComplexType(s:String, ?params) static var fieldsCache = new Map();
return TPath(asTypePath(s, params)); static public function getFields(t:Type, ?substituteParams = true)
return
static public function reduce(type:Type, ?once) { switch (reduce(t)) {
function rec(t:Type) case TInst(c, params):
return if (once) t else reduce(t, false); var id = c.toString(),
return switch type { c = c.get();
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t); if (!fieldsCache.exists(id)) {
case TLazy(f): rec(f()); var fields = [];
case TType(_, _): rec(Context.follow(type, once)); getDeclaredFields(c, fields, new Map());
default: type; fieldsCache.set(id, Success(fields));
} }
} var ret = fieldsCache.get(id);
if (substituteParams && ret.isSuccess()) {
static public function isVar(field:ClassField) ret = Success([
return switch (field.kind) { for (field in ret.sure()) ({
case FVar(_, _): true; name: field.name,
default: false; type: haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params),
} pos: field.pos,
meta: field.meta,
static public function register(type:Void->Type):Int { doc: field.doc,
types.set(idCounter, type); expr: function () return field.expr(),
return idCounter++; isPublic: field.isPublic,
} params: field.params,
kind: field.kind,
static function paramsToComplex(params:Array<Type>):Array<TypeParam> overloads: field.overloads,
return [for (p in params) TPType(toComplex(p))]; #if haxe4
isExtern: field.isExtern,
static function baseToComplex(t:BaseType, params:Array<Type>) isFinal: field.isFinal,
return asComplexType(t.module + '.' + t.name, paramsToComplex(params)); #end
}:ClassField)
static public function toComplex(type:Type, ?options:{ ?direct: Bool }):ComplexType { ]);
var ret = }
if (options == null || options.direct != true) tink.macro.Sisyphus.toComplexType(type); #if !haxe4
else null; fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
if (ret == null) #end
ret = lazyComplex(function () return type); ret;
return ret; case TAnonymous(anon): Success(anon.get().fields);
} default: Context.currentPos().makeFailure('type $t has no fields');
}
static public function lazyComplex(f:Void->Type)
return static public function getStatics(t:Type)
TPath({ return
pack : ['tink','macro'], switch (reduce(t)) {
name : 'DirectType', case TInst(t, _): Success(t.get().statics.get());
params : [TPExpr(register(f).toExpr())], default: Failure('type has no statics');
sub : null, }
});
static function resolveDirectType() static public function getPosition(t:Type)
return return
switch reduce(Context.getLocalType()) { switch t {
case TInst(_, [TInst(_.get() => { kind: KExpr(e) }, _)]): case TInst(_.get() => {pos: pos}, _)
types[e.getInt().sure()]();//When using compiler server, this call throws on occasion, in which case modifying this file (to update mtime and invalidate the cache) will solve the problem | TAbstract(_.get() => {pos: pos}, _)
default: | TType(_.get() => {pos: pos}, _)
throw 'assert'; | TEnum(_.get() => {pos: pos}, _) : Success(pos);
} case TMono(_.get() => t) if (t != null): getPosition(t);
case TLazy(f): getPosition(f());
} case TDynamic(v) if(v != null): getPosition(v);
default: Failure('type "$t" has no position');
}
static public function toString(t:ComplexType)
return new Printer().printComplexType(t);
static public function unifiesWith(from:Type, to:Type)
return Context.unify(from, to);
static public function isSubTypeOf(t:Type, of:Type, ?pos)
return
if (Context.unify(t, of)) ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof();
else Failure(new Error(t.toString() + ' should be ' + of.toString(), pos.sanitize()));
static public function isDynamic(t:Type)
return switch reduce(t) {
case TDynamic(_): true;
default: false;
}
static public function toType(t:ComplexType, ?pos:Position)
return (macro @:pos(pos.sanitize()) {
var v:$t = null;
v;
}).typeof();
static public inline function instantiate(t:TypePath, ?args, ?pos)
return ENew(t, args == null ? [] : args).at(pos);
static public function asTypePath(s:String, ?params):TypePath {
var parts = s.split('.');
var name = parts.pop(),
sub = null;
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) {
sub = name;
name = parts.pop();
if(sub == name) sub = null;
}
return {
name: name,
pack: parts,
params: params == null ? [] : params,
sub: sub
};
}
static public inline function asComplexType(s:String, ?params)
return TPath(asTypePath(s, params));
static public function reduce(type:Type, ?once) {
function rec(t:Type)
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));
default: type;
}
}
static public function isVar(field:ClassField)
return switch (field.kind) {
case FVar(_, _): true;
default: false;
}
static public function register(type:Void->Type):Int {
types.set(idCounter, type);
return idCounter++;
}
static function paramsToComplex(params:Array<Type>):Array<TypeParam>
return [for (p in params) TPType(toComplex(p))];
static function baseToComplex(t:BaseType, params:Array<Type>)
return asComplexType(t.module + '.' + t.name, paramsToComplex(params));
static public function toComplex(type:Type, ?options:{ ?direct: Bool }):ComplexType {
var ret =
if (options == null || options.direct != true) tink.macro.Sisyphus.toComplexType(type);
else null;
if (ret == null)
ret = lazyComplex(function () return type);
return ret;
}
static public function intersect(types:Array<ComplexType>, ?pos:Position):Outcome<ComplexType, Error> {
if (types.length == 1) return Success(types[1]);
var paths = [],
fields = [];
for (t in types)
switch t {
case TPath(p): paths.push(p);
case TAnonymous(f):
for (f in f) fields.push(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));
}
return Success(TExtend(paths, fields));
}
static public function lazyComplex(f:Void->Type)
return
TPath({
pack : ['tink','macro'],
name : 'DirectType',
params : [TPExpr(register(f).toExpr())],
sub : null,
});
static function resolveDirectType()
return
switch reduce(Context.getLocalType()) {
case TInst(_, [TInst(_.get() => { kind: KExpr(e) }, _)]):
types[e.getInt().sure()]();//When using compiler server, this call throws on occasion, in which case modifying this file (to update mtime and invalidate the cache) will solve the problem
default:
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 var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;
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
return {
name: p.name,
constraints: switch p.t {
case TInst(_.get() => { kind: KTypeParameter(c)}, _): [for(c in c) c.toComplex()];
case _: throw 'unreachable';
}
}
static public function toTypeParam(p:TypeParameter):TypeParam
return TPType(p.t.toComplex());
}

11
tests/Functions.hx Normal file
View 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
View 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());
}
}

View File

@@ -12,6 +12,8 @@ class Run {
new Types(), new Types(),
new Positions(), new Positions(),
new TypeMapTest(), new TypeMapTest(),
new Functions(),
new Misc(),
]; ];
#end #end
macro static function test() { macro static function test() {

View File

@@ -1,5 +1,6 @@
package ; package ;
#if macro
import haxe.macro.Expr; import haxe.macro.Expr;
import haxe.macro.Context; import haxe.macro.Context;
@@ -20,6 +21,7 @@ class Types extends Base {
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')));
} }
@@ -41,10 +43,20 @@ class Types extends Base {
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());
MacroApi.pos().makeBlankType().toString();
} }
#if haxe4
function testFinal() {
var t = macro : {
final foo:Int;
};
switch t.toType().sure() {
case TAnonymous(_.get().fields => [f]): assertTrue(f.isFinal);
default:
}
}
#end
function testExpr() { function testExpr() {
assertEquals('VarChar<255>', (macro : VarChar<255>).toType().sure().toComplex().toString()); assertEquals('VarChar<255>', (macro : VarChar<255>).toType().sure().toComplex().toString());
} }
@@ -53,4 +65,5 @@ 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());
} }
} }
#end