Compare commits

..

27 Commits

Author SHA1 Message Date
8b60a484b1 workaround for multiple mutability
Some checks failed
CI / test (stable, node) (push) Failing after 2m7s
CI / test (nightly, node) (push) Failing after 2m8s
CI / release (push) Has been skipped
2024-10-27 18:01:06 -05:00
Juraj Kirchheim
990096cfd5 Release 1.0.3 2023-11-09 07:34:56 +01:00
Juraj Kirchheim
1df0dd6d6c Make Member::hasMeta null safe. 2023-11-09 07:33:17 +01:00
Juraj Kirchheim
6b8be33832 Release 1.0.2 2023-11-09 06:35:53 +01:00
Juraj Kirchheim
8b769e5938 Add Member::hasMeta. 2023-11-09 06:32:57 +01:00
Juraj Kirchheim
0ca2e371d4 Remove 3.4.7 from CI. 2023-04-06 06:44:26 +00:00
Juraj Kirchheim
472916f2af Release 1.0.1 2023-04-06 06:42:09 +00:00
Juraj Kirchheim
2471637905 Fix tests for 4.3 and make use of getConfiguration. 2023-04-06 06:41:28 +00:00
Juraj Kirchheim
c400f5af51 Comment out test failing on nightly. 2021-07-07 09:09:40 +02:00
Juraj Kirchheim
32d711cd71 Release 1.0.0 2021-07-06 19:31:17 +02:00
Juraj Kirchheim
0947d1b916 Release 0.24.0 2021-06-02 10:23:41 +02:00
Juraj Kirchheim
aa186a1ec2 Add eval to Expr/TypedExpr. 2021-06-02 10:21:41 +02:00
Kevin Leung
40d7c2fbde Update badges 2021-05-22 12:07:27 +08:00
Kevin Leung
f1010a518f Bump dependency and fix haxe3 compatibility 2021-05-14 21:33:09 +08:00
Kevin Leung
54f592f14c Setup GitHub Actions 2021-05-14 21:29:25 +08:00
Juraj Kirchheim
e01bf6912a Improve VarAccess to String conversion. 2021-05-03 20:38:55 +02:00
Juraj Kirchheim
56fe8d7346 Release 0.23.0 2021-04-02 10:09:41 +02:00
Juraj Kirchheim
7e2dfad607 Make TypeMap work exacly with anons. 2021-03-14 13:25:20 +01:00
Juraj Kirchheim
94d02c91a8 Haxe 3 support. 2021-03-14 12:59:38 +01:00
Juraj Kirchheim
99a1fc3fa6 Exact type to string conversion. 2021-03-14 12:58:10 +01:00
Juraj Kirchheim
a383f7692b Argh ... 2021-02-25 11:37:04 +01:00
Juraj Kirchheim
5e5781e8bc Faster intersection for haxe4. 2021-02-25 11:36:26 +01:00
Juraj Kirchheim
63ce1853f5 Release 0.22.0 2021-02-22 11:46:09 +01:00
Juraj Kirchheim
f3ddaa6496 Merge pull request #33 from haxetink/deduce_common_type
Deduce common base type from a list of types
2020-12-23 09:32:31 +01:00
Kevin Leung
f46e49ce66 Fix haxe3 2020-12-22 17:15:28 +08:00
Kevin Leung
007c73d58e Deduce common base type from a list of types 2020-12-22 16:56:45 +08:00
Juraj Kirchheim
8c5903833c Merge pull request #32 from haxetink/isAbstract
haxe 4.2+ fix
2020-12-20 09:21:31 +01:00
29 changed files with 968 additions and 85 deletions

95
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: CI
on:
push:
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
haxe-version:
- stable
- nightly
target:
- node
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache Yarn
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Cache Haxe
uses: actions/cache@v1
with:
path: ~/haxe
key: haxe
- name: Install Lix
uses: lix-pm/setup-lix@master
- name: Install Haxe
run: lix install haxe ${{ matrix.haxe-version }}
- name: Install Haxe Libraries
run: lix download
- name: Run Test
run: lix run travix ${{ matrix.target }}
release:
runs-on: ubuntu-latest
needs: test
if: startsWith(github.ref, 'refs/tags/') # consider using the "release" event. see: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#release
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache Yarn
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Cache Haxe
uses: actions/cache@v1
with:
path: ~/haxe
key: haxe
- name: Install Lix
uses: lix-pm/setup-lix@master
- name: Install Haxe
run: lix install haxe stable
- name: Install Haxe Libraries
run: lix download
- name: Release to Haxelib
run: lix run travix release
env:
HAXELIB_AUTH: ${{ secrets.HAXELIB_AUTH }}

View File

@@ -1,4 +1,4 @@
{
"version": "4.0.5",
"version": "4.2.1",
"resolveLibs": "scoped"
}

17
.vscode/tasks.json vendored
View File

@@ -1,6 +1,19 @@
{
"version": "0.1.0",
"version": "2.0.0",
"command": "haxelib",
"args": ["run", "travix", "node"],
"problemMatcher": "$haxe"
"problemMatcher": "$haxe",
"tasks": [
{
"label": "haxelib",
"command": "haxelib",
"args": [
"run",
"travix",
"node"
],
"problemMatcher": "$haxe",
"group": "build"
}
]
}

View File

