Understanding the Role of boot.l in the Maru Bootstrap Process

boot.l serves as the minimal standard library that initializes the Maru VM's bootstrap environment, providing primitive wrappers, core macros, and a module loader that enables the self-hosting compiler to build itself from a cold start.

In the attila-lendvai/maru repository, boot.l is the first file evaluated when the virtual machine launches. It establishes a predictable, self-contained runtime that later stages—including boot-full.l and the generated eval binaries—depend on to complete the multi-stage bootstrap pipeline described in doc/bootstrap.md.

Establishing the Bootstrap Phase and Safety Controls

When the Maru VM starts, boot.l immediately marks the current execution context so that subsequent code can adapt its behavior. It defines two critical flags at lines 8–13:

(if (not (defined? 'bootstrapping?))
    (eval '(define bootstrapping? ())))
...
(if (not (defined? 'evolving?))
    (eval '(define evolving? ())))

The bootstrapping? flag indicates that the system is currently building itself, while evolving? signals that the compiler is being regenerated. These flags enable conditional diagnostic output, such as the warning message printed at lines 14–20 when *verbosity* is set appropriately.

Additionally, boot.l establishes the safety level for runtime checks via the *safety* variable at lines 31–34:

(define *safety* 1)

This setting determines which runtime checks are compiled into the bootstrap binaries, balancing performance against debugging capability during the early stages of compilation.

Exposing Host Primitives and Implementing the Loader

Since the full standard library is not yet available, boot.l must provide thin wrappers around the host VM's primitive functions. At lines 36–40, it aliases essential primitives with namespace prefixes:

(define primitive/print    print)
(define primitive/dump     dump)

These primitive/* wrappers allow bootstrap code to access host functionality without colliding with later definitions in the evolving system.

To enable modular development during bootstrap, boot.l implements a minimal file loader at lines 71–88. The load function maintains a load history to prevent duplicate inclusions, while require serves as the public interface:

(define load (lambda (path) 
               ... 
               (primitive/load path)))

(define require (lambda (path) 
                  (if (not (member path *load-history*))
                      (load path))))

This loader is essential for pulling in additional source files before the full library infrastructure exists.

Bootstrapping Core Data Structures and Macros

Before the compiler can process complex code, boot.l must define fundamental operations entirely in terms of the host environment. It implements core list operations starting at line 63, including list, assq (lines 92–104), and concat-list.

Crucially, boot.l provides the quasiquote implementation at lines 112–138, which includes the "level-shifted" primitives necessary for macro expansion during bootstrap. This allows the early compiler to handle complex quoting and unquoting before the full macro system is compiled.

The file also defines essential macro forms using define-form (lines 150–165), including:

(define-form unless (cond body)
  `(if (not ,cond) ,body))

Other critical utilities defined include let*, assert, and verbosity controls. These forms are wrapped in eval/quote pairs to guarantee they expand using host definitions while the slave environment (the code being compiled) may later provide its own implementations.

Loading the First Essential Modules

After establishing the core environment, boot.l pulls in the minimal modules needed to continue the bootstrap chain. It uses the require function to load:

(require "source/list-min.l")
(require "source/control-structures.l")

These files provide additional list utilities and control flow structures that the expanding compiler needs to process the rest of the language. This step transitions the system from the minimal bootstrap environment toward the full library defined in boot-full.l.

Bridging Host and Slave Environments

Throughout boot.l, many definitions are wrapped in explicit eval and quote calls to manage the distinction between the host (the currently running VM) and the slave (the compiler being built). This "level-shifting" technique, documented in the comments around lines 121–136, ensures that bootstrap definitions use the host's primitive operations even when the slave redefines those same symbols later in the process.

For example, the quasiquote implementation explicitly references bootstrap-level primitives to avoid circular dependencies during the construction of the new evaluator.

Code Examples

Starting the Bootstrap Process

When you run the generated binary, the VM automatically evaluates boot.l before any other code:


# The binary begins by loading boot.l to establish the environment

$ ./build/eval0

Accessing Primitives During Bootstrap

Code loaded during the bootstrap phase uses the primitive wrappers defined in boot.l:

;; Available immediately after boot.l is loaded
(primitive/print "Initializing bootstrap environment...\n")
(primitive/dump some-value)

Using Bootstrap-Level Macros

Macros defined via define-form in boot.l are available for subsequent compilation stages:

;; Use the unless macro defined in boot.l
(unless (defined? 'my-module)
  (define my-module (create-module)))

Requiring Additional Files

The require function prevents duplicate loads while bringing in essential utilities:

;; Load additional bootstrap utilities
(require "source/list-min.l")
(require "source/control-structures.l")

Summary

  • boot.l is the seed file evaluated immediately when the Maru VM starts, creating the minimal environment required for self-hosting.
  • It marks the bootstrap phase using bootstrapping? and evolving? flags, and sets the *safety* level for runtime checks.
  • It wraps host primitives (e.g., primitive/print) and implements a minimal load/require system to enable modular bootstrap development.
  • It defines core data structures (list, assq) and macro facilities (quasiquote, define-form) using level-shifted host primitives.
  • It loads the first essential modules (list-min.l, control-structures.l) to transition from the minimal environment to the full compiler build.

Frequently Asked Questions

What happens if boot.l contains an error during the bootstrap process?

If boot.l fails to load, the entire bootstrap chain collapses because no subsequent stages can execute without the primitive wrappers, loader, and core macros it defines. The VM would halt immediately with a parse or evaluation error, as eval0 and later stages depend on the environment established by this file according to the build logic in the Makefile.

How does boot.l differ from boot-full.l?

boot.l contains only the minimal definitions needed to get the compiler running—primitive aliases, a basic loader, and essential macros—while boot-full.l provides the complete standard library including the full set of list operations, parsing utilities, and compiler optimizations. The bootstrap process uses boot.l to build the first eval binary, which then loads boot-full.l to create a fully featured compiler.

Why does boot.l wrap some definitions in eval and quote?

These wrappers implement "level-shifting" to distinguish between the host VM (the executable currently running) and the slave system (the compiler being constructed). By explicitly evaluating certain forms in the host environment, boot.l ensures that bootstrap-critical operations use stable, host-provided primitives even when the slave later redefines those same symbols during its own compilation.

Can boot.l be modified without breaking the bootstrap?

Modifications to boot.l are possible but risky because every later stage depends on its specific definitions. Changes to fundamental forms like define-form, the require implementation, or the primitive wrappers (e.g., primitive/print) require corresponding updates in boot-full.l and the generated eval sources to maintain consistency across the host-slave boundary described in doc/bootstrap.md.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →