MALBR
MALBR — Micro ALB Rust — is the second compiler in the AdaLogic BASIC™ toolchain — ALB, also PolyGlot BASIC™ (PGB) — and the path by which ALB reaches machines its reference compiler cannot. Where albt is a complete, batteries-included compiler that assembles FASM directly and serves the retro backends, MALBR is deliberately small: it translates ALB source into clean, deterministic #![no_std] Rust and hands the result to rustc, which brings cross-compilation to dozens of targets, a mature optimizer, bare-metal support, and an enormous linker ecosystem. The dialect it accepts — Micro ALB — is a deliberately small, spec-frozen subset of the full ALB/PGB language, so the compiler itself stays auditable. MALBR does not try to be smarter than rustc — it emits predictable code and lets the backend machinery do what it was built to do.
Its sibling is MALB: the same ALB→Rust compiler written in ALB itself, roughly 3,900 lines of it, compiled first by MALBR and then by itself — proven self-hosting through a byte-identical bootstrap fixpoint. Together they move ALB toward a two-flagship toolchain: albt as the reference compiler, and the MALBR/MALB pair as the road to embedded targets, foreign operating systems, and a compiler maintained in the language it compiles.
Why a second compiler
albt knows the whole language — the full builtin set, the special blocks, the sixteen targets from native x86-64 down to 6502 assembly. What it cannot easily do is reach the places ALB increasingly wants to go: small boards, embedded targets, toolchains the project does not own. A FASM-centric pipeline cannot piggyback on someone else's backend ecosystem.
MALBR was created to fix exactly that gap. Rust already solves cross-compilation, optimization, and no_std bare-metal deployment — so MALBR emits Rust source rather than machine code, keeping itself small and auditable while inheriting everything rustc knows about target triples, linking, and code generation. The trade is explicit: one more language in the pipeline, in exchange for the widest backend reach a modern toolchain offers.
What the output looks like
Every MALBR program compiles to a crate with a fixed shape:
The program body lands in fn _alb_main_body(), behind a #[no_mangle] pub extern "C" fn main() that calls it and exits through support::_alb_exit(0). File-scope LET declarations become static mut with unsafe access; routine-local LETs stay ordinary let muts. String handling runs through a static arena — 64 KiB by default, resizable with --arena <bytes> — with CLAIM, SWEEP, and DROP as checkpoint/rewind/release controls. There is no heap and no crates.io dependency: the binary can be audited byte-for-byte and flashed directly.
Fault codes — one trap door
Every runtime trap in generated code funnels into a single function, support::_alb_fault(code) — the override point embedded ports replace:
| Code | Trap |
|---|---|
| 21 | REQUIRE contract violated |
| 22 | ENSURE contract violated |
| 23 | range-type bound violated |
| 24 | checked + − * overflow |
| 25 | checked / MOD — division by zero or MIN/-1 |
| 26 | unset routine-typed field invoked |
| 27 | ASSERT failed |
| 74 | --strict-io I/O failure |
| 101 | panic — bounds, arena exhaustion, unreachable |
A bare-metal port can hang, log over UART, or blink an LED on any of these — the generated code never decides the policy.
The support ABI — porting is six functions
MALBR's runtime boundary is support.rs, a three-file facade:
| File | Role |
|---|---|
support.rs | facade — ALB_ABI_VERSION = 1 plus two include!s; never edited |
alb_core.rs | tier 1 — the port: _alb_putchar, _alb_print_*, _alb_exit, _alb_fault |
alb_host.rs | tier 2 — files (memfs by default, hostfs via --hostfs), stdin, args, env, exec, clock, dir |
To take ALB to a new board you replace tier 1 — roughly six functions — and optionally tier 2 for environment services. Generated code only ever talks to support::_alb_*, so a port never touches emitted source. This is the same strategy small language ports have used since the early byte-code machines: separate what the program means from what the machine can do.
Semantics are the spec
MALBR's contract is behavioral, not structural. The test suite diffs real program output — a semantic drift is a test failure, not a shrug. The details it guarantees are the ones a user would notice:
|stays string concatenation (and&its alias); bitwise work uses
AND/OR/XOR keywords, ^ is exponentiation
NOTkeeps classic-BASIC precedence — looser than comparisons, tighter
than AND; NOT x = 5 means NOT (x = 5)
AND/ORnever short-circuit;ANDALSO/ORELSEdoPRINTtruncates floats to six decimals — display only, not the valueREAD(h, 0)reads the rest of the file; bothELSEIFandELSIFparse- reserved words error with line/column instead of cascading parse failures
Float math without a standard library
#![no_std] means no libm, so MALBR carries its own: Cody–Waite argument reduction plus minimax polynomials give sin, cos, atan, exp, and ln to about 1 ulp, with tan, asin, acos, atan2, and pow in the 2–3 ulp range. floor, ceil, trunc, round, and sqrt are integer casts with Newton fixups — the core intrinsics would pull in std. For an embedded BASIC, that is an unusually serious numerics story: see Scientific notation and History of Mathematics for why floating point conventions mattered enough to standardize.
MALB — ALB written in ALB