@@ -1,7 +1,9 @@
# Tinkerbell Macro Library
[![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)
[![Build Status](https://github.com/haxetink/tink_macro/actions/workflows/ci.yml/badge.svg)](https://github.com/haxetink/tink_macro/actions)
[![Gitter](https://img.shields.io/gitter/room/haxetink/public?logo=gitter&)](https://gitter.im/haxetink/public)
[![Discord](https://img.shields.io/discord/162395145352904705.svg?logo=discord)](https://discord.com/channels/162395145352904705/579634919576436736)
Explained in current marketing speak, `tink_macro` is *the* macro toolkit ;)

View File

@@ -0,0 +1,3 @@
# @install: lix --silent download "gh://github.com/haxetink/tink_chunk#f095072a029f1f50ce5f2cc0a690c3e834298d96" into tink_chunk/0.3.1/github/f095072a029f1f50ce5f2cc0a690c3e834298d96
-cp ${HAXE_LIBCACHE}/tink_chunk/0.3.1/github/f095072a029f1f50ce5f2cc0a690c3e834298d96/src
-D tink_chunk=0.3.1

View File

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

View File

@@ -1,3 +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
# @install: lix --silent download "haxelib:/tink_core#2.1.0" into tink_core/2.1.0/haxelib
-cp ${HAXE_LIBCACHE}/tink_core/2.1.0/haxelib/src
-D tink_core=2.1.0

View File

@@ -1,5 +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
# @install: lix --silent download "gh://github.com/haxetink/tink_io#e4a92421865adfd7037474937cffacaa1abd4338" into tink_io/0.8.0/github/e4a92421865adfd7037474937cffacaa1abd4338
-lib tink_chunk
-lib tink_streams
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_io/0.5.0/haxelib/src
-cp ${HAXE_LIBCACHE}/tink_io/0.8.0/github/e4a92421865adfd7037474937cffacaa1abd4338/src
-D tink_io=0.8.0

View File

@@ -1,4 +1,6 @@
-D tink_streams=0.2.1
# @install: lix --silent download "haxelib:/tink_streams#0.2.1" into tink_streams/0.2.1/haxelib
# @install: lix --silent download "gh://github.com/haxetink/tink_streams#aa006c354e742164c4f7bc8ec4532bc9154fb0d0" into tink_streams/0.3.3/github/aa006c354e742164c4f7bc8ec4532bc9154fb0d0
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_streams/0.2.1/haxelib/src
-cp ${HAXE_LIBCACHE}/tink_streams/0.3.3/github/aa006c354e742164c4f7bc8ec4532bc9154fb0d0/src
-D tink_streams=0.3.3
# temp for development, delete this file when pure branch merged
-D pure

View File

@@ -1,6 +1,7 @@
-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
# @install: lix --silent download "gh://github.com/back2dos/travix#354c2b2a82cc3b03e2f87cc1b6f0ddc0a6a5c133" into travix/0.15.0/github/354c2b2a82cc3b03e2f87cc1b6f0ddc0a6a5c133
# @post-install: cd ${HAXE_LIBCACHE}/travix/0.15.0/github/354c2b2a82cc3b03e2f87cc1b6f0ddc0a6a5c133 && haxe -cp src --run travix.PostDownload
# @run: haxelib run-dir travix ${HAXE_LIBCACHE}/travix/0.15.0/github/354c2b2a82cc3b03e2f87cc1b6f0ddc0a6a5c133
-lib tink_cli
-cp ${HAXE_LIBCACHE}/travix/0.12.2/github/7da3bf96717b52bf3c7e5d2273bf927a8cd7aeb5/src
-cp ${HAXE_LIBCACHE}/travix/0.15.0/github/354c2b2a82cc3b03e2f87cc1b6f0ddc0a6a5c133/src
-D travix=0.15.0
--macro travix.Macro.setup()

View File

@@ -9,8 +9,8 @@
"contributors": [
"back2dos"
],
"version": "0.21.1",
"releasenote": "Fix issue with build field retrieval.",
"version": "1.0.3",
"releasenote": "Make Member::hasMeta null safe.",
"tags": [
"tink",
"macro",

View File

@@ -33,15 +33,22 @@ class MacroApi {
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]);
#if (haxe_ver >= 4.3)
return switch haxe.macro.Compiler.getConfiguration().mainClass {
case null: None;
case p: Some(p.pack.concat([p.name]).join('.'));
}
#else
var args = Sys.args();
return None;
for (c in MAIN_CANDIDATES)
switch args.indexOf(c) {
case -1:
case v: return Some(args[v+1]);
}
return None;
#end
}
@:persistent static var idCounter = 0;

View File

@@ -3,7 +3,7 @@ package tink.macro;
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
using tink.macro.Positions;
using tink.macro.Exprs;
#end
@@ -15,7 +15,7 @@ package tink.macro;
static public function bounceExpr(e:Expr, transform:Expr->Expr) {
var id = idCounter++,
pos = e.pos;
outerMap.set(id, transform);
outerMap.set(id, transform);
return macro @:pos(e.pos) tink.macro.Bouncer.catchBounceExpr($e, $v{id});
}
static public function bounce(f:Void->Expr, ?pos:Position) {
@@ -29,35 +29,41 @@ package tink.macro;
pos = e.pos;
outerMap.set(id, transform);
return macro @:pos(e.pos) tink.macro.Bouncer.makeOuter($e).andBounce($v{id});
}
}
static function doOuter(id:Int, e:Expr) {
return
if (outerMap.exists(id))
if (outerMap.exists(id))
outerMap.get(id)(e);
else
Context.currentPos().error('unknown id ' + id);
Context.currentPos().error('unknown id ' + id);
}
static function doBounce(id:Int) {
return
if (bounceMap.exists(id))
if (bounceMap.exists(id))
bounceMap.get(id)();
else
Context.currentPos().error('unknown id ' + id);
Context.currentPos().error('unknown id ' + id);
}
static public var lastEvaled(default, null):Dynamic;
#else
@:noUsing static public function makeOuter<A>(a:A):Bouncer
@:noUsing static public function makeOuter<A>(a:A):Bouncer
return null;
#end
@:noUsing macro public function andBounce(ethis:Expr, id:Int)
@:noUsing macro public function andBounce(ethis:Expr, id:Int)
return
switch (ethis.expr) {
case ECall(_, params): doOuter(id, params[0]);
default: ethis.reject();
}
@:noUsing macro static public function catchBounce(id:Int)
@:noUsing static public macro function eval(f:Void->Dynamic) {
lastEvaled = f();
return macro null;
}
@:noUsing macro static public function catchBounce(id:Int)
return doBounce(id);
@:noUsing macro static public function catchBounceExpr(e:Expr, id:Int)
return doOuter(id, e);
}

View File

@@ -48,6 +48,11 @@ class Exprs {
static public inline function as(e:Expr, c:ComplexType)
return ECheckType(e, c).at(e.pos);
static public function eval(e:Expr):Dynamic {
Context.typeof(macro tink.macro.Bouncer.eval(function () return $e));
return Bouncer.lastEvaled;
}
static public function finalize(e:Expr, ?nuPos:Position, ?rules:Dict<String>, ?skipFields = false, ?callPos:PosInfos) {
if (nuPos == null)
nuPos = Context.currentPos();
@@ -261,7 +266,7 @@ class Exprs {
return e.pos.error(reason);
static public inline function toString(e:Expr):String
return new haxe.macro.Printer().printExpr(e);
return new tink.macro.Printer().printExpr(e);
static public inline function at(e:ExprDef, ?pos:Position)
return {

View File

@@ -98,6 +98,15 @@ abstract Member(Field) from Field to Field {
return
if (this.meta == null) [];
else [for (tag in this.meta) if (tag.name == name) tag];
public function hasMeta(name)
return switch this.meta {
case null | []: false;
case meta:
for (m in meta)
if (m.name == name) return true;
false;
}
public inline function asField():Field return this;
public function publish()

568
src/tink/macro/Printer.hx Normal file
View File

@@ -0,0 +1,568 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package tink.macro;
import haxe.macro.Expr;
using Lambda;
using StringTools;
/**
This class provides some utility methods to convert elements from the
macro context to a human-readable String representation.
*/
class Printer {
var tabs:String;
var tabString:String;
public function new(?tabString = "\t") {
tabs = "";
this.tabString = tabString;
}
public function printUnop(op:Unop)
return switch (op) {
case OpIncrement: "++";
case OpDecrement: "--";
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpSpread: "...";
}
public function printBinop(op:Binop)
return switch (op) {
case OpAdd: "+";
case OpMult: "*";
case OpDiv: "/";
case OpSub: "-";
case OpAssign: "=";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpAnd: "&";
case OpOr: "|";
case OpXor: "^";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpShl: "<<";
case OpShr: ">>";
case OpUShr: ">>>";
case OpMod: "%";
case OpInterval: "...";
case OpArrow: "=>";
case OpIn: "in";
case OpNullCoal: "??";
case OpAssignOp(op):
printBinop(op) + "=";
}
function escapeString(s:String, delim:String) {
return delim
+ s.replace('\\', '\\\\')
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\r", "\\r")
.replace("'", "\\'")
.replace('"', "\\\"") #if sys .replace("\x00", "\\x00") #end + delim;
}
public function printFormatString(s:String) {
return escapeString(s, "'");
}
public function printString(s:String) {
return escapeString(s, '"');
}
public function printConstant(c:Constant)
return switch (c) {
case CString(s, SingleQuotes): printFormatString(s);
case CString(s, _): printString(s);
case CIdent(s), CInt(s, null), CFloat(s, null):
s;
case CInt(s, suffix), CFloat(s, suffix):
s + suffix;
case CRegexp(s, opt): '~/$s/$opt';
}
public function printTypeParam(param:TypeParam)
return switch (param) {
case TPType(ct): printComplexType(ct);
case TPExpr(e): printExpr(e);
}
public function printTypePath(tp:TypePath)
return (tp.pack.length > 0 ? tp.pack.join(".") + "." : "")
+ tp.name
+ (tp.sub != null ? '.${tp.sub}' : "")
+ (tp.params == null ? "" : tp.params.length > 0 ? "<" + tp.params.map(printTypeParam).join(", ") + ">" : "");
// TODO: check if this can cause loops
public function printComplexType(ct:ComplexType)
return switch (ct) {
case TPath(tp): printTypePath(tp);
case TFunction(args, ret):
var wrapArgumentsInParentheses = switch args {
// type `:(a:X) -> Y` has args as [TParent(TNamed(...))], i.e `a:X` gets wrapped in `TParent()`. We don't add parentheses to avoid printing `:((a:X)) -> Y`
case [TParent(t)]: false;
// this case catches a single argument that's a type-path, so that `X -> Y` prints `X -> Y` not `(X) -> Y`
case [TPath(_) | TOptional(TPath(_))]: false;
default: true;
}
var argStr = args.map(printComplexType).join(", ");
(wrapArgumentsInParentheses ? '($argStr)' : argStr) + " -> " + (switch ret {
// wrap return type in parentheses if it's also a function
case TFunction(_): '(${printComplexType(ret)})';
default: (printComplexType(ret): String);
});
case TAnonymous(fields): "{ " + [for (f in fields) printField(f) + "; "].join("") + "}";
case TParent(ct): "(" + printComplexType(ct) + ")";
case TOptional(ct): "?" + printComplexType(ct);
case TNamed(n, ct): n + ":" + printComplexType(ct);
case TExtend(tpl, fields):
var types = [for (t in tpl) "> " + printTypePath(t) + ", "].join("");
var fields = [for (f in fields) printField(f) + "; "].join("");
'{${types}${fields}}';
case TIntersection(tl): tl.map(printComplexType).join(" & ");
}
public function printMetadata(meta:MetadataEntry)
return '@${meta.name}' + ((meta.params != null && meta.params.length > 0) ? '(${printExprs(meta.params, ", ")})' : "");
public function printAccess(access:Access)
return switch (access) {
case AStatic: "static";
case APublic: "public";
case APrivate: "private";
case AOverride: "override";
case AInline: "inline";
case ADynamic: "dynamic";
case AMacro: "macro";
case AFinal: "final";
case AExtern: "extern";
case AAbstract: "abstract";
case AOverload: "overload";
}
public function printField(field:Field) {
inline function orderAccess(access: Array<Access>) {
// final should always be printed last
// (does not modify input array)
return access.has(AFinal) ? access.filter(a -> !a.match(AFinal)).concat([AFinal]) : access;
}
return (field.doc != null
&& field.doc != "" ? "/**\n"
+ tabs
+ tabString
+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
+ "\n"
+ tabs
+ "**/\n"
+ tabs : "")
+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join('\n$tabs') + '\n$tabs' : "")
+ (field.access != null && field.access.length > 0 ? orderAccess(field.access).map(printAccess).join(" ") + " " : "")
+ switch (field.kind) {
case FVar(t, eo): ((field.access != null && field.access.has(AFinal)) ? '' : 'var ') + '${field.name}' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
case FProp(get, set, t, eo): 'var ${field.name}($get, $set)' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
case FFun(func): 'function ${field.name}' + printFunction(func);
}
}
public function printTypeParamDecl(tpd:TypeParamDecl)
return (tpd.meta != null && tpd.meta.length > 0 ? tpd.meta.map(printMetadata).join(" ") + " " : "")
+ tpd.name
+ (tpd.params != null && tpd.params.length > 0 ? "<" + tpd.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (tpd.constraints != null && tpd.constraints.length > 0 ? ":(" + tpd.constraints.map(printComplexType).join(", ") + ")" : "")
+ (tpd.defaultType != null ? "=" + printComplexType(tpd.defaultType) : "");
public function printFunctionArg(arg:FunctionArg)
return (arg.opt ? "?" : "") + arg.name + opt(arg.type, printComplexType, ":") + opt(arg.value, printExpr, " = ");
public function printFunction(func:Function, ?kind:FunctionKind) {
var skipParentheses = switch func.args {
case [{ type:null }]: kind == FArrow;
case _: false;
}
return (func.params == null ? "" : func.params.length > 0 ? "<" + func.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (skipParentheses ? "" : "(")
+ func.args.map(printFunctionArg).join(", ")
+ (skipParentheses ? "" : ")")
+ (kind == FArrow ? " ->" : "")
+ opt(func.ret, printComplexType, ":")
+ opt(func.expr, printExpr, " ");
}
public function printVar(v:Var) {
var s = v.name + opt(v.type, printComplexType, ":") + opt(v.expr, printExpr, " = ");
return switch v.meta {
case null|[]: s;
case meta: meta.map(printMetadata).join(" ") + " " + s;
}
}
public function printObjectFieldKey(of:ObjectField) {
return switch (of.quotes) {
case null | Unquoted: of.field;
case Quoted: '"${of.field}"'; // TODO: Have to escape that?
}
}
public function printObjectField(of:ObjectField) {
return '${printObjectFieldKey(of)} : ${printExpr(of.expr)}';
}
public function printExpr(e:Expr)
return e == null ? "#NULL" : switch (e.expr) {
case EConst(c): printConstant(c);
case EArray(e1, e2): '${printExpr(e1)}[${printExpr(e2)}]';
case EBinop(op, e1, e2): '${printExpr(e1)} ${printBinop(op)} ${printExpr(e2)}';
case EField(e1, n, kind): kind == Safe ? '${printExpr(e1)}?.$n' : '${printExpr(e1)}.$n';
case EParenthesis(e1): '(${printExpr(e1)})';
case EObjectDecl(fl):
"{ " + fl.map(function(fld) return printObjectField(fld)).join(", ") + " }";
case EArrayDecl(el): '[${printExprs(el, ", ")}]';
case ECall(e1, el): '${printExpr(e1)}(${printExprs(el, ", ")})';
case ENew(tp, el): 'new ${printTypePath(tp)}(${printExprs(el, ", ")})';
case EUnop(op, true, e1): printExpr(e1) + printUnop(op);
case EUnop(op, false, e1): printUnop(op) + printExpr(e1);
case EFunction(FNamed(no,inlined), func): (inlined ? 'inline ' : '') + 'function $no' + printFunction(func);
case EFunction(kind, func): (kind != FArrow ? "function" : "") + printFunction(func, kind);
case EVars([]): "var ";
case EVars([v]): ((v.isStatic) ? "static " : "") + ((v.isFinal) ? "final " : "var ") + printVar(v);
case EVars(vl): [for (v in vl) printExpr({expr: EVars([v]), pos: e.pos})].join("; ");
case EBlock([]): '{ }';
case EBlock(el):
var old = tabs;
tabs += tabString;
var s = '{\n$tabs' + printExprs(el, ';\n$tabs');
tabs = old;
s + ';\n$tabs}';
case EFor(e1, e2): 'for (${printExpr(e1)}) ${printExpr(e2)}';
case EIf(econd, eif, null): 'if (${printExpr(econd)}) ${printExpr(eif)}';
case EIf(econd, eif, eelse): 'if (${printExpr(econd)}) ${printExpr(eif)} else ${printExpr(eelse)}';
case EWhile(econd, e1, true): 'while (${printExpr(econd)}) ${printExpr(e1)}';
case EWhile(econd, e1, false): 'do ${printExpr(e1)} while (${printExpr(econd)})';
case ESwitch(e1, cl, edef):
var old = tabs;
tabs += tabString;
var s = 'switch ${printExpr(e1)} {\n$tabs'
+ cl.map(function(c) return 'case ${printExprs(c.values, ", ")}' + (c.guard != null ? ' if (${printExpr(c.guard)}):' : ":")
+ (c.expr != null ? (opt(c.expr, printExpr)) + ";" : ""))
.join('\n$tabs');
if (edef != null)
s += '\n${tabs}default:' + (edef.expr == null ? "" : printExpr(edef) + ";");
tabs = old;
s + '\n$tabs}';
case ETry(e1, cl):
'try ${printExpr(e1)}' + cl.map(function(c) return ' catch(${c.name}${c.type == null ? '' : (':' + printComplexType(c.type))}) ${printExpr(c.expr)}').join("");
case EReturn(eo): "return" + opt(eo, printExpr, " ");
case EBreak: "break";
case EContinue: "continue";
case EUntyped(e1): "untyped " + printExpr(e1);
case EThrow(e1): "throw " + printExpr(e1);
case ECast(e1, cto) if (cto != null): 'cast(${printExpr(e1)}, ${printComplexType(cto)})';
case ECast(e1, _): "cast " + printExpr(e1);
case EIs(e1, ct): '${printExpr(e1)} is ${printComplexType(ct)}';
case EDisplay(e1, _): '#DISPLAY(${printExpr(e1)})';
case ETernary(econd, eif, eelse): '${printExpr(econd)} ? ${printExpr(eif)} : ${printExpr(eelse)}';
case ECheckType(e1, ct): '(${printExpr(e1)} : ${printComplexType(ct)})';
case EMeta({ name:":implicitReturn" }, { expr:EReturn(e1) }): printExpr(e1);
case EMeta(meta, e1): printMetadata(meta) + " " + printExpr(e1);
}
public function printExprs(el:Array<Expr>, sep:String) {
return el.map(printExpr).join(sep);
}
function printExtension(tpl:Array<TypePath>, fields:Array<Field>) {
return '{\n$tabs>'
+ tpl.map(printTypePath).join(',\n$tabs>')
+ ","
+ (fields.length > 0 ? ('\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}") : ("\n}"));
}
function printStructure(fields:Array<Field>) {
return fields.length == 0 ? "{ }" : '{\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}";
}
public function printTypeDefinition(t:TypeDefinition, printPackage = true):String {
var old = tabs;
tabs = tabString;
var str = t == null ? "#NULL" : (printPackage && t.pack.length > 0 && t.pack[0] != "" ? "package " + t.pack.join(".") + ";\n" : "")
+ (t.doc != null && t.doc != "" ? "/**\n" + tabString + StringTools.replace(t.doc, "\n", "\n" + tabString) + "\n**/\n" : "")
+ (t.meta != null && t.meta.length > 0 ? t.meta.map(printMetadata).join(" ") + " " : "")
+ (t.isExtern ? "extern " : "")
+ switch (t.kind) {
case TDEnum:
"enum "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " {\n"
+ [
for (field in t.fields)
tabs
+ (field.doc != null
&& field.doc != "" ? "/**\n"
+ tabs
+ tabString
+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
+ "\n"
+ tabs
+ "**/\n"
+ tabs : "")
+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join(" ") + " " : "")
+ (switch (field.kind) {
case FVar(t, _): field.name + opt(t, printComplexType, ":");
case FProp(_, _, _, _): throw "FProp is invalid for TDEnum.";
case FFun(func): field.name + printFunction(func);
})
+ ";"].join("\n")
+ "\n}";
case TDStructure:
"typedef "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " = {\n"
+ [
for (f in t.fields) {
tabs + printField(f) + ";";
}
].join("\n")
+ "\n}";
case TDClass(superClass, interfaces, isInterface, isFinal, isAbstract):
(isFinal ? "final " : "")
+ (isAbstract ? "abstract " : "")
+ (isInterface ? "interface " : "class ")
+ t.name
+ (t.params != null && t.params.length > 0 ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (superClass != null ? " extends " + printTypePath(superClass) : "")
+ (interfaces != null ? (isInterface ? [for (tp in interfaces) " extends " + printTypePath(tp)] : [
for (tp in interfaces)
" implements " + printTypePath(tp)
]).join("") : "")
+ " {\n"
+ [
for (f in t.fields) {
tabs + printFieldWithDelimiter(f);
}
].join("\n")
+ "\n}";
case TDAlias(ct):
"typedef "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " = "
+ (switch (ct) {
case TExtend(tpl, fields): printExtension(tpl, fields);
case TAnonymous(fields): printStructure(fields);
case _: printComplexType(ct);
})
+ ";";
case TDAbstract(tthis, from, to):
"abstract "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (tthis == null ? "" : "(" + printComplexType(tthis) + ")")
+ (from == null ? "" : [for (f in from) " from " + printComplexType(f)].join(""))
+ (to == null ? "" : [for (t in to) " to " + printComplexType(t)].join(""))
+ " {\n"
+ [
for (f in t.fields) {
tabs + printFieldWithDelimiter(f);
}
].join("\n")
+ "\n}";
case TDField(kind, access):
tabs = old;
(access != null && access.length > 0 ? access.map(printAccess).join(" ") + " " : "")
+ switch (kind) {
case FVar(type, eo): ((access != null && access.has(AFinal)) ? '' : 'var ') + '${t.name}' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
case FProp(get, set, type, eo): 'var ${t.name}($get, $set)' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
case FFun(func): 'function ${t.name}' + printFunction(func) + switch func.expr { case {expr: EBlock(_)}: ""; case _: ";"; };
}
} tabs = old;
return str;
}
function printFieldWithDelimiter(f:Field):String {
return printField(f) + switch (f.kind) {
case FVar(_, _), FProp(_, _, _, _): ";";
case FFun({expr: null}): ";";
case FFun({expr: {expr: EBlock(_)}}): "";
case FFun(_): ";";
case _: "";
};
}
function opt<T>(v:T, f:T->String, prefix = "")
return v == null ? "" : (prefix + f(v));
public function printExprWithPositions(e:Expr) {
var buffer = new StringBuf();
function format4(i:Int) {
return StringTools.lpad(Std.string(i), " ", 4);
}
function loop(tabs:String, e:Expr) {
function add(s:String, ?p = null) {
if (p == null) {
p = e.pos;
}
var p = #if macro haxe.macro.Context.getPosInfos(p) #else e.pos #end;
buffer.add('${format4(p.min)}-${format4(p.max)} $tabs$s\n');
}
function loopI(e:Expr)
loop(tabs + tabString, e);
switch (e.expr) {
case EConst(c):
add(printConstant(c));
case EArray(e1, e2):
add("EArray");
loopI(e1);
loopI(e2);
case EBinop(op, e1, e2):
add("EBinop " + printBinop(op));
loopI(e1);
loopI(e2);
case EField(e, field):
add("EField " + field);
loopI(e);
case EParenthesis(e):
add("EParenthesis");
loopI(e);
case EObjectDecl(fields):
add("EObjectDecl");
for (field in fields) {
add(field.field); // TODO: we don't have the field pos?
loopI(field.expr);
}
case EArrayDecl(values):
add("EArrayDecl");
values.iter(loopI);
case ECall(e, params):
add("ECall");
loopI(e);
params.iter(loopI);
case ENew(tp, params):
add("ENew " + printTypePath(tp));
params.iter(loopI);
case EUnop(op, postFix, e):
add("EUnop " + printUnop(op));
loopI(e);
case EVars(vars):
add("EVars");
for (v in vars) {
if (v.expr != null) {
add(v.name);
loopI(v.expr);
}
}
case EFunction(_, f):
add("EFunction");
if (f.expr != null) {
loopI(f.expr);
}
case EBlock(exprs):
add("EBlock");
exprs.iter(loopI);
case EFor(it, expr):
add("EFor");
loopI(it);
loopI(expr);
case EIf(econd, eif, eelse):
add("EIf");
loopI(econd);
loopI(eif);
if (eelse != null) {
loopI(eelse);
}
case EWhile(econd, e, normalWhile):
add("EWhile");
loopI(econd);
loopI(e);
case ESwitch(e, cases, edef):
add("ESwitch");
loopI(e);
for (c in cases) {
for (pat in c.values) {
loop(tabs + tabString + tabString, pat);
}
if (c.expr != null) {
loop(tabs + tabString + tabString + tabString, c.expr);
}
}
if (edef != null) {
loop(tabs + tabString + tabString + tabString, edef);
}
case ETry(e, catches):
add("ETry");
loopI(e);
for (c in catches) {
loop(tabs + tabString + tabString, c.expr);
}
case EReturn(e):
add("EReturn");
if (e != null) {
loopI(e);
}
case EBreak:
add("EBreak");
case EContinue:
add("EContinue");
case EUntyped(e):
add("EUntyped");
loopI(e);
case EThrow(e):
add("EThrow");
loopI(e);
case ECast(e, t):
add("ECast");
loopI(e);
case EIs(e, t):
add("EIs");
loopI(e);
case EDisplay(e, displayKind):
add("EDisplay");
loopI(e);
case ETernary(econd, eif, eelse):
add("ETernary");
loopI(econd);
loopI(eif);
loopI(eelse);
case ECheckType(e, t):
add("ECheckType");
loopI(e);
case EMeta(s, e):
add("EMeta " + printMetadata(s));
loopI(e);
}
}
loop("", e);
return buffer.toString();
}
}

View File

@@ -2,6 +2,8 @@ package tink.macro;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
using tink.MacroApi;
class Sisyphus {
@@ -61,8 +63,7 @@ class Sisyphus {
}
}
public static function toComplexType(type : Null<Type>) : Null<ComplexType> return {
static public function toComplexType(type : Null<Type>) : Null<ComplexType> return {
inline function direct()
return Types.toComplex(type, { direct: true });
switch (type) {
@@ -136,4 +137,89 @@ class Sisyphus {
}],
}
}
static function exactBase<T:BaseType>(r:Ref<T>, params:Array<Type>) {
var t = r.get();
var isMain = !t.isPrivate && switch t.pack {
case []: t.module == t.name || t.module == 'StdTypes';
default: StringTools.endsWith(t.module, '.${t.name}');
}
return (
if (isMain) t.pack.concat([t.name]).join('.')
else t.module + '.' + t.name
) + switch params {
case []: '';
case params:
'<${params.map(toExactString).join(', ')}>';
}
}
static inline function isFinal(c:ClassField)
return #if haxe4 c.isFinal #else false #end;
static function exactAnonField(c:ClassField) {
var kw =
switch c.kind {
case FMethod(_): 'function';
case FVar(_):
if (isFinal(c)) 'final' else 'var';
}
return [for (m in c.meta.get()) m.toString() + ' '].join('') + '$kw ${c.name}' + (switch c.kind {
case FVar(read, write):
(
if (isFinal(c) || (read == AccNormal && write == AccNormal)) ''
else '(${read.accessToName()}, ${read.accessToName(false)})'
) + ':' + c.type.toExactString();
case FMethod(_):
switch haxe.macro.Context.follow(c.type) {
case TFun(arg, ret): exactSig(arg, ret, ':');
default: throw 'assert';
}
}) + ';';
}
static function exactSig(args:Array<{name:String, opt:Bool, t:Type}>, ret:Type, sep:String)
return '(${[for (a in args) (if (a.opt) '?' else '') + a.name + ':' + toExactString(a.t)].join(', ')})$sep${toExactString(ret)}';
static public function toExactString(t:Type)
return switch t {
case TMono(t): t.toString();
case TEnum(r, params): exactBase(r, params);
case TInst(r, params): exactBase(r, params);
case TType(r, params): exactBase(r, params);
case TAbstract(r, params): exactBase(r, params);
case TFun(args, ret): exactSig(args, ret, '->');
case TAnonymous(a): '{ ${[for (f in a.get().fields) exactAnonField(f)].join(' ')} }';
case TDynamic(null): 'Dynamic';
case TDynamic(t): 'Dynamic<${toExactString(t)}>';
case TLazy(f): toExactString(f());
}
static function eager(t:Type)
return switch t {
case TLazy(f): eager(f());
default: t;
}
static public function compare(t1:Type, t2:Type, ?follow:Bool = true) {
if (follow) {
t1 = t1.reduce();
t2 = t2.reduce();
}
else {
t1 = eager(t1);
t2 = eager(t2);
}
return switch t1.getIndex() - t2.getIndex() {
case 0:
switch Reflect.compare(t1.toString(), t2.toString()) {
case 0: Reflect.compare(t1.toExactString(), t2.toExactString());
case v: v;
}
case v: v;
}
}
}

View File

@@ -1,5 +1,6 @@
package tink.macro;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.ds.Option;
using haxe.macro.Tools;
@@ -31,6 +32,9 @@ class TypedExprs {
return None;
}
static public function eval(t:TypedExpr)
return Exprs.eval(Context.storeTypedExpr(t));
static public function isThis(t:TypedExpr):Bool
return switch t {
case null: false;

View File

@@ -10,6 +10,8 @@ using haxe.macro.Tools;
using tink.MacroApi;
using tink.CoreApi;
import haxe.macro.Type.Ref;
class Types {
static public function definedType(typeName:String)
@@ -39,9 +41,10 @@ class Types {
static public function accessToName(v:VarAccess, ?read = true)
return
switch (v) {
case AccNormal, AccInline: 'default';
case AccNormal: 'default';
case AccInline: if (read) 'default' else 'never';
case AccNo: 'null';
case AccNever: 'never';
case AccNever #if haxe4 | AccCtor #end: 'never';
case AccCall: if (read) 'get' else 'set';
default:
throw 'not implemented';
@@ -208,7 +211,22 @@ class Types {
default: Failure('type "$t" has no position');
}
static public function deduceCommonType(types:Array<Type>):Outcome<Type, Error> {
var exprs = types.map(function(t) {
var ct = t.toComplex();
return macro (cast null:$ct);
});
return switch (macro $a{exprs}).typeof() {
case Success(TInst(_, [v])): Success(v);
case Success(_): throw 'unreachable';
case Failure(e): Failure(new Error('Unable to deduce common type among $types'));
}
}
/// like haxe.macro.TypeTools.toString, but not lossy
static public function toExactString(t:Type)
return Sisyphus.toExactString(t);
static public function toString(t:ComplexType)
return new Printer().printComplexType(t);
@@ -296,6 +314,9 @@ class Types {
if (types.length == 1) return Success(types[1]);
#if haxe4
return Success(TIntersection(types));
#end
var paths = [],
fields = [];
@@ -337,18 +358,8 @@ class Types {
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 public function compare(t1:Type, t2:Type, ?follow:Bool = true)
return Sisyphus.compare(t1, t2, follow);
static var SUGGESTIONS = ~/ \(Suggestions?: .*\)$/;

View File

@@ -29,7 +29,9 @@ abstract PhysicalType<T>(Either<Class<T>, Enum<T>>) {
}
//TODO: this helper should go somewhere
class Base extends TestCase {
function stringCompare<A>(v1:A, v2:A, ?pos)
assertEquals(Std.string(v1), Std.string(v2), pos);
function fail(msg:String, ?c : PosInfos) {
currentTest.done = true;
currentTest.success = false;

8
tests/Dummy.hx Normal file
View File

@@ -0,0 +1,8 @@
class Dummy {
public function new() {}
static public var p(default, never) = new Private();
}
private class Private {
public function new() {}
}

21
tests/ExactStrings.hx Normal file
View File

@@ -0,0 +1,21 @@
import haxe.macro.Context.typeof;
using tink.MacroApi;
class ExactStrings extends Base {
function test() {
function expect(s:String, e, ?pos)
assertEquals(s, typeof(e).toExactString(), pos);
expect('Dummy', macro new Dummy());
expect('nested.Dummy', macro new nested.Dummy());
expect('Dummy.Private', macro Dummy.p);
expect('nested.Dummy.Private', macro nested.Dummy.p);
expect('{ @foo var x:Int; }', macro (null:{@foo var x:Int;}));
expect('{ @foo @bar var x:Int; }', macro (null:{@foo @bar var x:Int;}));
expect('{ @bar @foo var x:Int; }', macro (null:{@bar @foo var x:Int;}));// not 100% sure this is always the best choice, but let's roll with it
expect('{ @bar var x:Int; }', macro (null:{@bar var x:Int;}));
expect('{ var x:Int; var y:Int; }', macro (null:{x:Int,y:Int}));
expect('{ var x:Int; var y:Int; }', macro (null:{y:Int,x:Int}));
expect('{ function foo(x:Int, ?y:Int):Void; }', macro (null:{ function foo(x:Int, ?y:Int):Void; }));
}
}

View File

@@ -16,38 +16,47 @@ class Exprs extends Base {
assertTrue(id.length <= 3);
}
}
function testEval() {
var expr = macro (untyped {foo:[{bar:234},'bar']});
var str = Std.string(untyped {foo:[{bar:234},'bar']});
assertEquals(Std.string(expr.eval()), Std.string(untyped {foo:[{bar:234},'bar']}));
// This doesn't work in Haxe 4.3, which is correct, because typeExpr types an expression into target context, rather than macro context
// assertEquals(Std.string(Context.typeExpr(expr).eval()), Std.string(untyped {foo:[{bar:234},'bar']}));
}
function testGet() {
assertEquals('foo', (macro foo).getIdent().sure());
assertEquals('foo', (macro "foo").getString().sure());
assertEquals('foo', (macro foo).getName().sure());
assertEquals('foo', (macro "foo").getName().sure());
assertEquals(5, (macro 5).getInt().sure());
exprEq(macro [a, b, c], (macro function (a, b, c) [a, b, c]).getFunction().sure().expr);
assertEquals('a,b,c', [for (arg in (macro function (a, b, c) [a, b, c]).getFunction().sure().args) arg.name].join(','));
assertFalse((macro 'foo').getIdent().isSuccess());
assertFalse((macro foo).getString().isSuccess());
assertFalse((macro 5).getName().isSuccess());
assertFalse((macro 5.1).getInt().isSuccess());
assertFalse((macro foo).getFunction().isSuccess());
}
function testShortcuts() {
assertTrue(true);
}
function testIterType() {
assertEquals('Int', (macro [1, 2]).getIterType().sure().getID());
assertEquals('Int', (macro [1, 2].iterator()).getIterType().sure().getID());
assertEquals('Int', ECheckType(macro null, macro: Arrayish).at().getIterType().sure().getID());
}
function testYield() {
function yielder(e) return macro @yield $e;
function test(x:Expr, e:Expr, ?options)
exprEq(x, e.yield(yielder, options));
test(macro @yield foo, macro foo);
test(macro @yield (foo), macro (foo));
test(macro for (_) @yield foo, macro for (_) foo);
@@ -57,10 +66,10 @@ class Exprs extends Base {
}
function testSubstitute() {
exprEq(
macro foo.call(arg1, arg2),
macro foo.call(arg1, arg2),
(macro bar.call(x, y)).substitute({ x: macro arg1, y: macro arg2, bar: macro foo })
);
exprEq(
macro {
var x:Map<Int, String> = new Map(),
@@ -89,7 +98,7 @@ class Exprs extends Base {
])
);
}
function testConcat() {
exprEq(macro {a; b;}, (macro a).concat(macro b));
exprEq(macro {a; b; c;}, (macro {a; b;}).concat(macro c));

View File

@@ -6,8 +6,6 @@ import haxe.macro.Expr;
using tink.MacroApi;
class Positions extends Base {
function stringCompare<A>(v1:A, v2:A)
assertEquals(Std.string(v1), Std.string(v2));
function testSanitize() {
var p:Position = null;
@@ -19,6 +17,6 @@ class Positions extends Base {
function testBlank() {
var p:Position = null;
var t = p.makeBlankType();
stringCompare('TMono(<mono>)', cast t.toType().sure());
stringCompare('TMono(<mono>)', cast t.toType().sure().reduce());
}
}

View File

@@ -1,6 +1,9 @@
package ;
import haxe.unit.*;
#if macro
using haxe.macro.Tools;
#end
class Run {
#if !macro
@@ -14,6 +17,7 @@ class Run {
new TypeMapTest(),
new Functions(),
new Misc(),
new ExactStrings(),
];
#end
macro static function test() {

View File

@@ -11,6 +11,8 @@ class TypeMapTest extends TestCase {
var t = new TypeMap();
var t1 = (macro [{ foo: [{ bar: '5' }]}]).typeof().sure();
var t2 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
var t3 = (macro [{ foo: [{ bar: 5 }]}]).typeof().sure();
var t4 = (macro [{ foo: [({ bar: 5 }:{ @foo var bar:Int; })]}]).typeof().sure();
t.set(t1, 0);
assertEquals(Lambda.count(t), 1);
@@ -19,7 +21,10 @@ class TypeMapTest extends TestCase {
t.set(t1, 2);
assertEquals(Lambda.count(t), 2);
t.set(t2, 3);
t.set(t3, 3);
assertEquals(Lambda.count(t), 2);
t.set(t4, 4);
assertEquals(Lambda.count(t), 3);
assertEquals(t.get(t1), 2);
assertEquals(t.get(t2), 3);

View File

@@ -10,36 +10,35 @@ using tink.MacroApi;
class Types extends Base {
function type(c:ComplexType)
return c.toType().sure();
function resolve(type:String)
return Context.getType(type);
inline function assertSuccess<S, F>(o:Outcome<S, F>)
assertTrue(o.isSuccess());
inline function assertFailure<S, F>(o:Outcome<S, F>)
assertFalse(o.isSuccess());
function testIs() {
assertSuccess(resolve('Int').isSubTypeOf(resolve('Float')));
assertFailure(resolve('Float').isSubTypeOf(resolve('Int')));
}
}
function testFields() {
var expected = type(macro : Void -> Iterator<Arrayish>),
iterator = type(macro : haxe.ds.StringMap<Arrayish>).getFields(true).sure().filter(function (c) return c.name == 'iterator')[0];
assertSuccess(iterator.type.isSubTypeOf(expected));
assertSuccess(expected.isSubTypeOf(iterator.type));
}
function testConvert() {
assertSuccess((macro : Int).toType());
assertFailure((macro : Tni).toType());
function blank()
return type(MacroApi.pos().makeBlankType());
var bool = type(macro : Bool);
assertTrue(blank().isSubTypeOf(bool).isSuccess());
assertTrue(bool.isSubTypeOf(blank()).isSuccess());
@@ -65,5 +64,20 @@ class Types extends Base {
assertEquals('String', Context.getType('String').toComplex().toString());
assertEquals('tink.CoreApi.Noise', Context.getType('tink.CoreApi.Noise').toComplex().toString());
}
function testDeduceCommonType() {
function ct2t(ct:ComplexType) return ct.toType().sure();
assertEquals('StdTypes.Float', tink.macro.Types.deduceCommonType([(macro:Float), (macro:Int)].map(ct2t)).sure().toComplex().toString());
assertEquals('Types.CommonI1', tink.macro.Types.deduceCommonType([(macro:Types.CommonA), (macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
assertEquals('Types.CommonI2', tink.macro.Types.deduceCommonType([(macro:Types.CommonB), (macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
// assertEquals('Types.CommonI3', tink.macro.Types.deduceCommonType([(macro:Types.CommonC)].map(ct2t)).sure().toComplex().toString());
}
}
#end
#end
interface CommonI1 {}
interface CommonI2 {}
interface CommonI3 {}
class CommonA implements CommonI1 {}
class CommonB implements CommonI2 implements CommonI1 {}
class CommonC implements CommonI3 implements CommonI2 implements CommonI1 {}

10
tests/nested/Dummy.hx Normal file
View File

@@ -0,0 +1,10 @@
package nested;
class Dummy {
public function new() {}
static public var p(default, never) = new Private();
}
private class Private {
public function new() {}
}