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:
- Creates a registry with core primitives
- Adds all extensions (io, files, math, eval, namespace, threads, etc.)
- Creates a new Namespace with per-instance symbol interning
- Applies all primitives to the environment
- Registers syntax compilers and primitive expanders
- 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 ¶
- Variables
- func LoadBootstrapCore(ctx context.Context, env *environment.EnvironmentFrame, ...) (*environment.EnvironmentFrame, error)
- func NewLibraryEnvironmentFrame(ctx context.Context, callerEnv *environment.EnvironmentFrame, _ []string) (*environment.EnvironmentFrame, error)
- func NewNamespaceFrame(ctx context.Context) (*environment.EnvironmentFrame, error)
- func NewProfileEnvironment(ctx context.Context, callerNS *environment.Namespace, ...) (*environment.Namespace, error)
- func NewTopLevelWithRegistry(ctx context.Context) (*environment.EnvironmentFrame, *registry.PrimitiveRegistry, error)
- func ProfileExtensions(name string) ([]registry.Extension, error)
- type StrictLevel
Constants ¶
This section is empty.
Variables ¶
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.
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).
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
func LoadBootstrapCore(ctx context.Context, env *environment.EnvironmentFrame, reg *registry.PrimitiveRegistry, opts ...registry.ApplyOption) (*environment.EnvironmentFrame, error)
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:
- Apply the registry, routing runtime primitives to the runtime target.
- Register phase handlers (syntax compilers + primitive expanders).
- 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).
- Load bootstrap procedures into the runtime target.
- 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:
- Creates a registry with core primitives
- Adds all extensions (io, files, math, introspection, eval, namespace, threads, gointerop, all, system, process, sat, charsets, envvars, algebragraph)
- Creates a new Namespace with per-instance symbol interning
- Applies the registry to register all primitives
- Registers primitive compilers in the compile environment
- 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 ¶
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 … )).