This document describes how Haxe code is compiled to native executables via the Zyntax compiler.
┌─────────────┐
│ Haxe Source │
└──────┬──────┘
│
│ Haxe Compiler
▼
┌─────────────────┐
│ Haxe Typed AST │
└──────┬──────────┘
│
│ Reflaxe.Zyntax
▼
┌──────────────────────┐
│ TypedAST JSON Files │
└──────┬───────────────┘
│
│ Zyntax CLI
▼
┌─────────────┐
│ Zyntax HIR │
└──────┬──────┘
│
│ Cranelift/LLVM
▼
┌──────────────────┐
│ Native Executable│
└──────────────────┘
A Reflaxe backend that translates Haxe's typed AST into Zyntax TypedAST format as JSON.
Location: reflaxe.zyntax/
Key Files:
src/zyntax/ZyntaxCompiler.hx- Main AST translatorsrc/zyntax/ZyntaxCompilerInit.hx- Compiler initializationextraParams.hxml- Auto-included compiler settingshaxelib.json- Package metadata
Command-line tool that consumes TypedAST JSON and compiles to native code.
Location: crates/zyntax_cli/
Key Files:
src/main.rs- CLI implementation with subcommands
The Reflaxe.Zyntax backend runs as a Haxe compiler macro and outputs one JSON file per class/enum:
// Input: HelloWorld.hx
class HelloWorld {
static function main() {
trace("Hello!");
}
}// Output: HelloWorld.json
{
"type": "class_declaration",
"name": "HelloWorld",
"methods": [
{
"type": "method",
"name": "main",
"params": [],
"return_type": {"type": "primitive", "kind": "Unit"},
"body": { ... },
"is_static": true
}
]
}The Zyntax CLI deserializes JSON into TypedAST structs (via serde) and converts to HIR:
// Deserialize JSON
let program: TypedProgram = serde_json::from_str(&json_content)?;
// Convert to HIR (TODO: implement full conversion)
let hir_module = typed_ast_to_hir(&program)?;The HIR is compiled using the tiered JIT/AOT system:
// JIT compilation
let mut backend = CraneliftBackend::new()?;
backend.compile_module(&hir_module)?;
// Or AOT compilation
// llvm_backend.compile_to_object(&hir_module, "output.o")?;| Haxe Type | TypedAST JSON | Zyntax HIR Type |
|---|---|---|
Int |
{"type": "primitive", "kind": "I32"} |
HirType::I32 |
Float |
{"type": "primitive", "kind": "F64"} |
HirType::F64 |
Bool |
{"type": "primitive", "kind": "Bool"} |
HirType::Bool |
String |
{"type": "primitive", "kind": "String"} |
HirType::Pointer |
Void |
{"type": "primitive", "kind": "Unit"} |
HirType::Unit |
Array<T> |
{"type": "array", "element": T} |
Custom struct |
Class |
{"type": "nominal", "name": "..."} |
Custom struct |
Enum |
{"type": "enum", "name": "..."} |
Tagged union |
Function |
{"type": "function", "params": [...]} |
Function type |
// Haxe
var sum = a + b;// TypedAST JSON
{
"type": "binary",
"operator": "add",
"left": {"type": "variable", "name": "a"},
"right": {"type": "variable", "name": "b"}
}// HIR (pseudocode)
let sum = builder.add(a, b, HirType::I32);// Haxe
var result = fibonacci(10);// TypedAST JSON
{
"type": "call",
"callee": {"type": "identifier", "name": "fibonacci"},
"arguments": [
{"type": "literal", "kind": "integer", "value": 10}
]
}// HIR
let ten = builder.iconst(10, HirType::I32);
let result = builder.call(fibonacci_fn, vec![ten]);// Haxe
if (x > 0) {
trace("positive");
} else {
trace("non-positive");
}// TypedAST JSON
{
"type": "if",
"condition": {
"type": "binary",
"operator": "gt",
"left": {"type": "variable", "name": "x"},
"right": {"type": "literal", "value": 0}
},
"then_branch": { ... },
"else_branch": { ... }
}// HIR
let cmp = builder.icmp(IcmpOp::Gt, x, zero);
let then_block = builder.create_block("then");
let else_block = builder.create_block("else");
let merge = builder.create_block("merge");
builder.br_if(cmp, then_block, else_block);# Install Reflaxe
haxelib install reflaxe 4.0.0-beta
# Install reflaxe.zyntax (development mode)
cd reflaxe.zyntax
haxelib dev reflaxe.zyntax .
# Build Zyntax CLI
cd ..
cargo build --package zyntax_cli --release# Create Haxe build file
cat > build.hxml <<EOF
-lib reflaxe.zyntax
-main Main
-D zyntax-output=output
EOF
# Compile Haxe to TypedAST JSON
haxe build.hxml
# Compile TypedAST JSON to native
cargo run --package zyntax_cli -- compile output/*.json -o myprogram
# Or with the binary directly
./target/release/zyntax compile output/*.json -o myprogram --runSee reflaxe.zyntax/test/ for a complete example:
cd reflaxe.zyntax/test
./compile.sh✅ Type mapping - Primitives, classes, enums, functions ✅ Expression translation - Literals, variables, binary ops, calls ✅ Statement translation - Var decls, assignments, returns ✅ Control flow - If/else, while loops ✅ JSON serialization - Complete TypedAST to JSON
❌ HIR conversion - TypedAST JSON → HIR is stubbed ❌ Standard library - Haxe std lib → Zyntax runtime ❌ Generics - Type parameter instantiation ❌ Closures - Capture analysis and lifting ❌ Pattern matching - Switch/match translation ❌ Async/await - Async runtime integration ❌ Reflection - Runtime type information ❌ Execution - Actually running JIT-compiled code
The immediate priority is implementing the TypedAST JSON → HIR conversion in crates/zyntax_cli/src/main.rs:
fn typed_ast_to_hir(program: &TypedProgram) -> Result<HirModule, Box<dyn std::error::Error>> {
// TODO: Implement proper TypedAST -> HIR conversion
// For now, create an empty module
let mut arena = AstArena::new();
let builder = HirBuilder::new("main", &mut arena);
Ok(builder.finish())
}This needs to:
- Iterate through
program.declarations - For each function declaration:
- Create HIR function signature
- Convert body expressions to HIR instructions
- Handle parameters and return types
- For each class/enum:
- Register types in type system
- Convert methods to HIR functions
- Build complete
HirModulewith all functions
Once this is implemented, we'll have a complete Haxe → Native compilation pipeline!
Haxe-side tests verify JSON output:
cd reflaxe.zyntax/test
haxe test.hxml
# Verify JSON files match expected structureEnd-to-end tests compile and run programs:
cd reflaxe.zyntax/test
./compile.sh
# Should compile and execute HelloWorldPerformance comparisons with other Haxe backends:
# Haxe/C++
haxe -cpp out -main Benchmark
time ./out/Benchmark
# Haxe/Zyntax
haxe -lib reflaxe.zyntax -D zyntax-output=out -main Benchmark
zyntax compile out/*.json -o bench
time ./bench