-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
371 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
bin/* | ||
dump/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"version": "c85de49", | ||
"resolveLibs": "scoped" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,67 @@ | ||
# coerce | ||
|
||
Type conversion from one type to another. | ||
Helpful Types to select functions based on method signatures. | ||
~~Type conversion from one type to another.~~ | ||
|
||
### Types | ||
|
||
#### `Resolve` | ||
|
||
```haxe | ||
abstract Resolve<T:Function, @:const R:EReg> { | ||
@:from public static function coerce<In>(expr:haxe.macro.Expr.ExprOf<Class<Int>>):haxe.macro.Expr; | ||
} | ||
``` | ||
|
||
```haxe | ||
import be.types.Resolve.coerce; | ||
class Main { | ||
public static function main() { | ||
var input = '999'; | ||
trace( asInt(coerce(Std), input) ); // trace(999); | ||
trace( asInt(coerce(Fake), input) ); // trace(1000); | ||
} | ||
public static inline function asInt(r:Resolve<String->Int, ~/int/i>, v:String):Int return r(v); | ||
} | ||
class Fake { | ||
public static function parseFloat(v:String):Float return 0.0; | ||
public static function falseSig(v:String):Int return throw 'This is skipped due to the `~/int/i` regular expression'; | ||
public static function parseInt(v:String):Int return 1000; | ||
} | ||
``` | ||
|
||
#### `Pick` | ||
|
||
`Pick` is a `@:genericBuild` macro which wraps `Resolve` making it a more UX friendly type to work with. | ||
`Pick` only requires the type signature. | ||
|
||
```haxe | ||
import be.types.Pick; | ||
import be.types.Resolve.coerce; | ||
class Main { | ||
public static function main() { | ||
var input = '999'; | ||
trace( asInt(coerce(Std), input) ); // trace(999); | ||
trace( asInt(coerce(Fake), input) ); // trace(1000); | ||
} | ||
public static function asInt(r:Pick<String->Int>, v:String):Int return r(v); | ||
} | ||
class Fake { | ||
public static function parseFloat(v:String):Float return 0.0; | ||
public static function parseInt(v:String):Int return 1000; | ||
} | ||
``` | ||
|
||
### Notes | ||
|
||
- It's recommended to `import be.type.Resolve.coerce` and wrapping classes in `coerce` to avoid false autocompletion errors. | ||
|
||
### Defines | ||
|
||
- `-D coerce-verbose` - Paired with `-debug`, this will have the build macros print a bunch of trace statements. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
-lib tink_macro | ||
|
||
-cp src | ||
-cp tests | ||
|
||
-debug | ||
|
||
-D eval-stack | ||
-D analyzer-optimize | ||
-D coerce-verbose | ||
|
||
-main Entry | ||
|
||
-dce full | ||
|
||
--each | ||
|
||
-js bin/co.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
-D tink_core=1.18.0 | ||
# @install: lix --silent download "haxelib:/tink_core#1.18.0" into tink_core/1.18.0/haxelib | ||
-cp ${HAXE_LIBCACHE}/tink_core/1.18.0/haxelib/src |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-D tink_macro=0.17.2 | ||
# @install: lix --silent download "gh://github.com/haxetink/tink_macro#b7e413d839dbf8b81d4a064ae4e90013a2c8615b" into tink_macro/0.17.2/github/b7e413d839dbf8b81d4a064ae4e90013a2c8615b | ||
-lib tink_core | ||
-cp ${HAXE_LIBCACHE}/tink_macro/0.17.2/github/b7e413d839dbf8b81d4a064ae4e90013a2c8615b/src |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package be.co; | ||
|
||
#if (eval || macro) | ||
import haxe.macro.Context; | ||
using tink.MacroApi; | ||
#end | ||
|
||
@:callable @:notNull abstract Coerce<From, To>(From->To) from From->To { | ||
|
||
@:noCompletion public static var stringint:Coerce<String, Int> = Std.parseInt; | ||
@:noCompletion public static var stringfloat:Coerce<String, Float> = Std.parseFloat; | ||
|
||
|
||
public static macro function value<From, To>(input:ExprOf<From>):ExprOf<To> { | ||
trace( input ); | ||
var _input = input.typeof().sure(); | ||
var _return = Context.getExpectedType(); | ||
trace( _input, _return ); | ||
var _property = (_input.getID() + _return.getID()).toLowerCase(); | ||
return 'be.co.Coerce.$_property'.resolve(input.pos).call([input]); | ||
} | ||
|
||
} | ||
|
||
/** | ||
General Structure Layout | ||
--- | ||
Builtin @:from conversions for basic types. | ||
--- | ||
Self = { | ||
public function from${InputTypeName}:${Self}; | ||
public function as${OutputTypeName}(v:${InputType}):${OutputType}; | ||
... | ||
} | ||
**/ | ||
|
||
typedef InputString = { | ||
public function fromString(v:String):InputString; | ||
public function asInt():Int; | ||
public function asFloat():Int; | ||
public function asDate():Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package be.macros; | ||
|
||
import haxe.macro.Type; | ||
import haxe.macro.Expr.TypePath; | ||
import haxe.macro.Expr.ComplexType; | ||
import haxe.macro.*; | ||
import tink.macro.BuildCache; | ||
|
||
using tink.MacroApi; | ||
|
||
/** | ||
Provides a way to select any method that satifies | ||
- expected arg types | ||
- expected return type | ||
- method names using a regex | ||
--- | ||
be.types.Pick<String->Int> // Any method that unifies with String->Int signature. | ||
be.types.Pick<String->Rest->Int> // Any method that has non optional String, zero or more optional args, and returns Int. | ||
be.types.Pick<Int->Self> // Any method that unifies with Int->Self. Self means the method returns a type that can unify with the type/module whatever. | ||
be.types.Pick<Int->Self, ~/from[a-zA-Z]+/> // Same as before, but uses a regular expression to filter method names. | ||
--- | ||
thought1 | ||
Pick<String->Int>, Pick<T->Int> - this creates an abstract of `String->Int` or `T->Int` in these examples. | ||
Resolve<Class<Std>>, Resolve<Class<T>> - Class<T> is the underlying type. Resolve has a macro @:from function | ||
that detects the expected type of Pick<T> | ||
--- | ||
thought2 | ||
Instead of a generic build macro, Pick<T> is an abstract class. Underlying type Class<T>, with only a macro @:from | ||
function that searches the Class<T> for a compatible `Context.getExpectedType()`. | ||
**/ | ||
|
||
class PickBuilder { | ||
|
||
public static function search() { | ||
return BuildCache.getTypeN('be.types.Pick', function(ctx:BuildContextN) { | ||
var typeName = ctx.name; | ||
var signature = null; | ||
var signatureType = null; | ||
var signatureComplex = null; | ||
var reg = null; | ||
var ereg = macro ~//; | ||
var filter:EReg = null; | ||
var self = 'be.types.$typeName'.asComplexType(); | ||
var ctor = 'be.types.$typeName'.asTypePath(); | ||
|
||
for (type in ctx.types) { | ||
switch type { | ||
case TFun(args, ret) if (signature == null): | ||
signature = {args:args, ret:ret}; | ||
signatureType = type; | ||
signatureComplex = signatureType.toComplex(); | ||
|
||
case TInst(_.get() => {kind:KExpr( e = {expr:EConst( CRegexp(r, opt) ), pos:pos} )}, _): | ||
reg = {r:r, opt:opt}; | ||
filter = new EReg(r, opt); | ||
ereg = e; | ||
|
||
case x: | ||
#if (debug && coerce_verbose) | ||
trace( x ); | ||
#end | ||
} | ||
} | ||
|
||
var ctype = macro:be.types.Resolve<$signatureComplex>; | ||
var call = macro new $ctor(null); | ||
var td = macro class $typeName { | ||
public inline function new(v) this = v; | ||
@:from public static inline function fromResolve(r:$ctype) return new $ctor(r); | ||
} | ||
|
||
switch ctype { | ||
case TPath({params:params}): | ||
// Manually insert TPExpr as `macro:be.types.Resolve<$signatureComplex, $ereg>` fails | ||
params.push( TPExpr(ereg) ); | ||
|
||
case x: | ||
trace(x); | ||
} | ||
td.kind = TDAbstract(ctype, [ctype], [ctype]); | ||
td.meta = [ | ||
{name:':forward', params:[], pos:ctx.pos}, | ||
{name:':forwardStatics', params:[], pos:ctx.pos}, | ||
{name:':notNull', params:[], pos:ctx.pos}, | ||
{name:':callable', params:[], pos:ctx.pos} | ||
]; | ||
|
||
#if (debug && coerce_verbose) | ||
trace( new Printer().printTypeDefinition(td) ); | ||
#end | ||
|
||
return td; | ||
}); | ||
} | ||
|
||
public static function foo(expr:Expr):Expr { | ||
return macro {}; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package be.types; | ||
|
||
#if (eval||macro) | ||
import haxe.macro.*; | ||
|
||
using tink.MacroApi; | ||
#end | ||
|
||
#if !(eval || macro) | ||
@:genericBuild( be.macros.PickBuilder.search() ) | ||
#end | ||
class Pick<Rest> {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package be.types; | ||
|
||
import haxe.Constraints.Function; | ||
|
||
#if (eval || macro) | ||
import haxe.macro.*; | ||
|
||
using haxe.macro.Context; | ||
using tink.CoreApi; | ||
using tink.MacroApi; | ||
#end | ||
|
||
@:callable @:notNull abstract Resolve<T:Function, @:const R:EReg>(T) { | ||
|
||
@:noCompletion public inline function get():T return this; | ||
|
||
@:from private static inline function fromFunction<T:Function>(v:T):Resolve<T, ~//i> return (cast v:Resolve<T, ~//i>); | ||
|
||
@:from public static macro function coerce<In, Out:Function>(expr:ExprOf<Class<In>>):ExprOf<Resolve<Out, ~//i>> { | ||
var typeof = expr.typeof().sure(); | ||
var expectedType = Context.getExpectedType(); | ||
var expectedComplex = expectedType.toComplex(); | ||
var rawType = (macro (null:$expectedComplex).get()).typeof().sure(); | ||
var ereg:EReg = null; | ||
|
||
switch expectedType.reduce() { | ||
case TAbstract(_, params): | ||
for (param in params) switch param { | ||
case TInst(_.get() => {kind:KExpr({expr:EConst(CRegexp(r, opt)), pos:p})}, _): | ||
#if (debug && coerce_verbose) | ||
trace( r, opt ); | ||
#end | ||
ereg = new EReg(r, opt); | ||
|
||
case TFun(args, ret): | ||
rawType = param; | ||
|
||
case x: | ||
#if (debug && coerce_verbose) | ||
trace( x ); | ||
#end | ||
|
||
} | ||
|
||
case x: | ||
trace( x ); | ||
|
||
} | ||
|
||
var rawComplex = rawType.toComplex(); | ||
var result:Expr = null; | ||
|
||
switch typeof.reduce() { | ||
case TAnonymous(_.get() => { status: AClassStatics( _.get() => cls )}): | ||
var path = cls.pack.join('.'); | ||
path += (path == '' ? '' : '.') + cls.name; | ||
var tpath = path.asTypePath(); | ||
var matches = []; | ||
var eregMatch = true; | ||
|
||
for (field in cls.statics.get()) { | ||
#if (debug && coerce_verbose) | ||
trace( field.name ); | ||
if (ereg != null) trace( ereg.match( field.name ) ); | ||
#end | ||
if (ereg != null) eregMatch = ereg.match(field.name); | ||
if (eregMatch && field.type.unify(rawType)) matches.push( field ); | ||
|
||
} | ||
|
||
if (matches.length > 0) result = '$path.${matches[matches.length-1].name}'.resolve(); | ||
|
||
case x: | ||
//strace( x ); | ||
} | ||
|
||
#if (debug && coerce_verbose) | ||
trace( rawComplex.toString() ); | ||
trace( result.toString() ); | ||
#end | ||
return macro @:pos(expr.pos) ($result:$rawComplex); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package ; | ||
|
||
import be.types.Pick; | ||
import be.types.Resolve; | ||
import be.types.Resolve.coerce; | ||
|
||
class Entry { | ||
|
||
public static function main() { | ||
var m:Pick<String->Int> = coerce(Std); | ||
var a:Resolve<String->Int, ~/int(2)?/i> = coerce(Entry); | ||
|
||
trace( foo(m, '123') ); | ||
trace( foo(a, '124') ); | ||
trace( foo(_ -> 1, '125') ); | ||
|
||
var input = '999'; | ||
trace( asInt(coerce(Std), input) ); // trace(999); | ||
trace( asInt(coerce(Fake), input) ); // trace(1000); | ||
} | ||
|
||
public static function fake(v:String):Int return throw 'bugger'; | ||
public static function fakeParseInt1(v:String):Int return 10000; | ||
public static function fakeParseInt2(v:String):Int return 20000; | ||
|
||
public static inline function foo(func:Pick<String->Int, ~/int(2)?/i>, v:String):Int return func(v); | ||
|
||
public static inline function asInt(r:Resolve<String->Int, ~/int/i>, v:String):Int return r(v); | ||
|
||
} | ||
|
||
class Fake { | ||
public static function parseFloat(v:String):Float return 0.0; | ||
public static function falseSig(v:String):Int return throw 'This is skipped due to the `~/int/i` regular expression'; | ||
public static function parseInt(v:String):Int return 1000; | ||
} |