From 95a44e9d4526b25c988fd0b3570c6dee58ca622c Mon Sep 17 00:00:00 2001 From: Juraj Kirchheim Date: Tue, 1 Oct 2013 23:15:04 +0200 Subject: [PATCH 1/4] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e5d431..d9adae9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Explained in current marketing speak, `tink_macro` is *the* macro toolkit ;) ### History and Mission -Historically, this library's predecessor for Haxe 2 started out when macros were a completely new feature. Title "the ultimate macro utility belt" it implemented reification and expression pattern matching before they were Haxe language features, and added a higher level macro tooling API (for string conversion, expression traversal and what not) to fill in the holes that the standard library left. +Historically, this library's predecessor for Haxe 2 started out when macros were a completely new feature. Boldly titled "the ultimate macro utility belt" it implemented reification and expression pattern matching before they were Haxe language features, and added a higher level macro tooling API (for string conversion, expression traversal and what not) to fill in the holes that the standard library left. As Haxe evolved and some of the functionality has been integrated/reimplemented in the standard library or even as first class language feature, the mission of `tink_macro` has shifted. Rather than being a standalone solution for macro programming, it is now a complement to all the things the Haxe language and the `haxe.macro` package can do out of the box. @@ -373,4 +373,4 @@ Here, `Value` will just use a plain expression, whereas `Arg` and `OptArg` will ### Expression level transformation -Because the state of a constructor is rather delicate, the API prohibits you to just mess around with the whole constructor body at an expression level. For that to happen, you can register `onGenerate` hooks. These will be called when the corresponding `ClassBuilder` is does its export. The hooks are cleared after the export. \ No newline at end of file +Because the state of a constructor is rather delicate, the API prohibits you to just mess around with the whole constructor body at an expression level. For that to happen, you can register `onGenerate` hooks. These will be called when the corresponding `ClassBuilder` is does its export. The hooks are cleared after the export. From fe80d2631a507e5ae6fb12012ad30629af66180b Mon Sep 17 00:00:00 2001 From: Juraj Kirchheim Date: Tue, 1 Oct 2013 23:35:30 +0200 Subject: [PATCH 2/4] Code formatting. --- README.md | 73 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d9adae9..72c3cec 100644 --- a/README.md +++ b/README.md @@ -116,39 +116,52 @@ Attempts to determine the type of an expression. Note that you can use `locals` Will traverse an expression inside out and build a new one through the supplied transformer. - `function substitute(source:Expr, vars:Dynamic, ?pos:Position):Expr` Will build a new expression substituting identifiers given found as fields of `vars` through the corresponding expressions. -- `function substParams(source:Expr, rule: { var exists(default, null):String->Bool; var get(default, null):String->ComplexType; }, ?pos:Position)` -Traverse an expression and replace any *type* that looks like a type parameter following the given `rule`. A `StringMap` is a perfect match here, but you can do whatever you want. +- `function substParams(source:Expr, rule: ParamSubst, ?pos:Position):Expr` +Traverse an expression and replace any *type* that looks like a type parameter following the given `rule` of the following structure: + + ``` + typedef ParamSubst = { + var exists(default, null):String->Bool; + var get(default, null):String->ComplexType; + } + ``` + + A `StringMap` is a natural fit here, but you can do whatever you want. - `function typedMap(source:Expr, f:Expr->Array->Expr, ?ctx:Array, ?pos:Position):Expr` Similar to transform, but handles expressions in top-down order and keeps track of variable declarations, function arguments etc. Only expressions that are not changed by the transformer function `f` are traversed further. The second argument to `f` is the current context that you can use in `typeof` to determine the type of a subexpression. - `function bounce(f:Void->Expr, ?pos:Position):Expr` This is a way to "bounce" out of a macro for a while. Assume you have this expression: `{ var a = 5, b = 6; a + b; }` and you want to analyze the second statement, you either have to track variables manually or do a `typedMap` but that may be too much work. What you would do here is something like this (stupid example): -``` -function onBounce() { trace(block[1].typeof().sure()); return block[1]; } -[block[0], onBounce.bounce(block[1].pos)].toBlock(); -``` + + ``` + function onBounce() { trace(block[1].typeof().sure()); return block[1]; } + [block[0], onBounce.bounce(block[1].pos)].toBlock(); + ``` + - `function yield(source:Expr, yielder:Expr->Expr):Expr` This will traverse an expression and will apply the `yielder` to the "leafs", which in this context are the subexpressions that determine a return value. Example: -``` -yield( - macro if (foo) bar else { var x = y; for (i in a) bla; }, - function (e) return macro trace($e) -); -//becomes -if (foo) trace(bar) else { var x = y; for (i in a) trace(bla); } -``` -To implement array comprehensions yourself with this you would do: -``` -e.transform(function (e) return switch e { - case macro [for ($it) $body]: - macro { - var __ret = []; - for ($it) ${body.yield(function (e) return macro __ret.push(e))}; - __ret; - } - default: e; -}); -``` -- `function () + + ``` + yield( + macro if (foo) bar else { var x = y; for (i in a) bla; }, + function (e) return macro trace($e) + ); + //becomes + if (foo) trace(bar) else { var x = y; for (i in a) trace(bla); } + ``` + + To implement array comprehensions yourself with this you would do: + + ``` + e.transform(function (e) return switch e { + case macro [for ($it) $body]: + macro { + var __ret = []; + for ($it) ${body.yield(function (e) return macro __ret.push(e))}; + __ret; + } + default: e; + }); + ``` ### Position tools - tink.macro.Positions @@ -192,8 +205,8 @@ Will convert a `Type` to a `ComplexType`. Ideally this is done with `Context.toC - `function asExpr(f:Function, ?name:String, ?pos:Position):Expr` Converts a function to an expression, i.e. a local function definition. -- `function func(body:Expr, ?args:Array, ?ret:ComplexType, ?params:Array, ?makeReturn = true):Function` -Builds a `Function` from an expression. By default, the body is returned. +- `function func(body:Expr, ?args:Array, ?ret:ComplexType, ?params:Array, ?mkRet = true):Function` +Builds a `Function` from an expression. By default, the body is returned. Set `mkRet` to false otherwise. - `function toArg(name:String, ?t:ComplexType, ?opt = false, ?value:Expr = null):FunctionArg` A shorthand to create function arguments. - `function getArgIdents(f:Function):Array` @@ -217,7 +230,7 @@ Attempts to decompose an expression into the parts of a unary operation. - `function toMap(m:Metadata):Map>` 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" => [[]]]` -- `function getValues(m:Metadata, name:String):Array> +- `function getValues(m:Metadata, name:String):Array>` Will construct an array of the of the arguments lists of all occurences of the tag `name` in a given `Metadata`. The result is the same as `m.toMap()[name]` only it's far more efficient. # Build infrastructure From 1eb8f16e8f53e117f0cc61fc5edcfa87f2b58408 Mon Sep 17 00:00:00 2001 From: Juraj Kirchheim Date: Tue, 1 Oct 2013 23:38:18 +0200 Subject: [PATCH 3/4] Remove MacroError from doc. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 72c3cec..7ac4280 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,13 @@ Traces the string representation of an expression and returns it. - `function isWildcard(e:Expr):Bool` Checks whether an expression is the identifier `_` -- `function getInt(e:Expr):Outcome>` +- `function getInt(e:Expr):Outcome` Attempts extracting an integer constant from an expression -- `function getString(e:Expr):Outcome>` +- `function getString(e:Expr):Outcome` Attempts extracting a string constant from an expression -- `function getIdent(e:Expr):Outcome>` +- `function getIdent(e:Expr):Outcome` Attempts extracting an identifier from an expression. Note that an identifier can be a CIdent or CType, with the only difference being the capitalization of the first letter. -- `function getName(e:Expr):Outcome>` +- `function getName(e:Expr):Outcome` Attempts extracting a name, i.e. a string constant or identifier from an expression #### Building simple expressions @@ -186,9 +186,9 @@ Returns a String identifier for a type if available. By default, the type will b Attempts to get all fields of a type. By default, this call will perform a parameter substitution, i.e. called on `Array`, `pop` will be of type `Void->Int`. With `substituteParams = false`, `pop` will be of type `Void->Array.T` instead. - `function toString(t:ComplexType):String` Converts a `ComplextType` to corresponding Haxe code. No such thing exists for `Type` as it is actually is automatically converted to rather readable strings. -- `function isSubTypeOf(t:Type, of:Type, ?pos:Position):Outcome < Type, MacroError >` +- `function isSubTypeOf(t:Type, of:Type, ?pos:Position):Outcome < Type, tink.core.Error >` Checks whether one type is a subtype of another. Returns an `Outcome` to give back information on *why* `t` is not a subtype of `of`. -- `function toType(t:ComplexType, ?pos:Position):Outcome>` +- `function toType(t:ComplexType, ?pos:Position):Outcome` Attempts converting a `ComplextType` to a `Type`. This can fail for a number of reasons, such as no actual type being known for a supplied path. - `function asTypePath(s:String, ?params:Array):TypePath` Will build a `TypePath` from a '.'-separated path. @@ -221,9 +221,9 @@ Attempts to decompose an expression into the parts of a binary operation. - `function make(op:Binop, e1:Expr, e2:Expr, ?pos:Position):Expr` Builds a binary operation. Just syntactic sugar for the `Expr::binOp` listed above. It's often easier to read. -- `function get(o:Unop, e:Expr, postfix:Bool = false):Outcome<{ e:Expr, pos:Position }, MacroError>` +- `function get(o:Unop, e:Expr, postfix:Bool = false):Outcome<{ e:Expr, pos:Position }, tink.core.Error>` Attempts to extract a specific unary operation from an expression. -- `function getUnop(e:Expr):Outcome<{ op:Unop, e:Expr, postFix:Bool, pos:Position }, MacroError>` +- `function getUnop(e:Expr):Outcome<{ op:Unop, e:Expr, postFix:Bool, pos:Position }, tink.core.Error>` Attempts to decompose an expression into the parts of a unary operation. ### Metadata tools - tink.macro.Metadatas From 97e0cbf00595d16f3625b4d36a3f47462b7a05c7 Mon Sep 17 00:00:00 2001 From: Juraj Kirchheim Date: Tue, 1 Oct 2013 23:42:09 +0200 Subject: [PATCH 4/4] Make doc fit github. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ac4280..00307c2 100644 --- a/README.md +++ b/README.md @@ -205,8 +205,8 @@ Will convert a `Type` to a `ComplexType`. Ideally this is done with `Context.toC - `function asExpr(f:Function, ?name:String, ?pos:Position):Expr` Converts a function to an expression, i.e. a local function definition. -- `function func(body:Expr, ?args:Array, ?ret:ComplexType, ?params:Array, ?mkRet = true):Function` -Builds a `Function` from an expression. By default, the body is returned. Set `mkRet` to false otherwise. +- `function func(body:Expr, ?args, ?ret:ComplexType, ?params:Array, ?makeReturn = true):Function` +Builds a `Function` from an expression. By default, the body is returned. - `function toArg(name:String, ?t:ComplexType, ?opt = false, ?value:Expr = null):FunctionArg` A shorthand to create function arguments. - `function getArgIdents(f:Function):Array`