| Crates.io | kz80_ml |
| lib.rs | kz80_ml |
| version | 0.1.1 |
| created_at | 2025-12-17 17:57:35.621572+00 |
| updated_at | 2025-12-17 17:57:35.621572+00 |
| description | Tiny ML compiler for the Z80 processor with Hindley-Milner type inference |
| homepage | |
| repository | https://github.com/ajokela/kz80_ml |
| max_upload_size | |
| id | 1990850 |
| size | 412,576 |
A Tiny ML compiler for the Z80 processor with Hindley-Milner type inference.
cargo build --release
kz80_ml input.ml -o output.bin
Options:
-o <file>: Output binary file (default: a.out)-: Read source from stdinint - 16-bit signed integerdecimal - Fixed-point decimal with 2 decimal places (stored as 4-byte BCD)bool - Boolean (true/false)42 (* integer *)
3.14 (* decimal *)
true (* boolean *)
false
Arithmetic: +, -, *, /, mod
Comparison: ==, !=, <, <=, >, >=
Logical: not
let x = 42
let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1)
Functions are curried:
let add a b = a + b
let main = print_int (add 3 4) (* prints 7 *)
if condition then expr1 else expr2
match n with
| 0 -> 1
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
print_int e - Print an integerprint_decimal e - Print a decimal (displayed as integer, e.g., 3.14 shows as 314)let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
let main = print_int (factorial 6) (* prints 720 *)
let rec fib n =
match n with
| 0 -> 0
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
let main = print_int (fib 10) (* prints 55 *)
let price = 19.99
let quantity = 5
(* quantity (int) is auto-coerced to decimal *)
let subtotal = price * quantity
let main = print_decimal subtotal (* prints 9995, meaning 99.95 *)
let rec collatz n =
match n with
| 1 -> 0
| _ -> if n / 2 * 2 == n
then 1 + collatz (n / 2)
else 1 + collatz (3 * n + 1)
let main = print_int (collatz 27) (* prints 111 *)
Designed for the RetroShield Z80, but should work on any Z80 system with the above memory layout and I/O port 0x01 for character output.
The project includes a comprehensive test suite with three levels of testing:
Unit tests for the lexer, parser, type inference, and code generator:
cargo test
This runs 36 unit tests covering tokenization, parsing, type checking, and REPL code generation.
End-to-end tests that generate REPL binaries and run them through the Z80 emulator:
cargo test --test integration_tests
These 21 tests verify the REPL works correctly for arithmetic, variables, functions, conditionals, and more. Requires the emulator at ../emulator/retroshield.
Test programs written in the ML language itself:
./tests/ml/run_tests.sh
This runs 29 ML programs through the batch compiler and emulator, covering:
| Category | Tests |
|---|---|
| Arithmetic | +, -, *, /, %, negation |
| Precedence | operator precedence, parentheses |
| Comparisons | <, >, <=, >=, == |
| Control Flow | nested if-then-else |
| Let Bindings | simple, multiple, let-in |
| Functions | single/multi-arg, composition |
| Recursion | factorial, fibonacci |
Each test file contains an expected result comment that the runner verifies:
(* Test: Addition *)
(* Expected: 8 *)
let main = print_int (3 + 5)
The on-target REPL supports additional features not available in the batch compiler:
| Feature | Batch Compiler | REPL |
|---|---|---|
| Arithmetic | Yes | Yes |
| Comparisons | Yes | Yes |
| Functions | Yes | Yes |
| Recursion | Yes | Yes |
Bitwise (&, ` |
, ^`) |
No |
Shifts (<<, >>) |
No | Yes |
Logical (and, or, not) |
No | Yes |
Hex literals (0xFF) |
No | Yes |
Not equal (!=) |
No | Yes |
BSD-3-Clause