environment

package
v1.10.5 Latest Latest
Warning

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

Go to latest
Published: Apr 2, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

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:

Binding Lookup

EnvironmentFrame.GetBinding performs two-phase lookup:

  1. Local phase: traverse local bindings up the parent chain
  2. Global phase: search global bindings up the parent chain

EnvironmentFrame.GetBindingWithScopes adds hygiene awareness, using [Scope] sets to match identifiers according to Flatt's algorithm.

Index

Constants

View Source
const (
	// BindingTypeUnknown indicates a binding with unknown or uninitialized type.
	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
)
View Source
const (
	PhaseTemplate = -1 // for-template (template instantiation)
	PhaseRuntime  = 0  // Runtime execution (phase 0)
	PhaseExpand   = 1  // Macro expansion (for-syntax, phase 1)
	PhaseCompile  = 2  // Compile-time (for-meta 2, phase 2)
)

Phase level constants for standard Scheme phases. These match Racket's phase numbering convention.

Variables

This section is empty.

Functions

func ResolveFile added in v1.3.0

func ResolveFile(stack *LoadPathStack, path string, fallbackDirs []string) (string, error)

ResolveFile finds a file by trying resolution strategies in order:

  1. If path is absolute, use as-is
  2. If stack has a current directory, try relative to it
  3. Try each fallback directory in order

Returns the absolute path of the first match, or an error listing all searched paths.

All returned paths are guaranteed to be absolute. Symlinks in paths are preserved (not resolved to their target). For example, if /app/lib is a symlink to /usr/local/lib, resolving "foo.scm" from /app/lib/ will return /app/lib/foo.scm, not /usr/local/lib/foo.scm.

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

func (p *Binding) Copy() values.Value

Copy creates a deep copy of this binding. The meta struct is copied so that SetScopes on the original does 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

func (p *Binding) Doc() string

Doc returns the documentation string for this binding. Returns empty string for bindings without documentation.

func (*Binding) EqualTo

func (p *Binding) EqualTo(o values.Value) bool

EqualTo returns true if this binding is equal to the given value. Two bindings are equal if they have the same value and binding type.

func (*Binding) IsVoid

func (p *Binding) IsVoid() bool

IsVoid returns true if this binding is nil.

func (*Binding) SchemeString

func (p *Binding) SchemeString() string

SchemeString returns a string representation of this binding.

func (*Binding) Scopes

func (p *Binding) Scopes() []*syntax.Scope

Scopes returns the hygiene scopes associated with this binding. Returns nil for bindings without hygiene information.

func (*Binding) SetBindingType

func (p *Binding) SetBindingType(value BindingType)

SetBindingType updates the type of this binding.

func (*Binding) SetDoc added in v1.10.3

func (p *Binding) SetDoc(doc string)

SetDoc updates the documentation string for this binding.

func (*Binding) SetScopes

func (p *Binding) SetScopes(scopes []*syntax.Scope)

SetScopes updates the hygiene scopes associated with this binding.

func (*Binding) SetSource

func (p *Binding) SetSource(source *syntax.SourceContext)

SetSource updates the source location for this binding.

func (*Binding) SetValue

func (p *Binding) SetValue(value values.Value)

SetValue updates the value stored in this binding.

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.

func (*Binding) Value

func (p *Binding) Value() values.Value

Value returns the value stored in this binding.

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
}

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.

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 ─────── int (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              │
└───────────────────────────┘    │  namespace ──── *Namespace             │
                                 └────────────────────────────────────────┘

Ownership and Sharing

  • Namespace: Root owner. One per Wile VM instance.
  • EnvironmentFrame: Many per VM. Share namespace and phases references.
  • GlobalEnvironmentFrame: One per phase. Shares namespace reference.
  • 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 int) *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

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 returns true if the environment frame is equal to the given value. Two environment frames are equal if their local and global environments are equal, and their parent environments are either both nil or equal.

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 from the Namespace. Returns nil if no resolver has been set or if namespace is nil.

func (*EnvironmentFrame) GetBinding

func (p *EnvironmentFrame) GetBinding(key *values.Symbol) *Binding

GetBinding returns the binding for the given symbol, searching local bindings first (up the parent chain), then global bindings. It returns nil if the binding does not exist.

func (*EnvironmentFrame) GetBindingWithScopes

func (p *EnvironmentFrame) GetBindingWithScopes(key *values.Symbol, scopes []*syntax.Scope) *Binding

GetBindingWithScopes returns the binding for the given symbol that matches the provided scopes. This is used for hygienic variable resolution in macros. It searches local bindings first (walking up the parent chain), then globals, checking scope compatibility at each level.

For hygiene to work correctly with nested bindings of the same name:

  • Each let-bound variable has scopes from the binding site
  • A macro free identifier carries scopes from its definition site
  • We search ALL local bindings (not just innermost) to find one with matching scopes

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) *LocalIndex

GetLocalIndex returns the LocalIndex of the binding for the given symbol, searching local bindings in the current and parent environments. It returns nil if the binding does not exist.

