environment

package
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Feb 6, 2026 License: Apache-2.0 Imports: 6 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 symbol interning for eq? identity
  • R7RS library import/export mappings

Architecture

Each Wile VM instance owns a TopLevelEnvironment that provides:

  • Symbol 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 built-in primitive procedure.
	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

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), optional scopes for hygienic macro expansion, and optional 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, including the scopes slice.

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

type BindingType int

BindingType represents the type of a binding in the environment.

type Environment

type Environment interface {
	Parent() Environment
	Values() []*Binding
	SetValues(v []*Binding)
	Keys() map[string]int
}

Environment is the interface for environment frames that support parent traversal and binding storage.

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:

┌─────────────────────────────────────────────────────────────────────────┐
│                        TopLevelEnvironment                              │
│  (Per-VM instance: owns symbol/syntax interning, phases, libraries)    │
│                                                                         │
│  symbolInterns ──── map[Symbol]*Symbol (thread-safe, per-instance)     │
│  syntaxInterns ──── map[wrt]SyntaxValue (thread-safe)                │
│  phases ─────────── *PhaseRegistry                                     │
│  libraryRegistry ── any (*machine.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 (lambda params, let vars)   │
│  global ─────────── *GlobalEnvironmentFrame (define bindings)          │
│  phaseLevel ─────── int (0=runtime, 1=expand, 2=compile)               │
│  phases ─────────── *PhaseRegistry (shared reference)                  │
│  topLevel ───────── *TopLevelEnvironment (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              │
└───────────────────────────┘    │  topLevel ──── *TopLevelEnvironment    │
                                 └────────────────────────────────────────┘

Ownership and Sharing

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

TopLevelEnvironment
└── 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 TopLevelEnvironment for symbol/syntax interning.

Binding Lookup

Two-phase search: first all locals up parent chain, then globals.

func NewEnvironmentFrame

func NewEnvironmentFrame(local *LocalEnvironmentFrame, global *GlobalEnvironmentFrame) *EnvironmentFrame

NewEnvironmentFrame creates a new environment frame with the given local and global environment frames. The parent field is set to nil. This is typically used for isolated environments.

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 topLevel are inherited from the parent. Panics if parent is nil - use NewTopLevelEnvironmentFrame() instead.

func NewTopLevelEnvironmentFrame deprecated

func NewTopLevelEnvironmentFrame() *EnvironmentFrame

NewTopLevelEnvironmentFrame creates a new top-level global environment frame. This frame has no parent and contains the shared symbol/syntax interning maps. It also creates the PhaseRegistry for indexed phase access.

Deprecated: Use NewTopLevelEnvironment().Runtime() instead for per-instance symbol interning. This function now internally uses NewTopLevelEnvironment() 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 NewTopLevelEnvironment().

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 topLevel are shared between the original and the copy.

func (*EnvironmentFrame) CreateGlobalBinding

func (p *EnvironmentFrame) CreateGlobalBinding(key *values.Symbol, bt BindingType) (*GlobalIndex, bool)

CreateGlobalBinding creates a new global binding in the current global environment. It returns the GlobalIndex of the new binding and a boolean indicating whether the binding was created (true) or already existed (false).

func (*EnvironmentFrame) CreateLocalBinding

func (p *EnvironmentFrame) CreateLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)

CreateLocalBinding creates a new local binding 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) 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) GetBinding

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

GetBinding returns the binding for the given symbol, searching for local bindings first, then global bindings in the current and parent environments. 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 for local bindings first (walking up the parent chain), then global bindings, 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) GetIndex

func (p *EnvironmentFrame) GetIndex(key *values.Symbol) (*LocalIndex, *GlobalIndex, bool)

GetIndex returns the index of the binding for the given symbol. It returns either a LocalIndex or GlobalIndex depending on where the binding is found. The boolean return value indicates whether the binding was found. Note: This function has known bugs (skips first frame in loops) and may need fixes.

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

func (p *EnvironmentFrame) InternSymbol(q *values.Symbol) *values.Symbol

