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

> Discover the role of boot.l in the Maru bootstrap process. It initializes the Maru VM environment, providing essential primitives and a module loader for self-hosting compilation.

- Repository: [Attila Lendvai/maru](https://github.com/attila-lendvai/maru)
- Tags: internals
- Published: 2026-02-25

---

**`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`](https://github.com/attila-lendvai/maru/blob/main/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:

```lisp
(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:

```lisp
(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:

```lisp
(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:

```lisp
(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:

```lisp
(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:

```lisp
(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:

```bash

# 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`:

```lisp
;; 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:

```lisp
;; 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:

```lisp
;; 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`](https://github.com/attila-lendvai/maru/blob/main/doc/bootstrap.md).