.
52 |
53 | ### Appending a paragraph to DOM with jQuery
54 |
55 | This example is does pretty much same as the one above, but uses jQuery.
56 |
57 | Remember that jQuery in Haxe is simply an [extern](https://haxe.org/manual/lf-externs.html) (type definition). You need to add a script-tag that links to jQuery in the HTML file.
58 |
59 | ```haxe
60 | import js.jquery.JQuery;
61 |
62 | class Main {
63 | // static entrypoint
64 | static function main() new Main();
65 |
66 | // constructor
67 | function new() {
68 | trace("DOM example");
69 |
70 | new JQuery(function() {
71 | trace("DOM ready");
72 | new JQuery(".container").html("DOM ready
");
73 | });
74 | }
75 | }
76 | ```
77 |
78 | ## Compile
79 |
80 | Compile the example with `haxe -cp src -main Main -js bin/example.js -dce full`.
81 |
82 | There are a lot of different arguments that you are able to pass to the Haxe compiler.
83 | These arguments can be placed in a text file which has the file-extension _.hxml_.
84 |
85 | **build.hxml**
86 | ```hxml
87 | -cp src
88 | -main Main
89 | -js bin/example.js
90 | -dce full
91 | ```
92 |
93 | To run the build script, it can be passed to the Haxe compiler on the commandline: `haxe build.hxml`.
94 |
95 | Windows users can double click the hxml file to run it.
96 |
97 | ## index.html
98 |
99 | As final step, to run the example you need to create **index.html** in the **bin** folder. Note that it has to have a ``, because we're accessing this element in our code.
100 | You can download jQuery from or use CDN as illustrated in the example. Of course when you are not using jQuery you can leave out this script-tag.
101 |
102 | ```html
103 |
104 |
105 |
106 | Haxe/JavaScript - DOM example
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | ```
120 |
121 | ## Running the example
122 |
123 | Open the index.html in your favorite browser. It should display the text "DOM ready". Using the browser devtools (F12) you can inspect the DOM which shows the text is created inside a paragraph `` element.
124 |
125 |
--------------------------------------------------------------------------------
/assets/content/cookbook/JavaScript/assets/node-server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/content/cookbook/JavaScript/assets/node-server.png
--------------------------------------------------------------------------------
/assets/content/cookbook/JavaScript/creating-node-server.md:
--------------------------------------------------------------------------------
1 | [tags]: / "javascript,nodejs,server"
2 |
3 | # Create a server with Haxe/NodeJS
4 |
5 | The following tutorial creates a http server using Haxe/Node.js at port 8000 and demonstrates how to build and run it.
6 |
7 | > Haxe can compile to many targets, this example is specific for the Haxe/JavaScript target only.
8 |
9 | ## Installation
10 |
11 | - Install [Node.js](https://nodejs.org/) on your machine.
12 | - Install [hxnodejs](https://lib.haxe.org/p/hxnodejs) with `haxelib install hxnodejs` (released version).
13 | - .. or install the latest version from github `haxelib git hxnodejs https://github.com/HaxeFoundation/hxnodejs`.
14 |
15 | ## Structure of this example
16 |
17 | ```
18 | + example/
19 | - Main.hx
20 | - build.hxml
21 | - run.hxml
22 | ```
23 |
24 | ## Main.hx
25 |
26 | ```haxe
27 | class Main {
28 | static function main() {
29 | // Configure our HTTP server to respond with Hello World to all requests.
30 | var server = js.node.Http.createServer(function(request, response) {
31 | response.writeHead(200, {"Content-Type": "text/plain"});
32 | response.end("Hello World\n");
33 | });
34 |
35 | // Listen on port 8000, IP defaults to 127.0.0.1
36 | server.listen(8000);
37 |
38 | // Put a console.log on the terminal
39 | trace("Server running at 127.0.0.1:8000");
40 | }
41 | }
42 | ```
43 | > Note that each Haxe application has a static entrypoint which is called `main()`.
44 |
45 | ## Compile the Haxe/node.js server
46 |
47 | Compile the example with `haxe -lib hxnodejs -main Main -js main.js`. You can also save this build configuration in a _build.hxml_ file:
48 |
49 | ```hxml
50 | -lib hxnodejs
51 | -main Main
52 | -js main.js
53 | ```
54 |
55 | To run the build.hxml script, it can be passed to the Haxe compiler on the commandline: `haxe build.hxml`. Windows users can double click the hxml file to run it.
56 |
57 | ## Run the Haxe/node.js server
58 |
59 | To run the node application, use `node main.js`. Alternative, you can save this configuration in a _run.hxml_ file.
60 | ```hxml
61 | -cmd node main.js
62 | ```
63 |
64 | Then, load in a browser to see the server output.
65 |
66 |
67 |
68 | ### Generated output
69 |
70 | Haxe is an excellent JavaScript compiler, the output source is clean:
71 |
72 | ```js
73 | (function () { "use strict";
74 | var Main = function() { };
75 | Main.main = function() {
76 | var server = js_node_Http.createServer(function(request,response) {
77 | response.writeHead(200,{ "Content-Type" : "text/plain"});
78 | response.end("Hello World\n");
79 | });
80 | server.listen(8000);
81 | console.log("Server running at 127.0.0.1:8000");
82 | };
83 | var js_node_Http = require("http");
84 | Main.main();
85 | })();
86 | ```
87 |
88 | > **Related info:**
89 | >
90 | > * [Haxe/Node.js API documentation](http://haxefoundation.github.io/hxnodejs/js/Node.html)
91 | > * [Official Node.js documentation](http://nodejs.org/api/index.html)
--------------------------------------------------------------------------------
/assets/content/cookbook/JavaScript/using-haxe-classes-in-javascript.md:
--------------------------------------------------------------------------------
1 | [tags]: / "javascript,dead-code-elimination,libraries"
2 |
3 | # Using Haxe classes in JavaScript
4 |
5 | Normally, when compiling Haxe to JavaScript, the resulting code is kept away from the global scope. This means that you can't reach the Haxe generated code from other scripts.
6 | To make that possible, there's the `@:expose` metadata that can be used on a class. This makes the class "exposed" to the global scope, and therefore possible to use in plain JavaScript.
7 |
8 | > Haxe can compile to many targets, this example is specific for the Haxe/JavaScript target only.
9 |
10 | Here's an example of a simple utility class, where we use the `@:expose` metadata. To make sure that the class isn't accidentally stripped away by [dead code elimination](http://haxe.org/manual/cr-dce.html), we also add the `@:keep` metadata:
11 |
12 | ```haxe
13 | package foo;
14 |
15 | @:expose // <- makes the class reachable from plain JavaScript
16 | @:keep // <- avoids accidental removal by dead code elimination
17 | class MyUtils {
18 | public function new() { }
19 | public function multiply(a:Float, b:Float) return a * b;
20 | }
21 | ```
22 |
23 | This class can of course be called from Haxe the standard way:
24 | ```haxe
25 | class Main {
26 | static function main() {
27 | var utils = new foo.MyUtils();
28 | trace(utils.multiply(1.1, 3.3));
29 | }
30 | }
31 | ```
32 | ...and after compiling it with something like the following **build.hxml**:
33 | ```hxml
34 | -cp src
35 | -main Main
36 | -js bin/app.js
37 | -dce full
38 | ```
39 | ...and run in an **index.html** like the this one:
40 |
41 | ```html
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | ```
52 | ...it traces the multiplied result to the browser console.
53 |
54 | However, the MyUtils class is also exposed to the global scope and this makes it possible to use it for example in this way:
55 | ```html
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
71 | ```
72 |
73 |
74 | > Learn about the use of @:expose metadata here:
75 | >
76 | > Author: [Jonas Nyström](https://github.com/cambiata)
77 |
78 |
79 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/add-git-commit-hash-in-build.md:
--------------------------------------------------------------------------------
1 | [tags]: / "git,expression-macro,process"
2 |
3 | # Add git commit-hash in build
4 |
5 | This example executes a process on the system, compile-time.
6 | This allows to run a git command `git rev-parse HEAD` and use its result as the value.
7 |
8 | ```haxe
9 | class Version {
10 | public static macro function getGitCommitHash():haxe.macro.Expr.ExprOf {
11 | #if !display
12 | var process = new sys.io.Process('git', ['rev-parse', 'HEAD']);
13 | if (process.exitCode() != 0) {
14 | var message = process.stderr.readAll().toString();
15 | var pos = haxe.macro.Context.currentPos();
16 | haxe.macro.Context.error("Cannot execute `git rev-parse HEAD`. " + message, pos);
17 | }
18 |
19 | // read the output of the process
20 | var commitHash:String = process.stdout.readLine();
21 |
22 | // Generates a string expression
23 | return macro $v{commitHash};
24 | #else
25 | // `#if display` is used for code completion. In this case returning an
26 | // empty string is good enough; We don't want to call git on every hint.
27 | var commitHash:String = "";
28 | return macro $v{commitHash};
29 | #end
30 | }
31 | }
32 | ```
33 |
34 | ### Usage
35 |
36 | The function can be called like any other static function in Haxe.
37 |
38 | ```haxe
39 | // use as field
40 | @:keep public static var COMMIT_HASH(default, never):String = Version.getGitCommitHash();
41 |
42 | // ..or trace to output
43 | trace(Version.getGitCommitHash());
44 | ```
45 |
46 | > [sys.io.Process API documentation](http://api.haxe.org/sys/io/Process.html)
47 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/add-parameters-as-fields.md:
--------------------------------------------------------------------------------
1 | [tags]: / "expression-macro,building-fields"
2 |
3 | # Add parameters as fields
4 |
5 | This macro function automatically assigns parameters of method to local variables.
6 |
7 | ```haxe
8 | import haxe.macro.Context;
9 | import haxe.macro.Expr;
10 | using Lambda;
11 |
12 | class MyMacros {
13 | macro static public function initLocals():Expr {
14 | // Grab the variables accessible in the context the macro was called.
15 | var locals = Context.getLocalVars();
16 | var fields = Context.getLocalClass().get().fields.get();
17 |
18 | var exprs:Array = [];
19 | for (local in locals.keys()) {
20 | if (fields.exists(function(field) return field.name == local)) {
21 | exprs.push(macro this.$local = $i{local});
22 | } else {
23 | throw new Error(Context.getLocalClass() + " has no field " + local, Context.currentPos());
24 | }
25 | }
26 | // Generates a block expression from the given expression array
27 | return macro $b{exprs};
28 | }
29 | }
30 | ```
31 |
32 | ## Usage
33 |
34 | ```haxe
35 | class Test {
36 | public var name:String;
37 | public var x:Float;
38 | public var y:Float;
39 |
40 | public function new(name:String, x:Float, y:Float) {
41 | MyMacros.initLocals();
42 | }
43 | }
44 | ```
45 |
46 | This will be the same as writing this manually:
47 |
48 | ```haxe
49 | class Test {
50 | public var name:String;
51 | public var x:Float;
52 | public var y:Float;
53 |
54 | public function new(name:String, x:Float, y:Float) {
55 | this.name = name;
56 | this.x = x;
57 | this.y = y;
58 | }
59 | }
60 | ```
61 |
62 | > Author: [Mark Knol](https://github.com/markknol)
63 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/assert-with-values.md:
--------------------------------------------------------------------------------
1 | [tags]: / "expression-macro,validation"
2 |
3 | # Assert macro that shows sub-expression values
4 |
5 | Sometimes failed assertion checks make it difficult to tell what went wrong. For debugging the programmer not only wants to know
6 | *that* a check failed, but also *why* it failed. This macro outputs the values of all sub-expressions.
7 |
8 | ```haxe
9 | import haxe.macro.Expr;
10 | using haxe.macro.Tools;
11 |
12 | class Assert {
13 | static public macro function assert(e:Expr) {
14 | var s = e.toString();
15 | var p = e.pos;
16 | var el = [];
17 | var descs = [];
18 | function add(e:Expr, s:String) {
19 | var v = "_tmp" + el.length;
20 | el.push(macro var $v = $e);
21 | descs.push(s);
22 | return v;
23 | }
24 | function map(e:Expr) {
25 | return switch (e.expr) {
26 | case EConst((CInt(_) | CFloat(_) | CString(_) | CRegexp(_) | CIdent("true" | "false" | "null"))):
27 | e;
28 | case _:
29 | var s = e.toString();
30 | e = e.map(map);
31 | macro $i{add(e, s)};
32 | }
33 | }
34 | var e = map(e);
35 | var a = [for (i in 0...el.length) macro { expr: $v{descs[i]}, value: $i{"_tmp" + i} }];
36 | el.push(macro if (!$e) @:pos(p) throw new Assert.AssertionFailure($v{s}, $a{a}));
37 | return macro $b{el};
38 | }
39 | }
40 |
41 | private typedef AssertionPart = {
42 | expr: String,
43 | value: Dynamic
44 | }
45 |
46 | class AssertionFailure {
47 | public var message(default, null):String;
48 | public var parts(default, null):Array;
49 | public function new(message:String, parts:Array) {
50 | this.message = message;
51 | this.parts = parts;
52 | }
53 |
54 | public function toString() {
55 | var buf = new StringBuf();
56 | buf.add("Assertion failure: " + message);
57 | for (part in parts) {
58 | buf.add("\n\t" + part.expr + ": " + part.value);
59 | }
60 | return buf.toString();
61 | }
62 | }
63 | ```
64 |
65 | ## Usage
66 |
67 |
68 | ```haxe
69 | class Main {
70 | static function main() {
71 | //Error: Assertion failure: x == 7 && y == 11
72 | //x: 7
73 | //x == 7: true
74 | //y: 10
75 | //y == 11: false
76 | //x == 7 && y == 11: false
77 | var x = 7;
78 | var y = 10;
79 | Assert.assert(x == 7 && y == 11);
80 |
81 | //Error: Assertion failure: a.length > 0
82 | //a: []
83 | //a.length: 0
84 | //a.length > 0: false
85 | var a = [];
86 | Assert.assert(a.length > 0);
87 | }
88 | }
89 | ```
90 |
91 | > Inspired by
92 | > Author: [Simn](https://github.com/simn)
93 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/assets/haxe-json-macro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/content/cookbook/Macros/assets/haxe-json-macro.png
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/build-map.md:
--------------------------------------------------------------------------------
1 | [tags]: / "build-macro,building-fields"
2 |
3 | # Add a map
4 |
5 | > This snippet demonstrates how to add a map field to a type.
6 |
7 | We can use expression reification to turn basic types into appropriate expressions. Maps, however, are a _complex type_. As such they need to be treated a little differently.
8 |
9 | A map is a container of key-value pairs that can be observed as an array of `key => value` expressions. For example: `var m = [ 1 => "ONE", 2 => "TWO"]`. With that in mind, building a map field is no different than creating an array of the correct expressions.
10 |
11 | Note the use of `macro` to create the `key => value` expression. Also note the use of expression reification: `$v{}` to generate expressions from specified values, and `$a{}` to create the expression array.
12 |
13 | ## Build macro
14 | ```haxe
15 | import haxe.macro.Context;
16 | import haxe.macro.Expr;
17 |
18 | class MapBuilder {
19 | public static macro function build(names : Array) : Array {
20 | // The context is the class this build macro is called on
21 | var fields = Context.getBuildFields();
22 | // A map is an array of `key => value` expressions
23 | var map : Array = [];
24 | // We add a `key => value` expression for every name
25 | for (name in names) {
26 | // Expression reification generates expression from argument
27 | map.push(macro $v{name} => $v{haxe.crypto.Sha256.encode(name)});
28 | }
29 | // We push the map into the context build fields
30 | fields.push({
31 | // The line position that will be referenced on error
32 | pos: Context.currentPos(),
33 | // Field name
34 | name: "namesHashed",
35 | // Attached metadata (we are not adding any)
36 | meta: null,
37 | // Field type is Map, `map` is the map
38 | kind: FieldType.FVar(macro : Map, macro $a{map}),
39 | // Documentation (we are not adding any)
40 | doc: null,
41 | // Field visibility
42 | access: [Access.APublic, Access.AStatic]
43 | });
44 | // Return the context build fields to build the type
45 | return fields;
46 | }
47 | }
48 | ```
49 |
50 | ## Usage
51 |
52 | ```haxe
53 | @:build(MapBuilder.build(["Aaron", "Bobbi", "Carol", "Dennis", "Eric", "Frank"]))
54 | class Main {
55 | static function main() {
56 | trace(namesHashed.get("Bobbi"));
57 | }
58 | }
59 | ```
60 |
61 | > Learn about macros here:
62 | >
63 | > Author: [Domagoj Štrekelj](https://github.com/dstrekelj)
64 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/build-property-with-inline-getter.md:
--------------------------------------------------------------------------------
1 | [tags]: / "build-macro,building-fields"
2 |
3 | # Add property with getter
4 |
5 | > Virtually adds this property to a class:
6 | > ```haxe
7 | > public var myVar(get, null):Float;
8 | > private inline function get_myVar():Float {
9 | > return 1.5;
10 | > }
11 | > ```
12 |
13 | ## Build macro
14 |
15 | ```haxe
16 | import haxe.macro.Context;
17 | import haxe.macro.Expr;
18 | class MyMacro {
19 | public static function build():Array {
20 | // get existing fields from the context from where build() is called
21 | var fields = Context.getBuildFields();
22 |
23 | var value = 1.5;
24 | var pos = Context.currentPos();
25 | var fieldName = "myVar";
26 |
27 | var myFunc:Function = {
28 | expr: macro return $v{value}, // actual value
29 | ret: (macro:Float), // ret = return type
30 | args:[] // no arguments here
31 | }
32 |
33 | // create: `public var $fieldName(get,null)`
34 | var propertyField:Field = {
35 | name: fieldName,
36 | access: [Access.APublic],
37 | kind: FieldType.FProp("get", "null", myFunc.ret),
38 | pos: pos,
39 | };
40 |
41 | // create: `private inline function get_$fieldName() return $value`
42 | var getterField:Field = {
43 | name: "get_" + fieldName,
44 | access: [Access.APrivate, Access.AInline],
45 | kind: FieldType.FFun(myFunc),
46 | pos: pos,
47 | };
48 |
49 | // append both fields
50 | fields.push(propertyField);
51 | fields.push(getterField);
52 |
53 | return fields;
54 | }
55 | }
56 | ```
57 |
58 | ### Usage
59 |
60 | ```haxe
61 | @:build(MyMacro.build())
62 | class Main {
63 | public function new() {
64 | trace(this.myVar); // 1.5;
65 | }
66 | }
67 | ```
68 |
69 | > Author: [Mark Knol](https://github.com/markknol)
70 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/build-static-field.md:
--------------------------------------------------------------------------------
1 | [tags]: / "build-macro,building-fields"
2 |
3 | # Add a static field
4 |
5 | > Virtually adds this static variable to a class:
6 | > ```haxe
7 | > public inline static var STATIC_VAR:Float = 1.5;
8 | > ```
9 |
10 | ## Build macro
11 | ```haxe
12 | import haxe.macro.Context;
13 | import haxe.macro.Expr;
14 |
15 | class MyMacro {
16 | public static function build():Array {
17 | // get existing fields from the context from where build() is called
18 | var fields = Context.getBuildFields();
19 |
20 | // append a field
21 | fields.push({
22 | name: "STATIC_VAR",
23 | access: [Access.APublic, Access.AStatic, Access.AInline],
24 | kind: FieldType.FVar(macro:Float, macro $v{1.5}),
25 | pos: Context.currentPos(),
26 | });
27 |
28 | return fields;
29 | }
30 | }
31 | ```
32 |
33 | ## Usage
34 |
35 | ```haxe
36 | @:build(MyMacro.build())
37 | class Main {
38 | public function new() {
39 | trace(Main.STATIC_VAR); // 1.5;
40 | }
41 | }
42 | ```
43 |
44 | > Author: [Mark Knol](https://github.com/markknol)
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/build-value-objects.md:
--------------------------------------------------------------------------------
1 | [tags]: / "build-macro,building-fields"
2 |
3 | # Create value-objects
4 |
5 | This example generates a constructor-function for each field of a class to easily create value object classes.
6 |
7 | ```haxe
8 | import haxe.macro.Expr;
9 | import haxe.macro.Context;
10 |
11 | @:remove @:autoBuild(ValueClassImpl.build())
12 | extern interface ValueClass {}
13 |
14 | class ValueClassImpl {
15 | #if macro
16 | public static function build() {
17 | var fields = Context.getBuildFields();
18 | var args = [];
19 | var states = [];
20 | for (f in fields) {
21 | switch (f.kind) {
22 | case FVar(t,_):
23 | args.push({name:f.name, type:t, opt:false, value:null});
24 | states.push(macro $p{["this", f.name]} = $i{f.name});
25 | f.access.push(APublic);
26 | default:
27 | }
28 | }
29 | fields.push({
30 | name: "new",
31 | access: [APublic],
32 | pos: Context.currentPos(),
33 | kind: FFun({
34 | args: args,
35 | expr: macro $b{states},
36 | params: [],
37 | ret: null
38 | })
39 | });
40 | return fields;
41 | }
42 | #end
43 | }
44 | ```
45 |
46 | It is using an interface `ValueClass` marked with `@:remove` and `extern` so that it is 100% compile time only.
47 |
48 | ## Usage
49 |
50 | Create a class that implements `ValueClass`.
51 |
52 | ```haxe
53 | class ABC implements ValueClass {
54 | var a: Int;
55 | var b: Bool;
56 | var c: String;
57 | }
58 | ```
59 |
60 | Will be compiled as:
61 |
62 | ```haxe
63 | class ABC extends ValueClass {
64 | public var a(default, null): Int;
65 | public var b(default, null): Bool;
66 | public var c(default, null): String;
67 | public function new(a: Int, b: Bool, c: String) {
68 | this.a = a;
69 | this.b = b;
70 | this.c = c;
71 | }
72 | }
73 | ```
74 |
75 | > Source:
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/combine-objects.md:
--------------------------------------------------------------------------------
1 | [tags]: / "expression-macro"
2 |
3 | # Combine two or more structures
4 |
5 | Haxe makes it easy to define extensions for a `typedef`, but there is no easy way to combine
6 | the values of two or more structures to one, like `{a:2}` and `{b:"foo"}` to `{a:2,b:"foo"}`.
7 | The following macro does this for you.
8 |
9 | ## Implementation
10 |
11 | ```haxe
12 | #if macro
13 | import haxe.macro.Expr;
14 | import haxe.macro.Context;
15 | using haxe.macro.Tools;
16 | using Lambda;
17 | #end
18 |
19 |
20 | class StructureCombiner {
21 | // we use an Array, because we want the macro to work on variable amount of structures
22 | public static macro function combine(rest: Array): Expr {
23 | var pos = Context.currentPos();
24 | var block = [];
25 | var cnt = 1;
26 | // since we want to allow duplicate field names, we use a Map. The last occurrence wins.
27 | var all = new Map();
28 | for (rx in rest) {
29 | var trest = Context.typeof(rx);
30 | switch ( trest.follow() ) {
31 | case TAnonymous(_.get() => tr):
32 | // for each parameter we create a tmp var with an unique name.
33 | // we need a tmp var in the case, the parameter is the result of a complex expression.
34 | var tmp = "tmp_" + cnt;
35 | cnt++;
36 | var extVar = macro $i{tmp};
37 | block.push(macro var $tmp = $rx);
38 | for (field in tr.fields) {
39 | var fname = field.name;
40 | all.set(fname, { field: fname, expr: macro $extVar.$fname } );
41 | }
42 | default:
43 | return Context.error("Object type expected instead of "
44 | + trest.toString(), rx.pos);
45 | }
46 | }
47 | var result = {expr:EObjectDecl(all.array()), pos: pos};
48 | block.push(macro $result);
49 | return macro $b{block};
50 | }
51 |
52 | }
53 | ```
54 |
55 | ## Usage
56 |
57 | You can import the macro class with `using`.
58 |
59 | ```haxe
60 | using StructureCombiner;
61 |
62 | typedef Foo = { a: Int, b: Float }
63 |
64 | typedef FooBar = { > Foo, bar: String };
65 |
66 | static function callFoo() return { a: 42, b: 3.14 };
67 |
68 | static function callBar() return { bar: "42" };
69 |
70 | class Main {
71 | static function main() {
72 | // you can use the macro with function results
73 | var fb: FooBar = callFoo().combine(callBar());
74 | // or with variables and anonymous structures
75 | var foo: Foo = callFoo();
76 | fb = foo.combine({ bar: "42" });
77 | // more parameters are allowed
78 | fb = {a: 111}.combine({b:13.1}, {bar: "happy hour"});
79 | // when several structures have the same field, the last wins.
80 | var fb2 = fb.combine({bar: "lucky strike"});
81 | }
82 | }
83 | ```
84 |
85 | After compiling previous code, the statement `fb = {a: 111}.combine({b:13.1}, {bar: "happy hour"});` generates code like: `fb = {a: 111, b: 13.1, bar: "happy hour"};`. As you can see, the output code's variable declarations are assigned to values which are *already mixed* (at compile time).
86 |
87 | From performance point of view, this is optimal, even more than using native APIs, (like JavaScript's `Object.assign()` for example).
88 |
89 | > Author: [Adrian Veith](https://github.com/AdrianV)
90 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/completion-from-url.md:
--------------------------------------------------------------------------------
1 | [tags]: / "completion,build-macro"
2 |
3 | # Code completion from URL
4 |
5 | This example will load an URL, scrape all id's from the HTML page and use them for auto-completion.
6 |
7 | ```haxe
8 | import haxe.macro.Context;
9 | import haxe.macro.Expr;
10 |
11 | class MyMacro {
12 | public static function build(url:String) {
13 | var h = haxe.Http.requestUrl(url);
14 |
15 | //trace(h);
16 |
17 | var r = ~/id=["']([A-Za-z0-9]+)["']/;
18 | var ids = [];
19 | while (r.match(h)) {
20 | var id = r.matched(1);
21 | ids.remove(id);
22 | ids.push(id);
23 | h = r.matchedRight();
24 | }
25 |
26 | var fields = Context.getBuildFields();
27 | var gtype = TAnonymous([for (id in ids) {
28 | name : id,
29 | pos : Context.currentPos(),
30 | kind : FVar(macro:String)
31 | }]);
32 |
33 | var gids:Field = {
34 | name : "gids",
35 | pos : Context.currentPos(),
36 | kind : FVar(gtype),
37 | access : [AStatic],
38 | };
39 | fields.push(gids);
40 | return fields;
41 | }
42 | }
43 | ```
44 |
45 | ## Usage
46 |
47 | ```haxe
48 | @:build(MyMacro.build("http://www.msn.com/en-us/"))
49 | class Main {
50 | static function main() {
51 | Main.gids; // auto-complete here
52 | }
53 | }
54 | ```
55 |
56 | ## Demo
57 |
58 |
59 |
60 | ## Explanation in video
61 | _Start at 21:00_
62 |
63 | [youtube](https://www.youtube.com/embed/SEYCmjtKlVw)
64 |
65 | > More info:
66 | > Author: [Nicolas Cannasse](https://github.com/ncannasse)
67 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/enum-abstract-values.md:
--------------------------------------------------------------------------------
1 | [tags]: / "enum,expression-macro"
2 |
3 | # Get all values of an @:enum abstract
4 |
5 | The following macro function returns an array of all possible values of a given `@:enum abstract` type.
6 | Since it's not possible in runtime (because abstracts doesn't exist there), we need to use a macro for that.
7 |
8 | ## Implementation
9 |
10 | ```haxe
11 | #if macro
12 | import haxe.macro.Context;
13 | import haxe.macro.Expr;
14 | using haxe.macro.Tools;
15 | #end
16 |
17 | class AbstractEnumTools {
18 | public static macro function getValues(typePath:Expr):Expr {
19 | // Get the type from a given expression converted to string.
20 | // This will work for identifiers and field access which is what we need,
21 | // it will also consider local imports. If expression is not a valid type path or type is not found,
22 | // compiler will give a error here.
23 | var type = Context.getType(typePath.toString());
24 |
25 | // Switch on the type and check if it's an abstract with @:enum metadata
26 | switch (type.follow()) {
27 | case TAbstract(_.get() => ab, _) if (ab.meta.has(":enum")):
28 | // @:enum abstract values are actually static fields of the abstract implementation class,
29 | // marked with @:enum and @:impl metadata. We generate an array of expressions that access those fields.
30 | // Note that this is a bit of implementation detail, so it can change in future Haxe versions, but it's been
31 | // stable so far.
32 | var valueExprs = [];
33 | for (field in ab.impl.get().statics.get()) {
34 | if (field.meta.has(":enum") && field.meta.has(":impl")) {
35 | var fieldName = field.name;
36 | valueExprs.push(macro $typePath.$fieldName);
37 | }
38 | }
39 | // Return collected expressions as an array declaration.
40 | return macro $a{valueExprs};
41 | default:
42 | // The given type is not an abstract, or doesn't have @:enum metadata, show a nice error message.
43 | throw new Error(type.toString() + " should be @:enum abstract", typePath.pos);
44 | }
45 | }
46 | }
47 | ```
48 |
49 | ## Usage
50 |
51 | ```haxe
52 | @:enum abstract MyEnum(Int) {
53 | var A = 1;
54 | var B = 2;
55 | var C = 3;
56 | }
57 |
58 | class Main {
59 | static function main() {
60 | var values = AbstractEnumTools.getValues(MyEnum);
61 | trace(values); // [1, 2, 3]
62 | }
63 | }
64 | ```
65 |
66 | > Author: [Dan Korostelev](https://github.com/nadako)
67 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/extract-enum-value.md:
--------------------------------------------------------------------------------
1 | [tags]: / "enum,pattern-matching,expression-macro"
2 |
3 | # Extract values from known enum instances
4 |
5 | Sometimes we have an instance of `enum` that is of known constructor (or we only accept that constructor) and we
6 | want to extract values from that instance. Normally, to do this, we have to use pattern matching (`switch`), however
7 | it's quite verbose, so we can instead use this macro static extension method that will generate switch for us.
8 |
9 | ## Implementation
10 |
11 | ```haxe
12 | #if macro
13 | import haxe.macro.Expr;
14 | #end
15 |
16 | class Tools {
17 | public static macro function extract(value:ExprOf, pattern:Expr):Expr {
18 | switch (pattern) {
19 | case macro $a => $b:
20 | return macro switch ($value) {
21 | case $a: $b;
22 | default: throw "no match";
23 | }
24 | default:
25 | throw new Error("Invalid enum value extraction pattern", pattern.pos);
26 | }
27 | }
28 | }
29 | ```
30 |
31 | ## Usage
32 |
33 | ```haxe
34 | using Tools;
35 |
36 | class Main {
37 | static function main() {
38 | var opt = haxe.ds.Option.Some(10);
39 | var val = opt.extract(Some(v) => v);
40 | trace(val == 10); // true
41 | }
42 | }
43 | ```
44 |
45 | > Author: [Dan Korostelev](https://github.com/nadako)
46 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/extract-value-with-pattern-matching.md:
--------------------------------------------------------------------------------
1 | [tags]: / "enum,pattern-matching,macro-function"
2 |
3 | # Extract values with pattern matching
4 |
5 | Mostly useful to extract enum values from known enum instances. Allows to extract multiple variables at once.
6 | Takes most of expressions that are possible to use in switch expression.
7 |
8 | ## Implementation
9 |
10 | ```haxe
11 | package;
12 | #if macro
13 | import haxe.macro.Context;
14 | import haxe.macro.Expr;
15 | #end
16 |
17 | class Match {
18 |
19 | public static macro function extract(value:Expr, pattern:Expr, ifNot:Array) {
20 | var ifNot = switch(ifNot.length) {
21 | case 0: macro throw "don't match";
22 | case 1: ifNot[0];
23 | default: Context.error('too much arguments', ifNot[1].pos);
24 | }
25 |
26 | var params = [];
27 | function getParamNames(expr:ExprDef) {
28 | switch(expr) {
29 | case EConst(CIdent(name)) | EBinop(OpArrow, _, {expr:EConst(CIdent(name))}): if (name != '_' && params.indexOf(name) < 0) params.push(name);
30 | case ECall(_, params): for (param in params) getParamNames(param.expr);
31 | case EBinop(OpArrow, _, expr): getParamNames(expr.expr);
32 | case EBinop(OpOr, expr0, expr1): getParamNames(expr0.expr); getParamNames(expr1.expr);
33 | case EObjectDecl(fields): for (field in fields) getParamNames(field.expr.expr);
34 | case EArrayDecl(values): for (value in values) getParamNames(value.expr);
35 | case EParenthesis(expr): getParamNames(expr.expr);
36 | default:
37 | }
38 | }
39 | getParamNames(pattern.expr);
40 |
41 | var resultExpr = switch(params.length){
42 | case 1: macro $i{params[0]};
43 | case _: {expr:EObjectDecl(params.map(function(paramName) return {field:paramName, expr:macro $i{paramName}})), pos:Context.currentPos()};
44 | }
45 |
46 | return macro {
47 | switch($value) {
48 | case $pattern: $resultExpr;
49 | case _: $ifNot;
50 | }
51 | };
52 | }
53 |
54 |
55 | }
56 | ```
57 |
58 | ## Usage
59 |
60 | ```haxe
61 | package;
62 |
63 | enum Tree {
64 | Leaf(v:T);
65 | Node(l:Tree, r:Tree);
66 | }
67 |
68 | class MatchTest{
69 |
70 | static function assert(v, ?pos : haxe.PosInfos) if (!v) throw 'Assert failed. ' + pos;
71 |
72 | static function main() {
73 | //Enum matching
74 | assert("leaf0" == Match.extract(Leaf("leaf0"), Leaf(name)));
75 | assert("leaf1" == Match.extract(Node(Leaf("leaf0"), Leaf("leaf1")), Node(_, Leaf(leafName))));
76 |
77 | var result = Match.extract(Node(Leaf("leaf0"), Leaf("leaf1")), Node(Leaf(leafName0), Leaf(leafName1)));
78 | assert("leaf0" == result.leafName0);
79 | assert("leaf1" == result.leafName1);
80 |
81 | //Structure matching
82 | var myStructure = {
83 | name: "haxe",
84 | rating: "awesome"
85 | };
86 | assert("haxe" == Match.extract(myStructure, {name:name}));
87 | var result = Match.extract(myStructure, {name:n, rating:r});
88 | assert("haxe" == result.n);
89 | assert("awesome" == result.r);
90 |
91 | //Array matching
92 | var myArray = [1, 6];
93 | assert(6 == Match.extract(myArray, [1, a]));
94 |
95 | //Or patterns
96 | assert(2 == Match.extract(Node(Node(null, null), Leaf(2)), (Node(Leaf(s), _)|Node(_, Leaf(s)))));
97 |
98 | //Guards - not supported due to haxe macro syntax limitations
99 | //var result = Match.extract(myArray, [a, b] if a < b);
100 | //assert(result.a == 1 && result.b == 6);
101 |
102 | //Match on multiple values
103 | //have to force type to Array to make it work, looks ugly
104 | var result = Match.extract(([1, false, "foo"]:Array), [a, b, c]);
105 | assert(result.a == 1 && result.b == false && result.c == "foo");
106 |
107 | //Extractors
108 | assert('foo' == Match.extract(Leaf('Foo'), Leaf(_.toLowerCase() => r)));
109 |
110 | //default value if not match
111 | assert(3 == Match.extract(myArray, [a, -1], 3));
112 |
113 | trace('ok');
114 | }
115 |
116 | }
117 | ```
118 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/generate-dispatch-code.md:
--------------------------------------------------------------------------------
1 | [tags]: / "arguments,building-fields,build-macro"
2 |
3 | # Generate dispatch code
4 |
5 | Automatically generate dispatch functions as:
6 |
7 | ```haxe
8 | public function onDoubleArguments(one:String, two:Int) {
9 | for (listener in listeners) {
10 | listener.onDoubleArguments(one, two);
11 | }
12 | }
13 | ```
14 |
15 | ## Macro builder class
16 |
17 | ```haxe
18 | #if macro
19 | import haxe.macro.Context;
20 | import haxe.macro.Expr;
21 |
22 | class DispatchBuilder {
23 | macro static public function build():Array {
24 | var fields = Context.getBuildFields();
25 | for (field in fields) {
26 | // skip the constructor
27 | if (field.name == 'new') continue;
28 |
29 | switch (field.kind) {
30 | case FieldType.FFun(fn):
31 | // skip non-empty functions
32 | if (!isEmpty(fn.expr)) continue;
33 |
34 | // empty function found, create a loop and set as function body
35 | var fname = field.name;
36 | var args = [for (arg in fn.args) macro $i{arg.name}];
37 | fn.expr = macro {
38 | for (listener in listeners)
39 | listener.$fname( $a{args} );
40 | }
41 | default:
42 | }
43 | }
44 | return fields;
45 | }
46 |
47 | static function isEmpty(expr:Expr) {
48 | if (expr == null) return true;
49 | return switch (expr.expr) {
50 | case ExprDef.EBlock(exprs): exprs.length == 0;
51 | default: false;
52 | }
53 | }
54 | }
55 | #end
56 | ```
57 |
58 | ## Usage
59 |
60 | You will need to create a `Dispatcher` base class.
61 |
62 | ```haxe
63 | @:autoBuild(DispatchBuilder.build())
64 | class Dispatcher {
65 | var listeners:Array;
66 |
67 | public function new() {
68 | listeners = [];
69 | }
70 |
71 | // addListener, removeListener,...
72 | }
73 | ```
74 |
75 | Extend `Dispatcher` to profit from automatically generated dispatch functions.
76 |
77 | ```haxe
78 | class Example extends Dispatcher {
79 | public function onDoubleArguments(one:String, two:Int);
80 | public function onSingleArgument(one:String);
81 | public function onNoArgument();
82 | public function onEmptyBody() { }
83 | public function onNonEmptyBody() {
84 | trace('my code here');
85 | }
86 | }
87 | ```
88 |
89 | > Source:
90 | > Author: [Philippe elsassph](https://github.com/elsassph)
91 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/generating-code-in-a-macro.md:
--------------------------------------------------------------------------------
1 | [tags]: / "expression-macro"
2 |
3 | # Add build time using macro
4 |
5 | This example shows:
6 |
7 | - How Haxe code can be generated by using either the
8 | [`Context.parse()` API](http://api.haxe.org/haxe/macro/Context.html#parse) or the `macro` keyword for [expression reification](http://haxe.org/manual/macro-reification-expression.html).
9 | - The difference between code that's executed at compile time (calculating the `buildTime` timestamp in the macro function) and code that's executed at run time (calculating the `runTime` timestamp in expressions returned from the macro.)
10 | - How generated code is inlined in place of a macro function call.
11 |
12 | ```haxe
13 | import haxe.macro.Context;
14 | class Test {
15 | public static function main() {
16 | // The expressions returned form the macros are injected here in main()
17 | trace_build_age_with_parse();
18 | trace_build_age_with_reification();
19 | }
20 |
21 | // This macro generates code using Context.parse()
22 | public static macro function trace_build_age_with_parse() {
23 | var buildTime = Math.floor(Date.now().getTime() / 1000);
24 |
25 | var code = '{
26 | var runTime = Math.floor(Date.now().getTime() / 1000);
27 | var age = runTime - $buildTime;
28 | trace("Right now it\'s "+runTime+", and this build is "+age+" seconds old");
29 | }';
30 |
31 | return Context.parse(code, Context.currentPos());
32 | }
33 |
34 | // This macro generates the same code using the macro keyword (expressions reification)
35 | public static macro function trace_build_age_with_reification() {
36 | var buildTime = Math.floor(Date.now().getTime() / 1000);
37 |
38 | var e = macro {
39 | var runTime = Math.floor(Date.now().getTime() / 1000);
40 | var age = runTime - $v{ buildTime };
41 | trace("Right now it's "+runTime+", and this build is "+age+" seconds old");
42 | };
43 |
44 | return e;
45 | }
46 | }
47 | ```
48 |
49 | ## Usage
50 |
51 | Compile this example to Neko with:
52 |
53 | ```hxml
54 | haxe -main Test -neko test.n
55 | ```
56 |
57 | Run the program with `neko test.n` and observe the output:
58 |
59 | ```
60 | Test.hx:6: Right now it's 1466034708, and this build is 3 seconds old
61 | Test.hx:32: Right now it's 1466034708, and this build is 3 seconds old
62 | ```
63 |
64 | Wait a few seconds and run it again:
65 |
66 | ```
67 | Test.hx:6: Right now it's 1466034711, and this build is 6 seconds old
68 | Test.hx:32: Right now it's 1466034711, and this build is 6 seconds old
69 | ```
70 |
71 | Also, you can compile it to JavaScript and observe how the expressions the macro returned
72 | are indeed inlined directly in the main() function. The `buildTime` timestamp is now
73 | simply a literal:
74 |
75 | ```js
76 | Test.main = function() {
77 | var runTime = Math.floor(new Date().getTime() / 1000);
78 | var age = runTime - 1466034832;
79 | console.log("Right now it's " + runTime + ", and this build is " + age + " seconds old");
80 | var runTime1 = Math.floor(new Date().getTime() / 1000);
81 | var age1 = runTime1 - 1466034832;
82 | console.log("Right now it's " + runTime1 + ", and this build is " + age1 + " seconds old");
83 | };
84 | ```
85 |
86 | > Author: [Jeff Ward](https://github.com/jcward)
87 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/get-compiler-define-value.md:
--------------------------------------------------------------------------------
1 | [tags]: / "conditional-compilation,expression-macro"
2 |
3 | # Working with compiler flags
4 |
5 | > This snippet demonstrates how to get, set, check for, and list compiler flags.
6 |
7 | Compiler flags can be assigned values. For example, `-D key=value` defines a compiler flag called `key` with a value of `value`.
8 |
9 | Compiler flag values representing numbers can be evaluated to `Float` and `Int`. Everything else is considered a `String` constant. If no value is assigned to a compiler flag key, the compiler defaults it to `1`. If a value of an undefined compiler flag key is requested, the compiler returns `null`.
10 |
11 | It is possible to get and set compiler flags, check if they are defined, and list all defined compiler flags.
12 |
13 | ## Implementation
14 | ```haxe
15 | import haxe.macro.Compiler;
16 | import haxe.macro.Context;
17 | import haxe.macro.Expr;
18 |
19 | class Main {
20 | static function main() {
21 | trace("Warning: " + Compiler.getDefine("warning"));
22 | #if !foo
23 | setDefine("foo", "bar");
24 | #end
25 | trace(getDefines());
26 | }
27 |
28 | // Shorthand for setting compiler flags from non-macro code.
29 | static macro function setDefine(key : String, value : String) : Expr {
30 | Compiler.define(key, value);
31 | return macro null;
32 | }
33 |
34 | // Shorthand for retrieving a map of all defined compiler flags.
35 | static macro function getDefines() : Expr {
36 | var defines : Map = Context.getDefines();
37 | // Construct map syntax so we can return it as an expression
38 | var map : Array = [];
39 | for (key in defines.keys()) {
40 | map.push(macro $v{key} => $v{Std.string(defines.get(key))});
41 | }
42 | return macro $a{map};
43 | }
44 | }
45 | ```
46 |
47 | ## Usage
48 |
49 | Assume the following build file:
50 |
51 | ```hxml
52 | -main Main
53 | -neko main.n
54 | -D warning="Don't let the bed bugs bite!"
55 | ```
56 |
57 | Running `neko main.n` from the compiler output directory will result in:
58 |
59 | ```
60 | Warning: "Don't let the bed bugs bite!"
61 | {haxe3 => 1, haxe_ver => 3.201, dce => std, sys => 1, hxcpp_api_level => 321, true => 1, foo => bar, neko => 1, warning => "Don't let the bed bugs bite!"}
62 | ```
63 |
64 | The last line may differ depending on the Haxe version and compilation options, because it is the string representation of the map containing all defined compiler flags.
65 |
66 | > Learn about conditional compilation here:
67 | >
68 | > Learn about available global compiler flags here:
69 | >
70 | > Learn about macro classes here:
71 | >
72 | > Author: [Domagoj Štrekelj](https://github.com/dstrekelj)
73 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/strictly-typed-json.md:
--------------------------------------------------------------------------------
1 | [tags]: / "macro,json,expression-macro,configuration"
2 |
3 | # Strictly Typed JSON
4 |
5 | It's possible read JSON files at compile time into strictly typed objects in Haxe.
6 |
7 |
8 |
9 | Normally you might load a JSON file with something like this:
10 | ```haxe
11 | var json = haxe.Json.parse(sys.io.File.getContent(path));
12 | ```
13 |
14 | Instead, if you load the JSON in a macro, then the JSON data will be available at compile time and therefore the types will be known:
15 |
16 | Create a file called **JsonMacro.hx** (or whatever you like) and add this:
17 | ```haxe
18 | macro function load(path:String) {
19 | // Register a dependency to the external file so the Haxe compilation cache is invalidated if the file changes.
20 | haxe.macro.Context.registerModuleDependency(haxe.macro.Context.getLocalModule(), path);
21 | return try {
22 | var json = haxe.Json.parse(sys.io.File.getContent(path));
23 | macro $v{json};
24 | } catch (e) {
25 | haxe.macro.Context.error('Failed to load json: $e', haxe.macro.Context.currentPos());
26 | }
27 | }
28 | ```
29 |
30 | Then use this to load your JSON instead:
31 |
32 | ```haxe
33 | var leveldata = JsonMacro.load('leveldata.json');
34 |
35 | for (i in leveldata.array) { // works now because we know the type of the leveldata object
36 | }
37 | ```
38 |
39 | **Explanation**: We run the original `Json.parse(File.getContent())` snippet in a macro function so it will execute when Haxe compiles our calls to `JsonMacro.load()`. Instead of returning the JSON object, in macros we need to return _syntax_. So we must convert our JSON object into Haxe syntax – just as if we'd typed our JSON out manually as Haxe objects. Fortunately there's a built-in operator for converting values into Haxe syntax, it's the ['macro-reification-value-operator': `$v{ some-basic-value }`](https://haxe.org/manual/macro-reification-expression.html). We could also use [`Context.makeExpr(value, position)`](https://api.haxe.org/haxe/macro/Context.html#makeExpr) to do the same job. We wrap the JSON reading in a `try-catch` so we can tidy-up error reporting a little.
40 |
41 | With this approach, the JSON's content is embedded into your compiled program and _not_ loaded at runtime, therefore, the path argument must be a constant string and cannot be an expression evaluated at runtime.
42 |
43 | > Author: [George Corney](https://github.com/haxiomic)
44 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/threading-macro.md:
--------------------------------------------------------------------------------
1 | [tags]: / "pipe,macro,thread,operator,clojure"
2 |
3 | # Threading macro like Clojure and pipe operator
4 |
5 | ## Introduction
6 |
7 | Threading macros (or arrow macros) are used in Clojure for compose many function calls.
8 |
9 |
10 | Other language like Elixir, LiveScript or F# use a pipe operator |> for the same functionality.
11 |
12 | In Haxe we can create the same utility with a simple macro. Thanks to the static analyzer we can eliminate the temp variables
13 | used for memorize the intermediate values.
14 |
15 | ## The code macro
16 |
17 | ```haxe
18 | import haxe.macro.Expr;
19 | import haxe.macro.Context;
20 | using haxe.macro.ExprTools;
21 | using haxe.macro.MacroStringTools;
22 |
23 |
24 | class FunctionThread {
25 | public static macro function thread(exprs:Array) {
26 | var exprs = [ for (expr in exprs) macro var _ = $expr ];
27 | exprs.push(macro _);
28 | return macro $b{exprs};
29 | }
30 | }
31 | ```
32 |
33 | ## A simple example
34 |
35 | ```haxe
36 | import FunctionThread.thread;
37 |
38 | class Main {
39 | static function sum(a, b) return a + b;
40 |
41 | static function main() {
42 | var x = 0;
43 | var res = thread(
44 | sum(x,1),
45 | sum(_,1),
46 | sum(_,2),
47 | sum(3,_)
48 | );
49 | trace(res);
50 | }
51 | }
52 | ```
53 |
54 | ## The code generated in JavaScript without the static analyzer
55 |
56 | ```js
57 | // Generated by Haxe 3.3.0
58 | (function () { "use strict";
59 | var HxOverrides = function() { };
60 | HxOverrides.iter = function(a) {
61 | return { cur : 0, arr : a, hasNext : function() {
62 | return this.cur < this.arr.length;
63 | }, next : function() {
64 | return this.arr[this.cur++];
65 | }};
66 | };
67 | var Main = function() { };
68 | Main.sum = function(a,b,c) {
69 | if(c == null) {
70 | c = 0;
71 | }
72 | return a + b + c;
73 | };
74 | Main.main = function() {
75 | var x = 0;
76 | var _ = Main.sum(x,1);
77 | var _1 = Main.sum(_,1,5);
78 | var _2 = Main.sum(_1,2);
79 | var _3 = Main.sum(3,_2,7);
80 | var res = _3;
81 | console.log(res);
82 | };
83 | Main.main();
84 | })();
85 | ```
86 |
87 | ## The code generated with the static analyzer
88 |
89 | In this case thanks to the static analyzer we don't need of the temp variables
90 |
91 | ```js
92 | // Generated by Haxe 3.3.0
93 | (function () { "use strict";
94 | var HxOverrides = function() { };
95 | HxOverrides.iter = function(a) {
96 | return { cur : 0, arr : a, hasNext : function() {
97 | return this.cur < this.arr.length;
98 | }, next : function() {
99 | return this.arr[this.cur++];
100 | }};
101 | };
102 | var Main = function() { };
103 | Main.sum = function(a,b) {
104 | return a + b;
105 | };
106 | Main.main = function() {
107 | console.log(Main.sum(3,Main.sum(Main.sum(Main.sum(0,1),1),2)));
108 | };
109 | Main.main();
110 | })();
111 | ```
112 |
113 | ## With inline function
114 |
115 | With inline of sum and static analyzer the output is:
116 | ```
117 | console.log(7);
118 | ```
119 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Macros/validate-json.md:
--------------------------------------------------------------------------------
1 | [tags]: / "json,validation,expression-macro"
2 |
3 | # Validates a .JSON file compile-time
4 |
5 | Json is quite a strict format.
6 | Ensure all comma's and quotes are correct using a macro function, which is executed while compiling.
7 |
8 | ## Macro function
9 |
10 | ```haxe
11 | class Validator {
12 | public static macro function validateJson(path:String) {
13 | haxe.macro.Context.registerModuleDependency(haxe.macro.Context.getLocalModule(), path);
14 | if (sys.FileSystem.exists(path)) {
15 | var content = sys.io.File.getContent(path);
16 | try {
17 | // Test the json by parsing it.
18 | // It will throw an error when you made a mistake.
19 | haxe.Json.parse(content);
20 | } catch (error:String) {
21 | // create position inside the json, FlashDevelop handles this very nice.
22 | var position = Std.parseInt(error.split("position").pop());
23 | var pos = haxe.macro.Context.makePosition({
24 | min:position,
25 | max:position + 1,
26 | file:path
27 | });
28 | haxe.macro.Context.error(path + " is not valid Json. " + error, pos);
29 | }
30 | } else {
31 | haxe.macro.Context.warning(path + " does not exist", haxe.macro.Context.currentPos());
32 | }
33 | return macro null;
34 | }
35 | }
36 | ```
37 |
38 | ## Usage
39 |
40 | This example only validates the _.json_ files in debug builds and not in display mode (auto-completion).
41 | The function can be called from anywhere.
42 |
43 | ```haxe
44 | class Test {
45 | static function main() {
46 | #if (debug && !display)
47 | Validator.validateJson("assets/json/levels.json");
48 | Validator.validateJson("assets/json/copy.json");
49 | #end
50 | }
51 | }
52 | ```
53 |
54 | > Source:
55 | > Author: [Mark Knol](https://github.com/markknol)
56 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/Working-with-cppia/01.creating-cppia-script.md:
--------------------------------------------------------------------------------
1 | # Creating a cppia script
2 |
3 | A cppia script is created as any other Haxe program - with a class file (here we choose the name Script.hx) including a **static main** function:
4 |
5 | ```haxe
6 | // Script.hx
7 | class Script {
8 | static public function main():Void {
9 | trace("Hello from Cppia SCRIPT");
10 | }
11 | }
12 | ```
13 |
14 | ## script.hxml
15 |
16 | We can then compile this file into a cppia script using the following **script.hxml** file:
17 |
18 | ```hxml
19 | -cp src
20 | -main Script
21 | -cppia bin/script.cppia
22 | ```
23 | > Note: If you are using Haxe 3.3+ you can use `-cppia` instead of `-D cppia`.
24 |
25 | When we run the following command...
26 |
27 | `> haxe script.hxml`
28 |
29 | ...a **script.cppia** is created in the bin folder.
30 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/Working-with-cppia/02.testing-cppia-script.md:
--------------------------------------------------------------------------------
1 | # Testing our cppia script
2 |
3 | For testing purposes, the **haxelib hxcpp** installation includes a Cppia host program wich can
4 | be used to test simple scripts.
5 |
6 | Navigate to the **/bin** folder, and run the following command:
7 |
8 | `> haxelib run hxcpp script.cppia`
9 |
10 | This should output the following in the console:
11 |
12 | `> Script.hx:4: Hello from Cppia SCRIPT`
13 |
14 | Now, we know that our script works and can be executed by an cppia host.
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/Working-with-cppia/03.creating-cppia-host.md:
--------------------------------------------------------------------------------
1 | # Creating a cppia host
2 |
3 | In the previous section we learned how to create the **script.cppia** file needed in this example.
4 |
5 | A cppia host is a c++ executable compiled with the Haxe c++ target. To create a host, we start with a class file (here we choose the name Host.hx) including a **static main** function:
6 |
7 | ```haxe
8 | // Host.hx
9 | class Host {
10 | static public function main():Void {
11 | trace('Hello from cppia HOST');
12 | var scriptname = './script.cppia';
13 | cpp.cppia.Host.runFile(scriptname); // <- load and execute the .cppia script file
14 | }
15 | }
16 | ```
17 |
18 | As you can see in the code example above, at runtime our executable will start by tracing a simple "Hello from cppia HOST" message.
19 | Then it will load and execute the **script.cppia** file.
20 |
21 | ## host.hxml
22 |
23 | We can compile this file into a cpp executable using the following **host.hxml** file:
24 |
25 | ```hxml
26 | -cp src
27 | -main Host
28 | -cpp bin
29 | -D scriptable
30 | ```
31 |
32 | (Please note that we use the **-D scriptable** directive to tell the compiler to include the functionality needed by a cppia host.)
33 |
34 | When we run the following command...
35 |
36 | ```
37 | > haxe host.hxml
38 | ```
39 |
40 | ...the c++ compiler will start the two step compilation process (1. generate the c++ source files, and 2. kick off the c++ compiler to create the executable).
41 | The result will be a host executable called **bin/Host** on Linux/Mac and **bin/Host.exe** on Windows.
42 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/Working-with-cppia/04.testing-cppia-host.md:
--------------------------------------------------------------------------------
1 | # Testing the cppia host executable
2 |
3 | Navigate to the **/bin** folder and start the application from the terminal.
4 | On Windows, typically run `Host.exe`, and on Linux/Mac run `./Host`.
5 |
6 | This should start the application, and the following should be written to the terminal:
7 |
8 | `> Host.hx:4: Hello from Cppia Host`
9 | `> Script.hx:4: Hello from Cppia SCRIPT`
10 |
11 |
12 | This indicates that the host has run (traced its own message) and executed the script (traced the script message).
13 |
14 | If you get the following message, the host can't find the script file:
15 |
16 | `> Error: [file_contents,./script.cppia]`
17 |
18 | Make sure that you have followed the **Creating a cppia script** tutorial, and that the **script.cppia** is placed in the same folder as your Host executable.
19 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/Working-with-cppia/index.md:
--------------------------------------------------------------------------------
1 | [tags]: / "cppia"
2 |
3 | # Working with cppia
4 |
5 | This article is about cppia, a scriptable "cpp subtarget" for [Haxe](https://haxe.org). A _cppia script_ is a "instructions assembly" script that can be run inside a _cppia host_ and gives you [fast runtime speed](https://benchs.haxe.org/) at near-zero compilation time. It also lets add performance critical code to the host, wich gives you full cpp runtime speed for those parts.
6 |
7 | Information about cppia can be found in [Hugh Sanderson](https://twitter.com/GameHaxe)s [WWX2015 talk](https://www.youtube.com/watch?v=hltXpZ3Upxg) (cppia part starts around 15:45).
8 |
9 | > Author: [Jonas Nyström](https://github.com/cambiata)
10 |
--------------------------------------------------------------------------------
/assets/content/cookbook/Other/adding-static-methods-to-existing-classes.md:
--------------------------------------------------------------------------------
1 | [tags]: / "static-extension"
2 |
3 | # Adding static methods to existing classes
4 |
5 | Haxe allows you to add static methods to existing classes (eg. `Math`) via the static extensions feature. The "secret sauce" is to specify the first parameter of the extension as `Class` where `X` is the class you want to add static methods to (eg. `Math`), and to make the method public.
6 |
7 | Here's a class with a static method that adds a `randomBetween(a, b)` method to `Math`:
8 |
9 | ## Implementation
10 |
11 | ```haxe
12 | class MathExtensions {
13 | /** Returns a random number between a (inclusive) and b (exclusive). */
14 | public static function randomBetween(cl:Class