Compare commits
116 Commits
0.12.4
...
genes-issu
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a77aedb458 | ||
![]() |
b4fa5f4fe0 | ||
![]() |
e59fab499d | ||
![]() |
3356172b7f | ||
![]() |
4fd03ed667 | ||
![]() |
4cb5ee4196 | ||
![]() |
888481094a | ||
![]() |
7686fe3e35 | ||
![]() |
747d07bfc5 | ||
![]() |
00640d8328 | ||
![]() |
77973f6007 | ||
![]() |
0df9c28ace | ||
![]() |
d90fb966d2 | ||
![]() |
9018b06279 | ||
![]() |
6ed0424093 | ||
![]() |
53868cc6f3 | ||
![]() |
296cc6c861 | ||
![]() |
56c2ed9642 | ||
![]() |
ac908bc82d | ||
![]() |
1604a135ae | ||
![]() |
5e65f5354f | ||
![]() |
8f81256731 | ||
![]() |
1b717919fa | ||
![]() |
3d9aec835e | ||
![]() |
86fa47eb0d | ||
![]() |
b5605e48ac | ||
![]() |
6f5eba983e | ||
![]() |
d5a44a9268 | ||
![]() |
a11804c3ab | ||
![]() |
380ceb39ea | ||
![]() |
c48d445a54 | ||
![]() |
b7e413d839 | ||
![]() |
e1e487079b | ||
![]() |
5f959f788e | ||
![]() |
4f84570bb7 | ||
![]() |
e398d0e694 | ||
![]() |
13d11f4f66 | ||
![]() |
d6142847eb | ||
![]() |
e6a0304016 | ||
![]() |
69368937a4 | ||
![]() |
df2389d2c8 | ||
![]() |
aad80afec2 | ||
![]() |
b68c6ac4a1 | ||
![]() |
682aeacce8 | ||
![]() |
9bcec770eb | ||
![]() |
2310b6d8e4 | ||
![]() |
7f13916154 | ||
![]() |
2b7204f8d3 | ||
![]() |
7ca0d4a650 | ||
![]() |
e38f5ae147 | ||
![]() |
4186225eb5 | ||
![]() |
f5a3d73c55 | ||
![]() |
f65df0e94b | ||
![]() |
55a4b1463f | ||
![]() |
0a6e39969b | ||
![]() |
f051345863 | ||
![]() |
74e37b4bf1 | ||
![]() |
42821979d9 | ||
![]() |
2aa0b07e9f | ||
![]() |
67e680f67f | ||
![]() |
ebade45e63 | ||
![]() |
40497bb1fc | ||
![]() |
d80b03fe9d | ||
![]() |
f269d13abf | ||
![]() |
f271cc880a | ||
![]() |
9cd59d3989 | ||
![]() |
40ee99df39 | ||
![]() |
1cb27daf46 | ||
![]() |
a9bea4287a | ||
![]() |
0be2090232 | ||
![]() |
0dc774db21 | ||
![]() |
a7c22bf5af | ||
![]() |
a164b335ed | ||
![]() |
d02306eebd | ||
![]() |
ff9ef59445 | ||
![]() |
bfc5c2b88f | ||
![]() |
57042819b1 | ||
![]() |
82d15d9ffc | ||
![]() |
0d9fff7e01 | ||
![]() |
45d5f3e0ea | ||
![]() |
800891bee2 | ||
![]() |
0802bf7f79 | ||
![]() |
42897c7cd5 | ||
![]() |
0e902745b1 | ||
![]() |
3dec4fa404 | ||
![]() |
c0ec918f52 | ||
![]() |
4eb9729f0e | ||
![]() |
7586b54ece | ||
![]() |
21c7d12be7 | ||
![]() |
f27d2796b1 | ||
![]() |
417a92cabc | ||
![]() |
4457ea7dee | ||
![]() |
209847e5ea | ||
![]() |
c94e4a0337 | ||
![]() |
cc009b2026 | ||
![]() |
0a36ee8e2b | ||
![]() |
5879773566 | ||
![]() |
58262a1810 | ||
![]() |
9ceadeb88f | ||
![]() |
15d75965ad | ||
![]() |
c7634e0a29 | ||
![]() |
49e48bc801 | ||
![]() |
c150b84f18 | ||
![]() |
04f86ce341 | ||
![]() |
45380371d8 | ||
![]() |
d94d946301 | ||
![]() |
56b7caaf01 | ||
![]() |
73e9da8435 | ||
![]() |
bf23337923 | ||
![]() |
02bd3bf69d | ||
![]() |
82a3417b97 | ||
![]() |
1f986c3732 | ||
![]() |
20e998fd12 | ||
![]() |
0f9c0d1e97 | ||
![]() |
2ae13d534e | ||
![]() |
65d341f1e1 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/bin
|
54
.travis.yml
54
.travis.yml
@@ -1,27 +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
|
||||||
|
|
||||||
script:
|
|
||||||
- haxelib run travix node
|
|
||||||
|
|
||||||
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: ls && haxelib install travis && haxelib run travis 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
|
||||||
|
6
.vscode/tasks.json
vendored
Normal file
6
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "0.1.0",
|
||||||
|
"command": "haxelib",
|
||||||
|
"args": ["run", "travix", "node"],
|
||||||
|
"problemMatcher": "$haxe"
|
||||||
|
}
|
53
README.md
53
README.md
@@ -1,5 +1,7 @@
|
|||||||
# Tinkerbell Macro Library
|
# Tinkerbell Macro Library
|
||||||
[](https://gitter.im/haxetink/public)
|
|
||||||
|
[](https://travis-ci.org/haxetink/tink_macro)
|
||||||
|
[](https://gitter.im/haxetink/public)
|
||||||
|
|
||||||
Explained in current marketing speak, `tink_macro` is *the* macro toolkit ;)
|
Explained in current marketing speak, `tink_macro` is *the* macro toolkit ;)
|
||||||
|
|
||||||
@@ -15,18 +17,17 @@ The library is build on top of the haxe macro API and `tink_core`, having three
|
|||||||
|
|
||||||
<!-- START INDEX -->
|
<!-- START INDEX -->
|
||||||
- [Macro API](#macro-api)
|
- [Macro API](#macro-api)
|
||||||
-
|
- [Expression Tools](#expression-tools)
|
||||||
- [Expression Tools](#expression-tools)
|
- [Basic Helpers](#basic-helpers)
|
||||||
- [Basic Helpers](#basic-helpers)
|
- [Extracting Constants](#extracting-constants)
|
||||||
- [Extracting Constants](#extracting-constants)
|
- [Shortcuts](#shortcuts)
|
||||||
- [Shortcuts](#shortcuts)
|
- [Type Inspection](#type-inspection)
|
||||||
- [Type Inspection](#type-inspection)
|
- [Advanced Transformations](#advanced-transformations)
|
||||||
- [Advanced Transformations](#advanced-transformations)
|
- [Position Tools](#position-tools)
|
||||||
- [Position Tools](#position-tools)
|
- [Type Tools](#type-tools)
|
||||||
- [Type Tools](#type-tools)
|
- [Function Tools](#function-tools)
|
||||||
- [Function Tools](#function-tools)
|
- [Operation Tools](#operation-tools)
|
||||||
- [Operation Tools](#operation-tools)
|
- [Metadata Tools](#metadata-tools)
|
||||||
- [Metadata Tools](#metadata-tools)
|
|
||||||
- [Build Infrastructure](#build-infrastructure)
|
- [Build Infrastructure](#build-infrastructure)
|
||||||
- [Member](#member)
|
- [Member](#member)
|
||||||
- [ClassBuilder](#classbuilder)
|
- [ClassBuilder](#classbuilder)
|
||||||
@@ -49,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`.
|
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`
|
- `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 }`.
|
A short hand for creating expression as for example `EReturn(value).at(position)`, instead of the more verbose `{ expr: EReturn(value), pos: position }`.
|
||||||
@@ -67,7 +68,7 @@ Traces the string representation of an expression and returns it.
|
|||||||
- `concat(e1:Expr, e2:Expr):Expr`
|
- `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.
|
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`
|
- `isWildcard(e:Expr):Bool`
|
||||||
Checks whether an expression is the identifier `_`
|
Checks whether an expression is the identifier `_`
|
||||||
@@ -80,7 +81,7 @@ Attempts extracting an identifier from an expression. Note that an identifier ca
|
|||||||
- `getName(e:Expr):Outcome<String, tink.core.Error>`
|
- `getName(e:Expr):Outcome<String, tink.core.Error>`
|
||||||
Attempts extracting a name, i.e. a string constant or identifier from an expression
|
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.
|
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.
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ Generates a simple if statement.
|
|||||||
- `iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position):Expr`
|
- `iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position):Expr`
|
||||||
Will loop over `target` with a loop variable called `loopVar` using `body`-
|
Will loop over `target` with a loop variable called `loopVar` using `body`-
|
||||||
|
|
||||||
#### Type Inspection
|
### Type Inspection
|
||||||
|
|
||||||
- `is(e:Expr, c:ComplexType):Bool`
|
- `is(e:Expr, c:ComplexType):Bool`
|
||||||
Tells you whether a given expression has a given type.
|
Tells you whether a given expression has a given type.
|
||||||
@@ -127,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>`
|
- `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.
|
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 })`
|
- `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.
|
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.
|
||||||
@@ -185,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.
|
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`
|
- `sanitize(pos:Position):Position`
|
||||||
Returns the position ITself or `Context.currentPos()` if it's null.
|
Returns the position ITself or `Context.currentPos()` if it's null.
|
||||||
@@ -200,7 +201,7 @@ Creates a failed `Outcome` associated with the supplied position.
|
|||||||
- `getOutcome<D, F>(pos:Position, outcome:Outcome<D, F>):D`
|
- `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.
|
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>`
|
- `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.
|
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.
|
||||||
@@ -223,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`
|
- `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).
|
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`
|
- `asExpr(f:Function, ?name:String, ?pos:Position):Expr`
|
||||||
Converts a function to an expression, i.e. a local function definition.
|
Converts a function to an expression, i.e. a local function definition.
|
||||||
@@ -234,7 +235,7 @@ A shorthand to create function arguments.
|
|||||||
- `getArgIdents(f:Function):Array<Expr>`
|
- `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).
|
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>`
|
- `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.
|
Attempts to extract a specific binary operation from an expression.
|
||||||
@@ -248,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>`
|
- `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.
|
Attempts to decompose an expression into the parts of a unary operation.
|
||||||
|
|
||||||
### Metadata Tools
|
## Metadata Tools
|
||||||
|
|
||||||
- `toMap(m:Metadata):Map<String, Array<Array<Expr>>`
|
- `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" => [[]]]`
|
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" => [[]]]`
|
||||||
@@ -301,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`.
|
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.
|
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.
|
||||||
|
|
||||||
@@ -389,7 +390,7 @@ The `init` method is the swiss army knife of initializing fields. The `options.p
|
|||||||
|
|
||||||
#### Setter Bypass
|
#### 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.
|
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.
|
||||||
|
|
||||||
|
1
bin/.gitignore
vendored
1
bin/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*.n
|
|
6
dev.hxml
Normal file
6
dev.hxml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
tests.hxml
|
||||||
|
-lib travix
|
||||||
|
-lib tink_macro
|
||||||
|
-lib hx3compat
|
||||||
|
-js whatever.js
|
||||||
|
--no-output
|
3
haxe_libraries/hx3compat.hxml
Normal file
3
haxe_libraries/hx3compat.hxml
Normal 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
|
6
haxe_libraries/hxnodejs.hxml
Normal file
6
haxe_libraries/hxnodejs.hxml
Normal 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()
|
8
haxe_libraries/tink_cli.hxml
Normal file
8
haxe_libraries/tink_cli.hxml
Normal 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
|
3
haxe_libraries/tink_core.hxml
Normal file
3
haxe_libraries/tink_core.hxml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-D tink_core=1.24.0
|
||||||
|
# @install: lix --silent download "haxelib:/tink_core#1.24.0" into tink_core/1.24.0/haxelib
|
||||||
|
-cp ${HAXE_LIBCACHE}/tink_core/1.24.0/haxelib/src
|
5
haxe_libraries/tink_io.hxml
Normal file
5
haxe_libraries/tink_io.hxml
Normal 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
|
3
haxe_libraries/tink_macro.hxml
Normal file
3
haxe_libraries/tink_macro.hxml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-D tink_macro
|
||||||
|
-cp src
|
||||||
|
-lib tink_core
|
4
haxe_libraries/tink_streams.hxml
Normal file
4
haxe_libraries/tink_streams.hxml
Normal 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
|
4
haxe_libraries/tink_stringly.hxml
Normal file
4
haxe_libraries/tink_stringly.hxml
Normal 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
|
6
haxe_libraries/travix.hxml
Normal file
6
haxe_libraries/travix.hxml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-D travix=0.12.2
|
||||||
|
# @install: lix --silent download "gh://github.com/back2dos/travix#7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5" into travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5
|
||||||
|
# @post-install: cd ${HAXE_LIBCACHE}/travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5 && haxe -cp src --run travix.PostDownload
|
||||||
|
# @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
|
@@ -11,8 +11,8 @@
|
|||||||
"contributors": [
|
"contributors": [
|
||||||
"back2dos"
|
"back2dos"
|
||||||
],
|
],
|
||||||
"releasenote": "Alias for TypeMap",
|
"releasenote": "Compat with haxe 4 rc4 onward.",
|
||||||
"version": "0.13.2",
|
"version": "0.18.0",
|
||||||
"url": "http://haxetink.org/tink_macro",
|
"url": "http://haxetink.org/tink_macro",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tink_core": ""
|
"tink_core": ""
|
||||||
|
@@ -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,4 +50,90 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
@@ -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)
|
||||||
@@ -74,7 +74,11 @@ class BuildCache {
|
|||||||
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
|
var compound = ComplexType.TAnonymous([for (i in 0...types.length) {
|
||||||
name: 't$i',
|
name: 't$i',
|
||||||
pos: pos,
|
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();
|
}]).toType();
|
||||||
|
|
||||||
return getType(name, compound, pos, function (ctx) return build({
|
return getType(name, compound, pos, function (ctx) return build({
|
||||||
@@ -106,22 +110,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) {
|
static public function getType(name, ?type, ?pos:Position, build:BuildContext->TypeDefinition) {
|
||||||
|
|
||||||
if (pos == null)
|
|
||||||
pos = Context.currentPos();
|
|
||||||
|
|
||||||
if (type == null)
|
if (type == null)
|
||||||
switch Context.getLocalType() {
|
type = getParam(name, pos).sure();
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
var forName =
|
var forName =
|
||||||
switch cache[name] {
|
switch cache[name] {
|
||||||
@@ -129,7 +141,9 @@ class BuildCache {
|
|||||||
case v: v;
|
case v: v;
|
||||||
}
|
}
|
||||||
|
|
||||||
return forName.get(type, pos, build);
|
var ret = forName.get(type, pos.sanitize(), build);
|
||||||
|
ret.getFields();// workaround for https://github.com/HaxeFoundation/haxe/issues/7905
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,51 +9,51 @@ using tink.MacroApi;
|
|||||||
using Lambda;
|
using Lambda;
|
||||||
|
|
||||||
class ClassBuilder {
|
class ClassBuilder {
|
||||||
|
|
||||||
var memberList:Array<Member>;
|
var memberList:Array<Member>;
|
||||||
var macros:Map<String,Field>;
|
var macros:Map<String,Field>;
|
||||||
var constructor:Null<Constructor>;
|
var constructor:Null<Constructor>;
|
||||||
public var target(default, null):ClassType;
|
public var target(default, null):ClassType;
|
||||||
var superFields:Map<String,Bool>;
|
var superFields:Map<String,Bool>;
|
||||||
|
|
||||||
var initializeFrom:Array<Field>;
|
var initializeFrom:Array<Field>;
|
||||||
|
|
||||||
public function new(?target, ?fields) {
|
public function new(?target, ?fields) {
|
||||||
if (target == null)
|
if (target == null)
|
||||||
target = Context.getLocalClass().get();
|
target = Context.getLocalClass().get();
|
||||||
|
|
||||||
if (fields == null)
|
if (fields == null)
|
||||||
fields = Context.getBuildFields();
|
fields = Context.getBuildFields();
|
||||||
|
|
||||||
this.initializeFrom = fields;
|
this.initializeFrom = fields;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (initializeFrom == null) return;
|
if (initializeFrom == null) return;
|
||||||
|
|
||||||
var fields = initializeFrom;
|
var fields = initializeFrom;
|
||||||
initializeFrom = null;
|
initializeFrom = null;
|
||||||
|
|
||||||
this.memberList = [];
|
this.memberList = [];
|
||||||
this.macros = new Map();
|
this.macros = new Map();
|
||||||
|
|
||||||
for (field in fields)
|
for (field in fields)
|
||||||
if (field.access.has(AMacro))
|
if (field.access.has(AMacro))
|
||||||
macros.set(field.name, field)
|
macros.set(field.name, field)
|
||||||
else if (field.name == 'new') {
|
else if (field.name == 'new') {
|
||||||
var m:Member = field;
|
var m:Member = field;
|
||||||
this.constructor = new Constructor(this, m.getFunction().sure(), m.isPublic, m.pos, field.meta);
|
this.constructor = new Constructor(this, m.getFunction().sure(), m.isPublic, m.pos, field.meta);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
doAddMember(field);
|
doAddMember(field);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConstructor(?fallback:Function):Constructor {
|
public function getConstructor(?fallback:Function):Constructor {
|
||||||
init();
|
init();
|
||||||
if (constructor == null)
|
if (constructor == null)
|
||||||
if (fallback != null)
|
if (fallback != null)
|
||||||
constructor = new Constructor(this, fallback);
|
constructor = new Constructor(this, fallback);
|
||||||
else {
|
else {
|
||||||
@@ -63,19 +63,21 @@ class ClassBuilder {
|
|||||||
if (cl.constructor != null) {
|
if (cl.constructor != null) {
|
||||||
try {
|
try {
|
||||||
var ctor = cl.constructor.get();
|
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
|
for (arg in func.args) //this is to deal with type parameter substitutions
|
||||||
arg.type = null;
|
arg.type = null;
|
||||||
|
|
||||||
func.expr = "super".resolve().call(func.getArgIdents());
|
func.expr = "super".resolve().call(func.getArgIdents());
|
||||||
constructor = new Constructor(this, func);
|
constructor = new Constructor(this, func);
|
||||||
if (ctor.isPublic)
|
if (ctor.isPublic)
|
||||||
constructor.publish();
|
constructor.publish();
|
||||||
}
|
}
|
||||||
catch (e:Dynamic) {//fails for unknown reason
|
catch (e:Dynamic) {//fails for unknown reason
|
||||||
if (e == 'assert')
|
if (e == 'assert')
|
||||||
neko.Lib.rethrow(e);
|
tink.core.Error.rethrow(e);
|
||||||
constructor = new Constructor(this, null);
|
constructor = new Constructor(this, null);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -85,15 +87,15 @@ class ClassBuilder {
|
|||||||
if (constructor == null)
|
if (constructor == null)
|
||||||
constructor = new Constructor(this, null);
|
constructor = new Constructor(this, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasConstructor():Bool {
|
public function hasConstructor():Bool {
|
||||||
init();
|
init();
|
||||||
return this.constructor != null;
|
return this.constructor != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function export(?verbose):Array<Field> {
|
public function export(?verbose):Array<Field> {
|
||||||
if (initializeFrom != null) return null;
|
if (initializeFrom != null) return null;
|
||||||
var ret = (constructor == null || target.isInterface) ? [] : [constructor.toHaxe()];
|
var ret = (constructor == null || target.isInterface) ? [] : [constructor.toHaxe()];
|
||||||
@@ -108,24 +110,24 @@ class ClassBuilder {
|
|||||||
}
|
}
|
||||||
for (m in macros)
|
for (m in macros)
|
||||||
ret.push(m);
|
ret.push(m);
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
for (field in ret)
|
for (field in ret)
|
||||||
Context.warning(new Printer().printField(field), field.pos);
|
Context.warning(new Printer().printField(field), field.pos);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
public function iterator():Iterator<Member> {
|
public function iterator():Iterator<Member> {
|
||||||
init();
|
init();
|
||||||
return this.memberList.copy().iterator();
|
return this.memberList.copy().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasOwnMember(name:String):Bool {
|
public function hasOwnMember(name:String):Bool {
|
||||||
init();
|
init();
|
||||||
return
|
return
|
||||||
macros.exists(name) || memberByName(name).isSuccess();
|
macros.exists(name) || memberByName(name).isSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasSuperField(name:String):Bool {
|
public function hasSuperField(name:String):Bool {
|
||||||
if (superFields == null) {
|
if (superFields == null) {
|
||||||
superFields = new Map();
|
superFields = new Map();
|
||||||
@@ -139,55 +141,61 @@ class ClassBuilder {
|
|||||||
}
|
}
|
||||||
return superFields.get(name);
|
return superFields.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function memberByName(name:String, ?pos:Position) {
|
public function memberByName(name:String, ?pos:Position) {
|
||||||
init();
|
init();
|
||||||
for (m in memberList)
|
for (m in memberList)
|
||||||
if (m.name == name)
|
if (m.name == name)
|
||||||
return Success(m);
|
return Success(m);
|
||||||
|
|
||||||
return pos.makeFailure('unknown member $name');
|
return pos.makeFailure('unknown member $name');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeMember(member:Member):Bool {
|
public function removeMember(member:Member):Bool {
|
||||||
init();
|
init();
|
||||||
return
|
return
|
||||||
memberList.remove(member);
|
memberList.remove(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasMember(name:String):Bool
|
public function hasMember(name:String):Bool
|
||||||
return hasOwnMember(name) || hasSuperField(name);
|
return hasOwnMember(name) || hasSuperField(name);
|
||||||
|
|
||||||
function doAddMember(m:Member, ?front:Bool = false):Member {
|
function doAddMember(m:Member, ?front:Bool = false):Member {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (m.name == 'new')
|
if (m.name == 'new')
|
||||||
throw 'Constructor must not be registered as ordinary member';
|
throw 'Constructor must not be registered as ordinary member';
|
||||||
|
|
||||||
//if (hasOwnMember(m.name))
|
//if (hasOwnMember(m.name))
|
||||||
//m.pos.error('duplicate member declaration ' + m.name);
|
//m.pos.error('duplicate member declaration ' + m.name);
|
||||||
|
|
||||||
if (front)
|
if (front)
|
||||||
memberList.unshift(m);
|
memberList.unshift(m);
|
||||||
else
|
else
|
||||||
memberList.push(m);
|
memberList.push(m);
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addMember(m:Member, ?front:Bool = false):Member {
|
|
||||||
doAddMember(m, front);
|
|
||||||
|
|
||||||
if (!m.isStatic && hasSuperField(m.name))
|
|
||||||
m.overrides = true;
|
|
||||||
|
|
||||||
return m;
|
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);
|
||||||
|
|
||||||
|
if (!m.isStatic && hasSuperField(m.name))
|
||||||
|
m.overrides = true;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose) {
|
static public function run(plugins:Array<ClassBuilder->Void>, ?verbose) {
|
||||||
var builder = new ClassBuilder();
|
var builder = new ClassBuilder();
|
||||||
for (p in plugins)
|
for (p in plugins)
|
||||||
p(builder);
|
p(builder);
|
||||||
return builder.export(verbose);
|
return builder.export(verbose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
src/tink/macro/ConstParam.hx
Normal file
3
src/tink/macro/ConstParam.hx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package tink.macro;
|
||||||
|
|
||||||
|
class ConstParam<Const> {}
|
@@ -1,8 +1,10 @@
|
|||||||
package tink.macro;
|
package tink.macro;
|
||||||
|
|
||||||
|
import haxe.macro.Type;
|
||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
import haxe.macro.Context;
|
import haxe.macro.Context;
|
||||||
import tink.core.Pair;
|
import tink.core.Pair;
|
||||||
|
using haxe.macro.Tools;
|
||||||
using tink.MacroApi;
|
using tink.MacroApi;
|
||||||
|
|
||||||
enum FieldInit {
|
enum FieldInit {
|
||||||
@@ -19,24 +21,24 @@ class Constructor {
|
|||||||
var afterArgs:Array<FunctionArg>;
|
var afterArgs:Array<FunctionArg>;
|
||||||
var pos:Position;
|
var pos:Position;
|
||||||
var onGenerateHooks:Array<Function->Void>;
|
var onGenerateHooks:Array<Function->Void>;
|
||||||
var superCall:Expr;
|
var superCall:Array<Expr>;
|
||||||
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) {
|
||||||
@@ -47,73 +49,118 @@ 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 =
|
|
||||||
if (oldStatements.length == 0) [].toBlock();
|
for (i in 0...oldStatements.length)
|
||||||
else switch oldStatements[0] {
|
switch oldStatements[i] {
|
||||||
case macro super($a{_}): oldStatements.shift();
|
case macro super($a{_}):
|
||||||
default: [].toBlock();
|
superCall = oldStatements.splice(0, i + 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (superCall == null)
|
||||||
|
superCall = [];
|
||||||
}
|
}
|
||||||
public function addStatement(e:Expr, ?prepend)
|
|
||||||
|
public function getArgList():Array<FunctionArg>
|
||||||
|
return beforeArgs.concat(args).concat(afterArgs);
|
||||||
|
|
||||||
|
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 tmp = MacroApi.tempName();
|
||||||
|
var member = owner.memberByName(name).sure();
|
||||||
if (options.bypass) {
|
|
||||||
switch owner.memberByName(name) {
|
if (options.bypass && member.kind.match(FProp(_, 'never' | 'set', _, _))) {
|
||||||
case Success(member): member.addMeta(':isVar');
|
|
||||||
default:
|
member.addMeta(':isVar');
|
||||||
}
|
|
||||||
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((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
|
else
|
||||||
addStatement(macro @:pos(pos) this.$name = $e, options.prepend);
|
addStatement(macro @:pos(pos) this.$name = $e, 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),
|
||||||
@@ -132,4 +179,4 @@ class Constructor {
|
|||||||
meta : this.meta,
|
meta : this.meta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,8 +43,11 @@ 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 inline function as(e:Expr, c:ComplexType)
|
||||||
|
return ECheckType(e, c).at(e.pos);
|
||||||
|
|
||||||
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dynamic<String>, ?skipFields = false, ?callPos:PosInfos) {
|
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dynamic<String>, ?skipFields = false, ?callPos:PosInfos) {
|
||||||
if (nuPos == null)
|
if (nuPos == null)
|
||||||
nuPos = Context.currentPos();
|
nuPos = Context.currentPos();
|
||||||
@@ -241,7 +244,7 @@ class Exprs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position)
|
static public inline function iterate(target:Expr, body:Expr, ?loopVar:String = 'i', ?pos:Position)
|
||||||
return EFor(EIn(loopVar.resolve(pos), target).at(pos), body).at(pos);
|
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:Dynamic<Expr>, ?pos:Position)
|
||||||
return EObjectDecl([for (field in Reflect.fields(object))
|
return EObjectDecl([for (field in Reflect.fields(object))
|
||||||
@@ -311,17 +314,45 @@ class Exprs {
|
|||||||
|
|
||||||
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 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)
|
static public function typeof(expr:Expr, ?locals)
|
||||||
return
|
return
|
||||||
try {
|
try {
|
||||||
|
if (locals == null)
|
||||||
|
locals = scopes[scopes.length - 1];
|
||||||
if (locals != null)
|
if (locals != null)
|
||||||
expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos);
|
expr = [EVars(locals).at(expr.pos), expr].toMBlock(expr.pos);
|
||||||
Success(Context.typeof(expr));
|
Success(Context.typeof(expr));
|
||||||
}
|
}
|
||||||
catch (e:Error) {
|
catch (e:haxe.macro.Error) {
|
||||||
var m:Dynamic = e.message;
|
e.pos.makeFailure(e.message);
|
||||||
e.pos.makeFailure(m);
|
|
||||||
}
|
}
|
||||||
catch (e:Dynamic) {
|
catch (e:Dynamic) {
|
||||||
expr.pos.makeFailure(e);
|
expr.pos.makeFailure(e);
|
||||||
@@ -400,6 +431,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_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";
|
||||||
|
@@ -4,9 +4,18 @@ 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 {
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@ abstract Member(Field) from Field to Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var name(get, set):String;
|
public var name(get, set):String;
|
||||||
|
public var meta(get, set):Metadata;
|
||||||
public var doc(get, set):Null<String>;
|
public var doc(get, set):Null<String>;
|
||||||
public var kind(get, set):FieldType;
|
public var kind(get, set):FieldType;
|
||||||
public var pos(get, set):Position;
|
public var pos(get, set):Position;
|
||||||
@@ -39,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
|
||||||
@@ -76,6 +93,11 @@ abstract Member(Field) from Field to Field {
|
|||||||
}
|
}
|
||||||
return pos.makeFailure('missing @$name');
|
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 inline function asField():Field return this;
|
||||||
public function publish()
|
public function publish()
|
||||||
@@ -86,6 +108,12 @@ abstract Member(Field) from Field to Field {
|
|||||||
this.access.push(APublic);
|
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 get_name() return this.name;
|
||||||
inline function set_name(param) return this.name = param;
|
inline function set_name(param) return this.name = param;
|
||||||
|
|
||||||
@@ -99,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)
|
||||||
@@ -167,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;
|
||||||
@@ -190,4 +216,4 @@ abstract Member(Field) from Field to Field {
|
|||||||
if (add != null)
|
if (add != null)
|
||||||
this.access.push(add);
|
this.access.push(add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -12,7 +12,7 @@ class Positions {
|
|||||||
return
|
return
|
||||||
switch outcome {
|
switch outcome {
|
||||||
case Success(d): d;
|
case Success(d): d;
|
||||||
case Failure(f): sanitize(pos).error(f);
|
case Failure(f): pos.error(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function makeBlankType(pos:Position):ComplexType
|
static public function makeBlankType(pos:Position):ComplexType
|
||||||
@@ -32,7 +32,17 @@ class Positions {
|
|||||||
|
|
||||||
|
|
||||||
static public function error(pos:Position, error:Dynamic):Dynamic
|
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
|
static function contextError(pos:Position, error:String):Dynamic
|
||||||
return Context.fatalError(error, pos);
|
return Context.fatalError(error, pos);
|
||||||
|
@@ -17,19 +17,28 @@ class Sisyphus {
|
|||||||
case AccCall: getOrSet;
|
case AccCall: getOrSet;
|
||||||
case AccInline: "default";
|
case AccInline: "default";
|
||||||
case AccRequire(_, _): "default";
|
case AccRequire(_, _): "default";
|
||||||
|
default: throw "not implemented";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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: [
|
||||||
@@ -52,21 +61,33 @@ 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) {
|
switch (type) {
|
||||||
case null:
|
case null:
|
||||||
null;
|
null;
|
||||||
case TMono(_.get() => t):
|
case TEnum(_.get().isPrivate => true, _): direct();
|
||||||
t == null ? Types.toComplex(type, { direct: true }) : toComplexType(t);
|
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):
|
case TEnum(_.get() => baseType, params):
|
||||||
TPath(toTypePath(baseType, params));
|
TPath(toTypePath(baseType, params));
|
||||||
case TInst(_.get() => classType, params):
|
case TInst(_.get() => classType, params):
|
||||||
switch (classType.kind) {
|
switch (classType.kind) {
|
||||||
case KTypeParameter(_):
|
case KTypeParameter(_):
|
||||||
TPath({
|
var ct = Types.asComplexType(classType.name);
|
||||||
name: classType.name,
|
switch Types.toType(ct) {
|
||||||
pack: [],
|
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));
|
||||||
}
|
}
|
||||||
@@ -92,14 +113,23 @@ class Sisyphus {
|
|||||||
default:
|
default:
|
||||||
throw "Invalid type";
|
throw "Invalid type";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
static function toTypePath(baseType : BaseType, params : Array<Type>) : TypePath return {
|
static function toTypePath(baseType : BaseType, params : Array<Type>) : TypePath return {
|
||||||
var module = baseType.module;
|
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,
|
pack: baseType.pack,
|
||||||
name: module.substring(module.lastIndexOf(".") + 1),
|
name: name,
|
||||||
sub: baseType.name,
|
sub: sub,
|
||||||
params: [ for (t in params) TPType(toComplexType(t)) ],
|
params: [for (t in params) switch t {
|
||||||
|
case TInst(_.get().kind => KExpr(e), _): TPExpr(e);
|
||||||
|
default: TPType(toComplexType(t));
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -1,186 +1,282 @@
|
|||||||
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 neko.Lib.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 function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) {
|
case TInst(_.get().meta => m, _): [m];
|
||||||
for (field in t.fields.get())
|
case TEnum(_.get().meta => m, _): [m];
|
||||||
if (!marker.exists(field.name)) {
|
case TAbstract(_.get().meta => m, _): [m];
|
||||||
marker.set(field.name, true);
|
case TType(_.get() => t, _): [t.meta].concat(getMeta(t.type));
|
||||||
out.push(field);
|
case TLazy(f): getMeta(f());
|
||||||
}
|
default: [];
|
||||||
if (t.isInterface)
|
}
|
||||||
for (t in t.interfaces)
|
|
||||||
getDeclaredFields(t.t.get(), out, marker);
|
static function getDeclaredFields(t:ClassType, out:Array<ClassField>, marker:Map<String,Bool>) {
|
||||||
else if (t.superClass != null)
|
for (field in t.fields.get())
|
||||||
getDeclaredFields(t.superClass.t.get(), out, marker);
|
if (!marker.exists(field.name)) {
|
||||||
}
|
marker.set(field.name, true);
|
||||||
|
out.push(field);
|
||||||
static var fieldsCache = new Map();
|
}
|
||||||
static public function getFields(t:Type, ?substituteParams = true)
|
if (t.isInterface)
|
||||||
return
|
for (t in t.interfaces)
|
||||||
switch (reduce(t)) {
|
getDeclaredFields(t.t.get(), out, marker);
|
||||||
case TInst(c, params):
|
else if (t.superClass != null)
|
||||||
var id = c.toString(),
|
getDeclaredFields(t.superClass.t.get(), out, marker);
|
||||||
c = c.get();
|
}
|
||||||
if (!fieldsCache.exists(id)) {
|
|
||||||
var fields = [];
|
static var fieldsCache = new Map();
|
||||||
getDeclaredFields(c, fields, new Map());
|
static public function getFields(t:Type, ?substituteParams = true)
|
||||||
fieldsCache.set(id, Success(fields));
|
return
|
||||||
}
|
switch (reduce(t)) {
|
||||||
var ret = fieldsCache.get(id);
|
case TInst(c, params):
|
||||||
if (substituteParams && ret.isSuccess()) {
|
var id = c.toString(),
|
||||||
var fields = Reflect.copy(ret.sure());
|
c = c.get();
|
||||||
|
if (!fieldsCache.exists(id)) {
|
||||||
for (field in fields)
|
var fields = [];
|
||||||
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params);
|
getDeclaredFields(c, fields, new Map());
|
||||||
}
|
fieldsCache.set(id, Success(fields));
|
||||||
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
|
}
|
||||||
ret;
|
var ret = fieldsCache.get(id);
|
||||||
case TAnonymous(anon): Success(anon.get().fields);
|
if (substituteParams && ret.isSuccess()) {
|
||||||
default: Context.currentPos().makeFailure('type has no fields');
|
var fields = Reflect.copy(ret.sure());
|
||||||
}
|
|
||||||
|
for (field in fields)
|
||||||
static public function getStatics(t:Type)
|
field.type = haxe.macro.TypeTools.applyTypeParameters(field.type, c.params, params);
|
||||||
return
|
}
|
||||||
switch (reduce(t)) {
|
fieldsCache.remove(id);//TODO: find a proper solution to avoid stale cache
|
||||||
case TInst(t, _): Success(t.get().statics.get());
|
ret;
|
||||||
default: Failure('type has no statics');
|
case TAnonymous(anon): Success(anon.get().fields);
|
||||||
}
|
default: Context.currentPos().makeFailure('type $t has no fields');
|
||||||
|
}
|
||||||
static public function toString(t:ComplexType)
|
|
||||||
return new Printer().printComplexType(t);
|
static public function getStatics(t:Type)
|
||||||
|
return
|
||||||
static public function isSubTypeOf(t:Type, of:Type, ?pos)
|
switch (reduce(t)) {
|
||||||
return
|
case TInst(t, _): Success(t.get().statics.get());
|
||||||
ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof();
|
default: Failure('type has no statics');
|
||||||
|
}
|
||||||
static public function isDynamic(t:Type)
|
|
||||||
return switch reduce(t) {
|
|
||||||
case TDynamic(_): true;
|
static public function getPosition(t:Type)
|
||||||
default: false;
|
return
|
||||||
}
|
switch t {
|
||||||
|
case TInst(_.get() => {pos: pos}, _)
|
||||||
static public function toType(t:ComplexType, ?pos:Position)
|
| TAbstract(_.get() => {pos: pos}, _)
|
||||||
return (macro @:pos(pos.sanitize()) {
|
| TType(_.get() => {pos: pos}, _)
|
||||||
var v:$t = null;
|
| TEnum(_.get() => {pos: pos}, _) : Success(pos);
|
||||||
v;
|
case TMono(_.get() => t) if (t != null): getPosition(t);
|
||||||
}).typeof();
|
case TLazy(f): getPosition(f());
|
||||||
|
case TDynamic(v) if(v != null): getPosition(v);
|
||||||
static public inline function instantiate(t:TypePath, ?args, ?pos)
|
default: Failure('type "$t" has no position');
|
||||||
return ENew(t, args == null ? [] : args).at(pos);
|
}
|
||||||
|
|
||||||
static public function asTypePath(s:String, ?params):TypePath {
|
|
||||||
var parts = s.split('.');
|
|
||||||
var name = parts.pop(),
|
static public function toString(t:ComplexType)
|
||||||
sub = null;
|
return new Printer().printComplexType(t);
|
||||||
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) {
|
|
||||||
sub = name;
|
static public function unifiesWith(from:Type, to:Type)
|
||||||
name = parts.pop();
|
return Context.unify(from, to);
|
||||||
if(sub == name) sub = null;
|
|
||||||
}
|
static public function isSubTypeOf(t:Type, of:Type, ?pos)
|
||||||
return {
|
return
|
||||||
name: name,
|
if (Context.unify(t, of)) ECheckType(ECheckType(macro null, toComplex(t)).at(pos), toComplex(of)).at(pos).typeof();
|
||||||
pack: parts,
|
else Failure(new Error(t.toString() + ' should be ' + of.toString(), pos.sanitize()));
|
||||||
params: params == null ? [] : params,
|
|
||||||
sub: sub
|
static public function isDynamic(t:Type)
|
||||||
};
|
return switch reduce(t) {
|
||||||
}
|
case TDynamic(_): true;
|
||||||
|
default: false;
|
||||||
static public inline function asComplexType(s:String, ?params)
|
}
|
||||||
return TPath(asTypePath(s, params));
|
|
||||||
|
static public function toType(t:ComplexType, ?pos:Position)
|
||||||
static public inline function reduce(type:Type, ?once)
|
return (macro @:pos(pos.sanitize()) {
|
||||||
return Context.follow(type, once);
|
var v:$t = null;
|
||||||
|
v;
|
||||||
static public function isVar(field:ClassField)
|
}).typeof();
|
||||||
return switch (field.kind) {
|
|
||||||
case FVar(_, _): true;
|
static public inline function instantiate(t:TypePath, ?args, ?pos)
|
||||||
default: false;
|
return ENew(t, args == null ? [] : args).at(pos);
|
||||||
}
|
|
||||||
|
static public function asTypePath(s:String, ?params):TypePath {
|
||||||
static public function register(type:Void->Type):Int {
|
var parts = s.split('.');
|
||||||
types.set(idCounter, type);
|
var name = parts.pop(),
|
||||||
return idCounter++;
|
sub = null;
|
||||||
}
|
if (parts.length > 0 && parts[parts.length - 1].charCodeAt(0) < 0x5B) {
|
||||||
|
sub = name;
|
||||||
static function paramsToComplex(params:Array<Type>):Array<TypeParam>
|
name = parts.pop();
|
||||||
return [for (p in params) TPType(toComplex(p))];
|
if(sub == name) sub = null;
|
||||||
|
}
|
||||||
static function baseToComplex(t:BaseType, params:Array<Type>)
|
return {
|
||||||
return asComplexType(t.module + '.' + t.name, paramsToComplex(params));
|
name: name,
|
||||||
|
pack: parts,
|
||||||
static public function toComplex(type:Type, ?options:{ ?direct: Bool }):ComplexType {
|
params: params == null ? [] : params,
|
||||||
var ret =
|
sub: sub
|
||||||
if (options == null || options.direct != true) tink.macro.Sisyphus.toComplexType(type);
|
};
|
||||||
else null;
|
}
|
||||||
if (ret == null)
|
|
||||||
ret = lazyComplex(function () return type);
|
static public inline function asComplexType(s:String, ?params)
|
||||||
return ret;
|
return TPath(asTypePath(s, params));
|
||||||
}
|
|
||||||
|
static public function reduce(type:Type, ?once) {
|
||||||
static public function lazyComplex(f:Void->Type)
|
function rec(t:Type)
|
||||||
return
|
return if (once) t else reduce(t, false);
|
||||||
TPath({
|
return switch type {
|
||||||
pack : ['tink','macro'],
|
case TAbstract(_.get() => { name: 'Null', pack: [] }, [t]): rec(t);
|
||||||
name : 'DirectType',
|
case TLazy(f): rec(f());
|
||||||
params : [TPExpr(register(f).toExpr())],
|
case TType(_, _): rec(Context.follow(type, once));
|
||||||
sub : null,
|
default: type;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
static function resolveDirectType()
|
|
||||||
return
|
static public function isVar(field:ClassField)
|
||||||
switch reduce(Context.getLocalType()) {
|
return switch (field.kind) {
|
||||||
case TInst(_, [TInst(_.get() => { kind: KExpr(e) }, _)]):
|
case FVar(_, _): true;
|
||||||
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: false;
|
||||||
default:
|
}
|
||||||
throw 'assert';
|
|
||||||
}
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package ;
|
package ;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
using tink.MacroApi;
|
using tink.MacroApi;
|
||||||
|
|
||||||
@@ -7,6 +8,14 @@ class Exprs extends Base {
|
|||||||
function exprEq(e1:Expr, e2:Expr) {
|
function exprEq(e1:Expr, e2:Expr) {
|
||||||
assertEquals(e1.toString(), e2.toString());
|
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() {
|
function testGet() {
|
||||||
assertEquals('foo', (macro foo).getIdent().sure());
|
assertEquals('foo', (macro foo).getIdent().sure());
|
||||||
assertEquals('foo', (macro "foo").getString().sure());
|
assertEquals('foo', (macro "foo").getString().sure());
|
||||||
|
11
tests/Functions.hx
Normal file
11
tests/Functions.hx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
using tink.MacroApi;
|
||||||
|
|
||||||
|
class Functions extends Base {
|
||||||
|
function test() {
|
||||||
|
var f:Function = (macro function () {}).getFunction().sure();
|
||||||
|
f.asExpr('foo');
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
10
tests/Misc.hx
Normal file
10
tests/Misc.hx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import tink.MacroApi;
|
||||||
|
import haxe.unit.TestCase;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Misc extends TestCase {
|
||||||
|
function testMain() {
|
||||||
|
assertEquals('Run', MacroApi.getMainClass().force());
|
||||||
|
}
|
||||||
|
}
|
@@ -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() {
|
||||||
|
@@ -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,7 +43,27 @@ 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() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
1
tests/VarChar.hx
Normal file
1
tests/VarChar.hx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
typedef VarChar<Const> = String;
|
Reference in New Issue
Block a user