Compare commits

...

116 Commits

Author SHA1 Message Date
Juraj Kirchheim
a77aedb458 Partial workaround for https://github.com/benmerckx/genes/issues/6 2020-01-02 19:44:41 +01:00
Juraj Kirchheim
b4fa5f4fe0 Update lix version to download correct nightly. 2019-09-13 09:45:24 +02:00
Juraj Kirchheim
e59fab499d Release 0.18.0 2019-09-13 09:40:22 +02:00
Juraj Kirchheim
3356172b7f Update to newest haxe version. 2019-09-13 09:39:24 +02:00
Juraj Kirchheim
4fd03ed667 Release 0.17.7 2019-06-29 11:27:39 +02:00
Juraj Kirchheim
4cb5ee4196 Use @:persistent as necessary. 2019-06-22 15:16:04 +02:00
Kevin Leung
888481094a Pin lix version 2019-05-14 11:33:28 +08:00
Kevin Leung
7686fe3e35 Use xenial 2019-05-14 11:10:52 +08:00
Kevin Leung
747d07bfc5 Revert "Better type comparison (closes #25)"
This reverts commit 00640d8328.
2019-05-13 18:53:42 +08:00
Kevin Leung
00640d8328 Better type comparison (closes #25) 2019-05-13 17:01:54 +08:00
Kevin Leung
77973f6007 print metadata 2019-05-13 17:00:54 +08:00
Juraj Kirchheim
0df9c28ace Release 0.17.6 2019-05-07 22:43:21 +02:00
Juraj Kirchheim
d90fb966d2 Add call to find main class. 2019-05-07 22:42:39 +02:00
Kevin Leung
9018b06279 Release 0.17.5 2019-04-27 20:54:09 +08:00
Kevin Leung
6ed0424093 Convert TypeParameter to TypeParam 2019-04-23 18:21:03 +08:00
Juraj Kirchheim
53868cc6f3 Some fumbling with isSubTypeOf and add test for final. 2019-03-03 17:13:04 +01:00
Juraj Kirchheim
296cc6c861 Support final access on haxe4. Resolves #17. 2019-03-03 12:24:22 +01:00
Juraj Kirchheim
56c2ed9642 Don't do setter bypass if it's not really necessary. 2019-03-03 12:18:40 +01:00
Juraj Kirchheim
ac908bc82d Deal with somewhat obscure haxe 4 issue. 2019-03-03 12:18:07 +01:00
Juraj Kirchheim
1604a135ae Expose compiler args and completion point. 2019-02-26 16:01:39 +01:00
Juraj Kirchheim
5e65f5354f Add unification helper. 2019-02-24 08:25:04 +01:00
Juraj Kirchheim
8f81256731 Deal with null monos. 2019-02-24 08:24:50 +01:00
Kevin Leung
1b717919fa Release 0.17.4 2019-02-24 09:28:59 +08:00
Kevin Leung
3d9aec835e Merge pull request #24 from kevinresol/type_get_pos
Get Position from a Type
2019-02-24 00:26:07 +08:00
Kevin Leung
86fa47eb0d Get Position from a Type 2019-02-23 22:37:17 +08:00
Juraj Kirchheim
b5605e48ac Use nightly rather than latest. 2019-02-01 17:44:53 +01:00
Juraj Kirchheim
6f5eba983e Update travix. 2019-01-02 17:27:10 +01:00
Juraj Kirchheim
d5a44a9268 Release 0.17.3 2019-01-02 17:08:03 +01:00
Juraj Kirchheim
a11804c3ab Merge branch 'master' of https://github.com/haxetink/tink_macro 2019-01-02 16:59:06 +01:00
Juraj Kirchheim
380ceb39ea Add helper for transforming TypeParameter to TypeParamDecl. 2019-01-02 16:58:42 +01:00
Juraj Kirchheim
c48d445a54 Don't use get_field/set_field for field access anymore. 2018-11-08 11:27:37 +01:00
Juraj Kirchheim
b7e413d839 Release 0.17.2 2018-10-05 09:23:32 +02:00
Juraj Kirchheim
e1e487079b Add Exprs.as for ECheckType. 2018-10-03 11:50:21 +02:00
Juraj Kirchheim
5f959f788e Release 0.17.1 2018-10-02 09:12:28 +02:00
Juraj Kirchheim
4f84570bb7 Improve type parameter treatment. 2018-10-01 16:10:45 +02:00
Juraj Kirchheim
e398d0e694 Add helper for getting field suggestions. 2018-10-01 16:10:34 +02:00
Juraj Kirchheim
13d11f4f66 Make type comparator available. 2018-09-22 10:12:02 +02:00
Kevin Leung
d6142847eb Update ci script 2018-09-19 16:40:12 +08:00
Kevin Leung
e6a0304016 Release 0.17.0 2018-09-08 14:14:22 +08:00
Kevin Leung
69368937a4 let lix manage travix 2018-09-08 13:59:47 +08:00
Kevin Leung
df2389d2c8 Merge branch 'master' of https://github.com/haxetink/tink_macro 2018-09-08 13:53:37 +08:00
Kevin Leung
aad80afec2 Standardize build script 2018-09-08 13:53:27 +08:00
Juraj Kirchheim
b68c6ac4a1 Fix complex type intersection. 2018-09-03 18:49:08 +02:00
Juraj Kirchheim
682aeacce8 Add ComplexType::intersect. 2018-09-01 15:09:05 +02:00
Juraj Kirchheim
9bcec770eb Update travix. 2018-09-01 15:08:53 +02:00
Juraj Kirchheim
2310b6d8e4 Release 0.16.7 2018-08-23 11:24:32 +02:00
Kevin Leung
7f13916154 Fix haxe version check 2018-08-22 21:40:17 +08:00
Juraj Kirchheim
2b7204f8d3 Release 0.16.6 2018-07-26 12:57:57 +02:00
Juraj Kirchheim
7ca0d4a650 Merge branch 'master' of https://github.com/haxetink/tink_macro 2018-07-26 12:57:08 +02:00
Juraj Kirchheim
e38f5ae147 Expand metadata API of member. 2018-07-26 12:55:05 +02:00
Juraj Kirchheim
4186225eb5 Add haxe3 polyfill for ObjectField. 2018-07-26 12:54:31 +02:00
Juraj Kirchheim
f5a3d73c55 Merge pull request #22 from kLabz/fix/constructor-without-expr
Fix ClassBuilder's getConstructor() when super class is extern
2018-05-08 16:33:51 +02:00
k
f65df0e94b Fix ClassBuilder's getConstructor() when super class is extern
Handle super class constructor's expr being `null`, which happens
when the super class is an extern with a constructor definition.
2018-05-08 16:23:12 +02:00
Juraj Kirchheim
55a4b1463f Release 0.16.5 2018-04-07 04:04:16 +02:00
Juraj Kirchheim
0a6e39969b Fix direct initialization. 2018-04-07 04:03:09 +02:00
Juraj Kirchheim
f051345863 Various little fixes. 2018-04-02 13:06:42 +02:00
Juraj Kirchheim
74e37b4bf1 Release 0.16.4 2018-03-29 11:08:49 +02:00
Juraj Kirchheim
42821979d9 Totally insane workaround for https://github.com/HaxeFoundation/haxe/issues/5039 2018-03-29 02:00:05 +02:00
Juraj Kirchheim
2aa0b07e9f Helper for short identifiers. 2018-03-29 01:59:27 +02:00
Juraj Kirchheim
67e680f67f Release 0.16.3 2018-03-12 11:17:12 +01:00
Juraj Kirchheim
ebade45e63 Maintain Haxe 3 nullability semantics in Haxe 4. 2018-03-12 11:16:13 +01:00
Juraj Kirchheim
40497bb1fc Fix tests for Haxe 3.2.1. 2018-01-31 20:36:10 +01:00
Juraj Kirchheim
d80b03fe9d Release 0.16.2 2018-01-31 16:06:27 +01:00
Juraj Kirchheim
f269d13abf Add workaround for HaxeFoundation/haxe#6830. 2018-01-31 15:43:52 +01:00
Juraj Kirchheim
f271cc880a Release 0.16.1 2018-01-29 10:12:58 +01:00
Juraj Kirchheim
9cd59d3989 Handle module types properly. 2018-01-29 10:12:06 +01:00
Juraj Kirchheim
40ee99df39 Lixify dependencies. 2018-01-29 10:11:50 +01:00
Juraj Kirchheim
1cb27daf46 Fix tests for Haxe 4. 2018-01-16 17:21:27 +01:00
Juraj Kirchheim
a9bea4287a Fix headline structure. 2018-01-16 17:20:24 +01:00
Juraj Kirchheim
0be2090232 Release 0.16.0 2018-01-16 17:14:42 +01:00
Juraj Kirchheim
0dc774db21 Add support for scoping. 2017-12-24 12:23:10 +01:00
Juraj Kirchheim
a7c22bf5af Return bulk added members from ClassBuilder. 2017-12-18 14:26:25 +01:00
Juraj Kirchheim
a164b335ed Improve Type to ComplexType conversion for type parameters. 2017-12-18 11:28:23 +01:00
Juraj Kirchheim
d02306eebd Add class reification shorthand to ClassBuilder. 2017-12-18 11:27:59 +01:00
Juraj Kirchheim
ff9ef59445 Expose more logic from BuildCache. 2017-12-18 11:27:32 +01:00
Juraj Kirchheim
bfc5c2b88f Factor out build cache's param retrieval into separate function. 2017-12-15 12:27:01 +01:00
Juraj Kirchheim
57042819b1 Release 0.15.4 2017-12-08 10:41:26 +01:00
Juraj Kirchheim
82d15d9ffc Report invalid type in getFields. 2017-12-08 10:40:24 +01:00
Juraj Kirchheim
0d9fff7e01 Reverse order in setter bypass to make type inference suffer less. 2017-12-06 17:13:01 +01:00
Juraj Kirchheim
45d5f3e0ea Release 0.15.3 2017-12-01 16:32:19 +01:00
Juraj Kirchheim
800891bee2 Fixes #21. 2017-12-01 16:31:22 +01:00
Juraj Kirchheim
0802bf7f79 Minor tweak. 2017-12-01 16:30:42 +01:00
Juraj Kirchheim
42897c7cd5 Merge branch 'master' of https://github.com/haxetink/tink_macro 2017-11-30 13:05:06 +01:00
Juraj Kirchheim
0e902745b1 Release 0.15.2 2017-11-30 13:04:39 +01:00
Juraj Kirchheim
3dec4fa404 Improve position reporting. 2017-11-30 13:04:07 +01:00
Juraj Kirchheim
c0ec918f52 Merge pull request #20 from markknol/patch-1
Update README.md
2017-11-30 11:52:41 +01:00
Mark Knol
4eb9729f0e Update README.md
Fix links in index
2017-11-30 10:56:14 +01:00
Juraj Kirchheim
7586b54ece Release 0.15.1 2017-11-30 10:40:31 +01:00
Juraj Kirchheim
21c7d12be7 Don't drop positions when typing. 2017-11-30 10:39:57 +01:00
Juraj Kirchheim
f27d2796b1 Release 0.15.0 2017-09-25 10:52:48 +02:00
Juraj Kirchheim
417a92cabc Make it compile for Haxe 4. 2017-09-25 10:50:36 +02:00
Kevin Leung
4457ea7dee readme tweaks 2017-09-21 15:02:16 +08:00
Juraj Kirchheim
209847e5ea Release 0.14.2 2017-09-11 10:36:42 +02:00
Juraj Kirchheim
c94e4a0337 Add way to get constructor args. 2017-09-11 10:35:54 +02:00
Juraj Kirchheim
cc009b2026 Release 0.14.1 2017-08-28 15:02:56 +02:00
Juraj Kirchheim
0a36ee8e2b Merge pull request #16 from ibilon/haxe4
Support for haxe 4 EIn change to EBinop(OpIn)
2017-08-18 16:00:37 +02:00
Juraj Kirchheim
5879773566 Fix typo. 2017-08-18 15:41:31 +02:00
Juraj Kirchheim
58262a1810 Use reification to abstract over Haxe version specific differences. 2017-08-18 15:38:58 +02:00
Valentin Lemière
9ceadeb88f Support for haxe 4 EIn change to EBinop(OpIn) 2017-08-18 15:13:28 +02:00
Juraj Kirchheim
15d75965ad Release 0.14.0 2017-06-05 10:13:08 +02:00
Juraj Kirchheim
c7634e0a29 Just use Error.rethrow 2017-06-01 13:14:39 +02:00
Juraj Kirchheim
49e48bc801 Merge pull request #15 from Simn/eval
don't use neko.Lib.rethrow when not in neko
2017-06-01 13:06:27 +02:00
Simon Krajewski
c150b84f18 don't use neko.Lib.rethrow when not in neko 2017-06-01 13:01:52 +02:00
Kevin Leung
04f86ce341 official gitter badge [ci skip] 2017-05-26 07:59:52 +08:00
Kevin Leung
45380371d8 Add badge [ci skip] 2017-05-19 17:54:35 +08:00
Juraj Kirchheim
d94d946301 Update README.md 2017-05-18 13:43:49 +02:00
Juraj Kirchheim
56b7caaf01 Release 0.13.5 2017-05-06 12:42:01 +02:00
Juraj Kirchheim
73e9da8435 Fix 3.2.1. 2017-04-13 21:07:44 +02:00
Juraj Kirchheim
bf23337923 Release 0.13.4 2017-04-13 21:04:00 +02:00
Juraj Kirchheim
02bd3bf69d Add Types.getMeta. 2017-04-13 21:02:58 +02:00
Juraj Kirchheim
82a3417b97 Gitignore. 2017-04-13 20:58:16 +02:00
Juraj Kirchheim
1f986c3732 VSCode setup. 2017-04-13 20:57:29 +02:00
Kevin Leung
20e998fd12 Release 0.13.3 2017-03-20 20:01:41 +08:00
Kevin Leung
0f9c0d1e97 what the haxe? 2017-03-20 19:49:21 +08:00
Kevin Leung
2ae13d534e my turn 2017-03-20 19:41:16 +08:00
back2dos
65d341f1e1 "push and read travis log" is the best kind of debugging ... 2017-03-20 11:50:32 +01:00
36 changed files with 917 additions and 406 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/bin

4
.haxerc Normal file
View File

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

View File

@@ -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
View File

@@ -0,0 +1,6 @@
{
"version": "0.1.0",
"command": "haxelib",
"args": ["run", "travix", "node"],
"problemMatcher": "$haxe"
}

View File

@@ -1,5 +1,7 @@
# Tinkerbell Macro Library # Tinkerbell Macro Library
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?maxAge=2592000)](https://gitter.im/haxetink/public)
[![Build Status](https://travis-ci.org/haxetink/tink_macro.svg?branch=master)](https://travis-ci.org/haxetink/tink_macro)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](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
View File

@@ -1 +0,0 @@
*.n

6
dev.hxml Normal file
View File

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

View File

@@ -0,0 +1,3 @@
-D hx3compat=1.0.0
# @install: lix --silent download "haxelib:/hx3compat#1.0.0" into hx3compat/1.0.0/haxelib
-cp ${HAXE_LIBCACHE}/hx3compat/1.0.0/haxelib/std

View File

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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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": ""

View File

@@ -4,6 +4,7 @@ import haxe.macro.Expr.TypeDefinition;
using tink.CoreApi; using tink.CoreApi;
using tink.macro.Positions; using tink.macro.Positions;
using StringTools;
typedef Positions = tink.macro.Positions; typedef Positions = tink.macro.Positions;
typedef ExprTools = haxe.macro.ExprTools; typedef ExprTools = haxe.macro.ExprTools;
@@ -28,7 +29,20 @@ typedef TypeResolution = Ref<Either<String, TypeDefinition>>;
class MacroApi { class MacroApi {
static var idCounter = 0; static var MAIN_CANDIDATES = ['-main', '-x', '--run'];
static public function getMainClass():Option<String> {
var args = Sys.args();
for (c in MAIN_CANDIDATES)
switch args.indexOf(c) {
case -1:
case v: return Some(args[v+1]);
}
return None;
}
@:persistent static var idCounter = 0;
@:noUsing static public inline function tempName(?prefix:String = 'tmp'):String @:noUsing static public inline function tempName(?prefix:String = 'tmp'):String
return '__tink_' + prefix + Std.string(idCounter++); return '__tink_' + prefix + Std.string(idCounter++);
@@ -36,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

View File

@@ -33,7 +33,7 @@ typedef BuildContext3 = {>BuildContext2,
class BuildCache { class BuildCache {
static var cache = new Map(); @:persistent static var cache = new Map();
static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition) { static public function getType3(name, ?types, ?pos:Position, build:BuildContext3->TypeDefinition) {
if (types == null) if (types == null)
@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

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

View File

@@ -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,
} }
} }
} }

View File

@@ -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";

View File

@@ -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 {

View File

@@ -9,7 +9,7 @@ abstract Member(Field) from Field to Field {
name: name, name: name,
pos: pos, pos: pos,
access: [APublic], access: [APublic],
kind: FProp(noread ? 'null' : 'get_' + name, nowrite ? 'null' : ('set_' + name), t), kind: FProp(noread ? 'null' : 'get', nowrite ? 'null' : 'set', t),
} }
return ret; return ret;
} }
@@ -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);
} }
} }

View File

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

View File

@@ -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);

View File

@@ -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));
}],
} }
} }
} }

View File

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

View File

@@ -1,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());
}

View File

@@ -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
View File

@@ -0,0 +1,11 @@
import haxe.macro.Context;
import haxe.macro.Expr;
using tink.MacroApi;
class Functions extends Base {
function test() {
var f:Function = (macro function () {}).getFunction().sure();
f.asExpr('foo');
assertTrue(true);
}
}

10
tests/Misc.hx Normal file
View File

@@ -0,0 +1,10 @@
import tink.MacroApi;
import haxe.unit.TestCase;
using tink.CoreApi;
class Misc extends TestCase {
function testMain() {
assertEquals('Run', MacroApi.getMainClass().force());
}
}

View File

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

View File

@@ -1,5 +1,6 @@
package ; package ;
#if macro
import haxe.macro.Expr; import haxe.macro.Expr;
import haxe.macro.Context; import haxe.macro.Context;
@@ -20,6 +21,7 @@ class Types extends Base {
assertFalse(o.isSuccess()); assertFalse(o.isSuccess());
function testIs() { function testIs() {
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float'))); assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
assertFailure(resolve('Float').isSubTypeOf(resolve('Int'))); assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
} }
@@ -41,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
View File

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