Documentation
¶
Overview ¶
Package environment provides variable binding and scoping for the Scheme compiler.
The environment system manages the relationship between variables and values across:
- Lexical scoping via parent chain traversal
- Phase separation (Runtime, Expand, Compile)
- Hygienic macros via Flatt's "sets of scopes" model
- Per-instance syntax interning for consistent syntax identity
- R7RS library import/export mappings
Architecture ¶
Each Wile VM instance owns a Namespace that provides:
- Syntax interning (thread-safe, per-instance)
- Phase registry for accessing phase-specific environments
- The runtime EnvironmentFrame tree
EnvironmentFrame nodes form the lexical scope chain, each containing:
- Local bindings (LocalEnvironmentFrame) for lambda params and let-bound vars
- Global bindings (GlobalEnvironmentFrame) for top-level defines
- Parent pointer for enclosing scopes
Binding Lookup ¶
EnvironmentFrame.GetBinding performs two-phase lookup:
- Local phase: traverse local bindings up the parent chain
- Global phase: search global bindings up the parent chain
When scopes are provided, EnvironmentFrame.GetBinding adds hygiene awareness, using [Scope] sets to match identifiers according to Flatt's algorithm with maximal binding selection.
Index ¶
- Constants
- type Binding
- func (p *Binding) BindingType() BindingType
- func (p *Binding) Copy() *Binding
- func (p *Binding) Doc() string
- func (p *Binding) EnsureMeta() *BindingMeta
- func (p *Binding) IsImported() bool
- func (p *Binding) IsStable() bool
- func (p *Binding) Meta() *BindingMeta
- func (p *Binding) Scopes() []*syntax.Scope
- func (p *Binding) SetValue(value values.Value)
- func (p *Binding) Source() *syntax.SourceContext
- func (p *Binding) Value() values.Value
- type BindingID
- type BindingMeta
- type BindingType
- type EnvironmentFrame
- func (p *EnvironmentFrame) AtPhase(phase Phase) *EnvironmentFrame
- func (p *EnvironmentFrame) Compile() *EnvironmentFrame
- func (p *EnvironmentFrame) Copy() *EnvironmentFrame
- func (p *EnvironmentFrame) EnsureLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)
- func (p *EnvironmentFrame) EqualTo(value values.Value) bool
- func (p *EnvironmentFrame) Expand() *EnvironmentFrame
- func (p *EnvironmentFrame) FileResolver() FileResolver
- func (p *EnvironmentFrame) GetBinding(key *values.Symbol, scopes []*syntax.Scope) *Binding
- func (p *EnvironmentFrame) GetGlobalBinding(key *GlobalIndex) *Binding
- func (p *EnvironmentFrame) GetGlobalIndex(key *values.Symbol) *GlobalIndex
- func (p *EnvironmentFrame) GetGlobalIndexAcrossPhases(key *values.Symbol) *GlobalIndex
- func (p *EnvironmentFrame) GetGlobalIndexFromLibraryScopes(key *values.Symbol, scopes []*syntax.Scope) *GlobalIndex
- func (p *EnvironmentFrame) GetLocalBinding(li *LocalIndex) *Binding
- func (p *EnvironmentFrame) GetLocalBindingByIndex(i int) *Binding
- func (p *EnvironmentFrame) GetLocalBindingBySlotDepth(slot, depth int) *Binding
- func (p *EnvironmentFrame) GetLocalIndex(key *values.Symbol, scopes []*syntax.Scope) *LocalIndex
- func (p *EnvironmentFrame) GlobalEnvironment() *GlobalEnvironmentFrame
- func (p *EnvironmentFrame) HasLocalVariableBinding(sym *values.Symbol, scopes []*syntax.Scope) bool
- func (p *EnvironmentFrame) InitApplyFrame(dst *EnvironmentFrame)
- func (p *EnvironmentFrame) IsTopLevel() bool
- func (p *EnvironmentFrame) IsVoid() bool
- func (p *EnvironmentFrame) LibraryRegistry() LibrarySearcher
- func (p *EnvironmentFrame) LoadPathStack() PathTracker
- func (p *EnvironmentFrame) LocalBindingsSlice() []Binding
- func (p *EnvironmentFrame) LocalEnvironment() *LocalEnvironmentFrame
- func (p *EnvironmentFrame) MaybeCreateLocalBinding(key *values.Symbol, bt BindingType, scopes []*syntax.Scope, ...) (*LocalIndex, bool)
- func (p *EnvironmentFrame) MaybeCreateOwnGlobalBinding(key *values.Symbol, bt BindingType) (*GlobalIndex, bool)
- func (p *EnvironmentFrame) MutableRuntime() *EnvironmentFrame
- func (p *EnvironmentFrame) Namespace() *Namespace
- func (p *EnvironmentFrame) NewApplyFrame() *EnvironmentFrame
- func (p *EnvironmentFrame) Parent() *EnvironmentFrame
- func (p *EnvironmentFrame) PhaseLevel() Phase
- func (p *EnvironmentFrame) PreAllocateBindings(n int)
- func (p *EnvironmentFrame) ResetForPool()
- func (p *EnvironmentFrame) ResolveBindingID(key *values.Symbol, scopes []*syntax.Scope) (BindingID, bool)
- func (p *EnvironmentFrame) Runtime() *EnvironmentFrame
- func (p *EnvironmentFrame) SchemeString() string
- func (p *EnvironmentFrame) SealedBaseTarget() *EnvironmentFrame
- func (p *EnvironmentFrame) SetFileResolver(resolver FileResolver)
- func (p *EnvironmentFrame) SetGlobalBindingByIndex(i int, bd *Binding)
- func (p *EnvironmentFrame) SetLibraryRegistry(registry LibrarySearcher)
- func (p *EnvironmentFrame) SetLocalValue(li *LocalIndex, v values.Value) error
- func (p *EnvironmentFrame) SetLocalValueBySlotDepth(slot, depth int, v values.Value) error
- func (p *EnvironmentFrame) SetOwnGlobalValue(gi *GlobalIndex, v values.Value) error
- func (p *EnvironmentFrame) TopLevel() *EnvironmentFrame
- type FileResolver
- type GlobalEnvironmentFrame
- func (p *GlobalEnvironmentFrame) Bindings() []*Binding
- func (p *GlobalEnvironmentFrame) Copy() *GlobalEnvironmentFrame
- func (p *GlobalEnvironmentFrame) CreateGlobalBinding(key *values.Symbol, bt BindingType) (*GlobalIndex, bool)
- func (p *GlobalEnvironmentFrame) DeleteBinding(sym *values.Symbol) bool
- func (p *GlobalEnvironmentFrame) GetGlobalIndex(key *values.Symbol) *GlobalIndex
- func (p *GlobalEnvironmentFrame) GetOwnGlobalBinding(gi *GlobalIndex) *Binding
- func (p *GlobalEnvironmentFrame) Keys() map[values.Symbol]int
- func (p *GlobalEnvironmentFrame) SetBindings(vs []*Binding)
- func (p *GlobalEnvironmentFrame) SetOwnGlobalValue(gi *GlobalIndex, v values.Value) error
- type GlobalIndex
- type ImmutableLiterals
- type LibraryEnvFactory
- type LibrarySearcher
- type LocalEnvironmentFrame
- func (p *LocalEnvironmentFrame) Bindings() []Binding
- func (p *LocalEnvironmentFrame) Copy() *LocalEnvironmentFrame
- func (p *LocalEnvironmentFrame) CopyForApply() *LocalEnvironmentFrame
- func (p *LocalEnvironmentFrame) EnsureLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)
- func (p *LocalEnvironmentFrame) GetLocalBinding(li *LocalIndex) *Binding
- func (p *LocalEnvironmentFrame) GetLocalIndex(key *values.Symbol) *LocalIndex
- func (p *LocalEnvironmentFrame) Keys() map[values.Symbol][]int
- func (p *LocalEnvironmentFrame) MaybeCreateLocalBinding(key *values.Symbol, bt BindingType, scopes []*syntax.Scope, ...) (*LocalIndex, bool)
- func (p *LocalEnvironmentFrame) SetBindings(v []Binding)
- func (p *LocalEnvironmentFrame) SetLocalValue(li *LocalIndex, v values.Value) error
- type LocalIndex
- type ModuleInstance
- type Namespace
- func (p *Namespace) AtPhase(phase Phase) *EnvironmentFrame
- func (p *Namespace) AttachModule(path string, target *Namespace) error
- func (p *Namespace) Authorizer() security.Authorizer
- func (p *Namespace) BoundNamesAcrossPhases() []string
- func (p *Namespace) BoundSymbolNames() values.Value
- func (p *Namespace) Compile() *EnvironmentFrame
- func (p *Namespace) DeleteExtensionState(key any)
- func (p *Namespace) EnvMap() map[string]string
- func (p *Namespace) EqualTo(v values.Value) bool
- func (p *Namespace) Expand() *EnvironmentFrame
- func (p *Namespace) ExportIndex() (any, bool)
- func (p *Namespace) ExtensionState(key any) (any, bool)
- func (p *Namespace) FileResolver() FileResolver
- func (p *Namespace) ImmutableLiterals() *ImmutableLiterals
- func (p *Namespace) ImmutableTopLevel() bool
- func (p *Namespace) InternSyntax(k values.Value, v syntax.SyntaxValue) syntax.SyntaxValue
- func (p *Namespace) IsVoid() bool
- func (p *Namespace) LibraryEnvFactory() LibraryEnvFactory
- func (p *Namespace) LibraryRegistry() LibrarySearcher
- func (p *Namespace) LoadPathStack() PathTracker
- func (p *Namespace) LookupLibraryEnv(scope *syntax.Scope) *EnvironmentFrame
- func (p *Namespace) ModuleInstance(path string) (*ModuleInstance, bool)
- func (p *Namespace) NewChildNamespace(opts ...NamespaceOption) *Namespace
- func (p *Namespace) NewChildRuntime() *EnvironmentFrame
- func (p *Namespace) NewSchemeReportNamespace() *Namespace
- func (p *Namespace) Phases() *PhaseRegistry
- func (p *Namespace) RegisterLibraryScope(scope *syntax.Scope, env *EnvironmentFrame)
- func (p *Namespace) Registry() any
- func (p *Namespace) Runtime() *EnvironmentFrame
- func (p *Namespace) SchemeString() string
- func (p *Namespace) SealedBase() *EnvironmentFrame
- func (p *Namespace) SetAuthorizer(auth security.Authorizer)
- func (p *Namespace) SetEnvMap(m map[string]string)
- func (p *Namespace) SetExportIndex(idx any)
- func (p *Namespace) SetExtensionState(key, value any)
- func (p *Namespace) SetFileResolver(resolver FileResolver)
- func (p *Namespace) SetImmutableTopLevel(on bool)
- func (p *Namespace) SetLibraryEnvFactory(f LibraryEnvFactory)
- func (p *Namespace) SetLibraryRegistry(registry LibrarySearcher)
- func (p *Namespace) SetLoadPathStack(s PathTracker)
- func (p *Namespace) SetModuleInstance(path string, inst *ModuleInstance)
- func (p *Namespace) SetRegistry(reg any)
- func (p *Namespace) SyntaxInternCount() int
- type NamespaceOption
- type PathTracker
- type Phase
- type PhaseRegistry
Constants ¶
const ( // BindingTypeUnknown is the zero value used for pre-allocated, not-yet- // bound slots in LocalEnvironmentFrame. It must not be observed by // callers outside the environment package. BindingTypeUnknown = BindingType(iota) // BindingTypeVariable indicates a regular variable binding (from define, let, lambda parameters). BindingTypeVariable // BindingTypeSyntax indicates a syntax transformer binding (from define-syntax). // These bindings live in the expand phase environment. BindingTypeSyntax // BindingTypePrimitive indicates a compile-time binding (special forms, auxiliary syntax). BindingTypePrimitive )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Binding ¶
type Binding struct {
// contains filtered or unexported fields
}
Binding represents a variable binding in the environment. It stores the bound value, the binding type (variable, syntax, or primitive), and an optional pointer to compile-time metadata (scopes, source location).
func NewBinding ¶
func NewBinding(value values.Value, bindingType BindingType) *Binding
NewBinding creates a new binding with the given value and type. The binding has no scopes (for backward compatibility with non-hygienic code).
func NewBindingWithScopes ¶
func NewBindingWithScopes(value values.Value, bindingType BindingType, scopes []*syntax.Scope) *Binding
NewBindingWithScopes creates a binding with associated scopes (for hygiene)
func NewBindingWithSource ¶
func NewBindingWithSource(value values.Value, bindingType BindingType, scopes []*syntax.Scope, source *syntax.SourceContext) *Binding
NewBindingWithSource creates a binding with source location information.
func (*Binding) BindingType ¶
func (p *Binding) BindingType() BindingType
BindingType returns the type of this binding (variable, syntax, or primitive).
func (*Binding) Copy ¶
Copy creates a deep copy of this binding. The meta struct is copied so that mutations through EnsureMeta on the original do not affect the copy. This method is only used during compilation/expansion, never on the runtime hot path.
func (*Binding) Doc ¶ added in v1.10.3
Doc returns the documentation string for this binding. Returns empty string for bindings without documentation.
func (*Binding) EnsureMeta ¶ added in v1.16.0
func (p *Binding) EnsureMeta() *BindingMeta
EnsureMeta returns the BindingMeta pointer, lazily allocating an empty BindingMeta on first call. This is the only mutator API for metadata fields: callers assign directly, e.g.
b.EnsureMeta().Imported = true
Adding a new metadata field thus requires editing only the BindingMeta struct itself; no parallel getter/setter accessor pair is needed.
func (*Binding) IsImported ¶ added in v1.14.11
IsImported returns whether this binding was imported from a library.
func (*Binding) IsStable ¶ added in v1.17.0
IsStable reports the rebind-stability conclusion: the binding will not be rebound. Imported is standing evidence for that conclusion (R7RS forbids set! on imports); Stable carries it when a proof discharges it by other means. This is NOT a set!-permission — that is IsImported alone (R7RS §5.2). Read by the frame optimizer's MayCapture (sibling escape-gated plan). Renamed from the retired IsConstant, which falsely asserted "value known at compile time".
func (*Binding) Meta ¶ added in v1.16.0
func (p *Binding) Meta() *BindingMeta
Meta returns the BindingMeta pointer, or nil if no metadata has been attached. Callers that read metadata fields should nil-check the returned pointer; the convenience getters (Scopes, Source, Doc, IsImported, IsStable) wrap this pattern.
func (*Binding) Scopes ¶
Scopes returns the hygiene scopes associated with this binding. Returns nil for bindings without hygiene information.
func (*Binding) Source ¶
func (p *Binding) Source() *syntax.SourceContext
Source returns the source location where this binding was defined. Returns nil for bindings without source information.
type BindingID ¶ added in v1.9.4
type BindingID struct {
Frame *LocalEnvironmentFrame
Slot int
}
BindingID is a stable identifier for a local binding, safe to use as a map key even when the backing []Binding slice is reallocated by append. The Frame pointer identifies the heap-allocated LocalEnvironmentFrame (stable across slice growth) and Slot is the index within that frame.
This is the local-binding analog of GlobalIndex: GlobalIndex uses (*GlobalEnvironmentFrame, *Symbol), BindingID uses (*LocalEnvironmentFrame, int).
type BindingMeta ¶ added in v1.5.0
type BindingMeta struct {
Scopes []*syntax.Scope
Source *syntax.SourceContext
Doc string
Imported bool
// Stable is the conclusion of a rebind-stability proof: the binding will
// not be rebound. It is set ONLY by a completed proof, never as a synonym
// for evidence. Imported (above) is *evidence* sufficient for that
// conclusion — R7RS forbids set! on imports — so IsStable() treats Imported
// as standing evidence and this flag carries the conclusion when a proof
// discharges it by other means (defined-once ∧ ¬set! ∧ unit-closed for a
// top-level define). The opt-in WithImmutableTopLevel engine option
// discharges it for top-level defines: the compiler sets this from the
// validator's in-unit evidence (StableInUnit) and the language then forbids
// the cross-unit set!/redefine that evidence alone could not rule out (set!
// gate + redefine guard in compile_validated.go), making unit-closure hold
// by enforcement rather than inference. When the option is off (default),
// this flag stays false for non-imported bindings — asserting it from
// partial evidence would be a false conclusion. Read by the frame
// optimizer's MayCapture. Distinct from set!-permission (Imported alone,
// unless the option is on) and from the retired "Constant" flag (which
// conflated provenance, stability, and compile-time-value-known).
//
// A second writer also discharges it: under the same WithImmutableTopLevel
// option, registry.WithStableBasePrimitives stamps the ambient capture-safe
// core primitives (+, car, <, …) Stable at registration (registry/apply.go),
// backed by the same set!/redefine enforcement. Both writers mean the same
// thing — "non-rebindable" — which is why the redefine guard treats a Stable
// ambient primitive as frozen, stricter than an Imported binding (which a
// top-level define may still supersede per R7RS §5.3.1).
Stable bool
}
BindingMeta holds compile-time metadata (scopes and source location) that is never read during VM execution. Stored behind a pointer so that runtime Binding copies (the hot path) move 32 bytes instead of 56.
type BindingType ¶
type BindingType int
BindingType represents the type of a binding in the environment.
Three of the four constants are observable:
- BindingTypeVariable — regular runtime variables.
- BindingTypeSyntax — syntax transformers.
- BindingTypePrimitive — compile-time bindings (special forms, etc).
BindingTypeUnknown (the zero value) is internal scaffolding only. It is the type of pre-allocated slots in NewLocalEnvironment before they are assigned a real binding type, and is never observed by GetBinding or any external consumer. It exists solely to be a meaningful zero value for the BindingType field of a freshly constructed Binding in a pre-allocated frame slot. Removing it would require a sentinel layer (e.g. nil-binding markers in LocalEnvironmentFrame.bindings) and is not justified by the current call-site set.
type EnvironmentFrame ¶
type EnvironmentFrame struct {
// contains filtered or unexported fields
}
EnvironmentFrame represents an environment frame in the hierarchy.
Type Relationships ¶
The environment system has four types with distinct responsibilities:
┌─────────────────────────────────────────────────────────────────────────┐
│ Namespace │
│ (Per-VM instance: owns syntax interning, phases, libraries) │
│ │
│ syntaxInterns ──── map[Value]SyntaxValue (thread-safe) │
│ phases ─────────── *PhaseRegistry │
│ libraryRegistry ── LibrarySearcher (*compilation.LibraryRegistry) │
│ runtime ────────── *EnvironmentFrame (phase 0) │
└─────────────────────────────────────────────────────────────────────────┘
│
│ owns
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ EnvironmentFrame │
│ (Lexical scope node: links local/global bindings, parent chain) │
│ │
│ parent ─────────── *EnvironmentFrame (lexical parent, nil at top) │
│ local ──────────── LocalEnvironmentFrame (value; keys==nil → none) │
│ global ─────────── *GlobalEnvironmentFrame (define bindings) │
│ phaseLevel ─────── Phase (-1=template, 0=runtime, 1=expand, 2=compile) │
│ phases ─────────── *PhaseRegistry (shared reference) │
│ namespace ───────── *Namespace (back-reference) │
└─────────────────────────────────────────────────────────────────────────┘
│ │
│ contains │ contains
▼ ▼
┌───────────────────────────┐ ┌────────────────────────────────────────┐
│ LocalEnvironmentFrame │ │ GlobalEnvironmentFrame │
│ (Single scope bindings) │ │ (Phase-wide global bindings) │
│ │ │ │
│ keys ── map[Symbol][]int │ │ keys ──────── map[Symbol]int │
│ bindings ── []*Binding │ │ bindings ──── []*Binding │
└───────────────────────────┘ └────────────────────────────────────────┘
Ownership and Sharing ¶
- Namespace: Root owner. One per Wile VM instance.
- EnvironmentFrame: Many per VM. Share namespace and phases references.
- GlobalEnvironmentFrame: One per phase. Owned by EnvironmentFrame; no direct Namespace back-reference (reach Namespace via the owning frame).
- LocalEnvironmentFrame: One per lexical scope. No external references.
Lexical Hierarchy (parent chain) ¶
(lambda (x) ; EnvironmentFrame A: local={x}, parent=TopLevel
(let ((y 1)) ; EnvironmentFrame B: local={y}, parent=A
(lambda (z) ; EnvironmentFrame C: local={z}, parent=B
(+ x y z))))
Phase Hierarchy (via PhaseRegistry) ¶
Namespace
└── PhaseRegistry
├── [0] Runtime EnvironmentFrame (normal execution)
├── [1] Expand EnvironmentFrame (macro expansion, for-syntax)
├── [2] Compile EnvironmentFrame (syntax compilers, for-meta 2)
└── [-1] Template EnvironmentFrame (for-template, future)
Each phase has its own GlobalEnvironmentFrame but shares the same Namespace for syntax interning.
Binding Lookup ¶
Two-phase search: first all locals up parent chain, then globals.
func NewEnvironmentFrameWithParent ¶
func NewEnvironmentFrameWithParent(local *LocalEnvironmentFrame, parent *EnvironmentFrame) *EnvironmentFrame
NewEnvironmentFrameWithParent creates a new environment frame with the given local environment frame and parent environment frame. The global environment frame is inherited from the parent. This is used for creating child frames within a phase (e.g., lambda bodies, let-syntax). The phase level, registry, and namespace are inherited from the parent. Panics if parent is nil - use NewNamespaceFrame() instead.
func NewNamespaceFrame
deprecated
added in
v1.7.0
func NewNamespaceFrame() *EnvironmentFrame
NewNamespaceFrame creates a new root environment frame via NewNamespace.
Deprecated: Use NewNamespace().Runtime() instead for per-instance syntax interning. This function now internally uses NewNamespace() to provide proper isolation.
func (*EnvironmentFrame) AtPhase ¶
func (p *EnvironmentFrame) AtPhase(phase Phase) *EnvironmentFrame
AtPhase returns the environment for the given phase level, creating it if needed. Phase 0 is runtime, phase 1 is expansion (for-syntax), phase 2 is compile-time, etc. Negative phases (e.g., -1 for for-template) are also supported.
This is the primary method for cross-phase access with O(1) lookup time. The environment must have been created via NewNamespace().
func (*EnvironmentFrame) Compile ¶
func (p *EnvironmentFrame) Compile() *EnvironmentFrame
Compile returns the compile phase environment (phase 2), creating it if needed. This is where compile-time procedures (syntax compilers) are stored.
func (*EnvironmentFrame) Copy ¶
func (p *EnvironmentFrame) Copy() *EnvironmentFrame
Copy creates a deep copy of the environment frame. The parent, phase registry, and namespace are shared between the original and the copy.
func (*EnvironmentFrame) EnsureLocalBinding ¶ added in v1.2.0
func (p *EnvironmentFrame) EnsureLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)
EnsureLocalBinding returns the local binding for the given key, creating it if it does not already exist. Returns (index, true) if a new binding was created, or (index, false) if the binding already existed. Returns (nil, false) if the receiver is nil or has no local environment.
func (*EnvironmentFrame) EqualTo ¶
func (p *EnvironmentFrame) EqualTo(value values.Value) bool
EqualTo implements values.Value. R7RS §6.12 specifies that environments compare by eq? (pointer identity), not by structural equality of their bindings — the prior structural implementation was a latent correctness trap that no caller actually exercised. Use pointer identity here.
func (*EnvironmentFrame) Expand ¶
func (p *EnvironmentFrame) Expand() *EnvironmentFrame
Expand returns the expand phase environment (phase 1), creating it if needed. This is where syntax bindings from define-syntax are stored.
func (*EnvironmentFrame) FileResolver ¶ added in v1.6.0
func (p *EnvironmentFrame) FileResolver() FileResolver
FileResolver returns the file resolver. Shortcut for p.Namespace().FileResolver(); see the comment block above.
func (*EnvironmentFrame) GetBinding ¶
GetBinding returns the binding for the given symbol that matches the provided scopes. It searches local bindings first (walking up the parent chain), then globals.
Nil scopes means "match any" (no scope filtering). Non-nil scopes enables hygienic resolution per Flatt's model with maximal binding selection (consistent with GetLocalIndex).
func (*EnvironmentFrame) GetGlobalBinding ¶
func (p *EnvironmentFrame) GetGlobalBinding(key *GlobalIndex) *Binding
GetGlobalBinding returns the binding for the given GlobalIndex, searching global bindings in the current and parent environments. It returns nil if the binding does not exist.
func (*EnvironmentFrame) GetGlobalIndex ¶
func (p *EnvironmentFrame) GetGlobalIndex(key *values.Symbol) *GlobalIndex
GetGlobalIndex returns the GlobalIndex of the binding for the given symbol, searching global bindings in the current and parent environments. It returns nil if the binding does not exist.
The returned GlobalIndex records the specific global frame where the binding was found, enabling cross-library macro hygiene (see GlobalIndex.Env).
func (*EnvironmentFrame) GetGlobalIndexAcrossPhases ¶ added in v1.6.0
func (p *EnvironmentFrame) GetGlobalIndexAcrossPhases(key *values.Symbol) *GlobalIndex
GetGlobalIndexAcrossPhases searches for a global binding across phases (runtime → expand → compile) using read-only phase access. Returns the first GlobalIndex found, or nil if not found in any phase.
This is used during macro compilation to resolve free identifiers that may be defined in any phase (e.g., define in runtime, define-syntax in expand).
func (*EnvironmentFrame) GetGlobalIndexFromLibraryScopes ¶ added in v1.6.0
func (p *EnvironmentFrame) GetGlobalIndexFromLibraryScopes(key *values.Symbol, scopes []*syntax.Scope) *GlobalIndex
GetGlobalIndexFromLibraryScopes searches for a binding by checking each scope against the TLE's scope registry. For each scope that maps to a library env, performs a cross-phase lookup in that library's env. Returns the first match, or nil if no library binding is found.
func (*EnvironmentFrame) GetLocalBinding ¶
func (p *EnvironmentFrame) GetLocalBinding(li *LocalIndex) *Binding
GetLocalBinding returns the binding for the given LocalIndex. It returns nil if the binding does not exist.
func (*EnvironmentFrame) GetLocalBindingByIndex ¶
func (p *EnvironmentFrame) GetLocalBindingByIndex(i int) *Binding
GetLocalBindingByIndex returns the local binding at the given index in the current local environment. It does not search parent environments. It returns nil if the binding does not exist.
func (*EnvironmentFrame) GetLocalBindingBySlotDepth ¶ added in v1.4.0
func (p *EnvironmentFrame) GetLocalBindingBySlotDepth(slot, depth int) *Binding
GetLocalBindingBySlotDepth returns the binding at the given slot and depth without requiring a *LocalIndex allocation. This is the hot-path variant used by the VM's OpLoadLocal dispatch.
func (*EnvironmentFrame) GetLocalIndex ¶
func (p *EnvironmentFrame) GetLocalIndex(key *values.Symbol, scopes []*syntax.Scope) *LocalIndex
GetLocalIndex returns the LocalIndex of the binding for the given symbol that matches the given scopes. Nil scopes means "match any".
When scopes are provided, this implements Flatt's "maximal" binding resolution: among all bindings whose scopes are a subset of the reference's scopes, the one with the LARGEST scope set is returned.
Returns nil if no matching local binding exists.
func (*EnvironmentFrame) GlobalEnvironment ¶
func (p *EnvironmentFrame) GlobalEnvironment() *GlobalEnvironmentFrame
GlobalEnvironment returns the global environment frame.
func (*EnvironmentFrame) HasLocalVariableBinding ¶ added in v1.5.0
HasLocalVariableBinding reports whether sym has a local variable binding compatible with the given scopes. This is the shared implementation used by both the macro expander (to decide whether a local variable shadows a macro) and the validator (to decide whether a local variable shadows a special form).
The check implements Flatt's hygiene rule: a binding matches a reference when bindingScopes ⊆ useScopes. Bindings with no scopes (user code) match any use. Only BindingTypeVariable bindings are considered; syntax/primitive bindings do not shadow.
func (*EnvironmentFrame) InitApplyFrame ¶ added in v1.5.0
func (p *EnvironmentFrame) InitApplyFrame(dst *EnvironmentFrame)
InitApplyFrame populates dst from p's closure environment without allocating a new EnvironmentFrame. The caller is responsible for providing dst (e.g. from a pool). This is the pooling-friendly counterpart of NewApplyFrame.
func (*EnvironmentFrame) IsTopLevel ¶
func (p *EnvironmentFrame) IsTopLevel() bool
IsTopLevel returns true if this is the top-level environment frame (no parent).
func (*EnvironmentFrame) IsVoid ¶
func (p *EnvironmentFrame) IsVoid() bool
IsVoid reports whether this environment frame pointer is nil. Required by values.Value (see SchemeString comment).
func (*EnvironmentFrame) LibraryRegistry ¶
func (p *EnvironmentFrame) LibraryRegistry() LibrarySearcher
LibraryRegistry returns the library registry. Shortcut for p.Namespace().LibraryRegistry(); see the comment block above. Callers needing the full *compilation.LibraryRegistry can type-assert.
func (*EnvironmentFrame) LoadPathStack ¶ added in v1.3.0
func (p *EnvironmentFrame) LoadPathStack() PathTracker
LoadPathStack returns the load path tracker. Shortcut for p.Namespace().LoadPathStack(); see the comment block above.
func (*EnvironmentFrame) LocalBindingsSlice ¶ added in v1.9.1
func (p *EnvironmentFrame) LocalBindingsSlice() []Binding
LocalBindingsSlice returns the raw local bindings slice, bypassing the nil-keys check in LocalEnvironment(). This exposes the pre-allocated capacity that pooled frames retain across reset cycles.
func (*EnvironmentFrame) LocalEnvironment ¶
func (p *EnvironmentFrame) LocalEnvironment() *LocalEnvironmentFrame
LocalEnvironment returns the local environment frame, or nil if none.
func (*EnvironmentFrame) MaybeCreateLocalBinding ¶
func (p *EnvironmentFrame) MaybeCreateLocalBinding( key *values.Symbol, bt BindingType, scopes []*syntax.Scope, source *syntax.SourceContext, ) (*LocalIndex, bool)
MaybeCreateLocalBinding creates a local binding with scope-aware deduplication. Two bindings with the same key but incompatible scopes get separate slots; compatible scopes reuse the existing slot.
Nil scopes means "match any" during dedup (pre-hygiene callers). Returns (index, true) if created, (index, false) if already existed.
func (*EnvironmentFrame) MaybeCreateOwnGlobalBinding ¶
func (p *EnvironmentFrame) MaybeCreateOwnGlobalBinding(key *values.Symbol, bt BindingType) (*GlobalIndex, bool)
MaybeCreateOwnGlobalBinding creates a new global binding in the current global environment if it does not already exist. The key is interned before use (consistent with GlobalEnvironmentFrame.CreateGlobalBinding). It returns the GlobalIndex of the binding and a boolean indicating whether the binding was created (true) or already existed (false).
func (*EnvironmentFrame) MutableRuntime ¶ added in v1.17.0
func (p *EnvironmentFrame) MutableRuntime() *EnvironmentFrame
MutableRuntime returns the per-Engine MUTABLE runtime global of this frame's namespace — the user top level where user defines land and where eval/load and SRFI-18 threads store top-level state. It is the lexical CHILD of the immutable sealed base; resolution from it reaches sealed primitives via the parent walk.
Use this, NOT TopLevel(), when a primitive needs the frame for user-visible top-level mutations: after the layered-environment carve TopLevel() returns the immutable sealed-base root (home of the optimizer's Stable anchors), so storing a user define or thread state through TopLevel() would target the frozen base. This names the recurring intent that was previously spelled `.Namespace().Runtime()` at every call site. (It resolves the namespace's runtime, which for a flat library frame is the engine's mutable global rather than the library's own transient frame — unlike the receiver-relative Runtime().)
func (*EnvironmentFrame) Namespace ¶ added in v1.7.0
func (p *EnvironmentFrame) Namespace() *Namespace
Namespace returns the Namespace for this frame.
func (*EnvironmentFrame) NewApplyFrame ¶ added in v1.4.0
func (p *EnvironmentFrame) NewApplyFrame() *EnvironmentFrame
NewApplyFrame creates a new EnvironmentFrame for a closure application, fusing CopyForApply + NewEnvironmentFrameWithParent into one allocation. The source frame's local bindings are copied into the new frame, and the parent chain is set from the source's parent. It is the allocating counterpart of InitApplyFrame (the pooling-friendly form); both share the same parent-copy logic.
func (*EnvironmentFrame) Parent ¶
func (p *EnvironmentFrame) Parent() *EnvironmentFrame
Parent returns the parent environment frame.
func (*EnvironmentFrame) PhaseLevel ¶
func (p *EnvironmentFrame) PhaseLevel() Phase
PhaseLevel returns the phase level of this environment frame.
func (*EnvironmentFrame) PreAllocateBindings ¶ added in v1.9.1
func (p *EnvironmentFrame) PreAllocateBindings(n int)
PreAllocateBindings sets the local bindings slice to a zero-length slice with the given capacity. Used by the env frame pool to ensure fresh frames have sufficient capacity for copyForApplyInto to reslice instead of allocate. Must only be called on freshly constructed frames (before any other use). n must be non-negative; negative values are clamped to 0.
func (*EnvironmentFrame) ResetForPool ¶ added in v1.5.0
func (p *EnvironmentFrame) ResetForPool()
ResetForPool clears the EnvironmentFrame for return to the FreeList while preserving the local bindings backing array capacity. This mirrors the Stack pool pattern: clear full capacity (so GC can collect referenced values), zero the struct, then restore the slice header with len=0.
After reset, the frame is a valid zero-value EnvironmentFrame whose local.bindings has cap > 0 but len == 0. The next copyForApplyInto call will reslice instead of allocating when cap >= n.
func (*EnvironmentFrame) ResolveBindingID ¶ added in v1.9.4
func (p *EnvironmentFrame) ResolveBindingID(key *values.Symbol, scopes []*syntax.Scope) (BindingID, bool)
ResolveBindingID looks up a local binding by symbol and scopes and returns a stable BindingID. Returns the zero BindingID and false if the symbol does not resolve to a local binding.
func (*EnvironmentFrame) Runtime ¶
func (p *EnvironmentFrame) Runtime() *EnvironmentFrame
Runtime returns the runtime phase environment (phase 0). This is the root environment where normal bindings live.
func (*EnvironmentFrame) SchemeString ¶
func (p *EnvironmentFrame) SchemeString() string
SchemeString returns a Scheme-level string for this environment frame. EnvironmentFrame reaches the value plumbing because closures capture environments and store them as template literals (see machine.NativeTemplate.MaybeAppendLiteral); this method exists to satisfy values.Value, not because environment frames are ever printed by Scheme programs.
func (*EnvironmentFrame) SealedBaseTarget ¶ added in v1.17.0
func (p *EnvironmentFrame) SealedBaseTarget() *EnvironmentFrame
SealedBaseTarget returns the frame that should receive sealed (immutable) runtime bindings — primitives and bootstrap procedures — when a registry is applied with this frame as its target. For a namespace-owning runtime frame (this frame == its namespace's Runtime()) that is the namespace's sealed base; for a flat library frame (NewChildRuntime, which shares its parent's namespace and has no sealed-base parent to reach) it is the frame itself. This single predicate keeps the carve decision in one place across the engine-root, profile-child, and library-env apply paths.
func (*EnvironmentFrame) SetFileResolver ¶ added in v1.6.0
func (p *EnvironmentFrame) SetFileResolver(resolver FileResolver)
SetFileResolver sets the file resolver. Shortcut for p.Namespace().SetFileResolver(); see the comment block above. Panics if the frame has no namespace (configuration on an un-namespaced frame would be silently dropped — a programmer error).
func (*EnvironmentFrame) SetGlobalBindingByIndex ¶
func (p *EnvironmentFrame) SetGlobalBindingByIndex(i int, bd *Binding)
SetGlobalBindingByIndex sets the global binding at the given index in the current global environment. It does not search parent environments. Thread-safe: uses full Lock for write access.
func (*EnvironmentFrame) SetLibraryRegistry ¶
func (p *EnvironmentFrame) SetLibraryRegistry(registry LibrarySearcher)
SetLibraryRegistry sets the library registry. Shortcut for p.Namespace().SetLibraryRegistry(); see the comment block above. Panics if the frame has no namespace (see SetFileResolver).
func (*EnvironmentFrame) SetLocalValue ¶
func (p *EnvironmentFrame) SetLocalValue(li *LocalIndex, v values.Value) error
SetLocalValue sets the value of the binding for the given LocalIndex. It returns an error if the binding does not exist.
func (*EnvironmentFrame) SetLocalValueBySlotDepth ¶ added in v1.4.0
func (p *EnvironmentFrame) SetLocalValueBySlotDepth(slot, depth int, v values.Value) error
SetLocalValueBySlotDepth sets the value of the binding at the given slot and depth without requiring a *LocalIndex allocation. This is the hot-path variant used by the VM's OpStoreLocal dispatch.
func (*EnvironmentFrame) SetOwnGlobalValue ¶
func (p *EnvironmentFrame) SetOwnGlobalValue(gi *GlobalIndex, v values.Value) error
SetOwnGlobalValue sets the value of the binding for the given GlobalIndex. It returns an error if the binding does not exist.
func (*EnvironmentFrame) TopLevel ¶
func (p *EnvironmentFrame) TopLevel() *EnvironmentFrame
TopLevel returns the top-level environment frame in the hierarchy.
type FileResolver ¶ added in v1.10.5
type FileResolver interface {
// ResolveAndOpen finds a file by name and returns an open handle plus
// the resolved path (used for load-path-stack tracking and error messages).
ResolveAndOpen(ctx context.Context, path string) (fs.File, string, error)
}
FileResolver resolves and opens files for include/load operations. Implementations control where files are found: the OS filesystem, an embedded filesystem, or any other fs.FS.
The concrete implementations (OSFileResolver, FSFileResolver, EmbedFileResolver, ChainFileResolver) live in machine/compilation/resolver/, backed by sourceload.Finder for file search. This interface is defined here so environment/ can store it without creating a circular import.
type GlobalEnvironmentFrame ¶
type GlobalEnvironmentFrame struct {
// contains filtered or unexported fields
}
GlobalEnvironmentFrame represents global bindings for a single phase.
Design: GlobalEnvironmentFrame has no hierarchy of its own. The environment hierarchy is managed by EnvironmentFrame via its parent field. Each phase (runtime, expand, compile) has its own GlobalEnvironmentFrame.
Note: Symbol and syntax interning are delegated to Namespace via the owning EnvironmentFrame, ensuring R7RS symbol identity works correctly across all phases. GlobalEnvironmentFrame itself does not hold a back reference to its Namespace; ownership flows through EnvironmentFrame.
Thread safety: All access to keys and bindings is protected by mu. Fixes T2 from architectural review.
func NewGlobalEnvironmentFrame ¶
func NewGlobalEnvironmentFrame() *GlobalEnvironmentFrame
NewGlobalEnvironmentFrame creates a new global environment frame.
func (*GlobalEnvironmentFrame) Bindings ¶
func (p *GlobalEnvironmentFrame) Bindings() []*Binding
Bindings returns a copy of the bindings slice. Thread-safe: uses RLock for read-only access.
func (*GlobalEnvironmentFrame) Copy ¶
func (p *GlobalEnvironmentFrame) Copy() *GlobalEnvironmentFrame
Copy creates a deep copy of the global environment frame. Bindings are batch-allocated (contiguous array) for cache locality and reduced GC pressure. Thread-safe: uses RLock for read-only access.
func (*GlobalEnvironmentFrame) CreateGlobalBinding ¶
func (p *GlobalEnvironmentFrame) CreateGlobalBinding(key *values.Symbol, bt BindingType) (*GlobalIndex, bool)
CreateGlobalBinding creates a new global binding with the given key and type. Returns the GlobalIndex and whether a new binding was created (false if the binding already existed). Thread-safe: uses full Lock to prevent TOCTOU races.
func (*GlobalEnvironmentFrame) DeleteBinding ¶ added in v1.7.0
func (p *GlobalEnvironmentFrame) DeleteBinding(sym *values.Symbol) bool
DeleteBinding removes a global binding by symbol key. Returns true if the binding was found and removed, false if not found.
Note: the binding slot in p.bindings is not compacted — index-based references from compiled code would be stale. This is only safe for top-level REPL/eval bindings, not for bindings referenced by compiled bytecode.
Thread-safe: uses full Lock for write access.
func (*GlobalEnvironmentFrame) GetGlobalIndex ¶
func (p *GlobalEnvironmentFrame) GetGlobalIndex(key *values.Symbol) *GlobalIndex
GetGlobalIndex returns the GlobalIndex for the given symbol. Returns nil if the symbol is not bound in this global environment. Thread-safe: uses RLock for read-only access.
func (*GlobalEnvironmentFrame) GetOwnGlobalBinding ¶
func (p *GlobalEnvironmentFrame) GetOwnGlobalBinding(gi *GlobalIndex) *Binding
GetOwnGlobalBinding returns the binding for the given GlobalIndex from this frame only. Unlike EnvironmentFrame.GetGlobalBinding, this does NOT traverse the parent chain. Returns nil if the binding does not exist in this frame. Thread-safe: uses RLock for read-only access.
func (*GlobalEnvironmentFrame) Keys ¶
func (p *GlobalEnvironmentFrame) Keys() map[values.Symbol]int
Keys returns a copy of the symbol-to-index mapping. Thread-safe: uses RLock for read-only access.
func (*GlobalEnvironmentFrame) SetBindings ¶
func (p *GlobalEnvironmentFrame) SetBindings(vs []*Binding)
SetBindings replaces the bindings slice in this global environment. Thread-safe: uses full Lock for write access.
func (*GlobalEnvironmentFrame) SetOwnGlobalValue ¶
func (p *GlobalEnvironmentFrame) SetOwnGlobalValue(gi *GlobalIndex, v values.Value) error
SetOwnGlobalValue sets the value of the binding for the given GlobalIndex. Returns an error if the binding does not exist. Thread-safe: uses full Lock for write access.
type GlobalIndex ¶
type GlobalIndex struct {
Index *values.Symbol
Env *GlobalEnvironmentFrame
}
GlobalIndex identifies a global binding by its symbol key. Unlike LocalIndex which uses numeric indices, GlobalIndex uses the symbol directly since global bindings are accessed by name at runtime.
Env records the definition-site global frame for cross-library macro hygiene. When a macro references a non-exported helper from its defining library, Env ensures the VM resolves the binding in the library's environment rather than the use-site environment. Nil means "use the current environment" (backward compatible default).
func NewGlobalIndex ¶
func NewGlobalIndex(key *values.Symbol) *GlobalIndex
NewGlobalIndex creates a new GlobalIndex for the given symbol.
func (*GlobalIndex) EqualTo ¶
func (p *GlobalIndex) EqualTo(value values.Value) bool
EqualTo returns true if this global index equals the given value.
func (*GlobalIndex) IsVoid ¶
func (p *GlobalIndex) IsVoid() bool
IsVoid returns true if this global index is nil.
func (*GlobalIndex) SchemeString ¶
func (p *GlobalIndex) SchemeString() string
SchemeString returns a string representation of this global index.
type ImmutableLiterals ¶ added in v1.17.0
type ImmutableLiterals struct {
// contains filtered or unexported fields
}
ImmutableLiterals is the engine-scoped set of literal pair and vector objects that R7RS §4.1.2 makes immutable. Membership is determined once, at compile time, when a quoted literal is interned into a template's literal pool, and read on the cold mutation path (set-car!/set-cdr!/list-set!/ vector-set!/vector-fill!).
The set is grow-only and write-once-per-key. sync.Map is chosen for its lock-free read path (an atomic load of an internal read-only map), which matches the "written once at compile time, read many at run time" profile — reads land on set-car!/set-cdr!, a path the codebase keeps explicitly hot (registry/core/prim_pairs.go). No unsafe: keys are ordinary *Pair/*Vector pointers boxed as values.Value; pointer identity survives interface boxing, and Go's heap GC is non-moving.
The set is NOT a struct field on Pair/Vector by design: type Pair is [2]Value and type Vector is []Value (not structs), and adding a word would grow the 32-byte cons cell ~25% — the dominant heap object, directly opposing the GC-reduction goal. The side-set keeps pairs at 32 bytes; the cost lands only on the cold mutation path.
func (*ImmutableLiterals) Contains ¶ added in v1.17.0
func (p *ImmutableLiterals) Contains(v values.Value) bool
Contains reports whether v was marked immutable. Membership is by pointer identity, not equal? — a distinct but structurally-equal value is not a member unless it too was marked.
func (*ImmutableLiterals) IsImmutable ¶ added in v1.17.0
func (p *ImmutableLiterals) IsImmutable(v values.Value) bool
IsImmutable reports whether in-place mutation of v is forbidden, spanning both immutability mechanisms with a single query:
- Values that carry an intrinsic flag (values.Immutable, currently *String) answer from that flag. This works even when p is nil, since the flag does not depend on the side-set.
- Pair and Vector answer from this engine-scoped side-set by pointer identity (the literals marked at compile time per R7RS §4.1.2).
- Every other value — including non-aggregate scalars and unmarked, runtime-constructed pairs/vectors — is not constrained, so the result is false.
Mutation gate sites that already hold a concrete *Pair/*Vector use this in place of the inline `set != nil && set.Contains(v)` guard. *String primitives keep self-enforcing inside their mutators (SetChar/Fill), which is the correct layer for a value that owns its own bit; this predicate is the canonical answer for any caller that must ask without knowing the type.
func (*ImmutableLiterals) Mark ¶ added in v1.17.0
func (p *ImmutableLiterals) Mark(v values.Value)
Mark records v as immutable. Called at compile time from the quote hook.
type LibraryEnvFactory ¶ added in v1.4.0
type LibraryEnvFactory func(ctx context.Context, callerEnv *EnvironmentFrame, libraryName []string) (*EnvironmentFrame, error)
LibraryEnvFactory creates a fresh environment for an R7RS library. The returned environment must share the caller's Namespace for syntax interning, but have isolated bindings so library definitions don't leak.
The libraryName parameter contains the library name parts (e.g., ["scheme", "base"]) so the factory can implement per-library policies.
type LibrarySearcher ¶ added in v1.10.5
type LibrarySearcher interface {
GetSearchPaths() []string
}
LibrarySearcher is implemented by library registries that support path-based file discovery. It is the minimum interface environment/ needs from a LibraryRegistry: the set of directories to search when resolving include and load paths.
The full *compilation.LibraryRegistry type implements this interface. Callers that need the full registry can type-assert from LibrarySearcher.
type LocalEnvironmentFrame ¶
type LocalEnvironmentFrame struct {
// contains filtered or unexported fields
}
LocalEnvironmentFrame stores local variable bindings for a single scope. It maps symbols to binding indices for efficient lookup. Local environments are created for lambda parameters and let-bound variables. Note: LocalEnvironmentFrame has no hierarchy of its own; the hierarchy is managed by EnvironmentFrame via its parent field.
func NewLocalEnvironment ¶
func NewLocalEnvironment(pcnt int) *LocalEnvironmentFrame
NewLocalEnvironment creates a new local environment frame with pre-allocated slots for the given parameter count. Each slot is initialized with a void binding of unknown type.
func (*LocalEnvironmentFrame) Bindings ¶
func (p *LocalEnvironmentFrame) Bindings() []Binding
Bindings returns the slice of bindings in this local environment.
func (*LocalEnvironmentFrame) Copy ¶
func (p *LocalEnvironmentFrame) Copy() *LocalEnvironmentFrame
Copy creates a copy of this local environment frame. The keys map is shared by reference (copy-on-write) since it is only mutated during compilation. Copy-on-write (CoW): shares the keys map between original and copy until a mutation forces a clone. Most copies are never mutated, so the clone cost is avoided entirely. See BIBLIOGRAPHY.md "Copy-on-Write". Bindings are allocated as a single contiguous block to reduce GC pressure, and each binding's scopes slice is shared (immutable at runtime).
func (*LocalEnvironmentFrame) CopyForApply ¶ added in v1.4.0
func (p *LocalEnvironmentFrame) CopyForApply() *LocalEnvironmentFrame
CopyForApply creates a lightweight copy optimized for the Apply hot path. The keys map is shared between frames and must be treated as immutable at runtime; callers must not mutate the shared keys map or any map returned by Keys(). Bindings are batch-allocated (contiguous array) for cache locality and reduced GC pressure. Scopes and source are shared (immutable at runtime); only binding values are independent between original and copy.
func (*LocalEnvironmentFrame) EnsureLocalBinding ¶ added in v1.2.0
func (p *LocalEnvironmentFrame) EnsureLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)
EnsureLocalBinding returns the local binding for the given key, creating it if it does not already exist. Returns (index, true) if a new binding was created, or (index, false) if the binding already existed.
If the keys map is shared (from Copy), it is cloned before mutation (CoW). In practice, EnsureLocalBinding is only called during compilation, never at runtime, so the CoW path is a safety net rather than a hot path.
Note: With multi-slot keys, this returns slots[0] without scope discrimination. It is only valid for single-slot keys (fresh environments for lambda params, syntax-case pattern variables). Do not use on frames where MaybeCreateLocalBinding has created scope-distinct slots for the same key.
func (*LocalEnvironmentFrame) GetLocalBinding ¶
func (p *LocalEnvironmentFrame) GetLocalBinding(li *LocalIndex) *Binding
GetLocalBinding returns the binding at the given LocalIndex.
func (*LocalEnvironmentFrame) GetLocalIndex ¶
func (p *LocalEnvironmentFrame) GetLocalIndex(key *values.Symbol) *LocalIndex
GetLocalIndex returns the LocalIndex for the given symbol in this local environment. Returns the first slot for the key, or nil if not bound.
func (*LocalEnvironmentFrame) Keys ¶
func (p *LocalEnvironmentFrame) Keys() map[values.Symbol][]int
Keys returns a copy of the symbol-to-index mapping for this local environment. Each key maps to a slice of slot indices (common case: one element). Multiple slots per key occur when hygienic expansion creates same-name bindings with different scope sets in the same frame. The returned map is safe to mutate without affecting internal state.
func (*LocalEnvironmentFrame) MaybeCreateLocalBinding ¶ added in v1.10.7
func (p *LocalEnvironmentFrame) MaybeCreateLocalBinding( key *values.Symbol, bt BindingType, scopes []*syntax.Scope, source *syntax.SourceContext, ) (*LocalIndex, bool)
MaybeCreateLocalBinding creates a local binding with scope-aware deduplication. Two bindings with the same key but incompatible scopes get separate slots; compatible scopes reuse the existing slot. Nil scopes means "match any".
If the keys map is shared (from Copy), it is cloned before mutation (CoW). The three-index slice on append prevents mutating a shared backing array.
func (*LocalEnvironmentFrame) SetBindings ¶
func (p *LocalEnvironmentFrame) SetBindings(v []Binding)
SetBindings replaces the bindings slice in this local environment.
func (*LocalEnvironmentFrame) SetLocalValue ¶
func (p *LocalEnvironmentFrame) SetLocalValue(li *LocalIndex, v values.Value) error
SetLocalValue sets the value of the binding at the given LocalIndex.
type LocalIndex ¶
type LocalIndex [2]int
LocalIndex represents the location of a local binding as [slot, depth]. The first element (slot/over) is the index within the local environment frame. The second element (depth/up) is how many parent frames to traverse. Example: [2, 1] means "binding at slot 2 in the parent frame".
func NewLocalIndex ¶
func NewLocalIndex(over, up int) *LocalIndex
NewLocalIndex creates a new LocalIndex with the given slot and depth.
func (*LocalIndex) EqualTo ¶
func (p *LocalIndex) EqualTo(i *LocalIndex) bool
EqualTo returns true if this index equals the given index.
func (*LocalIndex) GetBinding ¶
func (p *LocalIndex) GetBinding(env *EnvironmentFrame) *Binding
GetBinding retrieves the binding at this index from the given environment.
func (*LocalIndex) Over ¶
func (p *LocalIndex) Over() int
Over returns the slot index within the local environment frame.
func (*LocalIndex) SchemeString ¶
func (p *LocalIndex) SchemeString() string
SchemeString returns a Scheme-style string representation.
func (*LocalIndex) String ¶
func (p *LocalIndex) String() string
String returns a string representation in "slot:depth" format.
func (*LocalIndex) Up ¶
func (p *LocalIndex) Up() int
Up returns the depth (number of parent frames to traverse).
type ModuleInstance ¶ added in v1.7.0
type ModuleInstance struct {
Env *EnvironmentFrame
Exports map[string]*GlobalIndex
}
ModuleInstance represents a loaded and initialized library.
type Namespace ¶ added in v1.7.0
type Namespace struct {
// Name is an optional descriptive name (e.g., "interaction-environment").
Name string
// contains filtered or unexported fields
}
Namespace represents a complete Wile VM instance. It owns per-instance syntax interning, phase registry, and library registry. This enables multiple independent Wile VMs in a single Go process.
Design: Namespace is the root of the environment hierarchy. Each EnvironmentFrame holds a reference back to its Namespace to access shared resources (syntax interning, phases, libraries).
Field inheritance policy ¶
Child namespaces (NewChildNamespace and NewSchemeReportNamespace) inherit fields from their parent in one of three ways. New fields MUST pick a policy explicitly — the existing per-field decisions are encoded here, not in the constructors.
Per-VM (each namespace has its own; no inheritance):
Name, parent, phases, runtime, moduleInstances, syntaxInterns
Note: syntaxInterns is nil in children; InternSyntax delegates
to parent so symbol identity remains globally consistent.
Captured at construction (the child stores its own copy of the
parent's pointer or map header at fork time; later *reassignments*
on the parent — e.g. parent.SetRegistry(other) — do not flow to
existing children. Mutations *through* the captured pointer — e.g.
parent.Registry().AddPrimitive(...) — ARE visible to children
because the pointer they hold is the same Go object):
libraryRegistry, libraryEnvFactory, registry, authorizer, envMap
Rationale: these are capability state. Reassignments on the
parent must not silently widen capability on existing children
(envMap is the load-bearing example — see the SetEnvMap doc
comment in this file for the discussion).
Delegated to the root (children read/write through to the topmost
parent; the field itself only ever lives on the root namespace):
fileResolver, loadPathStack, scopeRegistry, exportIndex/exportIndexBuilt
Rationale: these are per-VM resources whose identity must be
shared across the entire namespace tree (file resolution must
use the same paths in any child; library scopes registered by
one child must be visible to all). The root() helper walks the
parent chain in O(depth); per-method delegation prologues are
not used.
Adding a new field: choose a policy above, document it in this block, then:
- Captured: copy it from the parent in *both* NewChildNamespace and NewSchemeReportNamespace (these are the two constructors that populate child state from a parent; they share the same captured set).
- Delegated: define accessors that go through p.root() to read or write the field; do not store it on child namespaces.
Do NOT mix the two for one field — the asymmetry is the bug source the policy table exists to prevent.
func NewNamespace ¶ added in v1.7.0
func NewNamespace() *Namespace
NewNamespace creates a new Namespace. This is the primary entry point for creating an isolated Wile VM instance. Call SetLoadPathStack before any file loading operations.
func (*Namespace) AtPhase ¶ added in v1.7.0
func (p *Namespace) AtPhase(phase Phase) *EnvironmentFrame
AtPhase returns the environment for the given phase level, creating it if needed. Phase 0 is runtime, phase 1 is expansion (for-syntax), phase 2 is compile-time, etc. Negative phases (e.g., -1 for for-template) are also supported.
func (*Namespace) AttachModule ¶ added in v1.7.0
AttachModule copies a module instance from this namespace to the target. Returns an error if the module is not loaded in this namespace.
func (*Namespace) Authorizer ¶ added in v1.7.0
func (p *Namespace) Authorizer() security.Authorizer
Authorizer returns the security authorizer for this namespace.
func (*Namespace) BoundNamesAcrossPhases ¶ added in v1.17.0
BoundNamesAcrossPhases returns a sorted, deduplicated list of every binding name visible across all instantiated phases (runtime, expand, compile) plus the sealed base. Unlike BoundSymbolNames — which spans only the runtime global and the sealed base, returning a Scheme list for the bound-names primitives — this also walks the expand and compile phases, so macro and special-form keywords appear. It is the set a REPL wants for tab completion. Iteration order across phases does not change the result set (only names are collected); the output is sorted for determinism.
func (*Namespace) BoundSymbolNames ¶ added in v1.17.0
BoundSymbolNames returns a freshly-consed list of every symbol bound in the namespace's runtime, spanning BOTH the mutable runtime global (user defines) and the sealed base (primitives + sealed stdlib procedures). It is the shared body of the environment-bound-names and namespace-bound-names primitives. Post-carve the sealed base must be included or primitives like `car` vanish from the result (the key map carries no parent walk). Iteration order is unspecified; a name shadowed in both frames appears once (deduped via the seen set).
func (*Namespace) Compile ¶ added in v1.7.0
func (p *Namespace) Compile() *EnvironmentFrame
Compile returns the compile phase environment (phase 2), creating it if needed. This is where compile-time procedures (syntax compilers) are stored.
func (*Namespace) DeleteExtensionState ¶ added in v1.17.0
DeleteExtensionState removes any namespace-scoped extension state stored under key. Safe for concurrent use.
func (*Namespace) EnvMap ¶ added in v1.14.244
EnvMap returns the virtual environment variable map for this namespace, or nil if none has been configured. When non-nil, envvars primitives read from this map instead of the process environment, bypassing the authorizer gate.
func (*Namespace) EqualTo ¶ added in v1.7.0
EqualTo returns true if the environments are the same object.
func (*Namespace) Expand ¶ added in v1.7.0
func (p *Namespace) Expand() *EnvironmentFrame
Expand returns the expand phase environment (phase 1), creating it if needed. This is where syntax bindings from define-syntax are stored.
func (*Namespace) ExportIndex ¶ added in v1.14.244
ExportIndex returns the cached library export index and whether a build has been attempted. Returns (nil, false) if no build has run. Delegated to root. The concrete type is *compilation.LibraryExportIndex.
func (*Namespace) ExtensionState ¶ added in v1.17.0
ExtensionState returns the namespace-scoped state stored under key, and a boolean reporting whether any value was present. Extensions use this for per-Namespace storage whose lifetime is tied to the Namespace. The key is extension-chosen; an unexported sentinel type avoids cross-extension collisions. Safe for concurrent use.
func (*Namespace) FileResolver ¶ added in v1.7.0
func (p *Namespace) FileResolver() FileResolver
FileResolver returns the file resolver for include/load operations. Returns nil if no resolver has been set. Delegated to root.
func (*Namespace) ImmutableLiterals ¶ added in v1.17.0
func (p *Namespace) ImmutableLiterals() *ImmutableLiterals
ImmutableLiterals returns the engine-scoped set of immutable literal pair/vector objects (R7RS §4.1.2). Defined once on the root Namespace; children delegate through root(), so every mutator sees the same set.
func (*Namespace) ImmutableTopLevel ¶ added in v1.17.0
ImmutableTopLevel reports whether top-level-define immutability is enforced for THIS namespace. It is a property of the engine's PRIMARY (root) namespace only — the home of compiled-program top-level defines, which are the frame-reclaim optimizer's Stable anchors. CHILD namespaces (parent != nil) are mutable interaction/eval scratch spaces — (environment ...), scheme-report-environment, profile children — modeled on Chez's mutable interaction-environment (see plans/2026-06-13-immutable-toplevel-by-default-scoping.local.md:357-370): a define there shadows/redefines freely and its bindings are never stamped Stable. This is the "compilation units only" scope: immutability is for the compiled program, not for interactive eval. Reclaim soundness is preserved because set! of a Stable anchor copied into a child is still rejected by the set!-gate, which keys on IsStable() directly (compile_validated.go) rather than on this flag.
func (*Namespace) InternSyntax ¶ added in v1.7.0
func (p *Namespace) InternSyntax(k values.Value, v syntax.SyntaxValue) syntax.SyntaxValue
InternSyntax returns the canonical version of the given syntax value. If an equivalent syntax value has been seen before, it is returned. Otherwise, the value is added to the intern table and returned.
When a parent Namespace exists, interning is delegated to the parent to maintain syntax identity across environments.
This function is thread-safe.
func (*Namespace) LibraryEnvFactory ¶ added in v1.7.0
func (p *Namespace) LibraryEnvFactory() LibraryEnvFactory
LibraryEnvFactory returns the factory for creating library environments. Returns nil if no factory has been set.
func (*Namespace) LibraryRegistry ¶ added in v1.7.0
func (p *Namespace) LibraryRegistry() LibrarySearcher
LibraryRegistry returns the library registry for R7RS library loading. Returns nil if no registry has been set. Callers needing the full *compilation.LibraryRegistry can type-assert.
func (*Namespace) LoadPathStack ¶ added in v1.7.0
func (p *Namespace) LoadPathStack() PathTracker
LoadPathStack returns the load path tracker for tracking files currently being loaded. Delegated to root.
func (*Namespace) LookupLibraryEnv ¶ added in v1.7.0
func (p *Namespace) LookupLibraryEnv(scope *syntax.Scope) *EnvironmentFrame
LookupLibraryEnv returns the environment associated with the given library scope, or nil if not registered. Delegated to root. This function is thread-safe.
func (*Namespace) ModuleInstance ¶ added in v1.7.0
func (p *Namespace) ModuleInstance(path string) (*ModuleInstance, bool)
ModuleInstance returns the cached module instance for the given path, or (nil, false) if not loaded.
func (*Namespace) NewChildNamespace ¶ added in v1.7.0
func (p *Namespace) NewChildNamespace(opts ...NamespaceOption) *Namespace
NewChildNamespace creates a new Namespace whose syntax interning is delegated to the receiver (the parent).
Ownership structure ¶
The child is a fully independent Namespace with its own:
- EnvironmentFrame (runtime, phase 0) — the root lexical scope
- GlobalEnvironmentFrame — isolated global bindings (define, set!, etc.)
- PhaseRegistry — isolated phase hierarchy (expand, compile created on demand)
The child's runtime EnvironmentFrame.namespace points to the child (not the parent), so new global bindings created in the child are keyed against the child's GlobalEnvironmentFrame. This is what provides binding isolation: definitions in the child do not appear in the parent, and vice versa.
Parent Namespace (root)
+-----------------------------------------------+
| syntaxInterns: map[Value]SyntaxValue ◄────────────── all interning
| syntaxInternsMu (mutex) |
| parent: nil |
| phases: *PhaseRegistry ──► {0: envP} |
| runtime: envP ─────────────────────────────┐ |
| libraryRegistry: LibrarySearcher | |
+--------------------------------------------│--+
│
▼
EnvironmentFrame (envP, phase 0)
+-------------------------------+
| global: *GlobalEnvFrame ───┐ |
| namespace: ──► parent NS | |
+---------------------------│---+
▼
GlobalEnvironmentFrame
+-------------------------+
| keys: {x:0, y:1, ...} |
| bindings: [...] |
+-------------------------+
Child Namespace (returned by this method)
+-----------------------------------------------+
| syntaxInterns: nil (never accessed) |
| parent: ──► parent NS (interning delegate) |
| phases: *PhaseRegistry ──► {0: envC} |
| runtime: envC ─────────────────────────────┐ |
| libraryRegistry: ──► same pointer as parent| |
+--------------------------------------------│--+
│
▼
EnvironmentFrame (envC, phase 0)
+-------------------------------+
| global: *GlobalEnvFrame ───┐ |
| namespace: ──► child NS | |
+---------------------------│---+
▼
GlobalEnvironmentFrame
+-------------------------+
| keys: {} (empty) |
| bindings: [] |
+-------------------------+
Interning delegation ¶
The child stores a parent pointer and has nil interning maps. InternSyntax checks for a non-nil parent and delegates recursively, ultimately reaching the root Namespace where the maps and mutexes live. This avoids sharing map pointers across structs with independent mutexes (which would be a data race).
Inherited state ¶
The child inherits the parent's libraryRegistry (a LibrarySearcher, concretely *compilation.LibraryRegistry) by value copy. This allows the child to load libraries via (import ...) without requiring the caller to set the registry explicitly. The registry itself is a shared pointer; mutations to the registry (e.g., registering a new library) are visible to both parent and child.
The child also inherits the parent's envMap (virtual environment variable map) by reference. envMap is capability state — it constrains what the envvars primitives can read — so derived namespaces must not silently widen capability by acquiring a nil map that falls through to os.Getenv. The reference is safe to share because SetEnvMap always reassigns the field rather than mutating the existing map.
Contrast with NewChildRuntime ¶
NewChildRuntime returns an *EnvironmentFrame that shares the parent's Namespace directly (same pointer). It is used for library loading, where the library environment should share the same Namespace for syntax interning. However, because it shares the Namespace, it cannot be returned as a standalone environment value — calling Runtime() on the shared Namespace returns the parent's runtime frame, not the child's.
NewChildRuntime: NewChildNamespace:
Namespace (shared) Parent NS Child NS
+------------------+ +----------+ +----------+
| runtime: envP | | runtime: | | runtime: |
+------------------+ | envP | | envC |
│ +----------+ +----------+
│ │
┌────┴────┐ ▼
▼ ▼ EnvironmentFrame (envC)
envP envC ◄── new child +----------------------+
(parent (has own Global- | namespace: child NS |
frame) EnvFrame; reaches +----------------------+
shared NS via the
owning EnvFrame)
envC.Namespace() == parent envC.Namespace() == child
TLE.Runtime() returns envP child.Runtime() returns envC ✓
NewChildNamespace returns a new *Namespace that can be passed as a first-class Scheme value (e.g., returned from the (environment) primitive and accepted by eval). Its Runtime() returns the child's own runtime frame, and its AtPhase/Expand/Compile methods create phase environments scoped to the child.
Usage ¶
Used by PrimEnvironment and PrimNullEnvironment (R7RS §6.12) to create environments that are identity-compatible with the caller's symbol table while providing isolated bindings. Optional NamespaceOption arguments override fields that would otherwise be inherited from the parent (currently registry and authorizer); see WithRegistry and WithAuthorizer.
All captured fields (libraryRegistry, libraryEnvFactory, registry, authorizer, envMap) are copied from the parent in one place — adding a new captured field requires a single edit here, not a sweep of multiple constructors.
func (*Namespace) NewChildRuntime ¶ added in v1.7.0
func (p *Namespace) NewChildRuntime() *EnvironmentFrame
NewChildRuntime creates a new runtime environment frame that shares this Namespace for syntax interning, but has its own GlobalEnvironmentFrame and PhaseRegistry for isolated bindings.
This is used for library environments that need to:
- Share syntax interning
- Have isolated bindings (library definitions don't leak)
- Have their own phase hierarchy
func (*Namespace) NewSchemeReportNamespace ¶ added in v1.7.0
NewSchemeReportNamespace creates a new Namespace that is distinct from the receiver (so eq? returns #f) but contains a snapshot of the receiver's current global bindings at the time of the call.
This implements R7RS §6.12 scheme-report-environment semantics: the returned environment is a separate object from interaction-environment and contains the standard bindings. User definitions added after this call are NOT visible in the returned environment.
func (*Namespace) Phases ¶ added in v1.7.0
func (p *Namespace) Phases() *PhaseRegistry
Phases returns the phase registry.
func (*Namespace) RegisterLibraryScope ¶ added in v1.7.0
func (p *Namespace) RegisterLibraryScope(scope *syntax.Scope, env *EnvironmentFrame)
RegisterLibraryScope associates a library scope with its defining environment. This enables cross-library macro hygiene: when a symbol carries a library scope, the compiler can redirect binding lookup to the library's env. Delegated to root: the registry always lives on the root Namespace.
This function is thread-safe.
func (*Namespace) Registry ¶ added in v1.7.0
Registry returns the primitive registry. The caller must type-assert to *registry.Registry.
func (*Namespace) Runtime ¶ added in v1.7.0
func (p *Namespace) Runtime() *EnvironmentFrame
Runtime returns the runtime phase environment (phase 0). This is the main environment where top-level bindings live.
func (*Namespace) SchemeString ¶ added in v1.7.0
SchemeString returns the Scheme representation of the environment.
func (*Namespace) SealedBase ¶ added in v1.17.0
func (p *Namespace) SealedBase() *EnvironmentFrame
SealedBase returns this Namespace's immutable sealed-base runtime frame (phase 0), the lexical parent of the mutable runtime global. PER-NAMESPACE (NOT root-delegated, unlike immutableLiterals): each Namespace OWNS its sealed base so a profile child's curated apply does not write into the engine root's base. Report namespaces copy the parent's sealed base into their own (see NewSchemeReportNamespace).
func (*Namespace) SetAuthorizer ¶ added in v1.7.0
func (p *Namespace) SetAuthorizer(auth security.Authorizer)
SetAuthorizer sets the security authorizer for this namespace.
func (*Namespace) SetEnvMap ¶ added in v1.14.244
SetEnvMap sets the virtual environment variable map. When set, envvars primitives read from this map instead of os.Getenv.
The provided map is defensively copied so that subsequent mutation by the caller does not leak into the VM's sandbox state. A nil argument clears the virtual map (falls back to os.Getenv, gated by the authorizer).
Note: EnvMap() still returns the internal map by reference for zero-cost primitive access. Callers who reach for EnvMap() must treat the result as read-only; mutating it bypasses the defensive copy applied here.
func (*Namespace) SetExportIndex ¶ added in v1.14.244
SetExportIndex stores the library export index and marks it as built, preventing subsequent build attempts. Delegated to root.
func (*Namespace) SetExtensionState ¶ added in v1.17.0
SetExtensionState stores namespace-scoped extension state under key. Safe for concurrent use.
func (*Namespace) SetFileResolver ¶ added in v1.7.0
func (p *Namespace) SetFileResolver(resolver FileResolver)
SetFileResolver sets the file resolver for include/load operations. Delegated to root: the resolver always lives on the root Namespace.
func (*Namespace) SetImmutableTopLevel ¶ added in v1.17.0
SetImmutableTopLevel enables or disables top-level-define immutability for the engine. Set once at engine construction (WithImmutableTopLevel / WithMutableTopLevel). Stored on the root; child namespaces ignore it and are always mutable (see ImmutableTopLevel).
func (*Namespace) SetLibraryEnvFactory ¶ added in v1.7.0
func (p *Namespace) SetLibraryEnvFactory(f LibraryEnvFactory)
SetLibraryEnvFactory sets the factory for creating library environments.
func (*Namespace) SetLibraryRegistry ¶ added in v1.7.0
func (p *Namespace) SetLibraryRegistry(registry LibrarySearcher)
SetLibraryRegistry sets the library registry for R7RS library loading.
func (*Namespace) SetLoadPathStack ¶ added in v1.14.0
func (p *Namespace) SetLoadPathStack(s PathTracker)
SetLoadPathStack sets the load path tracker for this namespace. Delegated to root: the tracker always lives on the root Namespace. Must be called before any file loading operations.
func (*Namespace) SetModuleInstance ¶ added in v1.7.0
func (p *Namespace) SetModuleInstance(path string, inst *ModuleInstance)
SetModuleInstance caches a loaded module instance.
func (*Namespace) SetRegistry ¶ added in v1.7.0
SetRegistry sets the primitive registry.
func (*Namespace) SyntaxInternCount ¶ added in v1.7.0
SyntaxInternCount returns the number of interned syntax objects. This is intended for testing and debugging purposes.
type NamespaceOption ¶ added in v1.16.0
type NamespaceOption func(*namespaceConfig)
NamespaceOption configures a derived namespace at construction time. Use it with NewChildNamespace to override fields that would otherwise be inherited from the parent (e.g. a restricted registry or a different authorizer).
The "*Set" booleans on namespaceConfig let WithChildRegistry(nil) and WithChildAuthorizer(nil) mean "explicitly set the field to nil" — distinct from "no override supplied" which means "inherit from parent."
func WithChildAuthorizer ¶ added in v1.16.0
func WithChildAuthorizer(a security.Authorizer) NamespaceOption
WithChildAuthorizer overrides the parent's security authorizer on a derived namespace. Use this to give a child a stricter (or different) capability profile.
Distinct from wile.WithAuthorizer, which configures an EngineOption at the top level.
func WithChildRegistry ¶ added in v1.16.0
func WithChildRegistry(r any) NamespaceOption
WithChildRegistry overrides the parent's primitive registry on a derived namespace. Use this to install a restricted or alternative registry in a child without mutating the parent.
Distinct from wile.WithRegistry, which configures an EngineOption at the top level — the wile.* and environment.* option families operate on different config types and have different semantics.
type PathTracker ¶ added in v1.14.0
type PathTracker interface {
Push(path string)
Pop()
Current() string
CurrentDir() string
Depth() int
}
PathTracker tracks the stack of files currently being loaded. Implementations provide relative path resolution for include/load and load provenance introspection.
The concrete implementation is sourceload.LoadStack. This interface is defined here so environment/ can store it without importing machine/compilation/sourceload/.
type Phase ¶ added in v1.16.0
type Phase int8
Phase identifies a stage of compilation/evaluation. Values match Racket's phase numbering convention.
Phase indexes PhaseRegistry.envs and serves as the typed value for EnvironmentFrame.phaseLevel. The companion type registry.PhaseSet is a bitset over non-negative Phase values used for primitive registration.
ADDING A NEW PHASE requires updates in these locations:
- environment/phase_registry.go (this file) — add a Phase constant and a String() case.
- registry/phase.go — add the matching PhaseSet<Name> bit constant if the new phase is representable in a PhaseSet (i.e. phase ≥ 0 and phase < phaseSetBits). The init() assertion verifies the bit position matches the Phase index.
- registry/apply.go — extend phaseTargets if primitives may register at the new phase.
- wile/options.go — re-export so embedders can name the constant.
type PhaseRegistry ¶
type PhaseRegistry struct {
// contains filtered or unexported fields
}
PhaseRegistry manages phase-indexed environment frames. It provides O(1) access to any phase environment and supports lazy creation of phase environments on first access.
The registry is owned by the Namespace and shared across all child environments via pointer. This enables any environment frame to access any phase directly.
Thread-safe: All operations are protected by a read-write mutex to support concurrent macro expansion.
func (*PhaseRegistry) Get ¶
func (p *PhaseRegistry) Get(phase Phase) *EnvironmentFrame
Get returns the environment for the given phase, or nil if not yet created.
func (*PhaseRegistry) GetOrCreate ¶
func (p *PhaseRegistry) GetOrCreate(phase Phase) *EnvironmentFrame
GetOrCreate returns the environment for the given phase, creating it if needed. Phase 0 always returns the runtime environment. Other phases are lazily created with their own GlobalEnvironmentFrame.
func (*PhaseRegistry) Namespace ¶ added in v1.7.0
func (p *PhaseRegistry) Namespace() *Namespace
Namespace returns the owning Namespace.
func (*PhaseRegistry) Phases ¶
func (p *PhaseRegistry) Phases() []Phase
Phases returns all currently instantiated phase levels. Useful for debugging and introspection.
func (*PhaseRegistry) TopLevelFrame ¶
func (p *PhaseRegistry) TopLevelFrame() *EnvironmentFrame
TopLevelFrame returns the runtime (phase 0) environment frame.