Skip to content
ALB Wiki

MALBR

This is an old revision of this page — revision 3 (2026-10-15 14:56:39 UTC). It may differ from the current revision.

MALBR — Micro ALB Rust — is the second compiler in the AdaLogic BASIC toolchain, 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. 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:

CodeTrap
21REQUIRE contract violated
22ENSURE contract violated
23range-type bound violated
24checked + − * overflow
25checked / MOD — division by zero or MIN/-1
26unset routine-typed field invoked
27ASSERT failed
74--strict-io I/O failure
101panic — 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:

FileRole
support.rsfacade — ALB_ABI_VERSION = 1 plus two include!s; never edited
alb_core.rstier 1 — the port: _alb_putchar, _alb_print_*, _alb_exit, _alb_fault
alb_host.rstier 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

  • NOT keeps classic-BASIC precedence — looser than comparisons, tighter

than AND; NOT x = 5 means NOT (x = 5)

  • AND/OR never short-circuit; ANDALSO/ORELSE do
  • PRINT truncates floats to six decimals — display only, not the value
  • READ(h, 0) reads the rest of the file; both ELSEIF and ELSIF parse
  • 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

The MALBR mark — rusted gear, rusted letters
The MALBR mark — rusted gear, rusted letters

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:

The three-stage bootstrap — a byte-identical fixpoint
  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

CompilerWritten inEmitsRole
albtAdaFASM, C, and 14 more targetsreference compiler — batteries included
malbrAda#![no_std] Rustembedded/foreign targets — rustc does the rest
malbALBRustself-hosted twin of malbr — early access

See also

References

  1. "Update: MALBR and MALB — a second compiler, and ALB written in ALB," ALB repository, docs/updates/2026-10-malbr-malb-announcement.md (October 2026).
  2. MALBR Dialect Specification — Micro ALB, ALB repository, docs/MALBR_SPEC.md — the normative dialect contract.
  3. MALBR Internals — what the generated Rust actually looks like, docs/MALBR_INTERNALS.md.
  4. MALBR Porting Guide — taking ALB to a new target, docs/MALBR_PORTING.md.
  5. MALBR ABI, docs/MALBR_ABI.md — the support.rs two-tier contract.
  6. MALBR Roadmap — Micro ALB compiler → production, docs/MALBR_ROADMAP.md.
  7. ALB source — github.com/rockyoliver454/AdaLogic-BASIC-ALB-Programming-Language — Apache 2.0, including src/malbr.adb, src/EMITTER/emit_native_micro_rust.adb, and MALBR_BS/malb.alb.
  8. 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 albt already 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/OR do not?
  • Approximately how accurate are MALBR's sin/cos/exp/ln implementations?
  • 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.

This page was last edited 2026-10-15 14:56:39 UTC by ALB Team. What links here