Expand description
§bcgen_lang
Compiles HIR (the representation every LexerSketch language lowers into)
to LSB, the bytecode bvm-lang executes.
§The lazy path
Build or lower a resolved unit, compile it, and hand the module to a
VM:
use hir_lang::{Builder, Name, OpKind};
use intern_lang::Interner;
// fn add(a, b) { a + b }
let mut names = Interner::new();
let mut b = Builder::new();
let (pa, a) = b.local_param(Name::new(names.intern("a")));
let (pb, bb) = b.local_param(Name::new(names.intern("b")));
let (ua, ub) = (b.use_binder(a), b.use_binder(bb));
let sum = b.op(OpKind::Add, &[ua, ub]);
let body = b.block(&[], Some(sum));
let add = b.func(Name::new(names.intern("add")), &[pa, pb], body);
let root = b.module(None, &[add]);
let hir = b.finish(root)?;
let out = bcgen_lang::compile(&hir, &names)?;
let module = out.module();
assert!(module.function(out.function(add).unwrap()).is_some());
assert!(!bytecode_lang::encode(module).is_empty());Running it (with bvm-lang): Program::load(out.into_module(), &host),
then vm.run(func, &[Value::Int(2), Value::Int(3)]) returns Int(5).
§What it compiles
HIR spec v3 (hir-lang 0.4) to LSB format 2 (bytecode-lang 0.3), in the
dynamic profile: every value is an LSB dyn. Functions with defaults
(per call or once), named, variadic, and by-reference parameters, each
carrying its LSB parameter list so dynamic calls bind in the VM; locals in
registers, shared variables in LSB references; closures with explicit,
implicit, and recursive captures; generators (keyed yield, yield from), await, and spawn; if, the loop primitive with its step,
labeled break/continue, return; match by a sequential decision
procedure, raising NoMatch when no arm matches; direct, dynamic, host,
and method calls; maps and arrays with PHP value semantics, separation
that copies only what is shared, and key coercion (or shared references);
let_place and PHP references into array elements; every OPS v2
operation with its policy (pow, promote, shift = saturate);
union and intersection type tests and casts; throw and
try/catch/finally by LSB’s canonical lowering, defer; globals,
constants, statics, and the module body; named intrinsics through an
Intrinsics table (Intrinsics::mox maps every Mox intrinsic to an
inline fast path or a host function). Every instruction maps back to the
HIR node it came from (Compiled::origin) and to a line-table row.
Classes (0.4): classes, interfaces, and mixins (PHP traits) compile to LSB
structs with PHP’s semantics: single inheritance, constructors,
properties with defaults, instance and static methods, self::,
parent::, and late-bound static::, static properties, constants,
new/instanceof/clone as ClassOps of the intrinsic table,
visibility checked where PHP checks it (raised as TypeError with PHP’s
message), objects thrown and caught by class or interface, and the
destructor’s name for the VM (Compiled::destructor).
Not yet: declared records and sums, and typed fast paths (0.5); each is a
precise CompileError::Unsupported, as is every class form HIR or LSB
cannot express (multiple inheritance, impl blocks).
§Guarantees
- Loads. Every module it emits passes
bvm-lang’s load-time checks: registers, windows, constants, branch targets, and handlers in range. - Semantics. Differential tests run random programs through the compiler and the VM, and through a reference interpreter written in the tests, comparing results, error kinds, and error locations.
- No recursion over the input. Generation is an explicit task stack; any nesting depth compiles in bounded native stack.
- Budgeted and deterministic. Output is bounded by
Options::with_max_instructions; the same unit compiles to the same bytes.
§no_std
The crate needs only alloc. The default std feature is additive.
Structs§
- Arity
- How many operands an intrinsic takes: from
mintomax, both inclusive (at most 255, an import’s limit). - Compiled
- A compiled unit: the LSB module, its entry points, and the map from every instruction back to the HIR node it was compiled from.
- Compiler
- A configured compiler:
Optionsand, optionally, aLocatefor the line tables. - Intrinsic
Def - One entry of an
Intrinsicstable: the operands it takes and how it compiles. - Intrinsics
- The intrinsic table: named intrinsic → arity and lowering. A name that is
not in the table is
CompileError::UnknownIntrinsic. - Line
Index - A
Locatefor one source file: 1-based lines and byte columns from the file’s text, for spans that start atbasein the session byte space. - Location
- A source position for a line-table row: file, line, and column, numbered
as the
Locateimplementation numbers them. - Options
- Compilation options: the dynamic profile’s array and key semantics, the body’s role, host linkage, the intrinsic table, and budgets.
Enums§
- Array
Semantics - How arrays and maps behave when a value is copied.
- Body
Mode - What the module body (
Module.body) compiles to. - ClassOp
- A class operation: compiled inline against the classes the unit declares, with a host function for whatever the unit does not declare.
- Class
Problem - A class graph or class member LSB cannot hold, or one PHP rejects at compile time.
- Compile
Error - Why
compilerefused a unit. - Fast
- A guarded fast path: the operand kinds where an LSB sequence computes exactly the intrinsic’s result, tested at run time; outside that domain the intrinsic’s host function is called with the same operands.
- KeyCoercion
- How map keys are normalized before a map is indexed.
- Lowering
- How one named intrinsic compiles.
- LsbOp
- An LSB instruction sequence with LSB’s own semantics, hooks included (LSB §5.6–§5.10). These are the 0.2 built-in intrinsics.
- Unsupported
- A HIR form this release does not compile, named precisely.
Traits§
- Locate
- Maps HIR spans to source positions for LSB line tables.