Compare commits

...

30 Commits

Author SHA1 Message Date
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
Juraj Kirchheim
0be2090232 Release 0.16.0 2018-01-16 17:14:42 +01:00
Juraj Kirchheim
0dc774db21 Add support for scoping. 2017-12-24 12:23:10 +01:00
Juraj Kirchheim
a7c22bf5af Return bulk added members from ClassBuilder. 2017-12-18 14:26:25 +01:00
Juraj Kirchheim
a164b335ed Improve Type to ComplexType conversion for type parameters. 2017-12-18 11:28:23 +01:00
Juraj Kirchheim
d02306eebd Add class reification shorthand to ClassBuilder. 2017-12-18 11:27:59 +01:00
Juraj Kirchheim
ff9ef59445 Expose more logic from BuildCache. 2017-12-18 11:27:32 +01:00
Juraj Kirchheim
bfc5c2b88f Factor out build cache's param retrieval into separate function. 2017-12-15 12:27:01 +01:00
Juraj Kirchheim
57042819b1 Release 0.15.4 2017-12-08 10:41:26 +01:00
Juraj Kirchheim
82d15d9ffc Report invalid type in getFields. 2017-12-08 10:40:24 +01:00
Juraj Kirchheim
0d9fff7e01 Reverse order in setter bypass to make type inference suffer less. 2017-12-06 17:13:01 +01:00
Juraj Kirchheim
45d5f3e0ea Release 0.15.3 2017-12-01 16:32:19 +01:00
Juraj Kirchheim
800891bee2 Fixes #21. 2017-12-01 16:31:22 +01:00
Juraj Kirchheim
0802bf7f79 Minor tweak. 2017-12-01 16:30:42 +01:00
Juraj Kirchheim
42897c7cd5 Merge branch 'master' of https://github.com/haxetink/tink_macro 2017-11-30 13:05:06 +01:00
Juraj Kirchheim
0e902745b1 Release 0.15.2 2017-11-30 13:04:39 +01:00
Juraj Kirchheim
3dec4fa404 Improve position reporting. 2017-11-30 13:04:07 +01:00
Juraj Kirchheim
c0ec918f52 Merge pull request #20 from markknol/patch-1
Update README.md
2017-11-30 11:52:41 +01:00
Mark Knol
4eb9729f0e Update README.md
Fix links in index
2017-11-30 10:56:14 +01:00
Juraj Kirchheim
7586b54ece Release 0.15.1 2017-11-30 10:40:31 +01:00
Juraj Kirchheim
21c7d12be7 Don't drop positions when typing. 2017-11-30 10:39:57 +01:00
Juraj Kirchheim
f27d2796b1 Release 0.15.0 2017-09-25 10:52:48 +02:00
Juraj Kirchheim
417a92cabc Make it compile for Haxe 4. 2017-09-25 10:50:36 +02:00
Kevin Leung
4457ea7dee readme tweaks 2017-09-21 15:02:16 +08:00
17 changed files with 154 additions and 62 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" => [[]]]`
@@ -303,7 +302,7 @@ abstract Member from Field to Field {
Most of the API should be self-explaining. The `isBound` property is a bad name to convey the concept that a field can be either `inline` (`true`) or `dynamic` (`false`) or neither (`null`). Equally, `isPublic` is nullable which means that normally defaults to `private`.
The `publish` method will make a field `public` if it is not `private`. This can also be done with `if (m.isPublic == null) m.isPublic = true;` but the implementation is far more efficient - for what its worth.
The `publish` method will make a field `public` if it is not explicitly marked as `private`. This can also be done with `if (m.isPublic == null) m.isPublic = true;` but the implementation is far more efficient - for what its worth.
The `extractMeta` method will "peel of" the first tag with a given `name` - if available. Note that the tag will be removed from the member.
@@ -391,7 +390,7 @@ The `init` method is the swiss army knife of initializing fields. The `options.p
#### Setter Bypass
It is important to know that when you initialize a field with `options.bypass` set to true, existing setters will by bypassed. That's particularly helpful if your setter triggers a side effect that you don't want triggered. This is achieved by generating the assignment as `(untyped this).$name = $value`. To make the code typesafe again, this is prefixed with `if (false) { var __tmp = this.$name; __tmp = $value; }`. This code is later thrown out by the compiler. Its role is to ensure type safety without interfering with the normal typing order.
It is important to know that when you initialize a field with `options.bypass` set to true, existing setters will be bypassed. That's particularly helpful if your setter triggers a side effect that you don't want triggered. This is achieved by generating the assignment as `(untyped this).$name = $value`. To make the code typesafe again, this is prefixed with `if (false) { var __tmp = this.$name; __tmp = $value; }`. This code is later thrown out by the compiler. Its role is to ensure type safety without interfering with the normal typing order.
Setter bypass also causes the field to gain an `@:isVar`. And currently, with `-dce full`, additional code will be generated to avoid the field to be eliminated.

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": "Add way to get constructor args.",
"version": "0.14.2",
"releasenote": "Support Type->ComplexType conversion for expression type params.",
"version": "0.16.2",
"url": "http://haxetink.org/tink_macro",
"dependencies": {
"tink_core": ""

View File

@@ -106,22 +106,30 @@ class BuildCache {
}));
}
static public function getParams(name:String, ?pos:Position)
return
switch Context.getLocalType() {
case TInst(_.toString() == name => true, v):
Success(v);
case TInst(_.get() => { pos: pos }, _):
pos.makeFailure('Expected $name');
case v:
pos.makeFailure('$v should be a class');
}
static public function getParam(name:String, ?pos:Position)
return
getParams(name, pos)
.flatMap(function (args:Array<Type>) return switch args {
case [v]: Success(v);
case []: pos.makeFailure('type parameter expected');
default: pos.makeFailure('too many parameters');
});
static public function getType(name, ?type, ?pos:Position, build:BuildContext->TypeDefinition) {
if (pos == null)
pos = Context.currentPos();
if (type == null)
switch Context.getLocalType() {
case TInst(_.toString() == name => true, [v]):
type = v;
case TInst(_.toString() == name => true, _):
Context.fatalError('type parameter expected', pos);
case TInst(_.get() => { pos: pos }, _):
Context.fatalError('Expected $name', pos);
default:
throw 'assert';
}
type = getParam(name, pos).sure();
var forName =
switch cache[name] {
@@ -129,7 +137,7 @@ class BuildCache {
case v: v;
}
return forName.get(type, pos, build);
return forName.get(type, pos.sanitize(), build);
}
}

View File

@@ -175,6 +175,12 @@ class ClassBuilder {
return m;
}
public function addMembers(td:TypeDefinition):Array<Member> {
for (f in td.fields)
addMember(f);
return td.fields;
}
public function addMember(m:Member, ?front:Bool = false):Member {
doAddMember(m, front);

View File

@@ -100,7 +100,7 @@ class Constructor {
case Success(member): member.addMeta(':isVar');
default:
}
addStatement(macro @:pos(pos) (cast this).$name = if (true) $e else this.$name, options.prepend);//TODO: this seems to report type errors here rather than at the declaration position
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
}
else
addStatement(macro @:pos(pos) this.$name = $e, options.prepend);
@@ -136,4 +136,4 @@ class Constructor {
meta : this.meta,
}
}
}
}

View File

@@ -311,17 +311,45 @@ class Exprs {
static public inline function resolve(s:String, ?pos)
return drill(s.split('.'), pos);
static var scopes = new Array<Array<Var>>();
static function inScope<T>(a:Array<Var>, f:Void->T) {
scopes.push(a);
inline function leave()
scopes.pop();
try {
var ret = f();
leave();
return ret;
}
catch (e:Dynamic) {
leave();
return Error.rethrow(e);
}
}
static public function scoped<T>(f:Void->T)
return inScope([], f);
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)
expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos);
Success(Context.typeof(expr));
}
catch (e:Error) {
var m:Dynamic = e.message;
e.pos.makeFailure(m);
catch (e:haxe.macro.Error) {
e.pos.makeFailure(e.message);
}
catch (e:Dynamic) {
expr.pos.makeFailure(e);

View File

@@ -12,7 +12,7 @@ class Positions {
return
switch outcome {
case Success(d): d;
case Failure(f): sanitize(pos).error(f);
case Failure(f): pos.error(f);
}
static public function makeBlankType(pos:Position):ComplexType
@@ -32,7 +32,17 @@ class Positions {
static public function error(pos:Position, error:Dynamic):Dynamic
return errorFunc(sanitize(pos), Std.string(error));
return
switch Std.instance(error, tink.core.Error) {
case null: errorFunc(sanitize(pos), Std.string(error));
case error:
errorFunc(
error.pos,
error.message +
if (pos == null) ''
else ' (referenced from ' + Std.string(pos).substr(5)
);
}
static function contextError(pos:Position, error:String):Dynamic
return Context.fatalError(error, pos);

View File

@@ -17,6 +17,7 @@ class Sisyphus {
case AccCall: getOrSet;
case AccInline: "default";
case AccRequire(_, _): "default";
default: throw "not implemented";
}
}
if (cf.params.length == 0) {
@@ -52,21 +53,24 @@ class Sisyphus {
}
}
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return {
inline function direct()
return Types.toComplex(type, { direct: true });
switch (type) {
case null:
null;
case TMono(_.get() => t):
t == null ? Types.toComplex(type, { direct: true }) : toComplexType(t);
case TEnum(_.get().isPrivate => true, _): direct();
case TInst(_.get().isPrivate => true, _): direct();
case TType(_.get().isPrivate => true, _): direct();
case TAbstract(_.get().isPrivate => true, _): direct();
case TMono(_): direct();
case TEnum(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
case TInst(_.get() => classType, params):
switch (classType.kind) {
case KTypeParameter(_):
TPath({
name: classType.name,
pack: [],
});
direct();//TODO: check if the parameter is in scope, in which case the name can simply be used
default:
TPath(toTypePath(classType, params));
}
@@ -92,14 +96,23 @@ class Sisyphus {
default:
throw "Invalid type";
}
}
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

@@ -94,7 +94,7 @@ class Types {
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
ret;
case TAnonymous(anon): Success(anon.get().fields);
default: Context.currentPos().makeFailure('type has no fields');
default: Context.currentPos().makeFailure('type $t has no fields');
}
static public function getStatics(t:Type)

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 L> = String;