Once MALBR could compile real programs, one question became inevitable: can ALB compile its own compiler? MALB (MALBR_BS/malb.alb, about 3,900 lines) is the answer — a complete ALB→Rust compiler written in the language it compiles. The self-hosting claim is proven the hard way, not asserted:
malbr.exe builds malb.exe (stage 1 — Ada backend) malb.exe compiles malb.alb -> malb2.rs (stage 2) malb2.exe compiles malb.alb -> malb3.rs (stage 3) malb2.rs == malb3.rs — identical, byte for byte
When a compiler's own 3,900-line source is the test program and the stage-2 and stage-3 outputs agree exactly, "almost right" has nowhere to hide. This is the same fixpoint argument classic self-hosting compilers — from Pascal's P-system to GCC's three-stage build — have used to demonstrate correctness; see Compilers for the tradition.
Coverage and status
MALB currently passes 209 of 209 output-verified fixtures — emit → rustc → run → diff — including MD5, FFT, Jaro–Winkler, N-queens, maze generation, FFI calls into kernel32, and the semantics regression tests (short-circuit behavior, NOT precedence, checked overflow, contracts). The language coverage is full: modules and INCLUDE, contracts (REQUIRE/ENSURE/OLD), structs, enums, bitfields, range types, routine references and REF, floats, multi-dimensional arrays, SELECT, ENABLE RUST, and the complete builtin set.
The CLI is deliberately boring. MALB is in early access; the roadmap's priorities are performance of the self-built binary, --map-style audit output, and then the point of the exercise — malbr.adb freezing forever as the bootstrap compiler, with ALB's compiler maintained in ALB from that day forward.
Where it sits in the toolchain
| Compiler | Written in | Emits | Role |
|---|---|---|---|
albt | Ada | FASM, C, and 14 more targets | reference compiler — batteries included |
malbr | Ada | #![no_std] Rust | embedded/foreign targets — rustc does the rest |
malb | ALB | Rust | self-hosted twin of malbr — early access |
See also
- AdaLogic BASIC — the language these compilers serve
- ALB Ada backend — sibling backend in the same Ada toolchain
- ALB FASM backend — the workhorse native x86-64 target
- Rust — the language MALBR emits
- Ada Programming Language — the language both
albtandmalbrare written in - Compilers — self-hosting and bootstrap traditions
- Forth — another language that took tiny, auditable toolchains to telescopes and space probes
References
- "Update: MALBR and MALB — a second compiler, and ALB written in ALB," ALB repository,
docs/updates/2026-10-malbr-malb-announcement.md(October 2026). - MALBR Dialect Specification — Micro ALB, ALB repository,
docs/MALBR_SPEC.md— the normative dialect contract. - MALBR Internals — what the generated Rust actually looks like,
docs/MALBR_INTERNALS.md. - MALBR Porting Guide — taking ALB to a new target,
docs/MALBR_PORTING.md. - MALBR ABI,
docs/MALBR_ABI.md— thesupport.rstwo-tier contract. - MALBR Roadmap — Micro ALB compiler → production,
docs/MALBR_ROADMAP.md. - ALB source — github.com/rockyoliver454/AdaLogic-BASIC-ALB-Programming-Language — Apache 2.0, including
src/malbr.adb,src/EMITTER/emit_native_micro_rust.adb, andMALBR_BS/malb.alb. - ALB Handbook — alb-lang.org/handbook — the language reference shared by every backend.
Quiz — MALBR and MALB
- What does "MALBR" stand for, and what language does it emit?
- Why does ALB need a second compiler when
albtalready targets sixteen backends? - What two attributes appear at the top of every MALBR-generated crate?
- How large is MALBR's default string arena, and which flag resizes it?
- Name the function every runtime trap routes through, and give three fault codes.
- How many functions does a minimal MALBR board port require, and in which file do they live?
- In ALB, what does
|mean — and how does MALBR treat it in generated Rust? - Which ALB keywords provide short-circuit logic, since
AND/ORdo not? - Approximately how accurate are MALBR's
sin/cos/exp/lnimplementations? - What is MALB, and roughly how many lines of ALB is it?
- Describe the three stages of the MALB bootstrap and what equality proves.
- How many output-verified fixtures does MALB currently pass?
Answers
MALBR = Micro ALB Rust; it emits Rust source. albt cannot piggyback on rustc's cross-compilation, optimizer, and no_std ecosystem — MALBR fills that gap. Every crate starts #![no_std]/#![no_main. The arena is 64 KiB by default, resized with --arena. All traps route through support::_alb_fault(code) — e.g. 21 REQUIRE, 24 overflow, 101 panic. A minimal port is about six functions in alb_core.rs. | is string concatenation, and MALBR preserves that in Rust. ANDALSO and ORELSE short-circuit. The libm-grade functions are ~1 ulp (2–3 for the rest). MALB is the ALB→Rust compiler written in ALB, about 3,900 lines. Bootstrap: malbr builds malb.exe; malb.exe compiles malb.alb into malb2.rs/malb2.exe; malb2.exe compiles malb.alb into malb3.rs; and malb2.rs == malb3.rs byte-for-byte — the fixpoint that proves the compiler implements the language correctly. The suite: 209/209.