bootstrap

package
v1.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package bootstrap initializes the top-level Scheme environment.

The package creates and bootstraps complete runtime environments using the registry pattern:

env, err := bootstrap.NewNamespaceFrame(ctx)

Initialization

NewNamespaceFrame performs these steps:

  1. Creates a registry with core primitives
  2. Adds all extensions (io, files, math, eval, namespace, threads, etc.)
  3. Creates a new Namespace with per-instance symbol interning
  4. Applies all primitives to the environment
  5. Registers syntax compilers and primitive expanders
  6. Loads bootstrap macros

Library Environments

NewLibraryEnvironmentFrame creates environments for R7RS libraries that share symbol interning with the caller but isolate bindings:

libEnv, err := bootstrap.NewLibraryEnvironmentFrame(ctx, callerEnv, nil)

This ensures (eq? 'foo (string->symbol "foo")) returns #t across library boundaries per R7RS 6.5.

Index

Constants

This section is empty.

Variables

View Source
var ErrProfileWidensEngine = werr.NewStaticError("profile widens the engine's capability surface")

ErrProfileWidensEngine is returned when (environment '(wile <name>)) asks for a profile carrying primitives the calling engine never registered, and the engine's authorizer does not permit the widening.

View Source
var ErrUnknownProfile = werr.NewStaticError("unknown profile")

ErrUnknownProfile is returned by ProfileExtensions when given a name outside the known set (tiny, console, console-with-load, small, kitchen-sink).

View Source
var ErrUnknownStrictLevel = werr.NewStaticError("unknown strict level")

ErrUnknownStrictLevel is returned by ParseStrictLevel when given a name outside the known set (core, no-bindings).

Functions

func LoadBootstrapCore added in v1.19.0

LoadBootstrapCore runs the ordering-sensitive sequence that turns a bare frame plus a built registry into a usable base environment, and returns the runtime target frame the caller must use for any further sealed-base work.

The five steps are order-dependent and each depends on the one before it:

  1. Apply the registry, routing runtime primitives to the runtime target.
  2. Register phase handlers (syntax compilers + primitive expanders).
  3. Load bootstrap macros against the runtime target (the sealed base for a namespace-owning env). Their define-syntax writes land in sealedExpandBase, so they are immutable and a user define-syntax shadows in the mutable expand child. These must precede the procedures, which are written in terms of them (let, and).
  4. Load bootstrap procedures into the runtime target.
  5. Load the LATE macros, which reference bootstrap procedures (unless -> not, guard -> with-exception-handler). Loading them after step 4 is what makes their free identifiers pin to the sealed base instead of taking a nil pin and degrading to use-site resolution (R7RS 4.3.2).

This exists because the sequence had been duplicated in two places — this package's initializeEnvironmentWithRegistry and pkg/wile's applyBaseEnvironment — that had to be kept in step by comment alone. On 2026-07-18 step 5 was added to only the first; unless and guard became undefined for the CLI and every embedder while every Go unit test stayed green, because the test helpers reach bootstrap through this package. A single definition makes that divergence unrepresentable rather than merely discouraged.

What deliberately stays with the callers, because it genuinely differs between them: registry CONSTRUCTION (this package builds a fixed all-extensions registry; the Engine receives a filtered per-profile one), and post-steps (the Engine stamps inline HOFs, builds loop templates, and injects documentation). Errors are wrapped with terse context here and re-wrapped by each caller in its own idiom, so pkg/wile keeps its ErrEngineInit contract and this package keeps its plainer one.

func NewLibraryEnvironmentFrame

func NewLibraryEnvironmentFrame(ctx context.Context, callerEnv *environment.EnvironmentFrame, _ []string) (*environment.EnvironmentFrame, error)

NewLibraryEnvironmentFrame creates a new environment for a library that shares the Namespace with the caller. This ensures symbol identity is preserved across library boundaries per R7RS §6.5: (eq? 'foo (string->symbol "foo")) must be #t.

The library gets its own:

  • GlobalEnvironmentFrame for bindings (isolates library definitions)
  • PhaseRegistry for expand/compile phases

But shares with caller:

  • Namespace (symbol and syntax interning)
  • LibraryRegistry (for nested imports)

func NewNamespaceFrame

func NewNamespaceFrame(ctx context.Context) (*environment.EnvironmentFrame, error)

NewNamespaceFrame creates and initializes a complete Scheme runtime environment.

This function:

  1. Creates a registry with core primitives
  2. Adds all extensions (io, files, math, introspection, eval, namespace, threads, gointerop, all, system, process, sat, charsets, envvars, algebragraph)
  3. Creates a new Namespace with per-instance symbol interning
  4. Applies the registry to register all primitives
  5. Registers primitive compilers in the compile environment
  6. Loads bootstrap macros (and, or, let, let*, letrec, cond, when, unless, parameterize)

The resulting environment is ready for parsing, expanding, compiling, and executing Scheme programs.

func NewProfileEnvironment

func NewProfileEnvironment(ctx context.Context, callerNS *environment.Namespace, exts []registry.Extension, level StrictLevel) (*environment.Namespace, error)

NewProfileEnvironment creates a new *environment.Namespace initialized with the given extension set. It shares symbol interning with callerNS but has its own bindings so the caller's top-level definitions do not leak in. Intended for Scheme-level (environment '(wile <profile>)) construction.

level narrows the new namespace's visible top level; exts still decides what is registered, so the two arguments are independent and a narrowed environment can import its way back to the whole profile.

func NewTopLevelWithRegistry

func NewTopLevelWithRegistry(ctx context.Context) (*environment.EnvironmentFrame, *registry.PrimitiveRegistry, error)

NewTopLevelWithRegistry creates a top-level environment and returns both the environment frame and the primitive registry for doc introspection.

Top-level immutability is deliberately NOT enabled here. This is the policy-free bootstrap building block (sealed base + mutable runtime, fully populated); the immutable-top-level default is a PRODUCT policy applied by the public Engine (pkg/wile NewEngine via SetImmutableTopLevel). Callers using this internal constructor directly (test helpers, internal tooling) therefore get a mutable top level, which is the right default for redefine-heavy test code. Embedders use the public Engine and get the immutable default.

func ProfileExtensions

func ProfileExtensions(name string) ([]registry.Extension, error)

ProfileExtensions returns the extension set for a named profile. It is the single source of truth for profile→extensions mapping; callers in the public wile package (profile.go Profile.extensions) and in extensions/eval (tryWileProfile, for Scheme-level (environment '(wile <name>))) both dispatch through here.

Valid names: "tiny", "console", "console-with-load", "small", "kitchen-sink". An unknown name returns ErrUnknownProfile wrapped with the offending string.

Types

type StrictLevel added in v1.20.0

type StrictLevel int

StrictLevel narrows how much of the registry a profile environment binds at its visible top level. It is ORTHOGONAL to the profile: the profile picks what is registered (and is the capability boundary), the level picks how much of that is pre-bound versus reachable only through (import …). That is why it is threaded as a second argument rather than folded into the profile name — there is no "small-no-bindings" profile, there is a Small profile viewed strictly.

This mirrors pkg/wile's engine-side ladder; the two are separate because the two bootstrap sequences are separate (see bootstrap_core.go). Keep them in step.

const (
	// StrictOff binds the whole registry at the visible top level. Default.
	StrictOff StrictLevel = iota
	// StrictCore binds only the core primitives and core bootstrap macros.
	StrictCore
	// StrictNoBindings binds nothing; only the phase handlers (lambda, if,
	// define, import, …) remain, since those never come from a registry.
	StrictNoBindings
)

func ParseStrictLevel added in v1.20.0

func ParseStrictLevel(name string) (StrictLevel, error)

ParseStrictLevel maps a Scheme-level level name to a StrictLevel. The empty string means "no level given" and yields StrictOff, so a caller can forward an absent argument without special-casing it.

Valid names: "core", "no-bindings". They match the cmd/wile --strict values and the pkg/wile option names (WithStrictNamespace, WithoutAmbientBindings), so one vocabulary covers the CLI, the embedding API, and (environment '(wile … )).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL