Compare commits

..

24 Commits

Author SHA1 Message Date
Juraj Kirchheim
2310b6d8e4 Release 0.16.7 2018-08-23 11:24:32 +02:00
Kevin Leung
7f13916154 Fix haxe version check 2018-08-22 21:40:17 +08:00
Juraj Kirchheim
2b7204f8d3 Release 0.16.6 2018-07-26 12:57:57 +02:00
Juraj Kirchheim
7ca0d4a650 Merge branch 'master' of https://github.com/haxetink/tink_macro 2018-07-26 12:57:08 +02:00
Juraj Kirchheim
e38f5ae147 Expand metadata API of member. 2018-07-26 12:55:05 +02:00
Juraj Kirchheim
4186225eb5 Add haxe3 polyfill for ObjectField. 2018-07-26 12:54:31 +02:00
Juraj Kirchheim
f5a3d73c55 Merge pull request #22 from kLabz/fix/constructor-without-expr
Fix ClassBuilder's getConstructor() when super class is extern
2018-05-08 16:33:51 +02:00
k
f65df0e94b Fix ClassBuilder's getConstructor() when super class is extern
Handle super class constructor's expr being `null`, which happens
when the super class is an extern with a constructor definition.
2018-05-08 16:23:12 +02:00
Juraj Kirchheim
55a4b1463f Release 0.16.5 2018-04-07 04:04:16 +02:00
Juraj Kirchheim
0a6e39969b Fix direct initialization. 2018-04-07 04:03:09 +02:00
Juraj Kirchheim
f051345863 Various little fixes. 2018-04-02 13:06:42 +02:00
Juraj Kirchheim
74e37b4bf1 Release 0.16.4 2018-03-29 11:08:49 +02:00
Juraj Kirchheim
42821979d9 Totally insane workaround for https://github.com/HaxeFoundation/haxe/issues/5039 2018-03-29 02:00:05 +02:00
Juraj Kirchheim
2aa0b07e9f Helper for short identifiers. 2018-03-29 01:59:27 +02:00
Juraj Kirchheim
67e680f67f Release 0.16.3 2018-03-12 11:17:12 +01:00
Juraj Kirchheim
ebade45e63 Maintain Haxe 3 nullability semantics in Haxe 4. 2018-03-12 11:16:13 +01:00
Juraj Kirchheim
40497bb1fc Fix tests for Haxe 3.2.1. 2018-01-31 20:36:10 +01:00
Juraj Kirchheim
d80b03fe9d Release 0.16.2 2018-01-31 16:06:27 +01:00
Juraj Kirchheim
f269d13abf Add workaround for HaxeFoundation/haxe#6830. 2018-01-31 15:43:52 +01:00
Juraj Kirchheim
f271cc880a Release 0.16.1 2018-01-29 10:12:58 +01:00
Juraj Kirchheim
9cd59d3989 Handle module types properly. 2018-01-29 10:12:06 +01:00
Juraj Kirchheim
40ee99df39 Lixify dependencies. 2018-01-29 10:11:50 +01:00
Juraj Kirchheim
1cb27daf46 Fix tests for Haxe 4. 2018-01-16 17:21:27 +01:00
Juraj Kirchheim
a9bea4287a Fix headline structure. 2018-01-16 17:20:24 +01:00
20 changed files with 210 additions and 35 deletions

1
.haxerc Normal file
View File

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

View File

@@ -13,9 +13,10 @@ matrix:
install:
- haxelib install travix
- haxelib run travix install
- haxelib install hx3compat
script:
- haxelib run travix node
- haxelib run travix node -lib hx3compat
env:
secure: T4SCtY5qmEsK1ARWPevJmqLm23tv4CobLrbPOQV3FsoQno7FCP1S/+9GmuoJKzeTjWMzdTeDsp8TVwZ6AyGjvhl2nZNjhU+QTsir4tfbYYRyvsz/QK6pveFbPQVv7OsnnaB4wbZtqGZ8mzFeQf7Ol4tsNe7iUFJb/iVc+4/lUxo=

View File

@@ -17,18 +17,17 @@ The library is build on top of the haxe macro API and `tink_core`, having three
<!-- START INDEX -->
- [Macro API](#macro-api)
-
- [Expression Tools](#expression-tools)
- [Basic Helpers](#basic-helpers)
- [Extracting Constants](#extracting-constants)
- [Shortcuts](#shortcuts)
- [Type Inspection](#type-inspection)
- [Advanced Transformations](#advanced-transformations)
- [Position Tools](#position-tools)
- [Type Tools](#type-tools)
- [Function Tools](#function-tools)
- [Operation Tools](#operation-tools)
- [Metadata Tools](#metadata-tools)
- [Expression Tools](#expression-tools)
- [Basic Helpers](#basic-helpers)
- [Extracting Constants](#extracting-constants)
- [Shortcuts](#shortcuts)
- [Type Inspection](#type-inspection)
- [Advanced Transformations](#advanced-transformations)
- [Position Tools](#position-tools)
- [Type Tools](#type-tools)
- [Function Tools](#function-tools)
- [Operation Tools](#operation-tools)
- [Metadata Tools](#metadata-tools)
- [Build Infrastructure](#build-infrastructure)
- [Member](#member)
- [ClassBuilder](#classbuilder)
@@ -51,9 +50,9 @@ It is suggested to use this API by `using tink.MacroApi;`
Apart form `tink_macro` specific things, it will also use `haxe.macro.ExprTools` and `tink.core.Outcome`.
### Expression Tools
## Expression Tools
#### Basic Helpers
### Basic Helpers
- `at(e:ExprDef, ?pos:Position):Expr`
A short hand for creating expression as for example `EReturn(value).at(position)`, instead of the more verbose `{ expr: EReturn(value), pos: position }`.
@@ -69,7 +68,7 @@ Traces the string representation of an expression and returns it.
- `concat(e1:Expr, e2:Expr):Expr`
Concats two expressions into a block. If either sub-expression is a block itself, it gets flattened into the resulting block.
#### Extracting Constants
### Extracting Constants
- `isWildcard(e:Expr):Bool`
Checks whether an expression is the identifier `_`
@@ -82,7 +81,7 @@ Attempts extracting an identifier from an expression. Note that an identifier ca
- `getName(e:Expr):Outcome<String, tink.core.Error>`
Attempts extracting a name, i.e. a string constant or identifier from an expression
#### Shortcuts
### Shortcuts
Often reification is prefereable to these shortcuts - if applicable. Unlike reification, the position of these expressions will default to `Context.currentPos()` rather than the position where they were created.
@@ -119,7 +118,7 @@ Generates a simple if statement.
- `iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position):Expr`
Will loop over `target` with a loop variable called `loopVar` using `body`-
#### Type Inspection
### Type Inspection
- `is(e:Expr, c:ComplexType):Bool`
Tells you whether a given expression has a given type.
@@ -129,7 +128,7 @@ Inspects, whether an expression can be iterated over and if so returns the eleme
- `typeof(expr:Expr, ?locals:Array<Var>):Outcome<Type, tink.core.Error>`
Attempts to determine the type of an expression. Note that you can use `locals` to hint the compiler the type of certain identifiers. For example if you are in a build macro, and you want to get the type of a subexpression of a method body, you could "fake" the other members of the class as local variables, because in that context, the other members do not yet exists from the compiler's perspective.
#### Advanced Transformations
### Advanced Transformations
- `has(e:Expr, condition:Expr->Bool, ?options: { ?enterFunctions: Bool })`
This function actually does no transformation, but is very close to the rest of these functions. It allows you to check whether an expression has a sub-expression that satisfies `condition`. By default, it does not enter nested functions.
@@ -187,7 +186,7 @@ This will traverse an expression and will apply the `yielder` to the "leafs", wh
If you set `options.leaveLoops` to `true`, then loops (both for and while) will be considered leafs.
### Position Tools
## Position Tools
- `sanitize(pos:Position):Position`
Returns the position ITself or `Context.currentPos()` if it's null.
@@ -202,7 +201,7 @@ Creates a failed `Outcome` associated with the supplied position.
- `getOutcome<D, F>(pos:Position, outcome:Outcome<D, F>):D`
Attempts getting the result of the supplied outcome. If it is a failure, it will cause an error at the given position.
### Type Tools
## Type Tools
- `getID(t:Type, ?reduced = true):Null<String>`
Returns a String identifier for a type if available. By default, the type will be reduced prior to getting its name (typedefs are resolved etc.). With `reduced = false` you can also get the name of a typedef.
@@ -225,7 +224,7 @@ Will tell you whether a field is a variable or not. Signature is likely to chang
- `toComplex(type:Type, ?option:{ ?direct: Bool }):ComplexType`
Will convert a `Type` to a `ComplexType`. Ideally this is done with `Context.toComplexType` but for monomorphs and the like, this builtin method fails and `tink_macro` uses a hack to make it work none the less. You can also use `{ direct : true }` to force this hack in case the translation fails (which can be the case with private types).
### Function Tools
## Function Tools
- `asExpr(f:Function, ?name:String, ?pos:Position):Expr`
Converts a function to an expression, i.e. a local function definition.
@@ -236,7 +235,7 @@ A shorthand to create function arguments.
- `getArgIdents(f:Function):Array<Expr>`
Will extract the argument list of a function as an expression list of identifiers (usefull when writing call-forwarding macros or the like).
### Operation Tools
## Operation Tools
- `get(o:Binop, e:Expr):Outcome<{ e1:Expr, e2:Expr, pos:Position }, tink.core.Error>`
Attempts to extract a specific binary operation from an expression.
@@ -250,7 +249,7 @@ Attempts to extract a specific unary operation from an expression.
- `getUnop(e:Expr):Outcome<{ op:Unop, e:Expr, postFix:Bool, pos:Position }, tink.core.Error>`
Attempts to decompose an expression into the parts of a unary operation.
### Metadata Tools
## Metadata Tools
- `toMap(m:Metadata):Map<String, Array<Array<Expr>>`
Will deconstruct an array of metadata tags to a `Map` mapping the tag names to an array of the argument lists of each tag with that name. So `@foo(1) @foo(2) @bar` becomes `["foo" => [[1], [2]], "bar" => [[]]]`

View File

@@ -0,0 +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
--macro allowPackage('sys')
--macro _hxnodejs.VersionWarning.include()

View File

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

View File

@@ -0,0 +1,3 @@
-D tink_macro
-cp src
-lib tink_core

View File

@@ -0,0 +1,4 @@
# @run: haxelib run-dir travix ${HAXESHIM_LIBCACHE}/travix/0.10.3/haxelib
# @install: lix --silent download "haxelib:travix#0.10.3" into travix/0.10.3/haxelib
-D travix=0.10.3
-cp ${HAXESHIM_LIBCACHE}/travix/0.10.3/haxelib/src

View File

@@ -11,8 +11,8 @@
"contributors": [
"back2dos"
],
"releasenote": "Support artificial scopes when typing. Expose more BuildCache logic. Further improve Type -> ComplexType transformation.",
"version": "0.16.0",
"releasenote": "Fix haxe 4 compat.",
"version": "0.16.7",
"url": "http://haxetink.org/tink_macro",
"dependencies": {
"tink_core": ""

View File

@@ -37,3 +37,50 @@ class MacroApi {
return haxe.macro.Context.currentPos();
}
#if (haxe_ver >= 4)
typedef ObjectField = haxe.macro.Expr.ObjectField;
typedef QuoteStatus = haxe.macro.Expr.QuoteStatus;
#else
enum QuoteStatus {
Unquoted;
Quoted;
}
private typedef F = {
var field:String;
var expr:haxe.macro.Expr;
}
@:forward
abstract ObjectField(F) to F {
static var QUOTED = "@$__hx__";
inline function new(o) this = o;
public var field(get, never):String;
function get_field()
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:
new ObjectField({ field: o.field, expr: o.expr });
default:
new ObjectField({ field: QUOTED + o.field, expr: o.expr });
}
@:from static function ofOld(o:F):ObjectField
return new ObjectField(o);
}
#end

View File

@@ -74,7 +74,11 @@ class BuildCache {
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
name: 't$i',
pos: pos,
kind: FVar(types[i].toComplexType()),
kind: FVar(switch types[i] {
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({

View File

@@ -63,7 +63,9 @@ class ClassBuilder {
if (cl.constructor != null) {
try {
var ctor = cl.constructor.get();
var func = Context.getTypedExpr(ctor.expr()).getFunction().sure();
var ctorExpr = ctor.expr();
if (ctorExpr == null) throw 'Super constructor has no expression';
var func = Context.getTypedExpr(ctorExpr).getFunction().sure();
for (arg in func.args) //this is to deal with type parameter substitutions
arg.type = null;
@@ -196,4 +198,4 @@ class ClassBuilder {
p(builder);
return builder.export(verbose);
}
}
}

View File

@@ -0,0 +1,3 @@
package tink.macro;
class ConstParam<Const> {}

View File

@@ -1,8 +1,10 @@
package tink.macro;
import haxe.macro.Type;
import haxe.macro.Expr;
import haxe.macro.Context;
import tink.core.Pair;
using haxe.macro.Tools;
using tink.MacroApi;
enum FieldInit {
@@ -100,7 +102,43 @@ class Constructor {
case Success(member): member.addMeta(':isVar');
default:
}
addStatement(macro @:pos(pos) (cast this).$name = if (false) this.$name else $e, options.prepend);//TODO: this seems to report type errors here rather than at the declaration position
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) {
switch t.expr {
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:
pos.error('this direct initialization causes the compiler to do really weird things');
case f:
switch f.kind {
case FVar(_, AccNormal | AccNo):
macro @:pos(pos) this.$name = $e;
case FVar(AccNever, AccNever):
macro @:pos(pos) this.$name = $e;
case FVar(AccNo | AccNormal, AccNever):
setDirectly(Context.typeExpr(macro @:pos(pos) this.$name));
case FVar(AccCall, AccNever):
setDirectly(fields['get_$name'].expr());
case FVar(_, AccCall):
setDirectly(fields['set_$name'].expr());
default:
pos.error('not implemented');
}
}
}).bounce(), options.prepend);
}
else
addStatement(macro @:pos(pos) this.$name = $e, options.prepend);

View File

@@ -428,6 +428,22 @@ class Exprs {
}
}
static var FIRST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
static var LATER = FIRST + '0123456789';
static public function shortIdent(i:Int) {
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);
}
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";

View File

@@ -32,6 +32,7 @@ abstract Member(Field) from Field to Field {
}
public var name(get, set):String;
public var meta(get, set):Metadata;
public var doc(get, set):Null<String>;
public var kind(get, set):FieldType;
public var pos(get, set):Position;
@@ -76,6 +77,11 @@ abstract Member(Field) from Field to Field {
}
return pos.makeFailure('missing @$name');
}
public function metaNamed(name)
return
if (this.meta == null) [];
else [for (tag in this.meta) if (tag.name == name) tag];
public inline function asField():Field return this;
public function publish()
@@ -86,6 +92,12 @@ abstract Member(Field) from Field to Field {
this.access.push(APublic);
}
inline function get_meta() return switch this.meta {
case null: this.meta = [];
case v: v;
}
inline function set_meta(param) return this.meta = param;
inline function get_name() return this.name;
inline function set_name(param) return this.name = param;

View File

@@ -99,11 +99,20 @@ class Sisyphus {
}
static function toTypePath(baseType : BaseType, params : Array<Type>) : TypePath return {
var module = baseType.module;
var name = module.substring(module.lastIndexOf(".") + 1);
var sub = switch baseType.name {
case _ == name => true: null;
case v: v;
}
{
pack: baseType.pack,
name: module.substring(module.lastIndexOf(".") + 1),
sub: baseType.name,
params: [ for (t in params) TPType(toComplexType(t)) ],
name: name,
sub: sub,
params: [for (t in params) switch t {
case TInst(_.get().kind => KExpr(e), _): TPExpr(e);
default: TPType(toComplexType(t));
}],
}
}
}

View File

@@ -146,8 +146,16 @@ class Types {
static public inline function asComplexType(s:String, ?params)
return TPath(asTypePath(s, params));
static public inline function reduce(type:Type, ?once)
return Context.follow(type, once);
static public function 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) {

View File

@@ -1,5 +1,6 @@
package ;
import haxe.macro.Context;
import haxe.macro.Expr;
using tink.MacroApi;
@@ -7,6 +8,14 @@ class Exprs extends Base {
function exprEq(e1:Expr, e2:Expr) {
assertEquals(e1.toString(), e2.toString());
}
function testShort() {
for (i in 0...100) {
var id = (100 * i).shortIdent();
Context.parseInlineString(id, (macro null).pos);
assertTrue(id.length <= 3);
}
}
function testGet() {
assertEquals('foo', (macro foo).getIdent().sure());
assertEquals('foo', (macro "foo").getString().sure());

View File

@@ -44,4 +44,13 @@ class Types extends Base {
MacroApi.pos().makeBlankType().toString();
}
function testExpr() {
assertEquals('VarChar<255>', (macro : VarChar<255>).toType().sure().toComplex().toString());
}
function testToComplex() {
assertEquals('String', Context.getType('String').toComplex().toString());
assertEquals('tink.CoreApi.Noise', Context.getType('tink.CoreApi.Noise').toComplex().toString());
}
}

1
tests/VarChar.hx Normal file
View File

@@ -0,0 +1 @@
typedef VarChar<Const> = String;