├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CodeCookBook-hl.hxml ├── CodeCookBook-interp.hxml ├── CodeCookBook-neko.hxml ├── CodeCookBook-nodejs.hxml ├── CodeCookBook-python.hxml ├── CodeCookBook.hxml ├── CodeCookBook.hxproj ├── CodeCookBook.n ├── README.md ├── assets ├── content │ ├── 404.mtt │ ├── cookbook │ │ ├── Abstract types │ │ │ ├── EmailAddress.md │ │ │ ├── abstracts-with-type-params.md │ │ │ ├── array-access-db-manager.md │ │ │ ├── color.md │ │ │ ├── pipe.md │ │ │ ├── rounded-float.md │ │ │ ├── temperature-units.md │ │ │ └── using-iterators-as-generic-type-parameters.md │ │ ├── Beginner │ │ │ ├── Haxe-To-Emscripten-Hello-World.md │ │ │ ├── arrays.md │ │ │ ├── conditional-compilation.md │ │ │ ├── date-time.md │ │ │ ├── declare-classes-with-structinit.md │ │ │ ├── declare-classes.md │ │ │ ├── declare-functions.md │ │ │ ├── enum-adt.md │ │ │ ├── hello-world.md │ │ │ ├── lists.md │ │ │ ├── loading-external-files.md │ │ │ ├── maps.md │ │ │ ├── numbers-floats-ints.md │ │ │ ├── pattern-matching.md │ │ │ ├── reflection-method-call.md │ │ │ ├── regular-expressions.md │ │ │ ├── stdin-stdout-stderr.md │ │ │ ├── string-variable-reflection.md │ │ │ ├── strings.md │ │ │ ├── using-filesystem.md │ │ │ └── using-static-extensions.md │ │ ├── Compilation │ │ │ ├── compiling-libraries-without-main-class.md │ │ │ └── target-specific-modules-diff-by-filename.md │ │ ├── Data structures │ │ │ ├── grid-iterator.md │ │ │ ├── reverse-iterator.md │ │ │ ├── ring-array.md │ │ │ ├── sort-array.md │ │ │ └── step-iterator.md │ │ ├── Design-patterns │ │ │ ├── factory.md │ │ │ ├── lazy-initialization.md │ │ │ ├── method-chaining-fluent-interface.md │ │ │ ├── observer.md │ │ │ └── singleton.md │ │ ├── Functional Programming │ │ │ ├── enum-gadt.md │ │ │ └── functional-style-expression-evaluation.md │ │ ├── JavaScript │ │ │ ├── adding-element-to-dom.md │ │ │ ├── assets │ │ │ │ └── node-server.png │ │ │ ├── creating-node-server.md │ │ │ ├── javascript-inline-workers.md │ │ │ └── using-haxe-classes-in-javascript.md │ │ ├── Macros │ │ │ ├── add-git-commit-hash-in-build.md │ │ │ ├── add-parameters-as-fields.md │ │ │ ├── assert-with-values.md │ │ │ ├── assets │ │ │ │ └── haxe-json-macro.png │ │ │ ├── build-arrays.md │ │ │ ├── build-map.md │ │ │ ├── build-property-with-inline-getter.md │ │ │ ├── build-static-field.md │ │ │ ├── build-value-objects.md │ │ │ ├── combine-objects.md │ │ │ ├── completion-from-url.md │ │ │ ├── enum-abstract-values.md │ │ │ ├── extract-enum-value.md │ │ │ ├── extract-value-with-pattern-matching.md │ │ │ ├── generate-dispatch-code.md │ │ │ ├── generating-code-in-a-macro.md │ │ │ ├── get-compiler-define-value.md │ │ │ ├── include-file-next-to-module-file.md │ │ │ ├── strictly-typed-json.md │ │ │ ├── threading-macro.md │ │ │ └── validate-json.md │ │ ├── Other │ │ │ ├── Working-with-cppia │ │ │ │ ├── 01.creating-cppia-script.md │ │ │ │ ├── 02.testing-cppia-script.md │ │ │ │ ├── 03.creating-cppia-host.md │ │ │ │ ├── 04.testing-cppia-host.md │ │ │ │ └── index.md │ │ │ ├── adding-static-methods-to-existing-classes.md │ │ │ ├── assets │ │ │ │ └── deploy-haxelib-using-travis-and-github.gif │ │ │ ├── base64-encoding.md │ │ │ ├── compiling-cpp-code-windows-mingw.md │ │ │ ├── deploy-to-haxelib-using-travis-and-github-releases.md │ │ │ ├── haxe-zip.md │ │ │ ├── hxcpp-pointers.md │ │ │ ├── named-parameters.md │ │ │ ├── passing-different-types-to-a-function-parameter.md │ │ │ ├── ssl-socket-server.md │ │ │ └── vga-text-renderer.md │ │ └── Principles │ │ │ ├── Inheritance.md │ │ │ ├── Null-safety.md │ │ │ └── everything-is-an-expression.md │ ├── index.mtt │ ├── layout-page-main.mtt │ ├── layout-page-snippet.mtt │ ├── layout-page-toc.mtt │ ├── layout.mtt │ ├── redirection.mtt │ ├── rss.mtt │ ├── sitemap.mtt │ ├── table-of-content-category.mtt │ ├── table-of-content-serie.mtt │ └── tags.mtt └── includes │ ├── css │ ├── font-awesome.css │ ├── font-awesome.min.css │ ├── fonts.css │ ├── fonts.min.css │ ├── haxe-nav.css │ ├── haxe-nav.min.css │ ├── styles.css │ └── styles.min.css │ ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── open-sans-latin-ext.woff2 │ └── open-sans-latin.woff2 │ ├── img │ ├── completion-from-url.gif │ └── share.png │ └── js │ └── 404.js ├── highlighting.hxml ├── package-lock.json ├── package.json ├── src ├── Config.hx ├── Generator.hx ├── Highlighting.hx ├── Main.hx ├── Redirections.hx ├── data │ ├── Category.hx │ ├── Page.hx │ └── TemplateData.hx └── util │ ├── GitUtil.hx │ └── Minifier.hx └── utterances.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: haxe 4 | custom: ['https://haxe.org/foundation/support-plans.html', 'https://haxe.org/foundation/donate.html'] 5 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | submodules: true 12 | # we need full git history for article dates 13 | fetch-depth: 0 14 | 15 | - name: Setup Haxe 16 | uses: krdlab/setup-haxe@v1 17 | with: 18 | haxe-version: 4.3.5 19 | 20 | - name: Install 21 | # at some point all npm needs to be in package.json 22 | run: | 23 | npm install 24 | npm install -g less@2.7 25 | npm install -g less-plugin-clean-css@1.5 26 | haxelib install CodeCookBook-neko.hxml --always --quiet 27 | haxelib install highlighting.hxml --always --quiet 28 | haxelib list 29 | 30 | - name: Generate website 31 | run: | 32 | haxe CodeCookBook-neko.hxml 33 | haxe highlighting.hxml 34 | 35 | - name: Upload artifact 36 | uses: actions/upload-pages-artifact@v3 37 | with: 38 | path: ./output 39 | 40 | deploy: 41 | if: github.ref == 'refs/heads/master' 42 | permissions: 43 | contents: read 44 | pages: write 45 | id-token: write 46 | 47 | runs-on: ubuntu-latest 48 | needs: build 49 | environment: 50 | name: github-pages 51 | url: ${{steps.deployment.outputs.page_url}} 52 | steps: 53 | - name: Deploy artifact 54 | id: deployment 55 | uses: actions/deploy-pages@v4 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project generated files # 2 | ###################### 3 | output 4 | snippets/ 5 | 6 | # OS generated files # 7 | ###################### 8 | .DS_Store 9 | .DS_Store? 10 | ._* 11 | .Spotlight-V100 12 | .Trashes 13 | ehthumbs.db 14 | Thumbs.db 15 | 16 | CodeCookBook.hl 17 | CodeCookBook.js 18 | CodeCookBook.py 19 | node_modules/ 20 | bin/ 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "grammars/haxe-TmLanguage"] 2 | path = grammars/haxe-TmLanguage 3 | url = https://github.com/vshaxe/haxe-TmLanguage.git 4 | [submodule "grammars/language-javascript"] 5 | path = grammars/language-javascript 6 | url = https://github.com/atom/language-javascript.git 7 | [submodule "grammars/xml.tmbundle"] 8 | path = grammars/xml.tmbundle 9 | url = https://github.com/textmate/xml.tmbundle.git 10 | -------------------------------------------------------------------------------- /CodeCookBook-hl.hxml: -------------------------------------------------------------------------------- 1 | CodeCookBook.hxml 2 | 3 | -hl CodeCookBook.hl 4 | 5 | --next 6 | 7 | -cmd hl CodeCookBook.hl -------------------------------------------------------------------------------- /CodeCookBook-interp.hxml: -------------------------------------------------------------------------------- 1 | CodeCookBook.hxml 2 | 3 | --interp -------------------------------------------------------------------------------- /CodeCookBook-neko.hxml: -------------------------------------------------------------------------------- 1 | CodeCookBook.hxml 2 | 3 | -neko CodeCookBook.n 4 | 5 | --next 6 | 7 | -cmd neko CodeCookBook.n -------------------------------------------------------------------------------- /CodeCookBook-nodejs.hxml: -------------------------------------------------------------------------------- 1 | CodeCookBook.hxml 2 | 3 | -lib hxnodejs 4 | -js CodeCookBook.js 5 | 6 | --next 7 | 8 | -cmd node CodeCookBook.js -------------------------------------------------------------------------------- /CodeCookBook-python.hxml: -------------------------------------------------------------------------------- 1 | CodeCookBook.hxml 2 | 3 | -python CodeCookBook.py 4 | 5 | --next 6 | 7 | -cmd python CodeCookBook.py -------------------------------------------------------------------------------- /CodeCookBook.hxml: -------------------------------------------------------------------------------- 1 | -cmd lessc assets/includes/css/haxe-nav.css assets/includes/css/haxe-nav.min.css --clean-css="--s1 --advanced" 2 | -cmd lessc assets/includes/css/styles.css assets/includes/css/styles.min.css --clean-css="--s1 --advanced" 3 | 4 | --next 5 | 6 | -lib markdown 7 | -lib hxtemplo:git:https://github.com/Simn/hxtemplo.git 8 | -lib hxparse 9 | -cp src 10 | -main Main 11 | -dce full 12 | --macro keep("DateTools") 13 | --macro keep("haxe.ds.StringMap") 14 | #-D eval-stack 15 | -D analyzer 16 | -D analyzer-optimize 17 | 18 | # for debug-only fast compile use: 19 | #-D disable_git_dates 20 | #-D disable_git_authors 21 | #-D test_snippets -------------------------------------------------------------------------------- /CodeCookBook.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | haxe CodeCookBook-$(TargetBuild).hxml 44 | 45 | 46 | 47 | 48 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /CodeCookBook.n: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/CodeCookBook.n -------------------------------------------------------------------------------- /assets/content/404.mtt: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 7 | 8 |
-------------------------------------------------------------------------------- /assets/content/cookbook/Abstract types/EmailAddress.md: -------------------------------------------------------------------------------- 1 | [tags]: / "abstract-type,ereg,validation" 2 | 3 | # Email address as abstract type 4 | 5 | The following EmailAddress [Abstract type](http://haxe.org/manual/types-abstract.html) example is based on the underlying standard `String` type, but sets the restriction that it can only represent a valid email address. If not, an exception will be thrown. 6 | 7 | ```haxe 8 | abstract EmailAddress(String) to String { 9 | static var ereg = ~/.+@.+/i; 10 | inline public function new(address:String) { 11 | if (!ereg.match(address)) throw 'EmailAddress "$address" is invalid'; 12 | this = address.toLowerCase(); 13 | } 14 | 15 | @:from inline static public function fromString(address:String) { 16 | return new EmailAddress(address); 17 | } 18 | } 19 | ``` 20 | ## Usage 21 | 22 | ```haxe 23 | // The following works 24 | var address:EmailAddress = 'eve@paradise.com'; 25 | 26 | // The following throws an exception 27 | var address:EmailAddress = 'adam#paradise.com'; 28 | ``` 29 | 30 | > Learn about Haxe Abstracts here: 31 | > 32 | > Author: [Jonas Nyström](https://github.com/cambiata) 33 | -------------------------------------------------------------------------------- /assets/content/cookbook/Abstract types/array-access-db-manager.md: -------------------------------------------------------------------------------- 1 | [tags]: / "abstract-type" 2 | 3 | # Array access of a database manager 4 | 5 | When using SPOD database objects, or the [_record-macros_ library](https://github.com/HaxeFoundation/record-macros), instances of database models can be accessed using the manager's `get` function: 6 | 7 | ```haxe 8 | var user42 = User.manager.get(42); 9 | ``` 10 | 11 | By abstracting over the user's manager class, we can add array access functionality to easily grab a user by their primary key (id): 12 | 13 | ```haxe 14 | var user42 = userManager[42]; 15 | ``` 16 | 17 | We can also use a shortcut for inserting a new user at a specific id if we so desire: 18 | 19 | ```haxe 20 | userManager[21] = new User("Douglas"); 21 | ``` 22 | 23 | Or even abuse the array access by providing `null` to auto-assign a new id: 24 | 25 | ```haxe 26 | userManager[null] = new User("Zaphod"); 27 | ``` 28 | 29 | 30 | ## Implementation 31 | 32 | ```haxe 33 | import sys.db.Object; 34 | import sys.db.Types; 35 | import sys.db.Manager; 36 | 37 | class User extends Object { 38 | public var id:SId; 39 | public var name:SString<255>; 40 | 41 | public function new(name:String) { 42 | super(); 43 | this.name = name; 44 | } 45 | 46 | override public function toString() 47 | return this.name + ' (${this.id})'; 48 | } 49 | 50 | @:forward 51 | abstract UserManager(Manager) from Manager to Manager { 52 | public function new() 53 | this = User.manager; 54 | 55 | @:arrayAccess 56 | inline function getUserById(id:Int) 57 | return this.get(id); 58 | 59 | @:arrayAccess 60 | inline function setUserById(id:Null, user:User):User { 61 | if(id != null) user.id = id; 62 | user.insert(); 63 | return user; 64 | } 65 | } 66 | ``` 67 | 68 | ## Usage 69 | 70 | Description of how to use/test the code. 71 | 72 | ```haxe 73 | import sys.db.Sqlite; 74 | import sys.db.TableCreate; 75 | import sys.db.Manager; 76 | 77 | class Main { 78 | static function main() { 79 | Manager.cnx = Sqlite.open('array-access.db'); 80 | 81 | var users:UserManager = new UserManager(); 82 | if(!TableCreate.exists(users)) { 83 | Sys.println('Creating user table...'); 84 | TableCreate.create(users); 85 | } 86 | 87 | var user:User = new User("Bob"); 88 | user.insert(); 89 | Sys.println('Created new user: ${user}'); 90 | 91 | var uid:Int = user.id; 92 | users[42] = new User("Douglas"); 93 | Sys.println('Created another new user: ${users[42]}'); 94 | 95 | var thirdUser = new User("Abed"); 96 | users[null] = thirdUser; 97 | Sys.println('Created yet another new user: ${thirdUser}'); 98 | 99 | user.delete(); 100 | users[42].delete(); 101 | thirdUser.delete(); 102 | } 103 | } 104 | ``` 105 | 106 | Outputs: 107 | 108 | ``` 109 | Creating user table... 110 | Created new user: Bob (1) 111 | Created another new user: Douglas (42) 112 | Created yet another new user: Abed (43) 113 | ``` 114 | 115 | > More on this topic: 116 | > 117 | > * [Array Access in Haxe Manual](https://haxe.org/manual/types-abstract-array-access.html) 118 | > * [record-macros library](https://github.com/HaxeFoundation/record-macros) 119 | > 120 | > Author: [Kenton Hamaluik](https://github.com/hamaluik) -------------------------------------------------------------------------------- /assets/content/cookbook/Abstract types/pipe.md: -------------------------------------------------------------------------------- 1 | [tags]: / "abstract-type,pipe,operator-overloading" 2 | 3 | # Pipe using Abstract Operator Overloading 4 | 5 | > The following example demonstrates how the pipe operator is used to clean up nested function calls with Abstract Operator Overloading. 6 | 7 | ## Motivation 8 | 9 | Function calls can take up a lot of real estate when transforming data. A developer may need to transform data through multiple utility functions. In doing so the developer may create temporary variables to transfer value from and to each function or they may write code using function calls as function arguments. Piping data from one function to another and assigning the data to a single identifier cleans up unnecessary variables while still showing the developers intention. 10 | 11 | ## Abstract Pipe 12 | 13 | ```haxe 14 | abstract Pipe(T) to T { 15 | public inline function new(s:T) { 16 | this = s; 17 | } 18 | 19 | @:op(A | B) 20 | public inline function pipe1(fn:T->U):Pipe { 21 | return new Pipe(fn(this)); 22 | } 23 | 24 | @:op(A | B) 25 | public inline function pipe2(fn:T->A->B):PipeB> { 26 | return new Pipe(fn.bind(this)); 27 | } 28 | 29 | @:op(A | B) 30 | public inline function pipe3(fn:T->A->B->C):PipeB->C> { 31 | return new Pipe(fn.bind(this)); 32 | } 33 | 34 | @:op(A | B) 35 | public inline function pipe4(fn:T->A->B->C->D):PipeB->C->D> { 36 | return new Pipe(fn.bind(this)); 37 | } 38 | } 39 | ``` 40 | ## Usage 41 | 42 | ```haxe 43 | inline function addWorld(str:String):String { 44 | return str + " world!"; 45 | } 46 | 47 | inline function capitalize(str:String):String { 48 | return str.toUpperCase(); 49 | } 50 | 51 | inline function count(str:String):Int { 52 | return str.length; 53 | } 54 | 55 | function main() { 56 | // function call as argument version 57 | var nestedHelloWorld = capitalize(addWorld("Hello")); 58 | trace(nestedHelloWorld); // HELLO WORLD! 59 | 60 | // update variable version 61 | var hello = "Hello"; 62 | hello = addWorld(hello); 63 | hello = capitalize(hello); 64 | trace(hello); // HELLO WORLD! 65 | 66 | // piped version 67 | var helloWorld:String = new Pipe("Hello") 68 | | addWorld 69 | | capitalize; 70 | trace(helloWorld); // HELLO WORLD! 71 | 72 | // piped version changing type from String to Int 73 | var helloWorldCount:Int = new Pipe("Hello") 74 | | addWorld 75 | | capitalize 76 | | count; 77 | trace(helloWorldCount / 2); // 6 78 | } 79 | ``` 80 | 81 | > Learn about Haxe Abstracts here: 82 | > 83 | > Author: [Jeremy Meltingtallow](https://github.com/PongoEngine) 84 | -------------------------------------------------------------------------------- /assets/content/cookbook/Abstract types/rounded-float.md: -------------------------------------------------------------------------------- 1 | [tags]: / "abstract-type,math" 2 | 3 | # Rounded Float as abstract type 4 | 5 | This [abstract type](http://haxe.org/manual/types-abstract.html) is based on the underlying `Float` type, but 6 | whenever it is converted back to an actual `Float` it is rounded to avoid the famous [rounding errors](https://en.wikipedia.org/wiki/Round-off_error) 7 | occuring in floating point aritmetics. 8 | 9 | **Please note** that this example doesn't solve the rounding error problem - it just covers it by rounding the errors away. 10 | This shouldn't be used in situations where accumulated errors might cause critical problems - for example in financial calculations. 11 | For those cases, something like Franco Ponticelli's [thx.Decimal](https://github.com/fponticelli/thx.core/blob/master/src/thx/Decimal.hx) should be used instead. 12 | 13 | ```haxe 14 | abstract RFloat(Float) from Float { 15 | inline function new(value : Float) 16 | this = value; 17 | 18 | // The following rounds the result whenever converted to a Float 19 | @:to inline public function toFloat():Float { 20 | return roundFloat(this); 21 | } 22 | 23 | @:to inline public function toString():String { 24 | return Std.string(toFloat()); 25 | } 26 | 27 | // The number of zeros in the following valuer 28 | // corresponds to the number of decimals rounding precision 29 | static inline var multiplier = 10000000; 30 | 31 | static inline function roundFloat(value:Float):Float 32 | return Math.round(value * multiplier) / multiplier; 33 | } 34 | ``` 35 | 36 | ## Usage 37 | 38 | ```haxe 39 | // Standard float gives a result with rounding error 40 | var f:Float = 2.0 - 1.1; 41 | trace(f); // 0.8999999999999999 42 | 43 | // RFloat abstract rounds the error away 44 | var rf:RFloat = 2.0 - 1.1; 45 | trace(rf); // 0.9 46 | ``` 47 | 48 | A cool trick is to let the [compile time type check](http://haxe.org/manual/expression-type-check.html) force a cast to `RFloat` only in the moment when it's actually needed: 49 | 50 | ```haxe 51 | // We define the variable as a standard float 52 | var f:Float = 2.0 - 1.1; 53 | // In the moment we need the rounded version, we use the '(f:RFloat)' syntax to force a cast to 'RFloat': 54 | trace((f:RFloat)); // 0.9 55 | ``` 56 | 57 | > Learn about Haxe Abstracts here: 58 | > 59 | > Author: [Jonas Nyström](https://github.com/cambiata) 60 | 61 | -------------------------------------------------------------------------------- /assets/content/cookbook/Abstract types/temperature-units.md: -------------------------------------------------------------------------------- 1 | [tags]: / "abstract-type" 2 | 3 | # Temperature units as abstract type 4 | 5 | The following Celcius and Fahrenheit [Abstract types](http://haxe.org/manual/types-abstract.html) are based on the underlying `Float` type, but sets the restriction that it can never hold values below absolute zero. 6 | 7 | Also, the `@:to` [field casts](http://haxe.org/manual/types-abstract-implicit-casts.html) take care of automatically converting from one unit to the other. 8 | 9 | ```haxe 10 | abstract Celcius(Float) to Float { 11 | inline function new(value : Float) 12 | this = Math.max(value, -273.15); 13 | 14 | @:from inline static public function fromFloat(value : Float) : Celcius 15 | return new Celcius(value); 16 | 17 | // the following field cast automatically converts to Fahrenheit from Celcius 18 | @:to inline public function toFahrenheit() : Fahrenheit 19 | return (this / 5 * 9) + 32; 20 | } 21 | 22 | abstract Fahrenheit(Float) to Float { 23 | inline function new(value : Float) 24 | this = Math.max(value, -459.67); 25 | 26 | @:from inline static public function fromFloat(value : Float) : Fahrenheit 27 | return new Fahrenheit(value); 28 | 29 | // the following field cast automatically converts to Celcius from Fahrenheit 30 | @:to inline public function toCelcius() : Celcius 31 | return (this - 32) * 5 / 9; 32 | } 33 | ``` 34 | 35 | ## Usage 36 | 37 | ```haxe 38 | // Here we start by defining a temperature in Celcius 39 | var waterfreezeC:Celcius = 0; 40 | trace('Water freezes at $waterfreezeC degrees Celcius.'); 41 | 42 | // Please note the unit conversion in the following line, automatically 43 | // invoking the Celcius.toFahrenheit() method for us: 44 | var waterfreezeF:Fahrenheit = waterfreezeC; 45 | trace('Water freezes at $waterfreezeF degrees Fahrenheit.'); 46 | ``` 47 | 48 | ## Credits to Franco Ponticelli 49 | 50 | This example is inspired by [Franco Ponticelli's](https://github.com/fponticelli) [**Thx.Unit**](https://github.com/fponticelli/thx.unit) library. 51 | There, you can find lots of interesting examples of well written code using abstract types. 52 | To point out one intersting thing: In thx.unit, the temperature abstracts are based on another abstract type - Decimal - for greater floating point accuracy. 53 | 54 | > Learn about Haxe Abstracts here: 55 | > 56 | > Author: [Jonas Nyström](https://github.com/cambiata) 57 | 58 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/Haxe-To-Emscripten-Hello-World.md: -------------------------------------------------------------------------------- 1 | [tags]: / "Emscripten" 2 | # Haxe to Emscripten 3 | 4 | This is a hello world example which will be compiled and linked to [Emscripten](https://emscripten.org/). 5 | 6 | ## Setup 7 | 8 | * **OS** Windows 7 x64 (I have not tested on Mac or Linux). 9 | * **Haxe** version 4+ 10 | * **Emscripten** [emsdk lastest version](https://emscripten.org/docs/getting_started/downloads.html) 11 | 12 | ## Implementation 13 | 14 | 1. Install [emsdk.zip](https://emscripten.org/docs/getting_started/downloads.html). 15 | 2. Update it to latest version and activate it to the latest version. 16 | 17 | Create a simple Hello World Haxe code: 18 | 19 | __Main.hx__ 20 | 21 | ```haxe 22 | package; 23 | 24 | @:buildXml(" 25 | 26 | 27 | 28 | 29 | ") 30 | 31 | class Main { 32 | static function main(){ 33 | trace("Haxe is great!"); 34 | } 35 | } 36 | ``` 37 | 38 | __build.hxml__ 39 | ```build 40 | # Windows maybe needs this define 41 | -D EMSCRIPTEN_SDK 42 | 43 | # If you want the .html file showing how to embed the wasm 44 | -D HXCPP_LINK_EMSCRIPTEN_EXT=.html 45 | 46 | # Tell hxcpp to use emscripten-toolchain.xml 47 | -D emscripten 48 | 49 | -cpp out 50 | -main Main 51 | ``` 52 | > The link and build.hxml is based on 53 | 54 | It will then link "main.html". 55 | 56 | Open main.html in your browser to run it. 57 | 58 | # Troubleshooting 59 | **Problem**: `Unable to find the full path for emcc.` 60 | **Possible Solution**: 61 | The problem was traced to the variable EMSCRIPTEN_SDK, in the emscripten-toolchain.xml. Found in the "\HaxeToolkit\haxe\lib\hxcpp\4,0,19\toolchain" folder. When the EMSCRIPTEN_SDK, is set to the correct path for 'emcc'. The script still unable to find the full path to 'emcc'. 62 | 63 | The best option is to hard code the full path name in the emscripten-toolchain. 64 | 65 | Change from: 66 | ``` 67 |
68 | 69 | 70 | 71 |
72 | ``` 73 | To: 74 | ``` 75 |
76 | 77 | 78 | 79 |
80 | ``` 81 | 82 | With the above solution you will only need to install the latest version of emsdk. 83 | 84 | > Author: [Fhalo](https://github.com/Fhalo48) 85 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/conditional-compilation.md: -------------------------------------------------------------------------------- 1 | [tags]: / "conditional-compilation" 2 | 3 | # Conditional compilation 4 | 5 | > This snippet demonstrates use of conditional compilation with custom compiler flags. 6 | 7 | Conditional compilation is a tool commonly used to alter the flow of the compilation process. It relies on the use of compiler flags (also known as _defines_), which are configurable values that exist only during compilation. 8 | 9 | Compiler flags are set with `-D key` or `-D key=value` from the command-line or build file. The values of `Float`, `Int`, and `String` constants are used directly when evaluating conditionals. 10 | 11 | Note that those conditional compilation branches that the compiler doesn't enter are discarded while parsing the source file. 12 | 13 | To get a list of supported Haxe compiler flags, use `haxe --help-defines`. 14 | 15 | ## Implementation 16 | ```haxe 17 | class Main { 18 | static function main() { 19 | #if introduce 20 | trace("Hello! This is an example of conditional compilation."); 21 | #end 22 | 23 | #if (level > 4) 24 | trace("Welcome, administrator!"); 25 | #elseif (level > 2) 26 | trace("Welcome, super user!"); 27 | #else 28 | trace("Welcome, user!"); 29 | #end 30 | } 31 | } 32 | ``` 33 | 34 | ## Usage 35 | 36 | Assume the following build file: 37 | 38 | ```hxml 39 | -main Main 40 | -neko main.n 41 | -D introduce 42 | -D level=3 43 | ``` 44 | 45 | Running `neko main.n` from the compiler output directory will result in: 46 | 47 | ``` 48 | Hello, this is an example of conditional compilation. 49 | Welcome, super user! 50 | ``` 51 | 52 | > * Learn about conditional compilation here: 53 | > * Learn about available global compiler flags here: 54 | > 55 | > Author: [Domagoj Štrekelj](https://github.com/dstrekelj) 56 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/date-time.md: -------------------------------------------------------------------------------- 1 | [tags]: / "date,time" 2 | 3 | # Working with date and time 4 | 5 | The [Date class](http://api.haxe.org/Date.html) provides a basic structure for date and time related information. This article shows how to work with the date and time tools. 6 | 7 | In the context of Haxe dates, a timestamp is defined as the number of milliseconds elapsed since 1st January 1970. 8 | 9 | #### Create fixed date / time 10 | ```haxe 11 | var date = new Date(2020, 1, 2, 12, 30, 0); 12 | // Sun Feb 02 2020 12:30:00 GMT+0100 (W. Europe Standard Time) 13 | ``` 14 | 15 | #### Get the current date/time 16 | ```haxe 17 | var today = Date.now(); 18 | ``` 19 | 20 | ## Formatting a date to string 21 | 22 | You can grab the components of a date using these methods (all will return integers): 23 | 24 | * `date.getSeconds()` The seconds of this Date (0-59 range). 25 | * `date.getMinutes()` The minutes of this Date (0-59 range). 26 | * `date.getHours()` The hours of this Date (0-23 range). 27 | * `date.getDate()` The day of this Date (1-31 range). 28 | * `date.getDay()` The day of the week of this Date (0-6 range) where 0 is Sunday. 29 | * `date.getMonth()` The month of this Date (0-11 range). 30 | * `date.getFullYear()` The full year of this Date (4-digits). 31 | 32 | #### Formatting days / months 33 | 34 | _Day and month are starting from zero_, this is different from how you normally read a date, but is convenient when you manually want to format it: 35 | 36 | ```haxe 37 | var now = Date.now(); 38 | 39 | var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 40 | var monthName = monthNames[now.getMonth()]; 41 | trace("this month is called " + monthName); 42 | 43 | var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 44 | var dayName = dayNames[now.getDay()]; 45 | trace("this day is called " + dayName); 46 | ``` 47 | 48 | #### Formatting dates using `strftime` standard format 49 | 50 | The [DateTools](http://api.haxe.org/DateTools.html) class contains a format function to express time in a convenient way. 51 | 52 | ```haxe 53 | DateTools.format(Date.now(), "%Y-%m-%d_%H:%M:%S"); 54 | // 2018-07-08_14:44:05 55 | 56 | DateTools.format(Date.now(), "%r"); 57 | // 02:44:05 PM 58 | 59 | var t = DateTools.format(Date.now(), "%T"); 60 | // 14:44:05 61 | 62 | DateTools.format(Date.now(), "%F"); 63 | // 2018-07-08 64 | 65 | DateTools.format(Date.now(), "%b %d, %Y"); 66 | // Jan 08, 2018 67 | ``` 68 | 69 | > For a list of all strftime directives, check out 70 | 71 | ## Calculating with dates 72 | 73 | The [DateTools](http://api.haxe.org/DateTools.html) class contains even some more extra functionalities for calculating with Date instances and timestamps: 74 | 75 | [tryhaxe](https://try.haxe.org/embed/Da47E) 76 | 77 | ## How to create a countdown timer 78 | 79 | In this example we create a timer that counts down to a certain date. 80 | When countdown is running, it traces "5 days - 12:02:59" and when it is expired, the timer stops. 81 | 82 | The example makes use of the `haxe.Timer` class and the `StringTools` as static extension. Both are available in the standard library and run on any target. 83 | 84 | [tryhaxe](https://try.haxe.org/embed/71972B65) 85 | 86 | > Read more in the Haxe API documentation: 87 | > 88 | > * [Date API](http://api.haxe.org/Date.html) 89 | > * [DateTools API](http://api.haxe.org/DateTools.html) 90 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/declare-classes-with-structinit.md: -------------------------------------------------------------------------------- 1 | [tags]: / "class" 2 | 3 | # Declare classes using @:structInit 4 | 5 | As an alternative to the [traditional way of instantiating classes using the `new` keyword](/category/beginner/declare-classes.html) - which is how it's done in most object oriented languages such as Java or C# - you can use the `@:structInit` metadata when you declare the class. 6 | 7 | A class can be annotated with `@:structInit` metadata: 8 | 9 | ```haxe 10 | @:structInit class User { 11 | final name:String; 12 | var age:Int = 30; 13 | 14 | public function new(name:String, age:Int) { 15 | this.name = name; 16 | this.age = age; 17 | } 18 | 19 | public function greet() 20 | trace( 'Hello, I\'m $name, and I\'m $age years old!'); 21 | } 22 | ``` 23 | Then it can be instantiated traditionally, with the `new` keyword: 24 | 25 | ```haxe 26 | var bob:User = new User('Bob', 32); 27 | ``` 28 | 29 | But it can also be instantiated using a compatible object structure, like the following: 30 | 31 | ```haxe 32 | var bob:User = {name: 'Bob', age: 32}; 33 | ``` 34 | 35 | If the class is a simple data object, without any need for initial method calls when instantiated, it can be declared even more compactly by removing the constructor: 36 | 37 | ```haxe 38 | @:structInit class User { 39 | final name:String; 40 | var age:Int = 30; 41 | 42 | public function greet() 43 | trace('Hello, I\'m $name, and I\'m $age years old!'); 44 | } 45 | ``` 46 | 47 | Note that if the constructor is removed as above, `new` cannot be used for instantiaton: 48 | 49 | ```haxe 50 | var bob:User = {name: 'Bob', age: 32}; // works! 51 | var bob:User = {name: 'Bob'}; // defaults Bob's age to 30 years 52 | 53 | var bob:User = new User('Bob', 32);// gives error "User does not have a constructor" 54 | ``` 55 | 56 | ## Why use @:structInit? 57 | 58 | ### Simple typedef-like syntax but with full class power 59 | 60 | Typedefs might be the most common way to declare simple data objects, and `@:structInit` gives you a way to use the same simple syntax - but you also get all the bells and whistles of real classes: you can use `public` or `private` fields and methods, getters and setters etc. just like in any class. 61 | 62 | ### Performance gain on static targets 63 | 64 | Haxe might [perform better on static targets when using classes compared to using typedefs or anonymous objects](https://haxe.org/manual/types-structure-performance.html). As a consequence, replacing typedefs with `@:structInit` classes might slightly increase the runtime performance. 65 | 66 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/declare-classes.md: -------------------------------------------------------------------------------- 1 | [tags]: / "class" 2 | 3 | # Declare classes 4 | 5 | Create a new class with two functions and create a new instance of it. 6 | 7 | ```haxe 8 | class Calculator { 9 | public function new() { 10 | trace("A new calculator instance was created!"); 11 | } 12 | 13 | public function add(a:Int, b:Int): Int { 14 | return a + b; 15 | } 16 | 17 | public function multiply(a:Int, b:Int):Int { 18 | return a * b; 19 | } 20 | } 21 | 22 | // Create a new instance of the Calculator 23 | var calculator = new Calculator(); 24 | 25 | trace(calculator.add(1, 2)); 26 | trace(calculator.multiply(2, 3)); 27 | ``` 28 | > See 29 | 30 | ### Declare a class with inheritance 31 | 32 | Create a parent class and create another one which "inherits" it. 33 | 34 | ```haxe 35 | class Animal { 36 | public function new() { } 37 | 38 | public function sayHello() { 39 | trace("Hello!"); 40 | } 41 | } 42 | 43 | class Dog extends Animal { 44 | public function new() { 45 | super(); 46 | } 47 | } 48 | 49 | // Create a new Dog instance 50 | var myDog = new Dog(); 51 | 52 | // We can also access its parent's methods 53 | myDog.sayHello(); 54 | ``` 55 | > See 56 | 57 | ### Declare a class with fields 58 | 59 | Declare a new class with its own fields, create a new instance and also be able to access and hide its properties. 60 | 61 | ```haxe 62 | class User { 63 | public var name:String; 64 | private var age:Int; 65 | 66 | public function new(name:String, age:Int) { 67 | this.name = name; 68 | this.age = age; 69 | } 70 | } 71 | 72 | // Create a new User instance 73 | var user = new User("Mark", 31); 74 | 75 | // We can also access it's public variables 76 | trace(user.name); 77 | 78 | // But we cannot access it's private variables 79 | trace(user.age); // Error; 80 | ``` 81 | > See 82 | 83 | ### Declare a generic class 84 | 85 | Declare a new class with its own fields, create a new instance and also be able to access and hide its properties. 86 | 87 | ```haxe 88 | class Value { 89 | public var value:T; 90 | 91 | public function new(value:T) { 92 | this.value = value; 93 | } 94 | } 95 | 96 | // Create a new Value Int instance 97 | var myIntValue = new Value(5); 98 | 99 | // Create a new Value String instance 100 | var myStringValue = new Value("String"); 101 | 102 | ``` 103 | > See 104 | 105 | ### Declare an inline constructor 106 | 107 | Declare a new class with an inline constructor (`new()` function), create a new instance and reveal its effect. 108 | 109 | ```haxe 110 | class Point { 111 | public var x:Float; 112 | public var y:Float; 113 | 114 | public inline function new(x, y) { 115 | this.x = x; 116 | this.y = y; 117 | } 118 | } 119 | 120 | // Create a new Value Int instance 121 | var myPoint = new Point(100, 150); 122 | trace(myPoint.x); 123 | ``` 124 | In JavaScript, this will be compiled (where possible) as: 125 | ```js 126 | var myPoint_x = 100; 127 | var myPoint_y = 150; 128 | console.log(myPoint_x); 129 | ``` 130 | > See 131 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/declare-functions.md: -------------------------------------------------------------------------------- 1 | [tags]: / "class" 2 | 3 | # Declare functions 4 | 5 | ```haxe 6 | // Declare our new function 7 | function myFunction() { 8 | trace("Hello!"); 9 | } 10 | 11 | // Call it 12 | myFunction(); 13 | ``` 14 | 15 | ### Declare function with arguments 16 | 17 | ```haxe 18 | // Declare our new function 19 | function sayHelloTo(name:String) { 20 | trace('Hello ${name}'); 21 | } 22 | 23 | // Call it 24 | sayHelloTo("Mark"); 25 | ``` 26 | > See 27 | 28 | ### Declare functions with default arguments 29 | 30 | ```haxe 31 | // Declare a function with one parameter which has a default value set 32 | function sayHello(name:String = "Mark") { 33 | trace('Hello ${name}'); 34 | } 35 | 36 | // Call it without any parameter and the 'default' one will be used 37 | sayHello(); 38 | 39 | // Let's call it again with some parameter 40 | sayHello("John"); 41 | ``` 42 | > See 43 | 44 | ### Declare functions with optional arguments 45 | 46 | ```haxe 47 | // Declare a function with one optional parameter 48 | function sayHello(?name:String) { 49 | if (name != null) { 50 | trace('Hello ${name}'); 51 | } else { 52 | trace('No name'); 53 | } 54 | } 55 | 56 | // Call it without any parameter and the 'default' one will be used 57 | sayHello(); 58 | 59 | // Let's call it again with some parameter 60 | sayHello("Sander"); 61 | ``` 62 | > See 63 | 64 | ### Declare a function with return type 65 | 66 | ```haxe 67 | // Declare our new function 68 | function sum(a:Int, b:Int):Int { 69 | return a + b; 70 | } 71 | 72 | // Call it 73 | var result = sum(2, 4); 74 | trace(result); 75 | ``` 76 | > See 77 | 78 | ### Declare a function with parameterized arguments 79 | 80 | ```haxe 81 | // Declare our new function 82 | function equals(a:T, b:T):Bool { 83 | return a == b; 84 | } 85 | 86 | // Call it with integers 87 | trace(equals(2, 2)); // true 88 | trace(equals(2, 1)); // false 89 | 90 | // Call it with strings 91 | trace(equals("hello","hello")); // true 92 | trace(equals("hello","world")); // false 93 | ``` 94 | > See 95 | 96 | ### Declare an inline function 97 | 98 | ```haxe 99 | // Declare our new function 100 | inline function sum(a:Int, b:Int):Int { 101 | return a + b; 102 | } 103 | 104 | // Call it 105 | var result = sum(2, 4); 106 | trace(result); 107 | ``` 108 | In JavaScript this will be compiled (where possible) as: 109 | ```js 110 | var result = 6; 111 | console.log(result); 112 | ``` 113 | 114 | > See 115 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/enum-adt.md: -------------------------------------------------------------------------------- 1 | [tags]: / "enum, data-structures" 2 | 3 | # Using enum / ADT 4 | 5 | Haxe's enumeration types are algebraic data types. Their primary use is for describing data structures. 6 | 7 | ### Creation 8 | 9 | #### Defining an enum 10 | 11 | Enums are denoted by the `enum` keyword and contain one or more enum constructors. Enum constructors can contain an arbitrary number of constructor arguments. 12 | 13 | ```haxe 14 | // Describes a type of item that can be rewarded 15 | enum ItemType { 16 | Key; 17 | Sword(name:String, attack:Int); 18 | Shield(name:String, defense:Int); 19 | } 20 | 21 | // Describes a type of reward that can be given 22 | enum RewardType { 23 | Gold(value:Int); 24 | Experience(value:Int); 25 | Item(type:ItemType); 26 | } 27 | ``` 28 | 29 | #### Creating an enum instance 30 | 31 | To create an enum instance, call its constructor. 32 | 33 | ```haxe 34 | var gold = Gold(123); 35 | var experience = Experience(456); 36 | var item = Item(Key); 37 | ``` 38 | 39 | Alternatively, instantiate the enum via methods from `haxe.EnumTools`. Read more about it in the [Tooling](#enumtools) section. 40 | 41 | ```haxe 42 | // Creates Sword item type with name Slashy and strength 100 43 | var createdByName = ItemType.createByName("Sword", ["Slashy", 100]); 44 | // Creates Key item type, as it is the first constructor specified 45 | var createdByIndex = ItemType.createByIndex(0); 46 | ``` 47 | 48 | ### Usage 49 | 50 | Values passed to enum constructors can be obtained through pattern matching. 51 | 52 | ```haxe 53 | var reward = Item(Sword("Slashy", 100)); 54 | 55 | switch (reward) { 56 | case Gold(value): 57 | trace('I got $value gold!'); 58 | case Experience(value): 59 | trace('I got $value experience!'); 60 | case Item(type): 61 | switch (type) { 62 | case Key: 63 | trace('I got a key!'); 64 | case Sword(name, attack): 65 | trace('I got "$name", a sword with $attack attack!'); 66 | case Shield(name, defense): 67 | trace('I got "$name", a shield with $defense defense!'); 68 | } 69 | } 70 | 71 | // Output: I got "Slashy", a sword with 100 attack! 72 | ``` 73 | 74 | ### Tooling 75 | 76 | #### EnumTools 77 | 78 | The `haxe.EnumTools` module in the standard library contains several methods to help work with enums and enum constructors. They provide additional ways to create enum instances, as well as obtain information on enum constructors. 79 | 80 | These methods are automatically included in the module context when using enums, but usually they would be included explicitly through `using haxe.EnumTools;`. 81 | 82 | Some examples are presented below: 83 | 84 | ```haxe 85 | // Gets enum name, including path 86 | var enumName = ItemType.getName(); 87 | // Gets array of constructor names for provided enum 88 | var enumConstructorNames = ItemType.getNames(); 89 | ``` 90 | 91 | #### EnumValueTools 92 | 93 | The `haxe.EnumValueTools` module in the standard library contains several methods to help work with enum values. They provide additional ways to compare enum instances, and get their constructors and constructor arguments. 94 | 95 | These methods are automatically included in the module context when using enums, but usually they would be included explicitly through `using haxe.EnumValueTools;`. 96 | 97 | Some examples are presented below: 98 | 99 | ```haxe 100 | var item = Shield("Shieldy", 100); 101 | // Gets enum instance constructor name 102 | var constructorName = item.getName(); 103 | // Gets enum instance constructor index 104 | var constructorIndex = item.getIndex(); 105 | // Gets enum instance constructor arguments 106 | var constructorArguments = item.getParameters(); 107 | 108 | var otherItem = Sword("Slashy", 100); 109 | // Compares two enum instances recursively 110 | if (item.equals(otherItem)) trace("Items are equal!"); 111 | // Matches enum instance against pattern 112 | if (otherItem.match(Shield(_, _))) trace("Other item is a shield!"); 113 | ``` 114 | 115 | > Read more in the Haxe Manual: 116 | > 117 | > * [Enum](https://haxe.org/manual/types-enum-instance.html) 118 | > * [Pattern Matching](https://haxe.org/manual/lf-pattern-matching.html) 119 | > 120 | > Read more in the Haxe API documentation: 121 | > 122 | > * [EnumTools](http://api.haxe.org/haxe/EnumTools.html) 123 | > * [EnumValueTools](http://api.haxe.org/haxe/EnumValueTools.html) 124 | > 125 | > Author: [Domagoj Štrekelj](https://github.com/dstrekelj) 126 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello world 2 | 3 | > This tutorial demonstrates how to write and compile a Hello World Haxe program. It explains the involved file-format (.hx) and gives a basic explanation of what the Haxe Compiler does with them. 4 | 5 | #### Requirements 6 | 7 | * Haxe has to be installed and available from command line. 8 | * You have to know how to to save files on your computer. 9 | * You have to be able to open a command line, navigate to a directory and execute a command. 10 | 11 | ## Creating and saving the code 12 | 13 | Copy and paste the following code into any editor or IDE of your choice: 14 | 15 | ```haxe 16 | class HelloWorld { 17 | static public function main():Void { 18 | trace("Hello World"); 19 | } 20 | } 21 | ``` 22 | 23 | Save it as "HelloWorld.hx" anywhere you like. 24 | 25 | ## Executing Haxe to interpret the code 26 | 27 | Open a command prompt and navigate directories to where you saved "HelloWorld.hx" to in the previous step. Afterwards, execute this command: 28 | 29 | ```hxml 30 | haxe -main HelloWorld --interp 31 | ``` 32 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/lists.md: -------------------------------------------------------------------------------- 1 | [tags]: / "collections, data-structures" 2 | 3 | # Using lists 4 | 5 | In Haxe, the `List` type represents a linked-list of elements. 6 | 7 | ### Creation 8 | 9 | Lists can only be created through the use of a constructor. The constructor requires a type parameter to be passed, which specifies the type of the elements in the list. 10 | 11 | ```haxe 12 | var listOfInts = new List(); 13 | var listOfListsOfMyType = new List>(); 14 | ``` 15 | 16 | ### Adding elements 17 | 18 | An element can be appended to the end (tail) of the list, or prepended to the beginning (head) of the list. Elements cannot be inserted into a specific place in the list. 19 | 20 | ```haxe 21 | var listOfInts = new List(); 22 | // Adds 1 to the tail of the list 23 | listOfInts.add(1); 24 | // Adds 2 to the head of the list 25 | listOfInts.push(2); 26 | ``` 27 | 28 | ### Removing elements 29 | 30 | Elements can be removed by passing a reference or value of the list element to be removed. In that case, the first occurence of the passed element will be removed from the list. List elements can also be instantly removed from the top of the list. 31 | 32 | ```haxe 33 | var listOfInts = new List(); 34 | for(i in 0...5) listOfInts.add(i); 35 | // Removes first occurence of 1 in list 36 | listOfInts.remove(1); 37 | // Removes and returns the head element of the list 38 | listOfInts.pop(); 39 | ``` 40 | 41 | ### Retrieving elements 42 | 43 | Only the first (head) and last (tail) element of the list can be directly retrieved. 44 | 45 | ```haxe 46 | var listOfInts = new List(); 47 | for(i in 0...5) listOfInts.add(i); 48 | // Returns the head element of the list 49 | listOfInts.first(); 50 | // Returns the tail element of the list 51 | listOfInts.last(); 52 | ``` 53 | 54 | ### Iteration 55 | 56 | The list defines an iterator, and its elements can therefore be iterated over. 57 | 58 | ```haxe 59 | for (item in listOfInts) { 60 | // do something 61 | } 62 | ``` 63 | 64 | ### Operations 65 | 66 | #### Filter 67 | 68 | List elements can be filtered into a new list via a filtering function. Every list element for which the filtering function returns `true` is added to a new list. 69 | 70 | ```haxe 71 | var listOfEvenInts = listOfInts.filter(function (e) return e % 2 == 0); 72 | ``` 73 | 74 | #### Map 75 | 76 | List elements can be mapped to a new list of elements via a mapping function. The mapping is bijective, and every element from the inital list will have its mapping in the new list. 77 | 78 | ```haxe 79 | var listOfIntsAsStrings = listOfInts.map(function (e) return Std.string(e)); 80 | ``` 81 | 82 | ### Displaying list contents 83 | 84 | Lists can be prepared for printing by joining the elements together with a separator character, or by using the string representation of the list structure. 85 | 86 | ```haxe 87 | // Returns a string of list elements concatenated by separator string 88 | var withSeparator : String = listOfInts.join(" / "); 89 | // Returns a string representation of the list structure 90 | var asStructure : String = listOfInts.toString(); 91 | ``` 92 | 93 | > [List API documentation](http://api.haxe.org/List.html) 94 | > 95 | > [List manual entry](http://haxe.org/manual/std-List.html) 96 | > 97 | > Author: [Domagoj Štrekelj](https://github.com/dstrekelj) 98 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/loading-external-files.md: -------------------------------------------------------------------------------- 1 | # Loading a file from web 2 | 3 | This example uses `haxe.Http` to load external json file using and demonstrates how to handle the result. 4 | 5 | ## Loading a json file 6 | 7 | The following example loads your IP-address using a free-to-use web API. The service returns JSON formatted string `data`. 8 | This string is parsed to an object `result` using the `haxe.Json.parse` function. After this, we trace the IP-address `result.ip`. 9 | 10 | ```haxe 11 | var http = new haxe.Http("https://api6.ipify.org?format=json"); 12 | 13 | http.onData = function (data:String) { 14 | var result = haxe.Json.parse(data); 15 | trace('Your IP-address: ${result.ip}'); 16 | } 17 | 18 | http.onError = function (error) { 19 | trace('error: $error'); 20 | } 21 | 22 | http.request(); 23 | ``` 24 | 25 | > **More on this topic: ** 26 | > 27 | > * [haxe.Http API documentation](http://api.haxe.org/haxe/Http.html) 28 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/numbers-floats-ints.md: -------------------------------------------------------------------------------- 1 | [tags]: / "math" 2 | 3 | # Using numbers 4 | 5 | Define integers and floats: 6 | ```haxe 7 | var a:Float = 34; // Float 8 | var b:Int = 34; // Int 9 | var c = 34.00; // Float 10 | var d = 34; // Int 11 | ``` 12 | 13 | Calculating numbers using [arithmetic operators](https://haxe.org/manual/types-numeric-operators.html): 14 | ```haxe 15 | var a = 10; 16 | var b = 20; 17 | var c = (a + (2 * b)) / 5; 18 | trace(c); // 10 (Float) 19 | ``` 20 | 21 | Haxe interprets numeric constants as hexadecimal if they are preceded by `0x`: 22 | ```haxe 23 | var value = 0xFF; // 255 (Int) 24 | ``` 25 | 26 | Extra large or small numbers can be written with scientific exponent notation: 27 | ```haxe 28 | var x = 123e5; // 12300000 29 | var y = 123e-5; // 0.00123 30 | ``` 31 | 32 | Floating point arithmetic is not always 100% accurate 33 | ```haxe 34 | var value = 0.1 + 0.2; // 0.30000000000000004 35 | ``` 36 | 37 | Creating random numbers: 38 | ```haxe 39 | Std.random(10); // a random Int between 0 (included) and 10 (excluded) 40 | Math.random(); // a random Float between 0.0 (included) and 1.0 (excluded) 41 | ``` 42 | 43 | Defines infinity. Value equals [infinity](http://api.haxe.org/Math.html#POSITIVE_INFINITY): 44 | ```haxe 45 | var value = 1 / 0; // infinity 46 | 47 | trace((1 / 0) == Math.POSITIVE_INFINITY); // true 48 | trace((-1 / 0) == Math.NEGATIVE_INFINITY); // true 49 | ``` 50 | 51 | Defines and check to [NaN](http://api.haxe.org/Math.html#NaN) (Not A Number). 52 | ```haxe 53 | var value = Math.sqrt(-1); // NaN 54 | trace(Math.isNaN(value)); // true 55 | ``` 56 | 57 | ## Parsing numbers 58 | 59 | Parsing [String to Int](http://api.haxe.org/Std.html#parseInt): 60 | ```haxe 61 | Std.parseInt("3"); // 3 62 | Std.parseInt("3.5"); // 3 63 | Std.parseInt("3 kilo"); // 3 64 | Std.parseInt("kilo: 3.5"); // null 65 | ``` 66 | 67 | Parsing [String to Float](http://api.haxe.org/Std.html#parseFloat): 68 | ```haxe 69 | Std.parseFloat("3"); // 3.0 70 | Std.parseFloat("3.5"); // 3.5 71 | Std.parseFloat("3.5 kilo"); // 3.5 72 | Std.parseFloat("kilo: 3.5"); // Math.NaN 73 | ``` 74 | 75 | Convert [Float to Int](http://api.haxe.org/Std.html#int): 76 | ```haxe 77 | var value:Float = 3.3; 78 | Std.int(value); // 3 79 | Math.floor(value); // 3 80 | Math.round(value); // 3 81 | Math.ceil(value); // 4 82 | ``` 83 | 84 | Convert [numbers to string](http://api.haxe.org/Std.html#string): 85 | ```haxe 86 | var myInt = 10; 87 | var myFloat = 10.5; 88 | Std.string(myInt); // "10" 89 | Std.string(myFloat); // "10.5" 90 | ``` 91 | ## Math 92 | 93 | Calculating degrees to radians and radians to degrees: 94 | ```haxe 95 | var radians = Math.PI * 2; 96 | var degrees = radians * 180 / Math.PI; 97 | var radians = degrees * Math.PI / 180; 98 | ``` 99 | 100 | Using sinus and cosinus to set the position at a distance from the given angle: 101 | ```haxe 102 | var angle = Math.PI; 103 | var distance = 100; 104 | var x = Math.cos(angle) * distance; 105 | var y = Math.sin(angle) * distance; 106 | ``` 107 | 108 | Calculating the angle of two points: 109 | ```haxe 110 | var point1 = {x: 350, y: 0} 111 | var point2 = {x: 350, y: 150} 112 | 113 | var dx = point2.x - point1.x; 114 | var dy = point2.y - point1.y; 115 | var angle = Math.atan2(dy, dx); 116 | trace(angle); // PI/2 117 | ``` 118 | 119 | > **API documentation** 120 | > 121 | > * [Int](http://api.haxe.org/Int.html) and [Float](http://api.haxe.org/Float.html) API documentation 122 | > * [Math](http://api.haxe.org/Math.html) API documentation 123 | > 124 | > **Manual** 125 | > 126 | > * [Basic types in Haxe](https://haxe.org/manual/types-basic-types.html) 127 | > * [Math in Standard Library](http://haxe.org/manual/std-math.html) 128 | > * [Arithmetic operators](https://haxe.org/manual/types-numeric-operators.html) 129 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/reflection-method-call.md: -------------------------------------------------------------------------------- 1 | [tags]: / "reflection,dead-code-elimination" 2 | 3 | # Invoke object method by string 4 | 5 | To invoke method by it's name you will need to use Reflection API. 6 | 7 | Snippet bellow shows how to use a string as a object method identifier to invoke it. 8 | 9 | ## Usage 10 | ```haxe 11 | class MyClass { 12 | public function new () {} 13 | @:keep public function printName() { 14 | trace("MyClass printName is invoked"); 15 | } 16 | } 17 | 18 | class Main { 19 | static function main() { 20 | var myObject = new MyClass(); 21 | var fn = Reflect.field(myObject, "printName"); 22 | Reflect.callMethod(myObject, fn, []); 23 | } 24 | } 25 | ``` 26 | 27 | Haxe has [dead code elimination](https://haxe.org/manual/cr-dce.html) (DCE) which remove from generated code classes, methods and variables not used from directly. In the example, method `printName()` is used by reflection and has no referencies anywhere else, so it will be removed in compile time. To keep `printName()` method after compilation switch off DCE or mark this method by `@:keep` [metadata](https://haxe.org/manual/cr-metadata.html). 28 | 29 | **Tip:** Haxe has a wonderful type system, use this as much as possible. Reflection can be a powerful tool, but it's important to know it can be error prone, since the compiler can never validate if what you're doing makes sense and is also harder to optimize. 30 | 31 | > **More information:** 32 | > 33 | > * [Reflection documentation](http://haxe.org/manual/std-reflection.html) 34 | > * [Reflect API Documentation](http://api.haxe.org/Reflect.html) 35 | > 36 | > Author: [Alexey](https://github.com/alexey-kolonitsky) 37 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/stdin-stdout-stderr.md: -------------------------------------------------------------------------------- 1 | [tags]: / "io" 2 | 3 | # stdin, stdout, stderr 4 | 5 | Reading from stdin and writing to stdout and stderr. 6 | 7 | 8 | 9 | ## stdin 10 | 11 | You can read from stdin interactively from the command 12 | line, or can pipe input to your Haxe program like you would 13 | with any other command line utility. 14 | 15 | To read in one line: 16 | 17 | ```haxe 18 | Sys.println("Enter your name:"); 19 | var ans = Sys.stdin().readLine(); 20 | // `ans` is just the text --- no newline 21 | ``` 22 | 23 | If you want to iteratively read in lines: 24 | 25 | ```haxe 26 | var line:String; 27 | var lines:Array = []; 28 | try { 29 | while (true) { 30 | line = Sys.stdin().readLine(); 31 | lines.push(line); 32 | } 33 | } 34 | catch (e:haxe.io.Eof) { 35 | trace("done!"); 36 | } 37 | ``` 38 | 39 | You could also read in all the input in one shot: 40 | 41 | ```haxe 42 | var content = Sys.stdin().readAll().toString(); 43 | ``` 44 | 45 | 46 | 47 | ## stdout 48 | 49 | There's a few ways to write to stdout: 50 | 51 | ```haxe 52 | trace("Hello, trace!"); 53 | Sys.println("Hello, println!"); 54 | Sys.print("Hello, print!"); // no added newline 55 | ``` 56 | 57 | You can also use `Sys.stdout()` to grab the stdout object and call its write 58 | methods (see [haxe.io.Output](https://api.haxe.org/haxe/io/Output.html)). 59 | 60 | 61 | 62 | ## stderr 63 | 64 | To write to stderr: 65 | 66 | ```haxe 67 | Sys.stderr().writeString("Yow!\n"); 68 | ``` 69 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/string-variable-reflection.md: -------------------------------------------------------------------------------- 1 | [tags]: / "reflection,dead-code-elimination" 2 | 3 | # Access a field by string 4 | 5 | This snippet shows how to use a string as a variable identifier using reflection. 6 | 7 | ## Implementation 8 | ```haxe 9 | class MyObject { 10 | @:keep var myField:String = "This is Reflection Test"; 11 | 12 | public function new() { } 13 | } 14 | ``` 15 | 16 | ## Usage 17 | ```haxe 18 | class Main { 19 | static public function main():Void { 20 | var myObject = new MyObject(); 21 | 22 | var fieldName = "myField"; 23 | var myField:String = Reflect.field(myObject, fieldName); 24 | trace(myField); // "This is Reflection Test"; 25 | } 26 | } 27 | ``` 28 | 29 | Haxe has [dead code elimination](https://haxe.org/manual/cr-dce.html) (DCE). This compiler feature identifies and eliminates all unused code during compilation. In the example above, the variable `myField` is referenced only through reflection and not directly. Because of that, it will be marked for removal by DCE. To keep it from being eliminated, we need to add `@:keep` [compiler metadata](https://haxe.org/manual/cr-metadata.html) to the `myField` field. Note that `@:keep` could also be added to classes and functions. If you want to keep the class and its sub-classes, use `@:keepSub`. 30 | 31 | **Tip:** Haxe has a wonderful type system, use this as much as possible. Reflection can be a powerful tool, but it's important to know it can be error prone, since the compiler can never validate if what you're doing makes sense and is also harder to optimize. 32 | 33 | > **More information:** 34 | > 35 | > * [Reflection documentation](http://haxe.org/manual/std-reflection.html) 36 | > * [Reflect API Documentation](http://api.haxe.org/Reflect.html) 37 | > 38 | > Author: [MJ](https://github.com/flashultra) 39 | -------------------------------------------------------------------------------- /assets/content/cookbook/Beginner/using-filesystem.md: -------------------------------------------------------------------------------- 1 | [tags]: / "filesystem" 2 | 3 | # Using the file system 4 | 5 | Using file system in Haxe is made easy because of the [`sys` package](http://api.haxe.org/sys/). These are the Haxe targets that can directly access the filesystem: 6 | 7 | Name | Access to filesystem 8 | --- | --- | 9 | C++ | Yes 10 | C# | Yes 11 | PHP | Yes 12 | Java | Yes 13 | Python | Yes 14 | Lua | Yes 15 | Macro | Yes 16 | HL (HashLink) | Yes 17 | Neko | Yes 18 | JavaScript | No 19 | NodeJS (using [hxnodejs](http://lib.haxe.org/p/hxnodejs/)) | Yes 20 | ActionScript 3 | No 21 | Flash | No 22 | 23 | > Note that in macros you can access file system. 24 | 25 | ### Check if FileSystem is available 26 | 27 | You can safely access the `sys`-package if you wrap the code with [conditional compilation](http://haxe.org/manual/lf-condition-compilation.html): 28 | 29 | ```haxe 30 | #if sys 31 | trace("file system can be accessed"); 32 | #end 33 | ``` 34 | Otherwise you will get the error: 35 | _"You cannot access the sys package while targeting js (for sys.FileSystem)"_. 36 | 37 | ### Read content of a file 38 | 39 | This example reads a text file: 40 | ```haxe 41 | var content:String = sys.io.File.getContent('my_folder/my_file.txt'); 42 | trace(content); 43 | ``` 44 | 45 | ### Save file to disk 46 | 47 | This example writes an object `person` to a json file: 48 | ```haxe 49 | var user = {name:"Mark", age:31}; 50 | var content:String = haxe.Json.stringify(user); 51 | sys.io.File.saveContent('my_folder/my_file.json',content); 52 | ``` 53 | > Api documentation: 54 | 55 | ### Cross platform paths 56 | 57 | Dealing with paths, directories, slashes, extensions on multiple platforms or OSes can be slightly awkward. Haxe provides the `haxe.io.Path` class which supports the common path formats. 58 | 59 | Extracting info from a path: 60 | ```haxe 61 | var location = "path/to/file.txt"; 62 | var path = new haxe.io.Path(location); 63 | trace(path.dir); // path/to 64 | trace(path.file); // file 65 | trace(path.ext); // txt 66 | ``` 67 | 68 | Combining info into a new path: 69 | ```haxe 70 | var directory = "path/to/"; 71 | var file = "./file.txt"; 72 | trace(haxe.io.Path.join([directory, file])); // path/to/file.txt 73 | ``` 74 | 75 | > Api documentation: 76 | 77 | ### Recursive loop through all directories / files 78 | ```haxe 79 | function recursiveLoop(directory:String = "path/to/") { 80 | if (sys.FileSystem.exists(directory)) { 81 | trace("directory found: " + directory); 82 | for (file in sys.FileSystem.readDirectory(directory)) { 83 | var path = haxe.io.Path.join([directory, file]); 84 | if (!sys.FileSystem.isDirectory(path)) { 85 | trace("file found: " + path); 86 | // do something with file 87 | } else { 88 | var directory = haxe.io.Path.addTrailingSlash(path); 89 | trace("directory found: " + directory); 90 | recursiveLoop(directory); 91 | } 92 | } 93 | } else { 94 | trace('"$directory" does not exists'); 95 | } 96 | } 97 | ``` 98 | > Api documentation: 99 | 100 | ### Checking file attributes 101 | 102 | ```haxe 103 | var stat:sys.FileStat = sys.FileSystem.stat("myFile.txt"); 104 | trace("Last access time: " + stat.atime); 105 | trace("Last modification time: " + stat.mtime); 106 | trace("Last status change time: " + stat.ctime); 107 | trace("The user id: " + stat.uid); 108 | trace("File size: " + stat.size); 109 | ``` 110 | > Api documentation: 111 | 112 | -------------------------------------------------------------------------------- /assets/content/cookbook/Compilation/compiling-libraries-without-main-class.md: -------------------------------------------------------------------------------- 1 | [tags]: / "libraries,javascript,dead-code-elimination" 2 | 3 | # Compiling libraries without main class 4 | 5 | In most cases, you want a Main class with a static main function as entry point to start your program. 6 | However, there are cases where there is no need for this - for example if you are writing a library 7 | that other programs will be using. 8 | 9 | Here's how you can compile your code without having a Main class and a static entry main function: 10 | 11 | ### Create your library class(es) 12 | 13 | Let's create an library class called **BarLib** placed in a **foo** package: 14 | ```haxe 15 | package foo; 16 | 17 | class BarLib { 18 | public function new() {} 19 | 20 | public function test() { 21 | return "Hello from BarLib!"; 22 | } 23 | } 24 | ``` 25 | 26 | ### Create a build.hxml without -main 27 | Now, we can create a **build.hxml** *without* specifying the Main class `-main Main` the way we usually do. 28 | Instead we add the classes that we want to include, one per row, using the full class name including package path: 29 | 30 | ```hxml 31 | -cp src 32 | 33 | # add the class(es) that you want to include the following way, one per line 34 | foo.BarLib # <- Include the class foo.BarLib 35 | 36 | -js bin/lib.js # <- Compile the library, in this case to JavaScript 37 | ``` 38 | 39 | ### Compile the library 40 | 41 | Now, you can compile your library with `> haxe build.hxml`. 42 | 43 | If you compile to JavaScript, the result will be the following: 44 | ```javascript 45 | (function (console) { "use strict"; 46 | var foo_BarLib = function() { 47 | }; 48 | foo_BarLib.prototype = { 49 | test: function() { 50 | return "Hello from BarLib"; 51 | } 52 | }; 53 | })(typeof console != "undefined" ? console : {log:function(){}}); 54 | ``` 55 | 56 | You can include more than one package/class if needed in the build.hxml: 57 | ```hxml 58 | -cp src 59 | 60 | foo.BarLib # include class foo.BarLib 61 | buz.qux.Norf # include class buz.qux.Norf 62 | Config # include class Config 63 | ... 64 | 65 | ``` 66 | 67 | ### Caution with dead code elimination 68 | 69 | [Dead code elimination](http://haxe.org/manual/cr-dce.html) is a great Haxe compiler feature that lets the compiler remove code that isn't used by the program. In this case, when dealing with libraries, this might cause unwanted results: If you compile the examples above with full dead code elimination (using the compilation flag `-dce full`), all your library code will be stripped away! To avoid a class being stripped away like this, use the metadata `@:keep` before the class definition: 70 | 71 | ```haxe 72 | package foo; 73 | 74 | @:keep // <-- Avoid dead code elimination stripping this class away 75 | class BarLib { 76 | public function new() {} 77 | 78 | public function test() { 79 | return "Hello from BarLib!"; 80 | } 81 | } 82 | ``` 83 | **Note:** This works (as you would expect) the same for all Haxe targets. 84 | 85 | ### Exposing Haxe classes for JavaScript 86 | 87 | If you are writing libraries for JavaScript, you might want to use the `@:expose` metadata to make your code available in the global namespace. You can [read more about that in the Haxe manual](http://haxe.org/manual/target-javascript-expose.html) or in [this snippet](category/other/using-haxe-classes-in-javascript.html). 88 | 89 | > Author: [Jonas Nyström](https://github.com/cambiata) 90 | -------------------------------------------------------------------------------- /assets/content/cookbook/Compilation/target-specific-modules-diff-by-filename.md: -------------------------------------------------------------------------------- 1 | [tags]: / "haxe4,libraries,compiler" 2 | 3 | Please note that the file naming method described here requires for **Haxe 4**. 4 | 5 | # Writing target-specific modules differentiated by filename 6 | 7 | The standard way of naming module files in haxe is `.hx` - the module name spelled with first letter capital, and ending with `.hx` as extension. Let's say we have the following class called `Example`: 8 | 9 | ```haxe 10 | // Example.hx 11 | class Example { 12 | public function new() { 13 | trace('Hello from Example!'); 14 | } 15 | } 16 | ``` 17 | 18 | If we want to write target-specific code, the most common way is using [conditional compilation](https://haxe.org/manual/lf-condition-compilation.html): 19 | 20 | ```haxe 21 | // Example.hx 22 | class Example { 23 | public function new() { 24 | #if (js) 25 | // when compiled and run for JavaScript: 26 | trace('Hello from JavaScript-specific example!'); 27 | #else 28 | // when compiled and run on any other target: 29 | trace('Hello from Example!'); 30 | #end 31 | } 32 | } 33 | ``` 34 | 35 | # Using `..hx` as filename 36 | 37 | If using Haxe 4, we can as an alternative put the target specific code in a separate file, using the `..hx` naming convention. 38 | 39 | A JavaScript-specific file for our `Example` class would then be named `Example.js.hx`: 40 | ```haxe 41 | // Example.js.hx <-- Note .js. in the filename! 42 | class Example { 43 | public function new() { 44 | trace('Hello from JAVASCRIPT-SPECIFIC Example!'); 45 | } 46 | } 47 | ``` 48 | Whenever compiled for JavaScript, the compiler first looks for `Example.js.hx` and - if present - uses that. If a target specific file is not found, it looks for `Example.hx`. 49 | 50 | Please note that the `` name used should be the [target define](https://haxe.org/manual/lf-target-defines.html) for each target respectively: `js` for JavaScript, `cpp` for C++, `neko` for Neko target etc. 51 | 52 | > See 53 | 54 | > Author: [Jonas Nyström](https://github.com/cambiata) 55 | -------------------------------------------------------------------------------- /assets/content/cookbook/Data structures/grid-iterator.md: -------------------------------------------------------------------------------- 1 | [tags]: / "iterator" 2 | 3 | # Grid iterator 4 | 5 | Often, in games or UI, you might want to create a grid. 6 | 7 | A [custom iterators](http://haxe.org/manual/lf-iterators.html) can provide such functionality. 8 | 9 | ```haxe 10 | class GridIterator { 11 | var gridWidth:Int = 0; 12 | var gridHeight:Int = 0; 13 | var i:Int = 0; 14 | 15 | public inline function new(gridWidth:Int, gridHeight:Int) { 16 | this.gridWidth = gridWidth; 17 | this.gridHeight = gridHeight; 18 | } 19 | 20 | public inline function hasNext() { 21 | return i < gridWidth * gridHeight; 22 | } 23 | 24 | public inline function next() { 25 | return new GridIteratorObject(i++, gridWidth); 26 | } 27 | } 28 | 29 | class GridIteratorObject { 30 | public var index(default, null):Int; 31 | public var x(default, null):Int; 32 | public var y(default, null):Int; 33 | 34 | public inline function new(index:Int, gridWidth:Int) { 35 | this.index = index; 36 | this.x = index % gridWidth; 37 | this.y = Std.int(index / gridWidth); 38 | } 39 | } 40 | ``` 41 | 42 | ## Usage 43 | 44 | The following example uses the GridIterator and displays a grid of 6x5 using dark colored divs. 45 | Because of the used `js` package/features, it only compiles in the JavaScript target. 46 | 47 | [tryhaxe](http://try.haxe.org/embed/F80dA) 48 | 49 | --- 50 | 51 | # Grid Key value iterator 52 | 53 | In Haxe 4, as alternative, you can also use a [key value iterator](https://haxe.org/manual/expression-for.html#key-value-iteration). 54 | 55 | ```haxe 56 | class GridIterator { 57 | var gridWidth:Int = 0; 58 | var gridHeight:Int = 0; 59 | var i:Int = -1; 60 | 61 | public inline function new(gridWidth:Int, gridHeight:Int) { 62 | this.gridWidth = gridWidth; 63 | this.gridHeight = gridHeight; 64 | } 65 | 66 | public inline function hasNext() { 67 | return i < gridWidth * gridHeight; 68 | } 69 | 70 | public inline function next() { 71 | i++; 72 | return { key: i, value: { x: i % gridWidth, y: Std.int(i / gridWidth) } } 73 | } 74 | } 75 | ``` 76 | 77 | ## Usage 78 | 79 | ```haxe 80 | for (idx => pos in new GridIterator(6, 5)) { 81 | trace(idx, pos.x, pos.y); 82 | } 83 | ``` 84 | 85 | 86 | > Learn more about iterators here: 87 | -------------------------------------------------------------------------------- /assets/content/cookbook/Data structures/reverse-iterator.md: -------------------------------------------------------------------------------- 1 | [tags]: / "iterator" 2 | 3 | # Reverse iterator 4 | 5 | Haxe has a special [range operator](http://haxe.org/manual/expression-for.html) `for(i in 0...5)` to iterate forward. 6 | Because it requires `min...max`, you cannot do `for(i in 5...0)`, thus you cannot iterate backwards using this syntax. 7 | 8 | You could use a [while loop](http://haxe.org/manual/expression-while.html) for this: 9 | 10 | ```haxe 11 | var total = 5; 12 | var i = total; 13 | while(i >= 0) { 14 | trace(i); 15 | i --; 16 | } 17 | // 5 18 | // 4 19 | // 3 20 | // 2 21 | // 1 22 | // 0 23 | ``` 24 | 25 | This is not always optimal since you need variables outside the loop. 26 | 27 | You can also create [custom iterators](http://haxe.org/manual/lf-iterators.html) which provide such functionality. 28 | 29 | ```haxe 30 | class ReverseIterator { 31 | var end:Int; 32 | var i:Int; 33 | 34 | public inline function new(start:Int, end:Int) { 35 | this.i = start; 36 | this.end = end; 37 | } 38 | 39 | public inline function hasNext() return i >= end; 40 | public inline function next() return i--; 41 | } 42 | ``` 43 | 44 | ## Usage 45 | 46 | Loop from 5 to 0. 47 | 48 | [tryhaxe](http://try.haxe.org/embed/ae6ef) 49 | 50 | 51 | # Reverse array iterator 52 | 53 | Here is an example of a reverse iterator for arrays, which gives you value. 54 | 55 | ```haxe 56 | class ReverseArrayIterator { 57 | final arr:Array; 58 | var i:Int; 59 | 60 | public inline function new(arr:Array) { 61 | this.arr = arr; 62 | this.i = this.arr.length - 1; 63 | } 64 | 65 | public inline function hasNext() return i > -1; 66 | public inline function next() { 67 | return arr[i--]; 68 | } 69 | 70 | public static inline function reversedValues(arr:Array) { 71 | return new ReverseArrayIterator(arr); 72 | } 73 | } 74 | ``` 75 | 76 | ## Usage 77 | 78 | If you add `using ReverseArrayIterator` to your class (or to global imports.hx), you can use `for (item in array.reversedValues())` in your code. 79 | But if you don't want to do that you can always do `for (item in new ReverseArrayIterator(array))`. 80 | 81 | ```haxe 82 | using ReverseArrayIterator; 83 | 84 | class Test { 85 | public function new() { 86 | var fruits = ["apple", "banana", "pear"]; 87 | for (fruit in fruits.reversedValues()) { 88 | trace(fruit); 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | # Reverse key-value array iterator 95 | 96 | Key value iterators are great because it gives you both the key and the value while iterating. 97 | Here is an example of a reverse iterator for arrays, which gives you both index and value. 98 | 99 | ```haxe 100 | class ReverseArrayKeyValueIterator { 101 | final arr:Array; 102 | var i:Int; 103 | 104 | public inline function new(arr:Array) { 105 | this.arr = arr; 106 | this.i = this.arr.length - 1; 107 | } 108 | 109 | public inline function hasNext() return i > -1; 110 | public inline function next() { 111 | return {value: arr[i], key: i--}; 112 | } 113 | 114 | public static inline function reversedKeyValues(arr:Array) { 115 | return new ReverseArrayKeyValueIterator(arr); 116 | } 117 | } 118 | ``` 119 | 120 | ## Usage 121 | 122 | If you add `using ReverseArrayKeyValueIterator` to your class (or to global imports.hx), you can use `for (idx => item in array.reversedKeyValues())` in your code. 123 | But if you don't want to do that you can always do `for (idx => fruit in new ReverseArrayKeyValueIterator(fruits))`. 124 | 125 | ```haxe 126 | using ReverseArrayKeyValueIterator; 127 | 128 | class Test { 129 | public function new() { 130 | var fruits = ["apple", "banana", "pear"]; 131 | for (idx => fruit in fruits.reversedKeyValues()) { 132 | trace(idx, fruit); 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | > Learn more about iterators here: 139 | -------------------------------------------------------------------------------- /assets/content/cookbook/Data structures/ring-array.md: -------------------------------------------------------------------------------- 1 | [tags]: / "collections" 2 | 3 | # A fixed ring array 4 | 5 | A fixed ring array is especially useful when you need a hard upper bound for how much data can be in the queue. 6 | 7 | ```haxe 8 | // reference https://github.com/torvalds/linux/blob/master/include/linux/circ_buf.h 9 | @:generic 10 | class Ring { 11 | 12 | var head: Int; 13 | var tail: Int; 14 | var cap: Int; 15 | var a: haxe.ds.Vector; 16 | 17 | public function new(len) { 18 | if (len < 4) { 19 | len = 4; 20 | } else if (len & len - 1 > 0) { 21 | len--; 22 | len |= len >> 1; 23 | len |= len >> 2; 24 | len |= len >> 4; 25 | len |= len >> 8; 26 | len |= len >> 16; 27 | len++; // power of 2 28 | } 29 | cap = len - 1; // only "len-1" available spaces 30 | a = new haxe.ds.Vector(len); 31 | reset(); 32 | } 33 | 34 | public function reset() { 35 | head = 0; 36 | tail = 0; 37 | } 38 | 39 | public function push(v: T) { 40 | if (space() == 0) tail = (tail + 1) & cap; 41 | a[head] = v; 42 | head = (head + 1) & cap; 43 | } 44 | 45 | public function shift(): Null { 46 | var ret:Null = null; 47 | if (count() > 0) { 48 | ret = a[tail]; 49 | tail = (tail + 1) & cap; 50 | } 51 | return ret; 52 | } 53 | 54 | public function pop(): Null { 55 | var ret:Null = null; 56 | if (count() > 0) { 57 | head = (head - 1) & cap; 58 | ret = a[head]; 59 | } 60 | return ret; 61 | } 62 | 63 | public function unshift(v: T) { 64 | if (space() == 0) head = (head - 1) & cap; 65 | tail = (tail - 1) & cap; 66 | a[tail] = v; 67 | } 68 | 69 | public function toString() { 70 | return '[head: $head, tail: $tail, capacity: $cap]'; 71 | } 72 | 73 | public inline function count() return (head - tail) & cap; 74 | 75 | public inline function space() return (tail - head - 1) & cap; 76 | } 77 | ``` 78 | 79 | ## Usage 80 | 81 | It's easy to implement `undo/redo` operations. 82 | 83 | ```haxe 84 | @:generic class History { 85 | var re: Ring; 86 | var un: Ring; 87 | public function new(len){ 88 | re = new Ring(len); 89 | un = new Ring(len); 90 | } 91 | public function redo(): Null { 92 | var r = re.pop(); 93 | if (r != null) un.push(r); 94 | return r; 95 | } 96 | public function undo(): Null { 97 | var u = un.pop(); 98 | if (u != null) re.push(u); 99 | return u; 100 | } 101 | public function add(v: T) { 102 | un.push(v); 103 | re.reset(); 104 | } 105 | } 106 | 107 | class Main { 108 | static function main() { 109 | var h = new History(4); 110 | h.add(1); 111 | h.add(2); 112 | h.add(3); 113 | h.add(4); // overrides the 1 114 | h.add(5); 115 | eq(h.undo() == 5); 116 | eq(h.undo() == 4); 117 | eq(h.undo() == 3); 118 | eq(h.undo() == null); 119 | eq(h.redo() == 3); 120 | eq(h.redo() == 4); 121 | eq(h.redo() == 5); 122 | eq(h.redo() == null); 123 | trace("done!"); 124 | } 125 | static function eq(t: Bool, ?pos: haxe.PosInfos) { 126 | if (!t) throw '>>>>>> lineNumber: ${pos.lineNumber}'; 127 | } 128 | } 129 | ``` 130 | 131 | > Author: [R32](https://github.com/r32) 132 | -------------------------------------------------------------------------------- /assets/content/cookbook/Data structures/sort-array.md: -------------------------------------------------------------------------------- 1 | [tags]: / "array" 2 | 3 | # Sorting arrays 4 | 5 | ### Sort an array of values 6 | 7 | You can easily sort an array using the Array's `sort()` function. 8 | ```haxe 9 | var myArray = [1,5,3,7,6,2,4]; 10 | myArray.sort((a, b) -> a - b); 11 | trace(myArray); // 1,2,3,4,5,6,7 12 | ``` 13 | 14 | ### Sort using Reflect.compare 15 | 16 | Reflect.compare works like this: 17 | > If a is less than b, the result is negative. If b is less than a, the result is positive. If a and b are equal, the result is 0. 18 | 19 | It handles multiple types, so take in account `Reflect.compare` carries a bit of overhead. 20 | 21 | The previous example can be shortened if you are using `Reflect.compare`. 22 | ```haxe 23 | var myArray = [1,5,3,7,6,2,4]; 24 | myArray.sort(Reflect.compare); 25 | trace(myArray); // 1,2,3,4,5,6,7 26 | ``` 27 | 28 | These sortings aren't [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) on all targets. If you need to retain the order of equal elements you should use `haxe.ds.ArraySort` 29 | 30 | ### Using haxe.ds.ArraySort 31 | 32 | ```haxe 33 | var myArray = [1,5,3,7,6,2,4]; 34 | 35 | haxe.ds.ArraySort.sort(myArray, function(a, b):Int { 36 | return a - b; 37 | }); 38 | ``` 39 | > Learn more about `haxe.ds.ArraySort`: 40 | 41 | 42 | ## Usage 43 | 44 | ### Simple array with ints 45 | [tryhaxe](http://try.haxe.org/embed/D7880) 46 | 47 | ### Array with objects 48 | [tryhaxe](http://try.haxe.org/embed/76f24) 49 | 50 | Notice how the second array, when using `haxe.ds.ArraySort`, keeps the order of the elements with equal `i`. The sorting is stable. 51 | 52 | -------------------------------------------------------------------------------- /assets/content/cookbook/Data structures/step-iterator.md: -------------------------------------------------------------------------------- 1 | [tags]: / "iterator" 2 | 3 | # Stepped iterator 4 | 5 | Haxe has a special [range operator](http://haxe.org/manual/expression-for.html) `for(i in 0...5)` to iterate forward. 6 | This does not allow to modify `i` in place, thus you cannot make it iterate in steps. 7 | 8 | You could use a [while loop](http://haxe.org/manual/expression-while.html) for this: 9 | 10 | ```haxe 11 | var total = 10; 12 | var step = 2; 13 | var i = 0; 14 | while(i < total) { 15 | trace(i); 16 | i += step; 17 | } 18 | // 0 19 | // 2 20 | // 4 21 | // 6 22 | // 8 23 | ``` 24 | 25 | This is not always optimal since you need variables outside the loop. 26 | 27 | You can also create [custom iterators](http://haxe.org/manual/lf-iterators.html) which provide such functionality. 28 | 29 | ```haxe 30 | class StepIterator { 31 | var end:Int; 32 | var step:Int; 33 | var index:Int; 34 | 35 | public inline function new(start:Int, end:Int, step:Int) { 36 | this.index = start; 37 | this.end = end; 38 | this.step = step; 39 | } 40 | 41 | public inline function hasNext() return index < end; 42 | public inline function next() return (index += step) - step; 43 | } 44 | ``` 45 | 46 | ## Usage 47 | 48 | Loop in steps of two from 0 to 10. 49 | 50 | [tryhaxe](http://try.haxe.org/embed/9F186) 51 | 52 | > Learn more about iterators here: -------------------------------------------------------------------------------- /assets/content/cookbook/Design-patterns/factory.md: -------------------------------------------------------------------------------- 1 | # Factory 2 | 3 | This is a basic example of the [Factory](https://en.wikipedia.org/wiki/Factory_pattern) design pattern in Haxe. 4 | 5 | ```haxe 6 | class Item { 7 | // factory method 8 | public static function create():Item { 9 | return new Item(); 10 | } 11 | 12 | public var value:T; 13 | 14 | private function new () {} // private constructor 15 | } 16 | ``` 17 | 18 | ### Usage 19 | 20 | ```haxe 21 | class Main { 22 | public static function main () { 23 | // This works because of type-inference, the 24 | // compiler knows that the return type must be of type `Item`s 25 | var myItem:Item = Item.create(); 26 | myItem.value = 10; // works 27 | trace(myItem.value); // 10 28 | 29 | // complex type example 30 | var myItem2:Item> = Item.create(); 31 | myItem2.value = [1, 2, 3]; // works too 32 | trace(myItem2.value); // [1,2,3] 33 | } 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /assets/content/cookbook/Design-patterns/lazy-initialization.md: -------------------------------------------------------------------------------- 1 | # Lazy initialization 2 | 3 | This is a basic example of the [Lazy initialization](https://en.wikipedia.org/wiki/Lazy_initialization) design pattern in Haxe. 4 | 5 | ```haxe 6 | class Fruit { 7 | private static var _instances = new Map(); 8 | 9 | public var name(default, null):String; 10 | 11 | public function new(name:String) { 12 | this.name = name; 13 | } 14 | 15 | public static function getFruitByName(name:String):Fruit { 16 | if (!_instances.exists(name)) { 17 | _instances.set(name, new Fruit(name)); 18 | } 19 | return _instances.get(name); 20 | } 21 | 22 | public static function printAllTypes() { 23 | trace([for(key in _instances.keys()) key]); 24 | } 25 | } 26 | ``` 27 | 28 | ### Usage 29 | 30 | ```haxe 31 | class Test { 32 | public static function main () { 33 | var banana = Fruit.getFruitByName("Banana"); 34 | var apple = Fruit.getFruitByName("Apple"); 35 | var banana2 = Fruit.getFruitByName("Banana"); 36 | 37 | trace(banana == banana2); // true. same banana 38 | 39 | Fruit.printAllTypes(); // ["Banana","Apple"] 40 | } 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /assets/content/cookbook/Design-patterns/method-chaining-fluent-interface.md: -------------------------------------------------------------------------------- 1 | # Method chaining / Fluent interface 2 | 3 | This is an example of the [Method chaining](https://en.wikipedia.org/wiki/Method_chaining) design pattern in Haxe. 4 | 5 | Method chaining is calling a method of an object that return the same type of the object multiple times. 6 | 7 | One example is jQuery: 8 | 9 | ``` 10 | $("p.neat").addClass("ohmy").show("slow"); 11 | ``` 12 | 13 | A class that let us do method chaining is having a fluent interface. 14 | 15 | Fluent interface isn’t very good to be used in a strongly typed OOP language without careful consideration. 16 | Why? For example, if we want to extend TweenLiteVars to have one more property called “awesome”, and use it: 17 | 18 | ```haxe 19 | var vars:MyTweenLiteVars = 20 | new MyTweenLiteVars() 21 | .prop("x", 300) //prop() returns TweenLiteVars, not MyTweenLiteVars 22 | .awesome(true) //compiler error, TweenLiteVars does not have awesome :( 23 | .autoAlpha(0) 24 | .onComplete(myFunction, [mc]); 25 | ``` 26 | 27 | Method chaining is broken, not so awesome. 28 | There is no elegant way to do it properly in several languages. 29 | 30 | In Haxe it can be done using generic types. All we have to do is to create a base class, for example: 31 | 32 | ```haxe 33 | class Component> { 34 | public function clone():This { 35 | return throw "Needs to be overrided"; 36 | } 37 | } 38 | 39 | class NormalComponent extends Component { 40 | public function new() { 41 | 42 | } 43 | 44 | override public function clone():NormalComponent { 45 | return new NormalComponent(); 46 | } 47 | } 48 | ``` 49 | 50 | People now can extend Component and have `clone()` properly typed as the subclass: 51 | 52 | ```haxe 53 | /* 54 | * When a SpecialComponent is cloned, there is some chance it gives birth to a unicorn(!). 55 | */ 56 | class SpecialComponent extends Component { 57 | public var hasHorn(default, null):Bool; 58 | 59 | public function new() { 60 | super(); 61 | hasHorn = false; 62 | } 63 | 64 | override public function clone():SpecialComponent { 65 | var newComponent = new SpecialComponent(); 66 | newComponent.hasHorn = Math.random() > 0.8; 67 | return newComponent; 68 | } 69 | } 70 | ``` 71 | 72 | Here is an example of using the above Component classes: 73 | 74 | ```haxe 75 | class Main { 76 | static function main() { 77 | var NormalComponent = new NormalComponent(); 78 | trace("A clone of NormalComponent is..." + Type.getClassName(Type.getClass(NormalComponent.clone()))); 79 | trace("What about a SpecialComponent? Let see..."); 80 | 81 | var SpecialComponent = new SpecialComponent(); 82 | while (true) { 83 | if (SpecialComponent.hasHorn) { 84 | trace("This SpecialComponent has a horn! It's a unicorn!"); 85 | break; 86 | } else { 87 | trace("This SpecialComponent looks like a normal one. Let's clone it..."); 88 | SpecialComponent = SpecialComponent.clone(); 89 | } 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | Sample output of above: 96 | 97 | ``` 98 | Main.hx:4: A clone of NormalComponent is...NormalComponent 99 | Main.hx:7: What about a SpecialComponent? Let see... 100 | Main.hx:15: This SpecialComponent looks like a normal one. Let's clone it... 101 | Main.hx:15: This SpecialComponent looks like a normal one. Let's clone it... 102 | Main.hx:12: This SpecialComponent has a horn! It's a unicorn! 103 | ``` 104 | 105 | But of course, if you want to extend SpecialComponent, it suffers the same problem, unless you turn SpecialComponent into a base abstract class too. 106 | 107 | ```haxe 108 | class SpecialComponent> extends Component { 109 | public var hasHorn(default, null):Bool; 110 | 111 | private function new() { 112 | super(); 113 | hasHorn = false; 114 | } 115 | 116 | override public function clone():This { 117 | return throw "needs to be overrided"; 118 | } 119 | } 120 | ``` 121 | 122 | > Source: 123 | 124 | > Author: [andyli](https://github.com/andyli) 125 | 126 | Happy chaining! -------------------------------------------------------------------------------- /assets/content/cookbook/Design-patterns/observer.md: -------------------------------------------------------------------------------- 1 | # Observer 2 | 3 | This is a basic example of the [Observer](https://en.wikipedia.org/wiki/Observer_pattern) design pattern in Haxe. The pattern makes use of an `Observer` interface and an `Observable` base class to notify objects when another object's property is changed so that they can react accordingly. 4 | 5 | ```haxe 6 | interface Observer { 7 | public function notified(sender:Observable, ?data:Any) : Void; 8 | } 9 | 10 | class Observable { 11 | private var observers:Array = []; 12 | public function new() { } 13 | 14 | private function notify(?data:T) { 15 | for(obs in observers) 16 | obs.notified(this, data); 17 | } 18 | 19 | public function addObserver(observer:Observer) { 20 | observers.push(observer); 21 | } 22 | } 23 | ``` 24 | 25 | ### Usage 26 | 27 | [tryhaxe](https://try.haxe.org/embed/786A5) 28 | 29 | ### Notes 30 | 31 | - Extra care has to be put into making sure that an observable cannot register the same observer twice. 32 | - The fact that `Observable` is a class can make it hard to use because Haxe does not allow for multiple inheritance. Instead, we used a static extension for convenient usage. 33 | 34 | > Author: [matrefeytontias](https://github.com/matrefeytontias) 35 | -------------------------------------------------------------------------------- /assets/content/cookbook/Design-patterns/singleton.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | This is a basic example of the [Singleton](https://en.wikipedia.org/wiki/Singleton_pattern) design pattern in Haxe. 4 | 5 | ```haxe 6 | class MySingleton { 7 | // read-only property 8 | public static final instance:MySingleton = new MySingleton(); 9 | 10 | private function new () {} // private constructor 11 | } 12 | ``` 13 | 14 | ### Usage 15 | 16 | ```haxe 17 | class Main { 18 | public static function main () { 19 | // this will be the only way to access the instance 20 | MySingleton.instance; 21 | 22 | // This will throw error "Cannot access private constructor" 23 | // new MySingleton(); 24 | } 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /assets/content/cookbook/Functional Programming/enum-gadt.md: -------------------------------------------------------------------------------- 1 | [tags]: / "enum,pattern-matching,functional-programming" 2 | 3 | # Enums as GADTs 4 | 5 | As [already established](http://code.haxe.org/category/beginner/enum-adt.html) Haxe enums are a form of algebraic data types. In fact, they may even serve as so called "*generalized* algebraic data types" - GADTs for short. While for an "ordinary" enum every constructor yields the same type, with an GADT each constructor may yield a different type. 6 | 7 | To illustrate that, let's define a little language for arithmetic expressions: 8 | 9 | ```haxe 10 | enum Expr { 11 | Sum(a:Expr, b:Expr):Expr; 12 | Product(a:Expr, b:Expr):Expr; 13 | Power(a:Expr, b:Expr):Expr; // <-- this constructor returns an Expr ... 14 | 15 | GreaterThan(a:Expr, b:Expr):Expr;// <-- ... and this one an Expr 16 | Not(a:Expr):Expr; 17 | Or(a:Expr, b:Expr):Expr; 18 | And(a:Expr, b:Expr):Expr; 19 | 20 | Const(v:T):Expr; 21 | Equals(a:Expr, b:Expr):Expr; 22 | } 23 | ``` 24 | 25 | So now we can say: I want a numeric expression or a boolean one, by either saying `Expr` or `Expr`. 26 | 27 | The last two constructors in the example are particularly interesting: 28 | 29 | - `Const` may accept a value of any type and becomes and `Expr` of that type. 30 | - `Equals` has a type parameter. This is actually not GADT specific. Ordinary enum constructors may have this too, because at the bottom line they are functions and may therefore be parametrized. In the case of `Equals` it is the type of the `Expr` being compared. It is arbitrary, but still must be equal for both operands and the result will always be boolean. This models very closely how `==` works. 31 | 32 | For example `1.0 + 1.0 == 2` could be written as `Equals(Sum(Const(1.0), Const(1.0)), Const(2.0))` and will compile, as opposed to `Equals(Const(3.14), Const('test'))` which will fail with `String should be Float` exactly as `3.14 == 'test'`. 33 | 34 | The compiler performs the desired type checks when constructing GADTs. It does the same when deconstructing them. 35 | 36 | To see that in action, let's have a look at how we would evaluate a numeric expression: 37 | 38 | ```haxe 39 | function valueOf(f:Expr):Float { 40 | return switch f { 41 | case Const(v): v; 42 | case Sum(a, b): valueOf(a) + valueOf(b); 43 | case Product(a, b): valueOf(a) * valueOf(b); 44 | case Power(a, b): Math.pow(valueOf(a), valueOf(b)); 45 | } 46 | } 47 | ``` 48 | 49 | That's it already. Try omitting any constructor that can return `Expr` (which does include `Const` for which that is just a special case) and Haxe's exhaustiveness check will tell you a case is not covered. Check against a constructor that is `Expr` and Haxe will tell you this: 50 | 51 | > `Expr` should be `Expr` 52 | > Type parameters are invariant 53 | > `Bool` should be `Float` 54 | 55 | So if we pick the type parameter, Haxe will reduce the number of cases for us. If we leave the parameter unbound, we must treat all cases: 56 | 57 | ```haxe 58 | function eval(e:Expr):V { 59 | return switch e { 60 | case Const(v): 61 | $type(e); // Expr 62 | v; 63 | case Sum(a, b): 64 | $type(e); // Expr 65 | eval(a) + eval(b); 66 | case Product(a, b): 67 | eval(a) * eval(b); 68 | case Power(a, b): 69 | Math.pow(eval(a), eval(b)); 70 | case GreaterThan(a, b): 71 | eval(a) > eval(b); 72 | case Equals(a, b): 73 | $type(e); // Expr 74 | $type(a); // Expr 75 | eval(a) == eval(b); 76 | case Not(a): 77 | !eval(a); 78 | case Or(a, b): 79 | eval(a) || eval(b); 80 | case And(a, b): 81 | eval(a) && eval(b); 82 | } 83 | } 84 | ``` 85 | 86 | Notice how in each case `Expr.T` may assume a different type. In the first case it remains unbound, in the second it becomes `Float` and further below it is `Bool`. Try returning `5` in the first case and the compiler will tell you `Int` should be `eval.V`. 87 | 88 | All in all, this is a very powerful feature, capable of expressing extremely complex type structures. 89 | 90 | > Author: [back2dos](https://github.com/back2dos) 91 | -------------------------------------------------------------------------------- /assets/content/cookbook/Functional Programming/functional-style-expression-evaluation.md: -------------------------------------------------------------------------------- 1 | [tags]: / "functional programming, ADT, enum, parsing" 2 | 3 | # ML-Style Parse Tree Evaluation 4 | 5 | ML-like languages are great for creating interpreters or compilers, by virtue of Algebraic Data Types. 6 | Haxe's `enum` allow for writing similarly elegant code. 7 | 8 | Below is the Haxe version of F# code for a simple expression interpreter, described in [Programming Language Concepts](https://www.itu.dk/people/sestoft/plc/) (Sestoft). 9 | 10 | ## Implementation 11 | 12 | The `eval` function uses pattern matching against an expression, represented by a parametized enum data data type. 13 | ```haxe 14 | class Main { 15 | /** 16 | The eval function uses pattern matching against an expression, 17 | represented by a parametized enum data data type. 18 | **/ 19 | static public function eval(e:Expr):Int { 20 | return switch e { 21 | case CstI(x): x; 22 | case Prim("+", e1, e2): eval(e1) + eval(e2) ; 23 | case Prim("-", e1, e2): eval(e1) - eval(e2) ; 24 | case Prim("*", e1, e2): eval(e1) * eval(e2) ; 25 | case Prim(_) : throw "Unknown primitive"; 26 | } 27 | } 28 | 29 | // Some simple tests. 30 | static public function main():Void { 31 | 32 | // Evaluate the expression 23. 33 | trace( eval( CstI(23) ) ); 34 | 35 | // Evaluate the expression (7 * 9) + 10. 36 | trace( eval( Prim("+", Prim("*", CstI(7), CstI(9)), CstI(10)) ) ); 37 | } 38 | } 39 | 40 | /* Algabreic Data Type for an arithmetic expression. In F# would be: 41 | type expr = 42 | | CstI of int 43 | | Prim of string * exp * exp 44 | */ 45 | enum Expr { 46 | CstI( x:Int ); // An integer constant 47 | Prim( op:String, e1:Expr, e2:Expr ); // Or a primary arithmetic expression 48 | } 49 | ``` 50 | 51 | > Author: [Yves Cloutier](https://github.com/cloutiy) 52 | -------------------------------------------------------------------------------- /assets/content/cookbook/JavaScript/adding-element-to-dom.md: -------------------------------------------------------------------------------- 1 | [tags]: / "javascript,dom,html" 2 | 3 | # Adding a HTML element to the DOM 4 | 5 | This is an example to add a paragraph element to the HTML page when the DOM is ready. This tutorial contains two methodes; with and without jQuery. 6 | 7 | > Haxe can compile to many targets, this example is specific for the Haxe/JavaScript target only. 8 | 9 | ## Structure of this example 10 | 11 | Create a folder named **example** and create folders **bin** and **src**. 12 | 13 | See example below: 14 | 15 | ``` 16 | + example/ 17 | + bin/ 18 | + src/ 19 | - Main.hx 20 | - build.hxml 21 | ``` 22 | 23 | ### Appending a paragraph to DOM without jQuery 24 | 25 | This is the **Main.hx** that needs to be saved in the **src** folder. 26 | 27 | ```haxe 28 | import js.Browser.document; 29 | 30 | class Main { 31 | // static entrypoint 32 | static function main() new Main(); 33 | 34 | // constructor 35 | function new() { 36 | trace("DOM example"); 37 | 38 | document.addEventListener("DOMContentLoaded", function(event) { 39 | trace("DOM ready"); 40 | 41 | // Shorthand for document.createElement("p"); 42 | var p = document.createParagraphElement(); 43 | p.innerText = 'DOM ready'; 44 | 45 | document.querySelector(".container").appendChild(p); 46 | }); 47 | } 48 | } 49 | ``` 50 | 51 | > `DOMContentLoaded` is supported in IE8+ . 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 | Code completion from URL 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, a:Int, b:Int) { 15 | var diff:Int = b - a; 16 | return a + Math.floor(Math.random() * diff); 17 | } 18 | } 19 | ``` 20 | 21 | Note that the method is `public static`, and the first parameter is `Class` as this is required for static extensions to work. 22 | Also note we used `cl` and not `class` as parameter because that is a reserved keyword in Haxe. 23 | 24 | ## Usage 25 | 26 | Here's how to consume the code: 27 | 28 | ```haxe 29 | using MathExtensions; 30 | 31 | class Test { 32 | static function main() { 33 | trace("Random value between 10 and 20:" + Math.randomBetween(10, 20)); 34 | } 35 | } 36 | ``` 37 | 38 | Using this, you can add all kinds of interesting extensions to pre-existing classes (eg. core Haxe classes, or classes from other Haxe libraries). 39 | 40 | > More on this topic: 41 | > 42 | > Author: [ashes999](https://github.com/ashes999) 43 | -------------------------------------------------------------------------------- /assets/content/cookbook/Other/assets/deploy-haxelib-using-travis-and-github.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/content/cookbook/Other/assets/deploy-haxelib-using-travis-and-github.gif -------------------------------------------------------------------------------- /assets/content/cookbook/Other/base64-encoding.md: -------------------------------------------------------------------------------- 1 | # Base64 encoding 2 | 3 | This article shows how to use base64 in Haxe and how to use a custom charset. 4 | 5 | ```haxe 6 | var myString = "Hello world!"; 7 | var encoded = haxe.crypto.Base64.encode(haxe.io.Bytes.ofString(myString)); 8 | trace(encoded); // SGVsbG8gd29ybGQh 9 | 10 | var decoded = haxe.crypto.Base64.decode(encoded).toString(); 11 | trace(decoded); // Hello world! 12 | ``` 13 | 14 | ### Customizing charset 15 | 16 | As alternative to using the Base64 class as above, it's possible to encode with a custom set of chararacters. Lets use the Base64 chararacters first, which will result in the same as default base64 encoding. 17 | ```haxe 18 | var myString = "Hello world!"; 19 | 20 | var charset = haxe.crypto.Base64.CHARS; 21 | var baseCode = new haxe.crypto.BaseCode(haxe.io.Bytes.ofString(charset)); 22 | 23 | var encoded = baseCode.encodeString(myString); 24 | trace(encoded); // SGVsbG8gd29ybGQh 25 | 26 | var decoded = baseCode.decodeString(encoded); 27 | trace(decoded); // Hello world! 28 | ``` 29 | 30 | Now lets use a more funky charset. Note that the base length must be a [power of two](https://en.wikipedia.org/wiki/Power_of_two) (1, 2, 4, 8, 16, 32, 64, 128 etc). 31 | In this example we use the four characters `"1ILi"` as charset. The encoded string will be larger, but as expected only contains those characters. 32 | ```haxe 33 | var myString = "Hello world!"; 34 | 35 | var charset = "1ILi"; 36 | var baseCode = new haxe.crypto.BaseCode(haxe.io.Bytes.ofString(charset)); 37 | 38 | var encoded = baseCode.encodeString(myString); 39 | trace(encoded); // I1L1ILIIILi1ILi1ILii1L11IiIiILiiIi1LILi1ILI11L1I 40 | 41 | var decoded = baseCode.decodeString(encoded); 42 | trace(decoded); // Hello world! 43 | ``` 44 | 45 | With this knowledge it is possible to create a Base32 in Haxe; just use `"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"` as charset. Or Base16 would be `"0123456789ABCDEF"`. 46 | ```haxe 47 | var myString = "Hello world!"; 48 | 49 | var charset = "1ILi"; 50 | var baseCode = new haxe.crypto.BaseCode(haxe.io.Bytes.ofString(charset)); 51 | 52 | var encoded = baseCode.encodeString(myString); 53 | trace(encoded); // I1L1ILIIILi1ILi1ILii1L11IiIiILiiIi1LILi1ILI11L1I 54 | 55 | var decoded = baseCode.decodeString(encoded); 56 | trace(decoded); // Hello world! 57 | ``` 58 | -------------------------------------------------------------------------------- /assets/content/cookbook/Other/compiling-cpp-code-windows-mingw.md: -------------------------------------------------------------------------------- 1 | [tags]: / "cpp,compiler" 2 | 3 | # Compiling c++ code on Windows using mingw 4 | 5 | On Windows, the compiler by default expects an installation of Microsoft Visual Studio Community edition when targeting c++. 6 | 7 | However, there's the possibility of using [mingw](https://www.mingw-w64.org) instead, wich is much more quick and lightweight when it comes to installation size and hazzle. 8 | 9 | > Please keep in mind that mingw isn't the default compiler alternative, and that you might need to dive into the cold c++ sea to get other than basic stuff going. 10 | 11 | Setup and usage is very easy: 12 | 13 | ## Installing mingw-w64 14 | 15 | See [the mingw-w64 download page](https://www.mingw-w64.org/downloads/) for details about how to install. 16 | 17 | ## Add `-D toolchain=mingw` compiler directive 18 | 19 | You also have to add the `-D toolchain=mingw` to your project's .hxml file. Here's an example of how a .hxml-file might look: 20 | 21 | ``` 22 | # build.hxml example using mingw c++ compiler 23 | -cp src 24 | -main Main 25 | -D toolchain=mingw 26 | -cpp bin-mingw 27 | ``` 28 | 29 | That's it! 30 | 31 | > Author: [Jonas Nyström](https://github.com/cambiata) 32 | -------------------------------------------------------------------------------- /assets/content/cookbook/Other/haxe-zip.md: -------------------------------------------------------------------------------- 1 | [tags]: / "filesystem" 2 | 3 | # Zip files 4 | 5 | The [haxe.zip](http://api.haxe.org/haxe/zip/) package allows to zip and unzip files and directories using Haxe. This example shows how to use it. 6 | 7 | > **Note:** This only works on the [sys-targets](/category/beginner/using-filesystem.html). 8 | 9 | ## Zip a directory 10 | 11 | The following example reads the directory "build/game/" with all of its content (recursively) and writes it to a zip file called _output.zip_. 12 | 13 | ```haxe 14 | // recursive read a directory, add the file entries to the list 15 | function getEntries(dir:String, entries:List = null, inDir:Null = null) { 16 | if (entries == null) entries = new List(); 17 | if (inDir == null) inDir = dir; 18 | for(file in sys.FileSystem.readDirectory(dir)) { 19 | var path = haxe.io.Path.join([dir, file]); 20 | if (sys.FileSystem.isDirectory(path)) { 21 | getEntries(path, entries, inDir); 22 | } else { 23 | var bytes:haxe.io.Bytes = haxe.io.Bytes.ofData(sys.io.File.getBytes(path).getData()); 24 | var entry:haxe.zip.Entry = { 25 | fileName: StringTools.replace(path, inDir, ""), 26 | fileSize: bytes.length, 27 | fileTime: Date.now(), 28 | compressed: false, 29 | dataSize: sys.FileSystem.stat(path).size, 30 | data: bytes, 31 | crc32: haxe.crypto.Crc32.make(bytes) 32 | }; 33 | entries.push(entry); 34 | } 35 | } 36 | return entries; 37 | } 38 | // create the output file 39 | var out = sys.io.File.write("output.zip", true); 40 | // write the zip file 41 | var zip = new haxe.zip.Writer(out); 42 | zip.write(getEntries("build/game/")); 43 | ``` 44 | 45 | > **More information:** 46 | > 47 | > * [haxe.zip API documentation](http://api.haxe.org/haxe/zip/) 48 | > * More [format libraries](https://github.com/HaxeFoundation/format) 49 | > 50 | > Author: [Mark Knol](https://github.com/markknol) 51 | -------------------------------------------------------------------------------- /assets/content/cookbook/Other/hxcpp-pointers.md: -------------------------------------------------------------------------------- 1 | [tags]: / "externs,hxcpp" 2 | 3 | # Hxcpp Pointers 4 | 5 | This page covers the differences and use cases of the three main pointer types; `cpp.ConstRawPointer` and `cpp.RawPointer`, `cpp.ConstPointer` and `cpp.Pointer`, and `cpp.Star`. 6 | 7 | ## cpp.RawPointer 8 | 9 | As the name says, just bog standard c pointers. e.g. the following haxe function 10 | 11 | ```haxe 12 | function foo(cpp.RawPointer bar) {} 13 | ``` 14 | 15 | will produce the following C++ function 16 | 17 | ```cpp 18 | void foo(int* bar) {} 19 | ``` 20 | 21 | No special magic going on here. The `cpp.RawPointer`s can also be used like arrays from the haxe side making them very useful for representing c arrays. e.g. The following c++ struct 22 | 23 | ```cpp 24 | struct Foo { 25 | uint8_t bar[5]; 26 | }; 27 | ``` 28 | 29 | could be externed with the following haxe class and the `foo` variable could be indexed like a standard haxe array. 30 | 31 | ```haxe 32 | extern class Foo { 33 | var cpp.RawPointer bar; 34 | } 35 | ``` 36 | 37 | The downside with these is that its not ergonomic from a haxe pov to represent and consume pointers to objects. 38 | 39 | ```haxe 40 | extern class Foo { 41 | function bar():Int; 42 | } 43 | 44 | function myFunc(foo:cpp.RawPointer) { 45 | // Have to use [0] ... 46 | trace(foo[0].bar()); 47 | } 48 | ``` 49 | 50 | They also cannot be used in any situations where Dynamic is expected (explicitely or implicitly). 51 | 52 | ```haxe 53 | Array> // Will generate code which gives C++ compiler errors. 54 | ``` 55 | 56 | ## cpp.Pointer 57 | 58 | Similar to the above, but with some key differences. The code they generate does not map directly to pointers, but instead to a special `::cpp::Pointer` struct. 59 | 60 | ```haxe 61 | function foo(cpp.Pointer p) {} 62 | ``` 63 | 64 | ```cpp 65 | void foo(::cpp::Pointer p) {} 66 | ``` 67 | 68 | This type lives on the stack so does not contribute to GC pressure, it also is compatible with Dynamic, so you can pass `cpp.Pointer` types into dynamic places and use them with generic arguments. This type also contains loads of convenience functions for reinterpreting the pointer, arithmetic, conversions to haxe arrays and vectors, etc, etc. There are also member fields for accessing the `cpp.Pointer` as a `cpp.RawPointer` or `cpp.Star`. 69 | 70 | It also retains the array access that `cpp.RawPointer` does. On top of this the `::cpp::Pointer` type has implicit to and from conversions for the underlying pointer, which means you can extern the following function 71 | 72 | ```cpp 73 | void foo(int* v) {} 74 | ``` 75 | 76 | with this haxe function 77 | 78 | ```haxe 79 | void foo(v:cpp.Pointer) {} 80 | ``` 81 | 82 | You don't need to use `cpp.RawPointer` here, you can use `cpp.Pointer` and all the convenience it provides. So why would you ever want to use `cpp.RawPointer` if `cpp.Pointer` does everything it does and is compatible with more of haxe's type system. 83 | 84 | Function signatures. 85 | 86 | `cpp.Pointer` is an actual C++ class implemented in the hxcpp runtime, so if you had the following c function which takes in a function pointer. 87 | 88 | ```c++ 89 | typedef void(*bar)(int*) 90 | 91 | void foo(bar func) {} 92 | ``` 93 | 94 | you could not use the following haxe function to generate a function pointer to pass into it. 95 | 96 | ``` 97 | function haxe_foo(cpp.Pointer v) {} 98 | 99 | function main() { 100 | cpp.Callable.fromStaticFunction(haxe_foo); 101 | } 102 | 103 | ``` 104 | 105 | That `fromStaticFunction` call will generate a function pointer with the signature of `void(*)(::cpp::Pointer)` which is not compatible with the function pointer `bar`. 106 | 107 | ## cpp.Star 108 | 109 | This is the last pointer types and like `cpp.RawPointer` it generates raw C pointers in the output code, the key difference is that this type does not support array access and will auto de-reference when accessing the underlying data. This means it's ideal for representing pointers to objects. E.g. the following C++ 110 | 111 | ```c++ 112 | struct Bar { 113 | int baz(); 114 | }; 115 | 116 | bar* foo(); 117 | ``` 118 | 119 | could be externed using `cpp.Star` to make calling the `baz` function more ergonomic. 120 | 121 | ```haxe 122 | extern class Bar { 123 | function baz():Int; 124 | } 125 | 126 | extern function foo():cpp.Star; 127 | 128 | function main() { 129 | final bar = foo(); 130 | 131 | trace(bar.baz()); 132 | } 133 | ``` 134 | 135 | No weird `[0]` like you would need to with `cpp.RawPointer` and since it is an actual pointer it can be used in function signatures unlike `cpp.Pointer`. 136 | However, like `cpp.RawPointer` it is not compatible with Dynamic. But as previously mentioned `cpp.Pointer` has functions for wrapping a `cpp.Pointer` and accessing its underlying pointer as a `cpp.Star` so you can get the best of all worlds with `cpp.Pointer`. 137 | -------------------------------------------------------------------------------- /assets/content/cookbook/Other/named-parameters.md: -------------------------------------------------------------------------------- 1 | [tags]: / "arguments" 2 | 3 | # Named Parameters 4 | 5 | While there is no named parameter support in Haxe, anonymous structures can be used to obtain the same effect. 6 | 7 | * Pros: it can make your code more readable, especially functions with a long parameter list. 8 | 9 | * Cons: it imposes a performance penalty on each call (see the *Performance Considerations* section below for details) 10 | 11 | ## Usage 12 | ```haxe 13 | class Test { 14 | static public function main() { 15 | foo({ x: 12, y:1.0, name: "foo"}); 16 | bar({ x: 10, y:2.0, name: "bar"}); 17 | } 18 | 19 | // Could be useful to specify options 20 | static public function foo(options:{x:Int, y:Float, name:String}) { 21 | trace('Got options: ${options.x}, ${options.y} and ${options.name}'); 22 | } 23 | 24 | // Or could be useful to specify configuration 25 | static public function bar(config:{x:Int, y:Float, name:String}) { 26 | trace('Got configuration: ${config.x}, ${config.y} and ${config.name}'); 27 | } 28 | } 29 | ``` 30 | 31 | [Code in "Try Haxe"](http://try.haxe.org/#6Ce47) 32 | 33 | A more practical example can be found in the [Neko tutorial "Accessing to MySQL Database" page](http://old.haxe.org/doc/neko/mysql) 34 | 35 | ```haxe 36 | class Test { 37 | static function main() { 38 | var cnx = neko.db.Mysql.connect({ 39 | host: "localhost", 40 | port: 3306, 41 | user: "root", 42 | pass: "", 43 | socket: null, 44 | database: "MyBase" 45 | }); 46 | // ... 47 | cnx.close(); 48 | } 49 | } 50 | ``` 51 | 52 | ## Performance Considerations 53 | 54 | In short, this technique should be avoided in performance critical sections on any target. 55 | 56 | As noted in the Haxe manual ["Impact on Performance" section](http://haxe.org/manual/types-structure-performance.html), this technique will have a negative impact on static targets due to an **additional dynamic lookup**. Additionally, an **anonymous object is created as well** in the process, which further affects performances. 57 | 58 | The dynamic lookup is the more expensive bit on both the JVM (by far) and CLR (although barely). On other static and all dynamic platforms, the converse is true. The following code may remove the performance impact from anonymous object creation. (Source: [this comment from back2dos](https://github.com/HaxeFoundation/code-cookbook/pull/42#issuecomment-229000039)). 59 | 60 | ```haxe 61 | public inline function foo(options:{ x:Int, y:Float, z:String }) return _foo(options.x, options.y, options.z); 62 | private function _foo(x:Int, y:Float, z:String) return "$x, $y, $z"; 63 | ``` 64 | 65 | 66 | > More on this topic: 67 | > 68 | > - Anonymous structures in Haxe manual: 69 | > - Tinker Bell Language Extentions by back2dos provide a more integrated support for named parameters and discuss their usage: 70 | > 71 | > Author: [x2f](https://github.com/x2f), [back2dos](https://github.com/back2dos) 72 | -------------------------------------------------------------------------------- /assets/content/cookbook/Principles/Inheritance.md: -------------------------------------------------------------------------------- 1 | [tags]: / "class" 2 | 3 | # Inheritance 4 | 5 | Classes inherit from other classes using the `extends` keyword: 6 | 7 | ```haxe 8 | class Point2d { 9 | public var x:Int; 10 | public var y:Int; 11 | 12 | public function new(x, y) { 13 | this.x = x; 14 | this.y = y; 15 | } 16 | } 17 | 18 | class Point3d extends Point2d { 19 | public var z:Int; 20 | 21 | public function new(x, y, z) { 22 | super(x, y); 23 | this.z = z; 24 | } 25 | } 26 | ``` 27 | 28 | ## Interfaces can also extend 29 | 30 | ```haxe 31 | interface Debuggable extends Printable extends Serializable 32 | ``` -------------------------------------------------------------------------------- /assets/content/cookbook/Principles/Null-safety.md: -------------------------------------------------------------------------------- 1 | # Null safety 2 | 3 | > **NOTE:** Since Haxe 4.0, the compiler provides a more natural compile-time checking for nullable values that is aware of the control flow. See [official documentation](https://haxe.org/manual/cr-null-safety.html) for more details. We're leaving this example for the curious as it illustrates how [abstract types](https://haxe.org/manual/types-abstract.html) can be used for such thing. 4 | 5 | It's possible to write null-safe code without run-time overhead using an abstract similar to the following: 6 | 7 | ```haxe 8 | abstract Maybe(Null) from Null { 9 | 10 | public inline function exists():Bool { 11 | return this != null; 12 | } 13 | 14 | public inline function sure():T { 15 | return if (exists()) this else throw "No value"; 16 | } 17 | 18 | public inline function or(def:T):T { 19 | return if (exists()) this else def; 20 | } 21 | 22 | public inline function may(fn:T->Void):Void { 23 | if (exists()) fn(this); 24 | } 25 | 26 | public inline function map(fn:T->S):Maybe { 27 | return if (exists()) fn(this) else null; 28 | } 29 | 30 | public inline function mapDefault(fn:T->S, def:S):S { 31 | return if (exists()) fn(this) else def; 32 | } 33 | } 34 | ``` 35 | 36 | ## Usage 37 | 38 | ```haxe 39 | class Test { 40 | static function main() { 41 | // initialize from null... 42 | var value:Maybe = null; 43 | // ...or a value of underlying type 44 | value = 10; 45 | 46 | // compilation errors, so you can't use Maybe without explicit unwrapping 47 | // var v:Int = value; 48 | // var v = value + 5; 49 | 50 | // get value or raise exception 51 | var v = value.sure(); 52 | 53 | // get value or use default 54 | var v = value.or(0); 55 | 56 | // check whether value exists 57 | if (value.exists()) 58 | trace("value exists!"); 59 | 60 | // execute function if value exists 61 | value.may(function(value) trace("Value is " + value)); 62 | 63 | // map value to Maybe 64 | var valueString = value.map(function(value) return Std.string(value)); 65 | 66 | // map value to String or use default string 67 | var message = value.mapDefault(function(value) return "Value is " + value, "No value"); 68 | } 69 | } 70 | ``` 71 | 72 | > Author: [Dan Korostelev](https://github.com/nadako) 73 | -------------------------------------------------------------------------------- /assets/content/cookbook/Principles/everything-is-an-expression.md: -------------------------------------------------------------------------------- 1 | # Everything is an expression 2 | 3 | Many programming languages split code into two kinds of elements: *statements* and *expressions*. 4 | Statements perform some action (e.g. `if/else`) and expressions return values (e.g. `a + b`). 5 | 6 | This is **NOT** the case in Haxe. In Haxe, everything is an expression which means it can be used where value is expected. 7 | 8 | ## Examples 9 | 10 | `if/else` is an expression returning value of either `true` or `false` branch: 11 | ```haxe 12 | trace(if (Math.random() > 0.5) "Hello" else "Bye"); 13 | ``` 14 | 15 | 16 | `try/catch` is an expression returning value of `try` if everything is okay or `catch` if error is caught: 17 | ```haxe 18 | trace(try haxe.Json.parse("{") catch (e:Dynamic) null); 19 | ``` 20 | 21 | 22 | `switch` is an expression returning value of the matched `case` (or `default`): 23 | ```haxe 24 | trace(switch (Std.random(3)) { 25 | case 0: "zero"; 26 | case 1: "one"; 27 | case 2: "two"; 28 | default: "impossible!"; 29 | }); 30 | ``` 31 | 32 | Even blocks are just expressions, returning value of the last of their expressions: 33 | ```haxe 34 | trace({ 35 | var l = new List(); 36 | for (i in 0...10) l.add(i); 37 | l; 38 | }); 39 | ``` 40 | 41 | Actually, knowing that blocks are mere expressions, they are not necessarily required for e.g. functions 42 | so this is a perfectly valid function definition: 43 | ```haxe 44 | function toInt(s:String):Int return Std.parseInt(s); 45 | ``` 46 | 47 | Some expressions, such as loops or var declarations don't make any sense as values, 48 | so they will be typed as Void and thus won't be able to be used where value is expected. 49 | For example the following won't compile: 50 | ```haxe 51 | trace(for (i in 0...10) i); // ERROR: Cannot use Void as value 52 | ``` 53 | 54 | Some expressions such as `throw`, `continue` or `return` also don't make sense as values, 55 | however they are allowed to be in value places so code like the following is possible: 56 | ```haxe 57 | for (file in ["a.txt", "b.txt", "c.txt"]) { 58 | // try reading file or skip the loop iteration 59 | var content = try sys.io.File.getContent(file) catch (e:Dynamic) continue; 60 | trace(content); 61 | } 62 | ``` 63 | 64 | One interesting feature of the `return` expression is that it can be used with a non-empty expression 65 | for returning out of `Void` functions. While it can be confusing at first, it is very pragmatic when dealing 66 | with callback-based code. This kind of expressions will be transformed from `return someVoid();` to `someVoid(); return;`. 67 | For example: 68 | ```haxe 69 | function getContent(fileName:String, callback:String->Void):Void { 70 | if (fileName == "") 71 | return callback("New file"); // invoke callback and return early if `fileName` is empty string 72 | 73 | var content = 74 | try 75 | sys.io.File.getContent(fileName) 76 | catch (e:Dynamic) 77 | return callback("ERROR: " + e); // invoke callback and return early in case of error 78 | 79 | callback(content.toUpperCase()); // invoke callback normally 80 | } 81 | ``` 82 | 83 | > Author: [Dan Korostelev](https://github.com/nadako) 84 | -------------------------------------------------------------------------------- /assets/content/index.mtt: -------------------------------------------------------------------------------- 1 |

2 |
3 |

Haxe Code Cookbook

4 |

5 | Community driven Haxe code snippets, examples and tutorials. 6 |

7 |
8 |
9 | 10 |
11 | 12 |
13 |
14 |

Start browsing..

15 |
16 | ::foreach category sitemap:: 17 | ::category.title:: (::category.getPageCount()::) 18 | ::end:: 19 |
20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 |

Learn with Haxe

30 |

31 | There are many ways to learn Haxe. On this website you'll find a lot of resources you need to become a Haxe developer. 32 |

33 | So why not start learning and join the community today? If you haven’t learned Haxe yet, the time is now. 34 |

35 |
36 |
37 |
38 |

Latest articles

39 | ::foreach page latestCreatedPages(4):: 40 |

::DateTools.format(page.dates.created, "%b %d, %Y"):: 41 | ::if page.isSerieHome():: 42 | ::page.category.parent.title:: 43 | ::else:: 44 | ::page.category.title:: 45 | ::end:: 46 | / ::page.title:: 47 |

48 |

This article consist of ::page.category.pages.length-1:: pages

49 |

::page.description::‥

50 | Read » 51 |
52 | ::end:: 53 |
54 |
55 | 56 |
57 | 58 |
59 |
60 |

The cross-platform development toolkit

61 |

Haxe is a modern high-level strictly-typed programming language with a fast optimizing cross-compiler.

62 | Download Haxe 4.3.7
63 | Released: 2025-05-09 64 |
65 |
66 |

Learning by example

67 |

The Haxe Foundation facilitates the following code cookbook for the Haxe commmunity. 68 | Enjoy learning Haxe; It is great!

69 | More documentation: 70 |

71 |

Haxe Documentation

72 |

Haxe Manual

73 |

Haxe API documentation

74 |

You can try Haxe in the browser! try.haxe.org

75 |

76 |
77 |
78 |

Contribution is easy

79 |

The project is being developed on GitHub. Feel free to contribute code snippets, tutorials and how-to guides.

80 |

Haxe Code Cookbook on Github

81 |

82 | Star 83 |   Fork 84 | 85 |

86 |
87 |
88 | 89 |
90 | 91 | -------------------------------------------------------------------------------- /assets/content/layout-page-main.mtt: -------------------------------------------------------------------------------- 1 | ::use 'layout.mtt':: 2 | ::raw pageContent:: 3 | ::end:: -------------------------------------------------------------------------------- /assets/content/layout-page-toc.mtt: -------------------------------------------------------------------------------- 1 | ::use 'layout.mtt':: 2 |
3 |
4 |
5 |
Haxe Code Cookbook
6 |
7 |
8 |
9 |
10 |
11 | 32 | 40 | 41 |
42 |
::raw pageContent::
43 |
44 |
45 |
46 | ::end:: -------------------------------------------------------------------------------- /assets/content/redirection.mtt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page has moved - Haxe programming language 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 |

14 | If you are not redirected automatically please click here. 15 |

16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /assets/content/rss.mtt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Haxe programming language - Code Cookbook 5 | ::basePath:: 6 | Easy to read Haxe programming language examples. 7 | contact@haxe.org (Haxe Foundation) 8 | http://blogs.law.harvard.edu/tech/rss 9 | en 10 | ::convertDate(now):: 11 | 12 | 13 | ::foreach page latestCreatedPages(20):: 14 | 15 | ::page.title:: 16 | ::page.absoluteUrl:: 17 | ::page.absoluteUrl:: 18 | ::page.description:: 19 | ::convertDate(page.dates.created):: 20 | 21 | ::end:: 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/content/sitemap.mtt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ::foreach page pages:: 4 | 5 | ::page.absoluteUrl:: 6 | ::DateTools.format(page.dates.modified,"%Y-%m-%d"):: 7 | weekly 8 | 0.6 9 | 10 | ::end:: 11 | 12 | ::foreach category sitemap:: 13 | 14 | ::category.absoluteUrl:: 15 | weekly 16 | 0.4 17 | 18 | ::end:: 19 | 20 | ::foreach tag tags.keys():: 21 | 22 | ::basePath::tag/::tag::.html 23 | weekly 24 | 0.4 25 | 26 | ::end:: 27 | 28 | 29 | -------------------------------------------------------------------------------- /assets/content/table-of-content-category.mtt: -------------------------------------------------------------------------------- 1 | 2 | ::set hasParent = currentPage.category.parent!=null:: 3 | Haxe programming cookbook 4 | ::currentPage.category.parent.title:: 5 | ::currentCategory.title:: 6 | 7 | 8 |

::currentCategory.title::

9 |

::currentPage.description::

10 | 11 |
    12 | ::foreach page currentCategory.pages:: 13 |
  • 14 |

    ::DateTools.format(page.dates.created, "%b %d, %Y"):: 15 | ::if page.isSerieHome():: 16 | ::page.category.parent.title:: 17 | ::else:: 18 | ::page.category.title:: 19 | ::end:: 20 | / 21 |

    22 |

    0::> 23 | ::foreach tag page.tags:: 24 |  ::tag:: 25 | ::end:: 26 |

    27 |

    ::page.description::

    28 |
  • 29 | ::end:: 30 |
31 | 32 |
33 | 34 |
35 | Add new page 36 |
37 | -------------------------------------------------------------------------------- /assets/content/table-of-content-serie.mtt: -------------------------------------------------------------------------------- 1 | 2 | ::set hasParent = currentPage.category.parent!=null:: 3 | Haxe programming cookbook 4 | ::currentPage.category.parent.title:: 5 | ::currentPage.title:: 6 | 7 | 8 |

::currentCategory.title::

9 |

::raw currentCategory.content::

10 |
11 |

This article consist of ::currentCategory.pages.length-1:: pages:

12 |
    13 | ::foreach page currentCategory.pages:: 14 |
  1. 15 |

    16 | ::if page.isSerieHome():: 17 | ::page.category.parent.title:: 18 | ::else:: 19 | ::page.category.title:: 20 | ::end:: 21 | / 22 |

    23 |

    0::> 24 | ::foreach tag page.tags:: 25 |  ::tag:: 26 | ::end:: 27 |

    28 | 29 |

    ::page.description::‥

    30 |
  2. 31 | ::end:: 32 |
33 | 34 |
35 | 36 |
0::> 37 | Tags:   38 | 39 | ::foreach tag currentPage.tags:: 40 |  , 41 | ::end:: 42 | 43 |
44 |
45 | -------------------------------------------------------------------------------- /assets/content/tags.mtt: -------------------------------------------------------------------------------- 1 |

::getTagTitle(currentPage.customData.tag)::

2 |

::currentPage.description::

3 | 4 |
    5 | ::foreach page currentPage.customData.pages:: 6 |
  • 7 |

    ::DateTools.format(page.dates.created, "%b %d, %Y"):: 8 | ::if page.isSerieHome():: 9 | ::page.category.parent.title:: 10 | ::else:: 11 | ::page.category.title:: 12 | ::end:: 13 | / 14 |

    15 |

    0::> 16 | ::foreach tag page.tags:: 17 |  ::tag:: 18 | ::end:: 19 |

    20 |

    ::page.description::‥

    21 |
  • 22 | ::end:: 23 |
24 | -------------------------------------------------------------------------------- /assets/includes/css/fonts.css: -------------------------------------------------------------------------------- 1 | /* latin-ext */ 2 | @font-face { 3 | font-family: 'Open Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/open-sans-latin-ext.woff2) format('woff2'); 7 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 8 | } 9 | /* latin */ 10 | @font-face { 11 | font-family: 'Open Sans'; 12 | font-style: normal; 13 | font-weight: 400; 14 | src: local('Open Sans Regular'), local('OpenSans-Regular'), url(../fonts/open-sans-latin.woff2) format('woff2'); 15 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 16 | } -------------------------------------------------------------------------------- /assets/includes/css/fonts.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans Regular'),local('OpenSans-Regular'),url(../fonts/open-sans-latin-ext.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;} 2 | @font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:local('Open Sans Regular'),local('OpenSans-Regular'),url(../fonts/open-sans-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;} -------------------------------------------------------------------------------- /assets/includes/css/haxe-nav.css: -------------------------------------------------------------------------------- 1 | nav.nav { 2 | font-family: "Open Sans", sans-serif; 3 | font-size: 16px; 4 | margin:0; 5 | } 6 | 7 | nav .fa { 8 | color:#777; 9 | margin-right:5px; 10 | } 11 | 12 | body nav * {line-height:20px;} 13 | 14 | nav .navbar-inverse .navbar-inner { 15 | background:#13110f; 16 | border:0; 17 | } 18 | 19 | nav .navbar .nav { 20 | margin:0; 21 | } 22 | 23 | nav .dropdown-menu { 24 | background:#2c2722; 25 | font-size: 14px; 26 | border-radius:0px 0px 2px 2px; 27 | padding:10px 0px; 28 | border:1px solid #2c2722; 29 | margin-top:-1px; 30 | } 31 | 32 | nav .navbar .nav>li>a { 33 | padding: 14px 12px 15px 12px; 34 | color:#ccc; 35 | } 36 | 37 | nav .dropdown-menu li a { 38 | color: #b8b5b5; 39 | padding:3px 15px; 40 | } 41 | nav .divider { 42 | background-color:transparent; 43 | border-right:1px solid #39332d; 44 | margin:5px 20px 5px 10px; 45 | height:39px; 46 | } 47 | 48 | nav .dropdown-menu .divider { 49 | background-color:transparent; 50 | border:0; 51 | border-bottom:1px solid #39332d; 52 | margin:5px 0; 53 | } 54 | 55 | nav .navbar-inverse .nav .active>a, nav .navbar-inverse .nav .active>a:hover, nav .navbar-inverse .nav .active>a:focus, nav .navbar-inverse .nav li.dropdown.open>.dropdown-toggle, nav .navbar-inverse .nav li.dropdown.active>.dropdown-toggle, nav .navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle { 56 | color: #fff; 57 | background-color:transparent; 58 | background-image:none; 59 | } 60 | 61 | nav .navbar-inverse .nav .active>a:hover { 62 | background:#39332d; 63 | } 64 | 65 | nav .dropdown-menu>.active>a, nav .dropdown-menu>.active>a:hover, nav .dropdown-menu>.active>a:focus { 66 | font-weight:bold; 67 | color: #fff; 68 | background-image:none; 69 | background:#39332d; 70 | } 71 | 72 | nav .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus, nav .dropdown-submenu:hover>a, nav .dropdown-submenu:focus>a { 73 | background:#39332d; 74 | } 75 | 76 | nav .navbar .nav>li>.dropdown-menu:after { 77 | border-bottom-color:#2c2722; 78 | } 79 | 80 | /** MEDIA Q **/ 81 | 82 | @media (max-width: 979px) 83 | { 84 | nav .navbar-fixed-top .navbar-inner, nav .navbar-fixed-bottom .navbar-inner { 85 | padding: 0px; 86 | } 87 | nav .navbar-fixed-top { 88 | margin-bottom: 0; 89 | } 90 | .nav .divider { 91 | display: none; 92 | } 93 | } 94 | 95 | /** SUB BRAND LOGOS **/ 96 | 97 | .navbar .brand.haxe-logo { padding-right:0; margin: 3px 0 0 0px; } 98 | .navbar .brand.haxe-logo:hover { opacity:.9; } 99 | 100 | .navbar .brand.haxe-logo:after { 101 | content:" "; 102 | position:absolute; 103 | /* create arrow */ 104 | width: 0; 105 | height: 0; 106 | border-top: 22px solid transparent; 107 | border-bottom: 22px solid transparent; 108 | border-left: 7px solid #13110f; /* same color as nav bar */ 109 | top:0; 110 | } 111 | .navbar .brand.sub { 112 | background: #39332d; /* Old browsers */ 113 | background: -moz-linear-gradient(top, #39332d 50%, #2c2722 51%); /* FF3.6+ */ 114 | background: -webkit-gradient(linear, left top, left bottom, color-stop(50%,#39332d), color-stop(51%,#2c2722)); /* Chrome,Safari4+ */ 115 | background: -webkit-linear-gradient(top, #39332d 50%,#2c2722 51%); /* Chrome10+,Safari5.1+ */ 116 | background: -o-linear-gradient(top, #39332d 50%,#2c2722 51%); /* Opera 11.10+ */ 117 | background: -ms-linear-gradient(top, #39332d 50%,#2c2722 51%); /* IE10+ */ 118 | background: linear-gradient(to bottom, #39332d 50%,#2c2722 51%); /* W3C */ 119 | padding:14px 20px 13px 20px; 120 | margin:0 10px 0 0; 121 | font:bold 18px "arial black", "open sans"; 122 | line-height:22px; 123 | color:rgb(255,255,255); 124 | } 125 | .navbar .brand.sub:hover { 126 | color:rgb(230,230,230); 127 | background: #2c2722; /* Old browsers */ 128 | } 129 | 130 | .navbar .brand.sub:before { 131 | content:".."; /* two dots */ 132 | position:absolute; 133 | margin-left:-33px; 134 | margin-top:-28px; 135 | line-height:0px; 136 | font:bold 40px "Open Sans", sans-serif; 137 | letter-spacing:0px; 138 | color:#fff200; 139 | } 140 | 141 | /** SUB BRAND COLORS **/ 142 | 143 | .navbar .brand.sub.try:before { 144 | color:#f89c0e; 145 | } 146 | 147 | .navbar .brand.sub.api:before { 148 | color:#eedc16; 149 | } 150 | 151 | .navbar .brand.sub.lib:before { 152 | color:#f1471d; 153 | } 154 | .navbar .brand.sub.ide:before { 155 | color:#f89c0e; 156 | } 157 | -------------------------------------------------------------------------------- /assets/includes/css/haxe-nav.min.css: -------------------------------------------------------------------------------- 1 | nav.nav{font-family:"Open Sans",sans-serif;font-size:16px;margin:0}nav .fa{color:#777;margin-right:5px}body nav *{line-height:20px}nav .navbar-inverse .navbar-inner{background:#13110f;border:0}nav .navbar .nav{margin:0}nav .dropdown-menu{background:#2c2722;font-size:14px;border-radius:0 0 2px 2px;padding:10px 0;border:1px solid #2c2722;margin-top:-1px}nav .navbar .nav>li>a{padding:14px 12px 15px;color:#ccc}nav .dropdown-menu li a{color:#b8b5b5;padding:3px 15px}nav .divider{background-color:transparent;border-right:1px solid #39332d;margin:5px 20px 5px 10px;height:39px}nav .dropdown-menu .divider{background-color:transparent;border:0;border-bottom:1px solid #39332d;margin:5px 0}nav .navbar-inverse .nav .active>a,nav .navbar-inverse .nav .active>a:focus,nav .navbar-inverse .nav .active>a:hover,nav .navbar-inverse .nav li.dropdown.active>.dropdown-toggle,nav .navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle,nav .navbar-inverse .nav li.dropdown.open>.dropdown-toggle{color:#fff;background-color:transparent;background-image:none}.dropdown-menu>li>a:focus,.navbar .brand.sub,nav .dropdown-menu>li>a:hover,nav .dropdown-submenu:focus>a,nav .dropdown-submenu:hover>a,nav .navbar-inverse .nav .active>a:hover{background:#39332d}nav .dropdown-menu>.active>a,nav .dropdown-menu>.active>a:focus,nav .dropdown-menu>.active>a:hover{font-weight:700;color:#fff;background:#39332d}nav .navbar .nav>li>.dropdown-menu:after{border-bottom-color:#2c2722}@media (max-width:979px){nav .navbar-fixed-bottom .navbar-inner,nav .navbar-fixed-top .navbar-inner{padding:0}nav .navbar-fixed-top{margin-bottom:0}.nav .divider{display:none}}.navbar .brand.haxe-logo{padding-right:0;margin:3px 0 0}.navbar .brand.haxe-logo:hover{opacity:.9}.navbar .brand.haxe-logo:after{content:" ";position:absolute;width:0;height:0;border-top:22px solid transparent;border-bottom:22px solid transparent;border-left:7px solid #13110f;top:0}.navbar .brand.sub{background:-moz-linear-gradient(top,#39332d 50%,#2c2722 51%);background:-webkit-gradient(linear,left top,left bottom,color-stop(50%,#39332d),color-stop(51%,#2c2722));background:-webkit-linear-gradient(top,#39332d 50%,#2c2722 51%);background:-o-linear-gradient(top,#39332d 50%,#2c2722 51%);background:-ms-linear-gradient(top,#39332d 50%,#2c2722 51%);background:linear-gradient(to bottom,#39332d 50%,#2c2722 51%);padding:14px 20px 13px;margin:0 10px 0 0;font:700 18px "arial black","open sans";line-height:22px;color:#fff}.navbar .brand.sub:hover{color:#e6e6e6;background:#2c2722}.navbar .brand.sub:before{content:"..";position:absolute;margin-left:-33px;margin-top:-28px;line-height:0;font:700 40px "Open Sans",sans-serif;letter-spacing:0;color:#fff200}.navbar .brand.sub.try:before{color:#f89c0e}.navbar .brand.sub.api:before{color:#eedc16}.navbar .brand.sub.lib:before{color:#f1471d}.navbar .brand.sub.ide:before{color:#f89c0e} -------------------------------------------------------------------------------- /assets/includes/css/styles.min.css: -------------------------------------------------------------------------------- 1 | #title,body,h1,h2,h3,h4,h5,h6{font-weight:400}body{font-family:"Open Sans",sans-serif;font-size:16px;color:#1e1e1e;display:flex;flex-direction:column;height:100vh}body,body li,body p,body td{line-height:1.5}.btn,.btn-large,.btn-medium,.btn-small{border-radius:4px!important;margin-right:5px}.main-content{flex:1 0 auto}article h4,nav#sidebar li.active{font-weight:700}h3 code{font-size:20px}pre code{font-size:14px}#title{font-size:38.5px}article h1,article h2,article h3,article h4,article h5,article h6{margin-bottom:20px}article h2,article h3,article h4,article h5,article h6{margin-top:20px}article h3{border-bottom:1px dotted #ddd}article h2{border-bottom:1px dotted #aaa}a,a:hover,a>code{color:#257fc2}.contribution{color:#ccc}.contribution a{color:#444}.contribution .fa{color:#999}.authors img,.contributors a{margin-bottom:2px}@media (max-width:767px){body{padding:0}.container{padding:0 20px;margin:0}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{margin-left:0;margin-right:0}main.container section{display:flex;flex-direction:column}main.container section article{order:1}main.container section nav{order:2}nav#sidebar{margin-top:20px}nav#sidebar.sidebar-toc br,nav#sidebar.sidebar-toc li li{display:none}nav#sidebar.sidebar-tags li{display:inline-block;border-left:0;margin-right:20px}}@media (max-width:500px){body .hero-unit h3{font-size:90%}body .hero-unit{padding:2em}body .hero-unit h3 br{display:none}}@media (min-width:979px){.hero-unit,.hero-unit-small{margin-top:49px}body .hero-unit h3 br{display:block}}.hero-unit,.hero-unit-small{background-repeat:no-repeat;background:-webkit-gradient(radial,50% 80%,0,center center,100,from(183,77,49),to(100,33,38));background:-webkit-radial-gradient(50% 80%,circle,#b74d31,#642126);background:-moz-radial-gradient(50% 80%,circle,#b74d31,#642126);background:-ms-radial-gradient(50% 80%,circle,#b74d31,#642126);color:#f0f0f0;text-align:center;border-radius:0}.hero-unit{padding:4em;margin-bottom:0;box-shadow:inset 0 0 50px rgba(0,0,0,.35)}.hero-unit-small{padding:2em 0;margin-bottom:1.5em;border-bottom:15px solid #eee;box-shadow:inset 0 0 50px rgba(0,0,0,.3)}#title,.hero-unit h1,.hero-unit-small h1{color:#fff;text-shadow:2px 2px 2px rgba(0,0,0,.15)}#title small,.hero-unit h1 small,.hero-unit-small h1 small{color:#C06B69}.hero-unit-small #title,.hero-unit-small h1{margin-bottom:0}.logo{width:80%;max-width:140px;max-height:230px;margin:1em}.hero-unit-small .logo{margin:0;max-width:401px;max-height:90px}.hero-unit h3.lead{color:#EA8220}.hero-unit a,.hero-unit a:hover{color:#FBC707;text-decoration:none;border-bottom:1px dashed #fff}.dark,.dark-section{background:#141419;color:#fff}.dark-section{padding:2em 1em}footer{margin-top:40px;font-size:14px;flex-shrink:0}.copyright{padding:6px;overflow:hidden;text-align:center;margin:40px 0}.reading-time{position:relative;top:-1em;font-style:italic;font-size:90%}blockquote{margin-left:30px;border-left:4px solid #eee;font-style:italic;padding:15px 15px 15px 30px}blockquote.author-info{border-left:4px solid #EA8220}article table td:not(:first-child),article table th:not(:first-child),nav#sidebar li{border-left:1px solid #ccc}.dark a,.dark a:active,.dark a:hover{color:#ea8220}.dark a.flashdevelop,.dark a.flashdevelop:active,.dark a.flashdevelop:hover{color:#eee;text-decoration:underline}.btn-success{background:#ea8220}.btn-success.active,.btn-success:active,.btn-success:focus,.btn-success:hover{color:#fff;background:#e5801f}nav#sidebar *{line-height:1.5em}.page-list h4,nav#sidebar li.active li{font-weight:400}nav#sidebar li{padding-left:10px}nav#sidebar li li{border-left:0;font-size:14px}nav#sidebar li li a{color:#777}article table{min-width:100%}article table tr:not(:last-child){border-bottom:1px solid #ccc}article table td,article table th{padding:3px 5px;text-align:left}article table td:first-child,article table th:first-child{width:140px}code.prettyprint{padding:.5em;display:block}.tag{padding-left:5px;padding-right:10px}.tag small{color:#999;font-size:80%}.tag a{color:#A84B38}.tag a:hover{color:#EA8220}.fa,.fa-big{color:#555}.fa-big{font-size:32px;padding-left:15px;padding-right:15px}.btn{margin-bottom:5px}.try-haxe{width:100%;height:350px;margin:0;border:0}.page-list-item:not(last-child){margin-bottom:20px}.page-list-item p{margin-left:10px}.page-list-item{padding:1px 15px 15px;border:1px solid #eee;border-radius:5px;background:#fdfdfd}#___plus_0,.g-plus,.twitter-share-button{vertical-align:text-bottom!important}.flex-video{position:relative;padding-top:25px;padding-bottom:67.5%;height:0;margin-bottom:16px;overflow:hidden}.flex-video.widescreen{padding-bottom:57.25%}.flex-video.vimeo{padding-top:0}.flex-video embed,.flex-video iframe,.flex-video object{position:absolute;top:0;left:0;width:100%;height:100%}@media only screen and (max-device-width:800px),only screen and (device-width:1024px) and (device-height:600px),only screen and (width:1280px) and (orientation:landscape),only screen and (device-width:800px),only screen and (max-width:767px){.flex-video{padding-top:0}} 2 | 3 | .crumb span[itemprop="itemListElement"]:not(:last-child):after { 4 | content: ' › '; 5 | } -------------------------------------------------------------------------------- /assets/includes/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /assets/includes/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /assets/includes/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /assets/includes/fonts/open-sans-latin-ext.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/fonts/open-sans-latin-ext.woff2 -------------------------------------------------------------------------------- /assets/includes/fonts/open-sans-latin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/fonts/open-sans-latin.woff2 -------------------------------------------------------------------------------- /assets/includes/img/completion-from-url.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/img/completion-from-url.gif -------------------------------------------------------------------------------- /assets/includes/img/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaxeFoundation/code-cookbook/9fae30fb591fb799ea993aa2332e2c9818937da0/assets/includes/img/share.png -------------------------------------------------------------------------------- /assets/includes/js/404.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mark Knol 3 | */ 4 | (function() { 5 | var pages = window.pages; 6 | var searchResults = []; 7 | var path = window.location.href.split("/").pop().split(".").shift(); 8 | console.log(path); 9 | for(var i=0;i"+searchResults[i].title+""; 28 | } 29 | html += "" 30 | document.getElementById("result404").innerHTML = html; 31 | } 32 | })(); -------------------------------------------------------------------------------- /highlighting.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -main Highlighting 3 | -lib hxnodejs 4 | -lib highlighter 5 | -js bin/patch.js 6 | -cmd node bin/patch.js 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-cookbook", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "code-cookbook", 9 | "version": "0.1.0", 10 | "devDependencies": { 11 | "cson-parser": "3.0.0", 12 | "vscode-oniguruma": "^2.0.1", 13 | "vscode-textmate": "^9.1.0" 14 | } 15 | }, 16 | "node_modules/coffeescript": { 17 | "version": "1.12.7", 18 | "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", 19 | "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==", 20 | "dev": true, 21 | "bin": { 22 | "cake": "bin/cake", 23 | "coffee": "bin/coffee" 24 | }, 25 | "engines": { 26 | "node": ">=0.8.0" 27 | } 28 | }, 29 | "node_modules/cson-parser": { 30 | "version": "3.0.0", 31 | "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-3.0.0.tgz", 32 | "integrity": "sha512-khmLtNmwe6SSlWz5vrhay9yWd/Fwwyiel+vt+1vIcCT9AsdqNuLXuK9tYhhAw1FdSSHjLc56PW8xa565/x73XQ==", 33 | "dev": true, 34 | "dependencies": { 35 | "coffeescript": "^1.10.0" 36 | } 37 | }, 38 | "node_modules/vscode-oniguruma": { 39 | "version": "2.0.1", 40 | "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", 41 | "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", 42 | "dev": true 43 | }, 44 | "node_modules/vscode-textmate": { 45 | "version": "9.1.0", 46 | "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.1.0.tgz", 47 | "integrity": "sha512-lxKSVp2DkFOx9RDAvpiYUrB9/KT1fAfi1aE8CBGstP8N7rLF+Seifj8kDA198X0mYj1CjQUC+81+nQf8CO0nVA==", 48 | "dev": true 49 | } 50 | }, 51 | "dependencies": { 52 | "coffeescript": { 53 | "version": "1.12.7", 54 | "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", 55 | "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==", 56 | "dev": true 57 | }, 58 | "cson-parser": { 59 | "version": "3.0.0", 60 | "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-3.0.0.tgz", 61 | "integrity": "sha512-khmLtNmwe6SSlWz5vrhay9yWd/Fwwyiel+vt+1vIcCT9AsdqNuLXuK9tYhhAw1FdSSHjLc56PW8xa565/x73XQ==", 62 | "dev": true, 63 | "requires": { 64 | "coffeescript": "^1.10.0" 65 | } 66 | }, 67 | "vscode-oniguruma": { 68 | "version": "2.0.1", 69 | "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", 70 | "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", 71 | "dev": true 72 | }, 73 | "vscode-textmate": { 74 | "version": "9.1.0", 75 | "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.1.0.tgz", 76 | "integrity": "sha512-lxKSVp2DkFOx9RDAvpiYUrB9/KT1fAfi1aE8CBGstP8N7rLF+Seifj8kDA198X0mYj1CjQUC+81+nQf8CO0nVA==", 77 | "dev": true 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-cookbook", 3 | "version": "0.1.0", 4 | "devDependencies": { 5 | "cson-parser": "3.0.0", 6 | "vscode-oniguruma": "^2.0.1", 7 | "vscode-textmate": "^9.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Config.hx: -------------------------------------------------------------------------------- 1 | import haxe.io.Path; 2 | 3 | class Config { 4 | public static final contentPath = "./assets/content/"; 5 | public static final outputPath = "./output/"; 6 | public static final repositoryUrl = switch (Sys.getEnv("REPO_URL")) { 7 | case null: "https://github.com/HaxeFoundation/code-cookbook/"; 8 | case v: v; 9 | }; 10 | public static final repositoryBranch = switch (Sys.getEnv("REPO_BRANCH")) { 11 | case null: "master"; 12 | case v: v; 13 | }; 14 | public static final basePath = switch (Sys.getEnv("BASEPATH")) { 15 | case null: ""; 16 | case v: v; 17 | }; 18 | public static final titlePostFix = " - Haxe programming language cookbook"; 19 | public static final cookbookFolder = Path.addTrailingSlash("cookbook"); 20 | public static final assetsFolderName = "assets"; 21 | public static final snippetsFolder = Path.addTrailingSlash("snippets"); 22 | } 23 | -------------------------------------------------------------------------------- /src/Highlighting.hx: -------------------------------------------------------------------------------- 1 | import haxe.Json; 2 | import highlighter.Highlighter; 3 | import sys.io.File; 4 | 5 | class Highlighting { 6 | public static function main() { 7 | Sys.println("Applying syntax highlighting ..."); 8 | 9 | // Convert CSON grammar to json for vscode-textmate 10 | File.saveContent("bin/javascript.json", Json.stringify(CSON.parse(File.getContent("grammars/language-javascript/grammars/javascript.cson")))); 11 | 12 | var grammarFiles = [ 13 | "haxe" => "grammars/haxe-TmLanguage/haxe.tmLanguage", 14 | "hxml" => "grammars/haxe-TmLanguage/hxml.tmLanguage", 15 | "html" => "grammars/xml.tmbundle/Syntaxes/XML.plist", 16 | "js" => "bin/javascript.json", 17 | "javascript" => "bin/javascript.json", 18 | ]; 19 | 20 | Highlighter.loadHighlighters(grammarFiles, function(highlighters) { 21 | // Go over the generated HTML file and apply syntax highlighting 22 | var missingGrammars = Highlighter.patchFolder(Config.outputPath, highlighters, function(classList) { 23 | return classList.substr(12); 24 | }); 25 | 26 | for (g in missingGrammars) { 27 | Sys.println('Missing grammar for "${g}"'); 28 | } 29 | 30 | // Add CSS rules for highlighting 31 | var path = Config.outputPath + "/css/haxe-nav.min.css"; 32 | var baseStyle = File.getContent(path); 33 | var syntaxStyle = highlighters["haxe"].runCss(); 34 | File.saveContent(path, baseStyle + syntaxStyle); 35 | }); 36 | } 37 | } 38 | 39 | @:jsRequire("cson-parser") 40 | extern class CSON { 41 | static function parse (content:String) : Dynamic; 42 | } 43 | -------------------------------------------------------------------------------- /src/Main.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | /** 4 | * @author Mark Knol 5 | */ 6 | class Main { 7 | public static function main() { 8 | haxe.Timer.measure(function() { 9 | var generator = new Generator(); 10 | 11 | generator.build(); 12 | generator.includeDirectory("assets/includes"); 13 | 14 | Redirections.generate(); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Redirections.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import haxe.Template; 3 | import haxe.io.Path; 4 | import sys.FileSystem; 5 | import sys.io.File; 6 | 7 | /** 8 | * Generate redirections for moved pages. 9 | * 10 | * @author Mark Knol 11 | */ 12 | class Redirections 13 | { 14 | public static function generate() 15 | { 16 | var list = [ 17 | "category/abstract-types/rounded float.html" => "/category/abstract-types/rounded-float.html", 18 | "documentation/index.html" => "/", 19 | "category/other/step-iterator.html" => "/category/data-structures/step-iterator.html", 20 | "category/other/reverse-iterator.html" => "/category/data-structures/reverse-iterator.html", 21 | "category/other/grid-iterator.html" => "/category/data-structures/grid-iterator.html", 22 | "category/other/sort-array.html" => "/category/data-structures/sort-array.html", 23 | "category/other/using-filesystem.html" => "/category/beginner/using-filesystem.html", 24 | "category/beginner/everything-is-an-expression.html" => "/category/principles/everything-is-an-expression.html", 25 | "category/other/using-haxe-classes-in-javascript.html" => "/category/javascript/using-haxe-classes-in-javascript.html", 26 | "other/using-haxe-classes-in-javascript.html" => "/category/javascript/using-haxe-classes-in-javascript.html", 27 | "tag/clas.html" => "/tag/class.html", 28 | "tag/arra.html" => "/tag/array.html", 29 | "tag/macro-function.html" => "tag/expression-macro.html", 30 | ]; 31 | 32 | for (page in list.keys()) { 33 | var template = new Template(File.getContent(Config.contentPath + "redirection.mtt")); 34 | var content = template.execute({ 35 | redirectionLink: list.get(page) 36 | }); 37 | 38 | // make sure directory exists 39 | FileSystem.createDirectory(Config.outputPath + Path.directory(page)); 40 | 41 | // save to output 42 | File.saveContent(Config.outputPath + page, content); 43 | 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/data/Category.hx: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | /** 4 | * @author Mark Knol 5 | */ 6 | @:keep 7 | class Category { 8 | public var parent:Category; 9 | public var title:String; 10 | public var outputPath:String; 11 | public var absoluteUrl:String; 12 | public var id:String; 13 | public var folder:String; 14 | public var pages:Array; 15 | public var isSerie:Bool; 16 | 17 | public var content:String; 18 | 19 | public function new(id:String, title:String, folder:String, pages:Array){ 20 | this.id = id; 21 | this.title = title; 22 | this.folder = folder; 23 | this.pages = pages; 24 | this.outputPath = 'category/$id/'; 25 | } 26 | 27 | public function getPageCount():Int { 28 | return [for (page in pages) if (page.visible && !page.isSerieHome()) page].length; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/data/Page.hx: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import data.Category; 4 | import haxe.io.Path; 5 | import util.GitUtil.GitAuthorInfo; 6 | import util.GitUtil.GitDates; 7 | 8 | /** 9 | * @author Mark Knol 10 | */ 11 | @:keep 12 | class Page { 13 | public var visible:Bool = true; 14 | public var title:String; 15 | public var description:String; 16 | public var templatePath:Path; 17 | public var contentPath:Path; 18 | public var outputPath:Path; 19 | public var customData:Dynamic; 20 | public var tags:Array; 21 | public var absoluteUrl:String; 22 | public var editUrl:String; 23 | public var commitHistoryUrl:String; 24 | public var addLinkUrl:String; 25 | public var contributionUrl:String; 26 | public var baseHref:String; 27 | public var dates:GitDates; 28 | public var category:data.Category; 29 | public var authors:Array = []; 30 | public var contributors:Array = []; 31 | public var snippets:Array = []; 32 | 33 | public var level:Int; 34 | 35 | //only available in series 36 | public var next:Page; 37 | public var prev:Page; 38 | 39 | public var pageContent:String; 40 | 41 | public function new(templatePath:String, contentPath:String, outputPath:String) { 42 | this.templatePath = new Path(templatePath); 43 | this.contentPath = contentPath != null ? new Path(contentPath) : null; 44 | this.outputPath = outputPath != null ? new Path(outputPath) : null; 45 | } 46 | 47 | public function setCustomData(data:Dynamic):Page { 48 | this.customData = data; 49 | return this; 50 | } 51 | 52 | public function setTitle(title:String):Page { 53 | this.title = title; 54 | return this; 55 | } 56 | 57 | public function setDescription(description:String):Page { 58 | this.description = description; 59 | return this; 60 | } 61 | 62 | public function setTags(tags:Array):Page { 63 | this.tags = tags; 64 | return this; 65 | } 66 | 67 | public function hidden() { 68 | visible = false; 69 | return this; 70 | } 71 | 72 | public function isSerieHome():Bool { 73 | return outputPath.toString().indexOf("index.html") != -1; 74 | } 75 | 76 | // Idea adapted from 77 | // Read time (in minutes) is based on the average reading speed of an adult: 275 words per minute 78 | public function getReadTime():Float { 79 | if (pageContent == null) return 0.0; 80 | var wordCount = pageContent.split(" ").length; // Dumb assumption, but seems to work 81 | var minutes = wordCount / 275; 82 | 83 | return if (minutes < 5) 84 | Math.fround(minutes * 2) / 2; // round to half 85 | else 86 | Math.round(minutes); // just round 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/data/TemplateData.hx: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | /** 4 | * @author Mark Knol 5 | */ 6 | @:structInit 7 | @:keep 8 | class TemplateData { 9 | public var title : String; 10 | public var tags : haxe.ds.StringMap>; 11 | public var sitemap : Array; 12 | public var seriePages : Int -> Array; 13 | public var pages : Array; 14 | public var pageContent : String; 15 | public var now : Date; 16 | public var readingTime : Float; 17 | public var latestCreatedPages : Int -> Array; 18 | public var getTagTitle : String -> String; 19 | public var getSortedTags : Void -> Array; 20 | public var currentPage : data.Page; 21 | public var currentCategory : data.Category; 22 | public var convertDate : Date -> String; 23 | public var basePath : String; 24 | public var DateTools : Class; 25 | } 26 | -------------------------------------------------------------------------------- /src/util/GitUtil.hx: -------------------------------------------------------------------------------- 1 | package util; 2 | import haxe.crypto.Md5; 3 | import haxe.ds.StringMap; 4 | import haxe.io.Path; 5 | 6 | using StringTools; 7 | using Lambda; 8 | 9 | /** 10 | * @author Mark Knol 11 | */ 12 | class GitUtil 13 | { 14 | static var now = Date.now(); 15 | 16 | static var dateStringCache:Map = new Map(); 17 | 18 | static function getCreationDate(path:String):Date { 19 | var dateString = getDateLog(path); 20 | if (dateString == null) return now; 21 | dateString = { 22 | var lines = dateString.split("\n"); 23 | if (lines[lines.length - 1].length > 2) lines[lines.length - 1]; 24 | else lines[lines.length - 2]; // last line is sometimes empty 25 | } 26 | return parseDateString(dateString, now); 27 | } 28 | 29 | static function getModificationDate(path:String):Date { 30 | var dateString = getDateLog(path); 31 | if (dateString == null) return now; 32 | dateString = dateString.split("\n").shift(); 33 | return parseDateString(dateString, now); 34 | } 35 | 36 | static function parseDateString(dateString:Null, fallback:Date) { 37 | if (dateString == null || dateString.length < 10) return fallback; 38 | dateString = dateString.trim(); 39 | if (dateString.length != 10) throw 'invalid date: ' + dateString; // skip non-versioned files 40 | var splitted = dateString.split("-"); 41 | return new Date(Std.parseInt(splitted[0]), Std.parseInt(splitted[1]) - 1, Std.parseInt(splitted[2]), 1, 1, 1); 42 | } 43 | 44 | static function getDateLog(path:String):String { 45 | return if (dateStringCache.exists(path)) { 46 | dateStringCache[path]; 47 | } else { 48 | var process = new sys.io.Process('git log --date=short --format=%ad --follow -- "$path"'); 49 | if (process.exitCode() != 0) throw process.stderr.readAll().toString(); 50 | var dateString = process.stdout.readAll().toString(); 51 | dateStringCache[path] = dateString; 52 | dateString; 53 | } 54 | } 55 | 56 | public static function getAuthors(path:String, authorByName:StringMap):Array { 57 | #if (!display && !disable_git_authors) 58 | var process = new sys.io.Process('git log --follow --pretty=format:"%aN <%aE>" -- "$path"'); 59 | if (process.exitCode() != 0) throw process.stderr.readAll().toString(); 60 | var log = process.stdout.readAll().toString(); 61 | if (log == null || log.length == 0) return []; 62 | var ereg = ~/(.+?) <(.+?)>/g; 63 | var authors:Array = []; 64 | while (ereg.match(log)) { 65 | log = ereg.matchedRight(); 66 | var name = ereg.matched(1); 67 | if (name.toLowerCase() == "github") continue; 68 | 69 | var author:GitAuthorInfo = authorByName.exists(name) ? authorByName.get(name) : { name: name }; 70 | author.email = ereg.matched(2); 71 | author.hash = Md5.encode(ereg.matched(2).toLowerCase()); 72 | if (!authors.exists(function (a) return a.email == author.email)) { 73 | authors.push(author); 74 | } 75 | authorByName.set(name, author); 76 | } 77 | return authors; 78 | #else 79 | return []; 80 | #end 81 | } 82 | 83 | public static function getStat(path:String):GitDates { 84 | #if (!display && disable_git_dates) 85 | return { 86 | modified: now, 87 | created: now, 88 | } 89 | #else 90 | return { 91 | modified: getModificationDate(path), 92 | created: getCreationDate(path), 93 | } 94 | #end 95 | } 96 | } 97 | 98 | typedef GitDates = { 99 | modified: Date, 100 | created: Date, 101 | } 102 | 103 | typedef GitAuthorInfo = { 104 | ?username: String, 105 | ?profileLink: String, 106 | ?name: String, 107 | ?email: String, 108 | ?hash: String, 109 | } 110 | -------------------------------------------------------------------------------- /src/util/Minifier.hx: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author Mark Knol 5 | */ 6 | class Minifier { 7 | public static inline function minify(content:String){ 8 | // adapted from http://stackoverflow.com/questions/16134469/minify-html-with-boost-regex-in-c 9 | return new EReg("(?ix)(?>[^\\S]\\s*|\\s{2,})(?=[^<]*+(?:<(?!/?(?:textarea|pre|script|code)\\b)[^<]*+)*+(?:<(?>textarea|pre|script|code)\\b|\\z))", "ig").replace(content, " ");// .split("> <").join("><"); 10 | } 11 | 12 | public static inline function removeComments(content:String){ 13 | // adapted from http://stackoverflow.com/questions/16134469/minify-html-with-boost-regex-in-c 14 | return new EReg("", "g").replace(content, ""); 15 | } 16 | } -------------------------------------------------------------------------------- /utterances.json: -------------------------------------------------------------------------------- 1 | { 2 | "origins": ["http://localhost","https://code.haxe.org"] 3 | } 4 | --------------------------------------------------------------------------------