func (*EnvironmentFrame) GetLocalIndexWithScopes

func (p *EnvironmentFrame) GetLocalIndexWithScopes(
	key *values.Symbol,
	scopes []*syntax.Scope,
) *LocalIndex

GetLocalIndexWithScopes returns the LocalIndex of a local binding that matches the given scopes.

This implements Flatt's "maximal" binding resolution: among all bindings whose scopes are a subset of the reference's scopes, we return the one with the LARGEST scope set. This ensures that more specific bindings are preferred over less specific ones.

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

func (p *EnvironmentFrame) HasLocalVariableBinding(sym *values.Symbol, scopes []*syntax.Scope) bool

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 returns true if the environment frame is nil.

func (*EnvironmentFrame) LibraryRegistry

func (p *EnvironmentFrame) LibraryRegistry() LibrarySearcher

LibraryRegistry returns the library registry from the Namespace. Returns nil if no registry has been set or if namespace is nil. Callers needing the full *compilation.LibraryRegistry can type-assert.

func (*EnvironmentFrame) LoadPathStack added in v1.3.0

func (p *EnvironmentFrame) LoadPathStack() *LoadPathStack

LoadPathStack returns the load path stack for tracking files currently being loaded, or nil if this frame has no Namespace.

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) (*LocalIndex, bool)

MaybeCreateLocalBinding creates a new local binding in the current local environment or any parent local environment if it does not already exist. It returns the LocalIndex of the binding and a boolean indicating whether the binding was created (true) or already existed (false).

The parent-chain walk delegates to resolveLocal; creation uses EnsureLocalBinding on the innermost frame.

func (*EnvironmentFrame) MaybeCreateLocalBindingWithScopes

func (p *EnvironmentFrame) MaybeCreateLocalBindingWithScopes(key *values.Symbol, bt BindingType, scopes []*syntax.Scope, source *syntax.SourceContext) (*LocalIndex, bool)

MaybeCreateLocalBindingWithScopes creates a new local binding with associated scopes in the current local environment. It returns the LocalIndex of the new binding and a boolean indicating whether the binding was created (true) or already existed (false).

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) 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.

func (*EnvironmentFrame) Parent

func (p *EnvironmentFrame) Parent() *EnvironmentFrame

Parent returns the parent environment frame.

func (*EnvironmentFrame) PhaseLevel

func (p *EnvironmentFrame) PhaseLevel() int

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 string representation of the environment frame.

func (*EnvironmentFrame) SetFileResolver added in v1.6.0

func (p *EnvironmentFrame) SetFileResolver(resolver FileResolver)

SetFileResolver sets the file resolver on the Namespace. No-op if namespace is nil.

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 on the Namespace. No-op if namespace is nil.

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/. 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, ensuring R7RS symbol identity works correctly across all phases.

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

Copy creates a deep copy of the global environment frame. Note that namespace is shared (not copied) between original and copy. 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) EqualTo

func (p *GlobalEnvironmentFrame) EqualTo(o values.Value) bool

EqualTo returns true if this global environment equals the given value. Two global environments are equal if they have the same bindings. Thread-safe: uses RLock for read-only access on both frames.

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) IsVoid

func (p *GlobalEnvironmentFrame) IsVoid() bool

IsVoid returns true if this global environment frame is nil.

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) SchemeString

func (p *GlobalEnvironmentFrame) SchemeString() string

SchemeString returns a string representation of this global environment.

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 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 LoadPathStack added in v1.3.0

type LoadPathStack struct {
	// contains filtered or unexported fields
}

LoadPathStack tracks the stack of files currently being loaded. It maintains a LIFO stack of file paths (absolute or relative), enabling path resolution and load provenance tracking.

Threading and Concurrency

The stack is thread-safe for concurrent access (uses sync.RWMutex), but does not guarantee correct LIFO ordering when multiple goroutines push/pop concurrently. This is an acceptable limitation:

  • Single-threaded loading (the common case): Fully correct LIFO semantics
  • SRFI-18 threads calling (load ...) concurrently: LIFO can corrupt
  • Impact: Relative path resolution may use wrong directory

Design Rationale: Per-VM vs Per-Thread

LoadPathStack is stored on Namespace (per-VM, not per-thread). This choice supports library loading across environment boundaries: when a library is loaded, it needs to resolve paths relative to the importing file, even though the library executes in its own isolated environment.

Alternative considered: per-thread stacks (map[threadID]*LoadPathStack). Pros: Correct LIFO even with concurrent loads. Cons: More complex, and Wile's SRFI-18 threading is not yet complete enough to justify the complexity.

Future: If SRFI-18 threading becomes more complete and concurrent file loading becomes common, consider migrating to per-thread stacks.

func NewLoadPathStack added in v1.3.0

func NewLoadPathStack() *LoadPathStack

NewLoadPathStack creates an empty load path stack.

func (*LoadPathStack) Current added in v1.3.0

func (s *LoadPathStack) Current() string

Current returns the path at the top of the stack without removing it. Returns empty string if the stack is empty.