InternSymbol interns the given symbol. Delegates to the TopLevelEnvironment for this frame. Per R7RS §6.5: "Two symbols are identical (in the sense of eq?) if and only if their names are spelled the same way." Panics if topLevel is nil (legacy environments no longer supported).

func (*EnvironmentFrame) InternSyntax

InternSyntax interns the given syntax value. Delegates to the TopLevelEnvironment for this frame. Panics if topLevel is nil (legacy environments no longer supported).

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

LibraryRegistry returns the library registry from the top-level environment. The caller must type-assert to *machine.LibraryRegistry. Returns nil if no registry has been set.

func (*EnvironmentFrame) LocalEnvironment

func (p *EnvironmentFrame) LocalEnvironment() *LocalEnvironmentFrame

LocalEnvironment returns the local environment frame.

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

func (*EnvironmentFrame) MaybeCreateLocalBindingWithScopes

func (p *EnvironmentFrame) MaybeCreateLocalBindingWithScopes(key *values.Symbol, bt BindingType, scopes []*syntax.Scope) (*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 or parent global environments if it does not already exist. It returns the GlobalIndex of the binding and a boolean indicating whether the binding was created (true) or already existed (false).

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

func (p *EnvironmentFrame) Runtime() *EnvironmentFrame

Runtime returns the runtime phase environment (phase 0). This is the TopLevel environment where normal bindings live.

func (*EnvironmentFrame) SchemeString

func (p *EnvironmentFrame) SchemeString() string

SchemeString returns a string representation of the environment frame.

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.

func (*EnvironmentFrame) SetLibraryRegistry

func (p *EnvironmentFrame) SetLibraryRegistry(registry any)

SetLibraryRegistry sets the library registry on the top-level environment. The registry should be a *machine.LibraryRegistry.

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

func (*EnvironmentFrame) TopLevelEnv

func (p *EnvironmentFrame) TopLevelEnv() *TopLevelEnvironment

TopLevelEnv returns the TopLevelEnvironment for this frame. Returns nil for legacy environments created without TopLevelEnvironment.

type EnvironmentNavigation

type EnvironmentNavigation interface {
	// TODO: remove LocalEnvironment and GlobalEnvironment methods once
	// meta environments are fully integrated.
	Meta()
	Parent() Environment
	LocalEnvironment() Environment
	GlobalEnvironment() Environment
}

EnvironmentNavigation is the interface for navigating between environment frames, including meta phases and local/global scopes.

type ExceptImportDirective

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

ExceptImportDirective indicates that the given identifier should not be imported.

func (*ExceptImportDirective) Next

Next returns the next import spec in the chain.

type ExportSet

type ExportSet struct{}

ExportSet holds the resolved export mappings for a library.

func NewExportSet

func NewExportSet(spec ExportSpec) (*ExportSet, error)

NewExportSet creates an ExportSet from a chain of export specs.

type ExportSpec

type ExportSpec interface {
	Next() ExportSpec
}

ExportSpec is the interface for export directives in library definitions.

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

func NewGlobalEnvironmentFrame

func NewGlobalEnvironmentFrame() *GlobalEnvironmentFrame

NewGlobalEnvironmentFrame creates a new global environment frame.

func (*GlobalEnvironmentFrame) Bindings

func (p *GlobalEnvironmentFrame) Bindings() []*Binding

Bindings returns the slice of bindings in this global environment.

func (*GlobalEnvironmentFrame) Copy

Copy creates a deep copy of the global environment frame. Note that topLevel is shared (not copied) between original and copy.

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. The key is interned before use. Returns the GlobalIndex and whether a new binding was created (false if the binding already existed).

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.

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.

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.

func (*GlobalEnvironmentFrame) InternSymbol

func (p *GlobalEnvironmentFrame) InternSymbol(q *values.Symbol) *values.Symbol

InternSymbol returns the canonical version of the given symbol. Delegates to TopLevelEnvironment. Per R7RS §6.5: "Two symbols are identical (in the sense of eq?) if and only if their names are spelled the same way." Panics if topLevel is nil.

func (*GlobalEnvironmentFrame) InternSyntax

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 map and returned. Delegates to TopLevelEnvironment. Panics if topLevel is nil.

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 the symbol-to-index mapping for this global environment.

func (*GlobalEnvironmentFrame) LibraryRegistry

func (p *GlobalEnvironmentFrame) LibraryRegistry() any

LibraryRegistry returns the library registry for R7RS library loading. The caller must type-assert to *machine.LibraryRegistry. Returns nil if no registry has been set. Delegates to TopLevelEnvironment. Panics if topLevel is nil.

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.

func (*GlobalEnvironmentFrame) SetLibraryRegistry

func (p *GlobalEnvironmentFrame) SetLibraryRegistry(registry any)

SetLibraryRegistry sets the library registry for R7RS library loading. The registry should be a *machine.LibraryRegistry. Delegates to TopLevelEnvironment. Panics if topLevel is nil.

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.

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 ImportSet

type ImportSet struct{}

ImportSet holds the resolved import mappings for a library.

func NewImportSet

func NewImportSet(spec ImportSpec) (*ImportSet, error)

NewImportSet creates an ImportSet from a chain of import specs.

type ImportSpec

type ImportSpec interface {
	Next() ImportSpec
}

ImportSpec is the interface for import directives in library definitions.

type LibraryImportDirective

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

LibraryImportDirective indicates that all identifiers from the given library should be imported.

func (*LibraryImportDirective) Next

Next returns the next import spec in the chain.

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 deep copy of this local environment frame.

func (*LocalEnvironmentFrame) CreateLocalBinding

func (p *LocalEnvironmentFrame) CreateLocalBinding(key *values.Symbol, bt BindingType) (*LocalIndex, bool)

CreateLocalBinding creates a new local binding with the given key and binding type.

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 the symbol-to-index mapping for this local environment.

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 OnlyExportDirective

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

OnlyExportDirective indicates that only the given identifier should be exported.

func (*OnlyExportDirective) Next

func (p *OnlyExportDirective) Next() ExportSpec

Next returns the next export spec in the chain.

type OnlyImportDirective

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

OnlyImportDirective indicates that only the given identifier should be imported.

func (*OnlyImportDirective) Next

func (p *OnlyImportDirective) Next() ImportSpec

Next returns the next import spec in the chain.

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 TopLevel environment 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 TopLevel environment. Other phases are lazily created with their own GlobalEnvironmentFrame.

func (*PhaseRegistry) Phases

func (p *PhaseRegistry) Phases() []int

Phases returns all currently instantiated phase levels. Useful for debugging and introspection.

func (*PhaseRegistry) TopLevelEnv

func (p *PhaseRegistry) TopLevelEnv() *TopLevelEnvironment

TopLevelEnv returns the owning TopLevelEnvironment. Returns nil for legacy environments created without TopLevelEnvironment.

func (*PhaseRegistry) TopLevelFrame

func (p *PhaseRegistry) TopLevelFrame() *EnvironmentFrame

TopLevelFrame returns the runtime (phase 0) environment frame.

type PrefixImportDirective

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

PrefixImportDirective indicates that any identifier with the given prefix should be imported.

func (*PrefixImportDirective) Next

Next returns the next import spec in the chain.

type RenameExportDirective

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

RenameExportDirective indicates that an identifier should be exported under a different name.

func (*RenameExportDirective) Next

Next returns the next export spec in the chain.

type RenameImportDirective

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

RenameImportDirective indicates that an identifier should be imported under a different name.

func (*RenameImportDirective) Next

Next returns the next import spec in the chain.

type TopLevelEnvironment

type TopLevelEnvironment struct {
	// Name is an optional descriptive name (e.g., "interaction-environment").
	Name string
	// contains filtered or unexported fields
}

TopLevelEnvironment represents a complete Wile VM instance. It owns per-instance symbol interning, syntax interning, phase registry, and library registry. This enables multiple independent Wile VMs in a single Go process.

Design: TopLevelEnvironment is the root of the environment hierarchy. Each EnvironmentFrame holds a reference back to its TopLevelEnvironment to access shared resources (interning, phases, libraries).

Symbol interning is per-TopLevelEnvironment (not global) to support:

  • Multiple isolated Wile VMs
  • Clean VM teardown without affecting other instances
  • R7RS §6.5 symbol identity: "Two symbols are identical (in the sense of eq?) if and only if their names are spelled the same way."

func NewTopLevelEnvironment

func NewTopLevelEnvironment() *TopLevelEnvironment

NewTopLevelEnvironment creates a new TopLevelEnvironment. This is the primary entry point for creating an isolated Wile VM instance.

func (*TopLevelEnvironment) AtPhase

func (p *TopLevelEnvironment) 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 (*TopLevelEnvironment) Compile

func (p *TopLevelEnvironment) 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 (*TopLevelEnvironment) EqualTo

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

EqualTo returns true if the environments are the same object.

func (*TopLevelEnvironment) Expand

Expand returns the expand phase environment (phase 1), creating it if needed. This is where syntax bindings from define-syntax are stored.

func (*TopLevelEnvironment) InternSymbol

func (p *TopLevelEnvironment) InternSymbol(s *values.Symbol) *values.Symbol

InternSymbol returns the canonical interned version of the given symbol. If a symbol with the same name has been interned before, that pointer is returned. Otherwise, the symbol is added to the intern table and returned. This ensures symbol identity (eq?) works correctly per R7RS §6.5.

When a parent TopLevelEnvironment exists (child environments created via NewChildTopLevelEnvironment), interning is delegated to the parent to maintain symbol identity across environments.

This function is thread-safe.

func (*TopLevelEnvironment) InternSyntax

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 TopLevelEnvironment exists, interning is delegated to the parent to maintain syntax identity across environments.

This function is thread-safe.

func (*TopLevelEnvironment) IsVoid

func (p *TopLevelEnvironment) IsVoid() bool

IsVoid returns true if the environment is nil.

func (*TopLevelEnvironment) LibraryRegistry

func (p *TopLevelEnvironment) LibraryRegistry() any

LibraryRegistry returns the library registry for R7RS library loading. The caller must type-assert to *machine.LibraryRegistry. Returns nil if no registry has been set.

func (*TopLevelEnvironment) NewChildRuntime

func (p *TopLevelEnvironment) NewChildRuntime() *EnvironmentFrame

NewChildRuntime creates a new runtime environment frame that shares this TopLevelEnvironment for symbol and syntax interning, but has its own GlobalEnvironmentFrame and PhaseRegistry for isolated bindings.

This is used for library environments that need to:

  • Share symbol interning (for R7RS §6.5 symbol identity)
  • Have isolated bindings (library definitions don't leak)
  • Have their own phase hierarchy

func (*TopLevelEnvironment) NewChildTopLevelEnvironment

func (p *TopLevelEnvironment) NewChildTopLevelEnvironment() *TopLevelEnvironment

NewChildTopLevelEnvironment creates a new TopLevelEnvironment whose symbol and syntax interning is delegated to the receiver (the parent). This ensures R7RS §6.5 symbol identity across environment boundaries: symbols interned in the child resolve to the same pointer as identically-named symbols in the parent, so eq? comparisons between them return #t.

Ownership structure

The child is a fully independent TopLevelEnvironment 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.topLevel 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 TopLevelEnvironment (root)
+-----------------------------------------------+
| symbolInterns: map[Symbol]*Symbol  ◄──────────────── all interning
| syntaxInterns: map[Value]SyntaxValue ◄────────────── goes here
| symbolInternsMu / syntaxInternsMu  (mutexes)  |
| parent: nil                                   |
| phases: *PhaseRegistry ──► {0: envP}          |
| runtime: envP ─────────────────────────────┐  |
| libraryRegistry: *machine.LibraryRegistry  |  |
+--------------------------------------------│--+
                                             │
                                             ▼
                         EnvironmentFrame (envP, phase 0)
                         +-------------------------------+
                         | global: *GlobalEnvFrame ───┐  |
                         | topLevel: ──► parent TLE   |  |
                         +---------------------------│---+
                                                     ▼
                                  GlobalEnvironmentFrame
                                  +-------------------------+
                                  | keys: {x:0, y:1, ...}   |
                                  | bindings: [...]         |
                                  | topLevel: ──► parent TLE|
                                  +-------------------------+

Child TopLevelEnvironment (returned by this method)
+-----------------------------------------------+
| symbolInterns: nil  (never accessed)          |
| syntaxInterns: nil  (never accessed)          |
| parent: ──► parent TLE  (interning delegate)  |
| phases: *PhaseRegistry ──► {0: envC}          |
| runtime: envC ─────────────────────────────┐  |
| libraryRegistry: ──► same pointer as parent|  |
+--------------------------------------------│--+
                                             │
                                             ▼
                         EnvironmentFrame (envC, phase 0)
                         +-------------------------------+
                         | global: *GlobalEnvFrame ───┐  |
                         | topLevel: ──► child TLE    |  |
                         +---------------------------│---+
                                                     ▼
                                  GlobalEnvironmentFrame
                                  +-------------------------+
                                  | keys: {}  (empty)       |
                                  | bindings: []            |
                                  | topLevel: ──► child TLE |
                                  +-------------------------+

Interning delegation

The child stores a parent pointer and has nil interning maps. InternSymbol and InternSyntax check for a non-nil parent and delegate recursively, ultimately reaching the root TopLevelEnvironment where the maps and mutexes live. This avoids sharing map pointers across structs with independent mutexes (which would be a data race).

child.InternSymbol(s)
    │
    │  p.parent != nil
    ▼
parent.InternSymbol(s)
    │
    │  p.parent == nil (root)
    ▼
p.symbolInternsMu.RLock()   ◄── mutex lives on root only
check p.symbolInterns[*s]   ◄── map lives on root only
    │
    ├── found: return canonical *Symbol
    │
    └── not found:
        p.symbolInternsMu.Lock()
        double-check, insert, return

Inherited state

The child inherits the parent's libraryRegistry (the *machine.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 TopLevelEnvironment directly (same pointer). It is used for library loading, where the library environment should intern symbols through the same TopLevelEnvironment. However, because it shares the TopLevelEnvironment, it cannot be returned as a standalone environment value — calling Runtime() on the shared TopLevelEnvironment returns the parent's runtime frame, not the child's.

NewChildRuntime:                NewChildTopLevelEnvironment:

  TopLevelEnvironment (shared)    Parent TLE        Child TLE
  +------------------+            +----------+      +----------+
  | runtime: envP    |            | runtime: |      | runtime: |
  +------------------+            | envP     |      | envC     |
          │                       +----------+      +----------+
          │                                            │
     ┌────┴────┐                                       ▼
     ▼         ▼                           EnvironmentFrame (envC)
   envP      envC ◄── new child            +---------------------+
   (parent   (has own Global-              | topLevel: child TLE |
    frame)    EnvFrame, but                +---------------------+
              topLevel points
              to shared TLE)

envC.TopLevelEnv() == parent    envC.TopLevelEnv() == child
TLE.Runtime() returns envP     child.Runtime() returns envC  ✓

NewChildTopLevelEnvironment returns a new *TopLevelEnvironment 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 (*TopLevelEnvironment) Phases

func (p *TopLevelEnvironment) Phases() *PhaseRegistry

Phases returns the phase registry.

func (*TopLevelEnvironment) Runtime

func (p *TopLevelEnvironment) Runtime() *EnvironmentFrame

Runtime returns the runtime phase environment (phase 0). This is the main environment where top-level bindings live.

func (*TopLevelEnvironment) SchemeString

func (p *TopLevelEnvironment) SchemeString() string

SchemeString returns the Scheme representation of the environment.

func (*TopLevelEnvironment) SetLibraryRegistry

func (p *TopLevelEnvironment) SetLibraryRegistry(registry any)

SetLibraryRegistry sets the library registry for R7RS library loading. The registry should be a *machine.LibraryRegistry.

func (*TopLevelEnvironment) SymbolInternCount

func (p *TopLevelEnvironment) SymbolInternCount() int

SymbolInternCount returns the number of interned symbols. This is intended for testing and debugging purposes.

func (*TopLevelEnvironment) SyntaxInternCount

func (p *TopLevelEnvironment) SyntaxInternCount() int

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

Jump to

Keyboard shortcuts

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