AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

Looking back at the week of 22–28 January 2025

A tiny compiler makes the Wasm boundary visible

Follow one illustrative arithmetic expression from tokens to a Wasm module. Understand validation and encoding without turning code golf into production advice.

Citiți articolul în română

The value of a tiny compiler is that you can see the boundary between a source language and an executable module. Its source length is an interesting constraint for teaching. It is a poor criterion for selecting the compiler infrastructure that a business must maintain.

The Wasm from the Ground Up tutorial, dated 24 January 2025, presents a compact JavaScript compiler for arithmetic in reverse Polish notation. Looking back in November 2026, the useful lesson is how each stage makes a promise to the next. The following expression is explicitly illustrative, not a report of running the author's compiler or an AL+ experiment.

Start with a language small enough to describe

Use 2 3 +. In reverse Polish notation, the operands precede the operation. Splitting this deliberately simple input at spaces gives three tokens. The first two denote integers; the last denotes addition. There is no precedence puzzle because the operation consumes the two values already available.

That simplicity is a language decision. It avoids defining parentheses, ordinary infix precedence and variable lookup. It does not justify accepting arbitrary input strings as programs. A useful teaching exercise is to write down exactly which tokens are allowed and what should happen when the sequence cannot produce one result.

For example, an implementation needs a policy for missing operands or extra values. It also needs to distinguish a malformed number from an unsupported operation. Rejecting such input at the source boundary can produce a better explanation than passing malformed output to the runtime and hoping its error identifies the original mistake. These are proposed requirements for an expanded teaching implementation.

Tokens become instructions, then a module

For this example, the conceptual instruction sequence pushes integer 2, pushes integer 3 and adds them. A stack description explains the dependency: after the first token there is one value, after the second there are two, and addition consumes both and leaves one. The intended result is 5, derived here from arithmetic rather than observed execution.

A module still needs an envelope. The WebAssembly binary module specification separates function types, function declarations, exports and code bodies into sections. For this teaching design, describe one function with no parameters and one integer result, associate its declaration with that type, and put the instruction sequence in its body. The module header identifies the format before those sections are read.

An export supplies the connection to the host. A function can exist inside a module without being the function that JavaScript is able to call by a chosen name. The export maps a name to the function's index. Documenting that mapping makes the external interface explicit instead of leaving the caller dependent on an unexplained array of bytes.

The specification linked here is mutable and currently displays a November 2026 revision. It explains the format; it is not evidence that every feature in that revision belonged to the January 2025 tutorial. This example uses only the small integer-function boundary needed for the argument.

Byte length is part of correctness

The binary values specification describes integer encodings, including LEB128. A number represented as a JavaScript value is not automatically its correct binary representation. Section lengths, counts and integer constants have encoding rules that a generator must obey.

The values in 2 3 + make the explanation convenient because they are small. A teaching compiler that emits a constant through one immediate byte cannot be generalized merely by accepting longer decimal strings. Signed integer constants and unsigned lengths must be encoded according to their respective rules. Larger input can also increase a section's encoded length, so fixing constants alone would be insufficient.

This is where short source can conceal a long assumption. A compressed expression may be correct inside a tight range while looking like a general arithmetic translator. Explain the supported range before inviting reuse. An explicit rejection outside the range is more useful than a compact implementation whose apparent grammar promises too much.

Validation is a separate boundary

The instruction validation rules constrain operand and result types. Valid bytes are not enough if the instructions fail their stack requirements or the function body does not agree with its declared result. The source language's checks and the module validator protect different boundaries.

For the illustrative expression, the argument is straightforward: the proposed integer addition has the two operands it needs and leaves the intended result type. That reasoning does not certify a general compiler. It says nothing about a parser's treatment of all strings, all integer boundaries or unexpected operators.

A team extending the exercise should keep source errors separate from module-construction errors. If a supported source program produces an invalid module, investigate the generator. If the source program violates the small grammar, explain that to the caller. Combining both into one generic failure makes later maintenance harder.

Production favors maintained machinery

The strongest counterargument is practical: most products should use a maintained generator or compiler rather than write binary sections manually. That transfers substantial format work to an implementation with a broader remit. It still requires a documented source-language boundary and useful failure behavior, but it removes code golf as a maintenance obligation.

A toy compiler earns its place when the learning objective is visible transformation. Its grammar, numeric range and absent features belong beside the example. In custom software development, use that understanding to review a generated module's interface and failure modes. Choose production machinery by correctness, support and the cost of future changes; leave the character count as the tutorial's constraint.