func (*LoadPathStack) CurrentDir added in v1.3.0

func (s *LoadPathStack) CurrentDir() string

CurrentDir returns the directory of the path at the top of the stack. Returns empty string if the stack is empty.

func (*LoadPathStack) Depth added in v1.3.0

func (s *LoadPathStack) Depth() int

Depth returns the number of paths on the stack.

func (*LoadPathStack) Pop added in v1.3.0

func (s *LoadPathStack) Pop()

Pop removes the top path from the stack. Does nothing if the stack is empty (no error, no panic). This silent behavior is intentional to support defer patterns where the depth cannot be checked before popping.

func (*LoadPathStack) Push added in v1.3.0

func (s *LoadPathStack) Push(p string) error

Push adds a path to the top of the stack. Returns a wrapped ErrInvalidLoadPath if the path is empty.

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

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.

func (*LocalEnvironmentFrame) EqualTo

func (p *LocalEnvironmentFrame) EqualTo(o values.Value) bool

EqualTo returns true if this local environment is equal to the given value. Two local environments are equal if they have the same bindings.

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 nil if the symbol is not bound in this environment.

func (*LocalEnvironmentFrame) IsVoid

func (p *LocalEnvironmentFrame) IsVoid() bool

IsVoid returns true if this local environment frame is nil.

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. The returned map is safe to mutate without affecting internal state.

func (*LocalEnvironmentFrame) SchemeString

func (p *LocalEnvironmentFrame) SchemeString() string

SchemeString returns a string representation of this local environment.

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).

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.

func (*Namespace) AtPhase added in v1.7.0

func (p *Namespace) AtPhase(phase int) *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

func (p *Namespace) AttachModule(path string, target *Namespace) error

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) 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) Derive added in v1.7.0

func (p *Namespace) Derive() *Namespace

Derive creates a child namespace that shares syntax interning with the parent but has isolated bindings. The parent's registry and authorizer are shared by pointer — safe because registries are immutable after construction and authorizers are stateless interfaces.

func (*Namespace) DeriveWith added in v1.7.0

func (p *Namespace) DeriveWith(opts ...NamespaceDeriveOption) *Namespace

DeriveWith creates a child namespace with option overrides. Use this when the child needs a restricted registry or different authorizer.

func (*Namespace) EqualTo added in v1.7.0

func (p *Namespace) EqualTo(v values.Value) bool

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) FileResolver added in v1.7.0

func (p *Namespace) FileResolver() FileResolver

FileResolver returns the file resolver for include/load operations. Delegates to parent when non-nil, so child environments share the root resolver. Returns nil if no resolver has been set.

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) IsVoid added in v1.7.0

func (p *Namespace) IsVoid() bool

IsVoid returns true if the environment is nil.

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() *LoadPathStack

LoadPathStack returns the load path stack for tracking files currently being loaded. Delegates to parent when non-nil, ensuring child environments share the same stack as the root Namespace.

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. 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() *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 GlobalEnvironmentFrame.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: [...]         |
                                  | namespace: ──► parent NS|
                                  +-------------------------+

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: []            |
                                  | namespace: ──► child NS |
                                  +-------------------------+

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.

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, but                +----------------------+
              namespace points
              to shared NS)

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. TODO: review whether libraryRegistry should be copied here TODO: review for optimization/refactoring opportunities

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

func (p *Namespace) NewSchemeReportNamespace() *Namespace

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.

This function is thread-safe.

func (*Namespace) Registry added in v1.7.0

func (p *Namespace) Registry() any

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

func (p *Namespace) SchemeString() string

SchemeString returns the Scheme representation of the environment.

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) SetFileResolver added in v1.7.0

func (p *Namespace) SetFileResolver(resolver FileResolver)

SetFileResolver sets the file resolver for include/load operations. Delegates to parent when non-nil, matching the getter's delegation, so the resolver is always stored on the root Namespace.

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) 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

func (p *Namespace) SetRegistry(reg any)

SetRegistry sets the primitive registry.

func (*Namespace) SyntaxInternCount added in v1.7.0

func (p *Namespace) SyntaxInternCount() int

SyntaxInternCount returns the number of interned syntax objects. This is intended for testing and debugging purposes.

type NamespaceDeriveConfig added in v1.7.0

type NamespaceDeriveConfig struct {
	Registry   any                 // if non-nil, overrides parent's registry
	Authorizer security.Authorizer // if non-nil, overrides parent's authorizer
}

NamespaceDeriveConfig holds options for DeriveWith. Zero value means "inherit everything from parent."

type NamespaceDeriveOption added in v1.7.0

type NamespaceDeriveOption func(*NamespaceDeriveConfig)

NamespaceDeriveOption configures a derived namespace.

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 int) *EnvironmentFrame

Get returns the environment for the given phase, or nil if not yet created.

func (*PhaseRegistry) GetOrCreate

func (p *PhaseRegistry) GetOrCreate(phase int) *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() []int

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.

Jump to

Keyboard shortcuts

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