>_ENGINEERING REPORT
scalars is a Scala frontend in Rust, hosted on the fusevm bytecode VM and its three-tier Cranelift JIT. This report describes the architecture, the value model, the current state of the tree, and the dependency posture. The statements below are facts about the design and the manifest, not aspirational metrics.
Summary
The reference Scala compiles to JVM bytecode and runs on HotSpot. scalars takes a different path: it lexes and parses Scala to an AST, lowers that to fusevm bytecode, and runs it on a compiled VM with a Cranelift JIT — no JVM. Arithmetic and comparison operators lower to native VM ops so the JIT can trace hot loops; Scala-specific behaviour is served by a strict numeric hook through a numbered builtin-call protocol. It joins fusevm alongside zshrs, stryke, awkrs, elisp, rubylang, and pythonrs, and reuses that shared bytecode VM and JIT rather than shipping its own.
The current tree implements the newline-inferring lexer, the entry-object parser, AST-to-bytecode lowering, val/var bindings, if/while/for (range, collection and yield forms), println/print, and the arithmetic/comparison/boolean and bitwise/shift operators with faithful Int-vs-Double division dispatch, String + concatenation, structural ==, and Java Double.toString formatting. On top of that it carries user-defined methods (including block-local ones), classes / objects / case classes / traits with inheritance, virtual dispatch and constructor pattern matching, try/catch/throw, first-class functions and partial functions, and the collection library — List/Seq/Vector/Set/Map/Array/Range plus the scala.collection.mutable buffers, sets and maps, each reproducing Scala’s own iteration order. It also ships editor and debugger tooling — an LSP server (--lsp) and a source-line DAP debugger (--dap) — plus token/AST/bytecode introspection and a differential parity fuzzer against a real Scala toolchain. See BUGS.md for the honest ledger of what is not carried.
Hosting on fusevm
scalars contains no virtual machine, JIT, or JVM of its own. The execution path is:
Scala source → lexer → parser → AST → lower to fusevm bytecode → fusevm VM + Cranelift JIT
│
host: strict numeric hook (String +, Int/Double /, ==) + Predef print
Shared engine
fusevm is pulled from crates.io with the jit, jit-disk-cache, and aot features. JIT and VM improvements land once and benefit zshrs, stryke, awkrs, elisp, rubylang, pythonrs, and scalars together.
Native arithmetic
Operators lower to native fusevm ops; the strict numeric hook supplies Scala semantics (String concatenation, structural comparison, Long wrapping, Double promotion past 253) only for the cases the VM can't compute natively.
Runtime division dispatch
fusevm's native Op::Div always floats, but Scala's / truncates for two Ints and floats when either operand is a Double. A type-dispatching SDIV builtin makes the choice at runtime (src/host.rs).
Persistent native code
jit-disk-cache persists compiled machine code across runs, keyed by chunk hash, so warm runs of hot loops skip recompilation.
Value model
Slice-1 scalars runs on the fusevm value model directly, with no object heap of its own yet. Int rides through the VM as a native integer, Double as a float, Boolean as a bool, String as a VM string, and the null literal as Value::Undef. Locals are addressed by name through GetVar/SetVar — a single entry frame with no lexical scopes, so the lowering stays direct and readable.
Two places need Scala semantics fusevm's default awk/shell flavour does not provide. Printing: println/print lower to a registered builtin that formats through Scala rules (true/false, whole doubles with a trailing .0, null), and Double.toString reproduces Java's decimal-vs-scientific notation threshold faithfully. Operator overloading: once a numeric hook is installed the VM runs strict, delegating any operation with a non-numeric operand to the host — where + concatenates when a String is involved and ==/!=/ordering compare structurally. Strict mode also delegates the two numeric cases an f64 cannot answer exactly: integer overflow, where the host wraps as Scala's Long does, and a mixed Int/Double pair whose integer is past 253, where the host applies Scala's binary numeric promotion (so 16677181699666569L == 1.6677181699666568E16 is true, matching reference scala).
Component status
| Component | State | Notes |
|---|---|---|
| lexer / parser → AST | Implemented | Newline-inferring tokenizer; single entry-object grammar (src/lexer.rs, src/parser.rs). |
| AST → fusevm bytecode | Implemented | Native arithmetic + host hook; no local VM, no JVM (src/compiler.rs). |
val/var, if/while/range for | Implemented | Bindings, assignment, compound assignment, counted range loops (src/compiler.rs). |
Int/Double numerics, String +, structural == | Implemented | Runtime division dispatch, Java Double.toString, concatenation coercion (src/host.rs). |
println/print | Implemented | Scala-formatted output through the Predef print builtins (src/host.rs). |
introspection (--dump-tokens/--dump-ast/--disasm) | Implemented | Token stream, AST, and fusevm bytecode listing (src/main.rs). |
LSP server (--lsp) | Implemented | Keyword/Predef/operator completion + hover from the corpus, live diagnostics from the real parser (src/lsp.rs). |
DAP adapter (--dap) | Implemented | Per-statement line markers; breakpoints, stepping, stack/scopes/variables, stdout forwarding (src/dap.rs). |
parity fuzzer vs reference scala | Implemented | Differential probes diffed against a real Scala toolchain, frozen for CI (src/bin/parity_fuzz.rs). |
generated reference (gen-docs) | Implemented | docs/reference.html emitted from the LSP corpus (src/bin/gen_docs.rs). |
| user methods / classes / traits | Implemented | Block-local defs with lambda lifting, case class pattern matching, trait mixin and virtual dispatch (src/resolve.rs, src/compiler.rs). |
| collections (immutable + mutable) | Implemented | The combinator set, Scala’s CHAMP HashSet/HashMap trie order, and the mutable buffers/sets/maps in their hash table’s order (src/host.rs). |
lazy views / Iterator / LazyList | Planned | .view, .iterator and user Orderings pending. |
Dependency posture
Dependencies are kept foundational and durable — the goal is a crate that still builds cleanly years from now. Direct dependencies from Cargo.toml:
| Crate | Role |
|---|---|
| fusevm | Language-agnostic bytecode VM + three-tier Cranelift JIT (jit, jit-disk-cache, aot) |
| thiserror | Error type derivation |
| lsp-server / lsp-types | LSP transport + protocol types (--lsp server) |
| serde / serde_json / libc | DAP/LSP JSON-RPC plumbing; libc pipe + dup2 to capture debuggee stdout during --dap |
Compatibility & longevity
Reference semantics
Behaviour tracks the reference scala — Int / truncates toward zero, Double.toString matches Java's notation, == is structural — verified by a differential fuzzer against a real Scala toolchain.
Cross-architecture
macOS aarch64 and Linux x86_64 / aarch64 via the Cranelift JIT; portable bytecode underneath, no JVM required.
Standalone crate
An explicit empty [workspace] keeps scalars buildable on its own, independent of the meta repo.
Honest ledger
BUGS.md tracks documented slice-1 gaps (integer division by zero, val-reassignment, unsupported member calls) rather than hiding them.
Links
- Docs — index.html
- Language reference — reference.html
- Source — github.com/MenkeTechnologies/scalars
- fusevm — github.com/MenkeTechnologies/fusevm (the shared VM)
- License — MIT (LICENSE)