Compare commits

..

20 Commits

Author SHA1 Message Date
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
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
17 changed files with 207 additions and 60 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

@@ -1,20 +1,20 @@
{
"name": "tink_macro",
"license": "MIT",
"description": "The macro toolkit ;)",
"classPath": "src",
"dependencies": {
"tink_core": ""
},
"url": "http://haxetink.org/tink_macro",
"contributors": [
"back2dos"
],
"version": "0.16.4",
"releasenote": "Helper for short identifiers.",
"tags": [
"tink",
"macro",
"utility"
],
"classPath": "src",
"description": "The macro toolkit ;)",
"contributors": [
"back2dos"
],
"releasenote": "Report invalid type in getFields.",
"version": "0.15.4",
"url": "http://haxetink.org/tink_macro",
"dependencies": {
"tink_core": ""
}
"license": "MIT"
}

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

@@ -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 Context.getLocalClass().get().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: //trace(Context.getLocalClass().get().fields.get().length); throw ('assert');
macro @:pos(pos) this.$name = $e;
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

@@ -311,10 +311,39 @@ 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));
@@ -399,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

@@ -70,10 +70,7 @@ class Sisyphus {
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));
}
@@ -102,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;