compilation

package
v1.17.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// TransformerSyntaxRules is the leading symbol for (syntax-rules ...) transformers.
	TransformerSyntaxRules = "syntax-rules"
	// TransformerERMacro is the leading symbol for (er-macro-transformer ...) transformers.
	TransformerERMacro = "er-macro-transformer"
	// FormDefineSyntax is the form name for (define-syntax ...) definitions.
	FormDefineSyntax = "define-syntax"
)
View Source
const DefaultInlineThreshold = 5

DefaultInlineThreshold is the default maximum body length for procedure inlining. A lambda body with more expressions than this is not inlined.

View Source
const DefaultMaxExpandDepth int = 50000

DefaultMaxExpandDepth bounds structural recursion depth during macro expansion. Without a bound, deeply nested syntax — reachable not from text (the parser caps that, see internal/parser DefaultMaxParseDepth) but from programmatically-constructed syntax such as macro output, datum->syntax, and quasiquote — triggers a fatal, unrecoverable Go stack overflow that kills the host process. 0 means unlimited. Mirrors the VM's DefaultMaxCallDepth and the parser's DefaultMaxParseDepth.

Chosen empirically: the expander overflows the Go stack between ~400k (heavy macro-re-expansion paths) and ~800k (light procedure-call nesting) levels; 50000 leaves an order-of-magnitude margin below the crash while sitting far above any practical program. A flat recursive macro (and/or/cond with N clauses) accumulates expansion depth linearly in N, but such forms are O(N^2) to expand and unusable well before 50000 clauses, so the bound does not regress any program anyone actually runs. Callers with genuinely deeper machine-generated syntax opt out via SetMaxDepth(0) / WithMaxExpandDepth(0).

View Source
const ImplementationName = "wile"

ImplementationName is the name of this Scheme implementation.

View Source
const SchemeIncludePathEnv = resolver.SchemeIncludePathEnv

SchemeIncludePathEnv is the environment variable name for the Scheme include path.

Variables

View Source
var DefaultLibraryPaths = []string{
	".",
	"./stdlib/lib",
}

DefaultLibraryPaths are the default directories to search for libraries.

Functions

func AllFeatures

func AllFeatures() []string

AllFeatures returns all supported feature identifiers.

func CompileSyntaxRules

func CompileSyntaxRules(ctx context.Context, env *environment.EnvironmentFrame, syntaxRulesForm syntax.SyntaxValue, libraryScope *syntax.Scope) (*machine.MachineClosure, error)

CompileSyntaxRules compiles a syntax-rules form into a transformer procedure.

R7RS Forms:

(syntax-rules (literal ...) (pattern template) ...)
(syntax-rules <ellipsis> (literal ...) (pattern template) ...)  ; custom ellipsis

The compilation process:

  1. Parse optional custom ellipsis identifier
  2. Parse the literals list - these symbols are matched literally, not as variables
  3. For each clause, identify pattern variables (symbols not in literals list)
  4. Compile each pattern to bytecode (see match/syntax_compiler.go)
  5. Create a machine.MachineClosure that, when invoked: - Tries each pattern in order against the input form - On first match, expands the template with captured bindings - Adds an "intro scope" to the expansion for hygiene

The returned closure is stored in the environment with BindingTypeSyntax, allowing the expander to recognize it as a macro transformer.

func CopyLibraryBindingsToEnv

func CopyLibraryBindingsToEnv(lib *CompiledLibrary, bindings map[string]string, targetEnv *environment.EnvironmentFrame) error

CopyLibraryBindingsToEnv copies exported bindings from a library to an environment. bindings is the map from localName -> externalName produced by ApplyToExports. Both runtime and syntax bindings are copied. This is a convenience wrapper that imports to phase 0 (runtime).

func CopyLibraryBindingsToEnvAtPhase

func CopyLibraryBindingsToEnvAtPhase(lib *CompiledLibrary, bindings map[string]string, targetEnv *environment.EnvironmentFrame, targetPhase environment.Phase) error

func ExpandAndCompile added in v1.13.21

func ExpandAndCompile(ctx context.Context, env *environment.EnvironmentFrame, stx syntax.SyntaxValue, resolver FileResolver, inlineThreshold int, maxExpandDepth int) (*machine.NativeTemplate, error)

ExpandAndCompile runs the expand+compile pipeline on a single syntax value, returning a ready-to-execute template.

A single MacroEvaluator is shared across both phases. If resolver is non-nil, it is set on the compiler for include/load file resolution. inlineThreshold controls procedure inlining (0 disables). maxExpandDepth bounds expander recursion to prevent a fatal Go stack overflow on deeply nested syntax (0 disables the bound; pass DefaultMaxExpandDepth for the standard limit). Errors are wrapped with phase context ("expansion" or "compilation"); callers may add site-specific context on top. Callers may call tpl.Optimize() on the returned template if desired.

func ImportSpecInto added in v1.17.0

func ImportSpecInto(ctx context.Context, specVal values.Value, callerEnv, targetEnv *environment.EnvironmentFrame, evaluator machine.MacroEvaluator, op string) error

ImportSpecInto parses a single import-spec datum, loads the named library, applies the import set's modifiers (only/except/prefix/rename), and copies the resulting bindings into targetEnv at the spec's phase shift. It is the shared core of the (environment ...), (make-namespace ...), and (namespace-require ...) primitives; op names the calling primitive for error context. callerEnv supplies the library registry for resolution.

func IsFeatureSupported

func IsFeatureSupported(feature string) bool

IsFeatureSupported checks if a feature identifier is supported.

func LoadBootstrapSources added in v1.17.0

func LoadBootstrapSources(ctx context.Context, env *environment.EnvironmentFrame, sources []string, resolver FileResolver, kind string) error

LoadBootstrapSources runs the canonical bootstrap pipeline — parse → expand → compile → optimize → execute — for each source string against env, using the default inline and expand-depth thresholds. Templates ARE optimized and executed on a pooled top-level context, matching production engine initialization. The kind argument ("macro" / "procedure") names the source category in error context.

This is the single implementation shared by the engine-root (pkg/wile) and internal (internal/bootstrap) bootstrap paths. Each previously kept its own copy of this loop, and the copies had drifted: the internal one ran on a raw, unpooled context and did NOT optimize, so bootstrap procedures loaded via the internal API (test helpers, embedders on the internal API) ran unoptimized while the public NewEngine path optimized them. Consolidating here removes that divergence.

func LookupPhaseBinding

func LookupPhaseBinding[T any](
	phaseEnv *environment.EnvironmentFrame,
	sym *values.Symbol,
	scopes []*syntax.Scope,
) T

LookupPhaseBinding looks up a binding by symbol in the target phase environment. Returns the value cast to type T if found, or the zero value if not found or if the value is not of type T.

This function handles hygiene by using scoped lookup - it will only match bindings whose scopes are a subset of the symbol's scopes.

func NewERCompareClosure

func NewERCompareClosure(useEnv *environment.EnvironmentFrame) *machine.ForeignClosure

NewERCompareClosure creates the `compare` closure for an ER macro invocation. useEnv is the use-site environment for resolving identifiers. The closure accepts two identifier arguments and returns #t if both resolve to the same binding (pointer equality) or both are unbound with the same name.

func NewERRenameClosure

func NewERRenameClosure(
	defExpandEnv *environment.EnvironmentFrame,
	introScope *syntax.Scope,
) *machine.ForeignClosure

NewERRenameClosure creates the `rename` closure for an ER macro invocation. defExpandEnv is the definition-site expand environment. introScope is a fresh scope unique to this macro invocation, used to ensure that renamed symbols not found in the definition-site env (e.g., temporary names like 'tmp') get a unique identity that prevents variable capture. The returned closure accepts a single symbol argument and returns a SyntaxSymbol that resolves to the definition-site binding. Results are cached per symbol name so that (eq? (rename 'x) (rename 'x)) is #t.

func RegisterAllPhaseHandlers added in v1.10.7

func RegisterAllPhaseHandlers(env *environment.EnvironmentFrame) error

RegisterAllPhaseHandlers registers both syntax compilers (compile phase) and primitive expanders (expand phase) in the correct order. Use this instead of calling RegisterSyntaxCompilers and RegisterPrimitiveExpanders separately at engine/bootstrap/test init sites.

func RegisterPhaseBindings

func RegisterPhaseBindings[F any](
	env *environment.EnvironmentFrame,
	phaseEnv func() *environment.EnvironmentFrame,
	entries []PhaseEntry[F],
	wrapper func(name string, fn F) values.Value,
) error

RegisterPhaseBindings binds all entries in the target phase environment. This is a generic helper for registering primitives in expand or compile phases.

Parameters:

  • env: The top-level environment
  • phaseEnv: Accessor for the target phase (e.g., env.Expand or env.Compile)
  • entries: Slice of (name, function) pairs to register
  • wrapper: Creates the values.Value wrapper from name and function

func RegisterPrimitiveExpanders

func RegisterPrimitiveExpanders(env *environment.EnvironmentFrame) error

func RegisterSyntaxCompilers

func RegisterSyntaxCompilers(env *environment.EnvironmentFrame) error

RegisterSyntaxCompilers binds all syntax compilers in the compile-time environment (env.Compile()). These bindings serve two purposes:

  1. Library export/import: findLibraryBinding in library_bindings.go searches the compile environment to locate syntax compilers when exporting or importing forms like syntax-case, define-syntax, etc.
  2. Scope-aware lookup via LookupSyntaxCompiler for hygiene resolution.

Compilation dispatch itself goes through the forms registry (register.go), not through these bindings. Both paths are populated from syntaxCompilerEntries to stay in sync.

The syntax compilers are bound with BindingTypePrimitive to distinguish them from syntax transformers (BindingTypeSyntax) and regular variables.

func ResolveAndInstallImportSet

func ResolveAndInstallImportSet(ctx context.Context, datum values.Value, env *environment.EnvironmentFrame, phase environment.Phase, evaluator machine.MacroEvaluator) error

ResolveAndInstallImportSet resolves an import set and installs bindings into env at the appropriate phase. Used for top-level imports (both expander and compiler). Library-internal imports share the resolution step (resolveImportSet) but use copyLibraryBindingsDirect for installation.

func ResolveLibraryFile added in v1.14.0

func ResolveLibraryFile(ctx context.Context, res FileResolver, name LibraryName) (fs.File, string, error)

ResolveLibraryFile resolves a library name to an open file handle, trying each extension in libraryExtensions order. Non-file-not-found errors (security denial, I/O) propagate immediately.

func VerifyAllPhaseHandlers added in v1.10.7

func VerifyAllPhaseHandlers() error

VerifyAllPhaseHandlers cross-checks all three phase registries: form validators (internal/forms), compilers (Tier 1 + Tier 2), and primitive expanders. Returns the first inconsistency found, or nil.

Call from tests only — not on the production init path.

func VerifyCompilers

func VerifyCompilers() error

VerifyCompilers checks that every form registered in the forms package has a corresponding compiler — either in the registry (Tier 2) or in the type switch (Tier 1). Returns an error listing any gaps.

func VerifyExpanders added in v1.10.7

func VerifyExpanders() error

VerifyExpanders checks that every Tier 2 syntax compiler entry has a corresponding primitive expander entry. A Tier 2 form without an expander is silently treated as a procedure call during expansion — the most dangerous form of registration drift.

Types

type ChainFileResolver

type ChainFileResolver = resolver.ChainFileResolver

func NewChainFileResolver

func NewChainFileResolver(resolvers []environment.FileResolver) *ChainFileResolver

NewChainFileResolver creates a resolver that searches multiple resolvers.

type ClausesWrapper added in v1.13.21

type ClausesWrapper struct {
	Clauses []*SyntaxRulesClause
}

ClausesWrapper wraps a slice of SyntaxRulesClause as a values.Value for storage in NativeTemplate literals.

func (*ClausesWrapper) EqualTo added in v1.13.21

func (p *ClausesWrapper) EqualTo(other values.Value) bool

func (*ClausesWrapper) IsVoid added in v1.13.21

func (p *ClausesWrapper) IsVoid() bool

func (*ClausesWrapper) SchemeString added in v1.13.21

func (p *ClausesWrapper) SchemeString() string

type CompileTimeCallContext

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

CompileTimeCallContext carries contextual information through the compilation process. It tracks whether an expression is in tail position, which controls whether the compiler emits SaveContinuation (non-tail) or reuses the current frame (tail).

This structure is passed by value (not pointer) through the compiler, allowing each compilation step to create modified copies without affecting the caller's context.

Tail Call Optimization

Tail call optimization (Steele 1977, R7RS §3.5): tail calls reuse the caller's continuation frame instead of allocating a new one, making recursive procedures in tail position run in constant stack space. See BIBLIOGRAPHY.md "Tail Call Optimization".

The inTail flag tracks whether an expression is in tail position. An expression is in tail position if its value will be the final result of the enclosing procedure. When inTail is true, the compiler can generate a tail call that reuses the current stack frame instead of creating a new one, preventing stack overflow in recursive procedures.

Per R7RS Section 3.5, these positions are tail positions:

  • The body of a lambda expression
  • The last expression in a begin sequence (if the begin is in tail position)
  • The consequent and alternative of an if expression (if the if is in tail position)
  • The body of a let/let*/letrec (if the let is in tail position)

These are NOT tail positions (use NotInTail()):

  • Function arguments: (f (g x)) - the call to g is not in tail position
  • Condition of if: (if (pred x) ...) - pred is not in tail position
  • Definitions: (define x (expr)) - expr is not in tail position
  • Non-final expressions in begin: (begin (a) (b) (c)) - only c is in tail position

func NewCompileTimeCallContext

func NewCompileTimeCallContext(ctx context.Context, inTail bool) CompileTimeCallContext

NewCompileTimeCallContext creates a new compile-time context. Parameters:

  • inTail: true if compiling an expression in tail position

func (CompileTimeCallContext) Context

Context returns the context associated with this compile-time call context.

func (CompileTimeCallContext) NotInTail

NotInTail returns a copy of the context with inTail set to false. Use this when compiling sub-expressions that are not in tail position:

  • Function arguments
  • Condition expressions in if
  • Initial values in define/let bindings
  • Non-final expressions in begin

Example:

// Compiling (f (g x)) - the call to g is not in tail position
err := p.CompileExpression(ctctx.NotInTail(), argExpr)

func (CompileTimeCallContext) WithFrameReuse added in v1.17.0

func (p CompileTimeCallContext) WithFrameReuse(fr frameReuse) CompileTimeCallContext

WithFrameReuse returns a copy armed with the given frame-reuse disposition. Preserves inTail and ctx.

func (CompileTimeCallContext) WithoutFrameReuse added in v1.17.0

func (p CompileTimeCallContext) WithoutFrameReuse() CompileTimeCallContext

WithoutFrameReuse returns a copy with the frame-reuse context cleared. Used when descending into a frame-pushing form (let): its body runs in a pushed frame, so a call there is no longer at the parameter-frame depth — neither the in-place OpSelfTailCall nor OpReleaseEnvFrame (both act on the parameter frame) may fire.

type CompileTimeContinuation

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

CompileTimeContinuation is a continuation used during the compilation phase

func NewCompileTimeContinuation

func NewCompileTimeContinuation(tpl *machine.NativeTemplate, env *environment.EnvironmentFrame, evaluator machine.MacroEvaluator) *CompileTimeContinuation

NewCompileTimeContinuation creates a new CompileTimeContinuation. The file resolver defaults to the one stored on the Namespace. If none is set, falls back to a fresh OSFileResolver.

func (*CompileTimeContinuation) AppendOperations

func (p *CompileTimeContinuation) AppendOperations(ops ...machine.Operation)

AppendOperations appends operations tagged with the current source from the source stack. Routes through the integer-dispatch code[] path: Wave 1-3 operations become direct instructions, everything else goes via machine.OpComplex to the sideTable.

func (*CompileTimeContinuation) CompileBeginForSyntax

func (p *CompileTimeContinuation) CompileBeginForSyntax(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileBeginForSyntax handles (begin-for-syntax expr ...).

Evaluates a sequence of expressions at compile time in the expand phase environment. Used for setting up compile-time state (hash tables, registries) that macros can access. No runtime effect — used for side effects only.

func (*CompileTimeContinuation) CompileCondExpand

func (p *CompileTimeContinuation) CompileCondExpand(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileCondExpand compiles a cond-expand expression. cond-expand is evaluated at compile-time and expands to the body of the first clause whose feature requirement is satisfied.

Syntax: (cond-expand <clause> ...) where <clause> is (<feature-requirement> <expression> ...)

Example:

(cond-expand
  (r7rs (display "R7RS"))
  (else (display "other")))

func (*CompileTimeContinuation) CompileDefineForSyntax

func (p *CompileTimeContinuation) CompileDefineForSyntax(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileDefineForSyntax handles (define-for-syntax name expr) or (define-for-syntax (name args...) body...).

This form defines a binding in the expand phase environment that is available during macro expansion. The expression is compiled and evaluated at compile time, and the result is stored in env.Expand().

Unlike define-syntax (which stores macro transformers), define-for-syntax stores regular values with BindingTypeVariable.

func (*CompileTimeContinuation) CompileDefineLibrary

func (p *CompileTimeContinuation) CompileDefineLibrary(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileDefineLibrary handles (define-library (lib-name) <library-declaration> ...).

R7RS library syntax:

(define-library <library-name>
  <library-declaration> ...)

<library-declaration> =
  | (export <export-spec> ...)
  | (import <import-set> ...)
  | (begin <command-or-definition> ...)
  | (include <filename> ...)
  | (include-ci <filename> ...)

This creates an isolated environment for the library, processes declarations in order, and registers the compiled library in the registry.

func (*CompileTimeContinuation) CompileDefineSyntax

func (p *CompileTimeContinuation) CompileDefineSyntax(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileDefineSyntax handles (define-syntax keyword transformer-expr).

This is the compile-time handler for R7RS define-syntax. Unlike most definitions, define-syntax is processed entirely at compile time:

  1. Parse the form: (define-syntax keyword (syntax-rules ...))
  2. Compile the syntax-rules transformer to a machine.MachineClosure
  3. Store the closure in the environment with BindingTypeSyntax
  4. Emit NO runtime operations (the binding is already established)

The BindingTypeSyntax marker is crucial: when the expander encounters a symbol, it checks if that symbol is bound to a syntax transformer. If so, it invokes the transformer closure to expand the macro.

This is how derived expressions like 'let' work: they're defined as macros using define-syntax, and expand to lambda expressions:

(define-syntax let
  (syntax-rules ()
    ((let ((name val) ...) body)
     ((lambda (name ...) body) val ...))))

Reference: R7RS Section 5.4 (Syntax definitions)

func (*CompileTimeContinuation) CompileEvalWhen

func (p *CompileTimeContinuation) CompileEvalWhen(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileEvalWhen handles (eval-when (phase ...) body ...).

This form controls when code is evaluated based on phase specifiers. Phase names follow Chez Scheme's eval-when (Dybvig, TSPL §12.10):

  • expand: evaluate during macro expansion (at compile time)
  • compile: evaluate during compilation (currently same as expand)
  • run: evaluate at runtime (generate code for normal execution)

Multiple phases can be specified. If both expand and run are specified, the body is evaluated at compile time AND code is generated for runtime.

eval-when is not part of R7RS-small; it is a Wile extension.

Examples:

(eval-when (expand)
  (display "at expansion time"))

(eval-when (run)
  (display "at runtime"))

(eval-when (expand run)
  (display "both times"))

func (*CompileTimeContinuation) CompileExport

CompileExport handles top-level (export <export-spec> ...).

This is only valid within a library definition. At top-level, it's an error.

func (*CompileTimeContinuation) CompileExpression

func (p *CompileTimeContinuation) CompileExpression(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileExpression compiles a general expression. Pushes the expression's source context onto the source stack so that all operations emitted during compilation (including infrastructure ops like Branch and Push) are tagged with the source location.

func (*CompileTimeContinuation) CompileImport

CompileImport handles top-level (import <import-set> ...).

This is for top-level imports outside of a library definition. It loads the specified libraries and binds their exports in the current environment.

Supports Racket-style phased imports:

  • (import (scheme base)) ; Phase 0 (runtime)
  • (import (for-syntax (scheme base))) ; Phase 1 (expand)
  • (import (for-template (scheme base))) ; Phase -1
  • (import (for-meta 2 (scheme base))) ; Phase 2

func (*CompileTimeContinuation) CompileInclude

CompileInclude compiles an include expression. It reads and compiles all forms from the specified files in order. Each form is expanded and compiled in the current environment.

func (*CompileTimeContinuation) CompileIncludeCi

CompileIncludeCi compiles an include-ci expression.

func (*CompileTimeContinuation) CompileMeta

CompileMeta compiles a meta expression.

func (*CompileTimeContinuation) CompileQuasisyntax

func (p *CompileTimeContinuation) CompileQuasisyntax(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileQuasisyntax compiles the (quasisyntax template) form.

quasisyntax is like quasiquote but for syntax objects. It supports:

  • (unsyntax expr) - evaluate expr and splice result at depth 1
  • (unsyntax-splicing expr) - evaluate and splice list at depth 1
  • nested quasisyntax increases depth

Like quasiquote, unsyntax only evaluates when depth reaches 0. The result is a syntax object, not a raw datum.

func (*CompileTimeContinuation) CompileSelfEvaluating

func (p *CompileTimeContinuation) CompileSelfEvaluating(_ CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileSelfEvaluating compiles a self-evaluating expression (literal).

func (*CompileTimeContinuation) CompileSymbol

CompileSymbol compiles a syntax symbol expression.

Resolution order (Flatt 2016 "sets of scopes"): a scope-set local match wins over everything, including a ResolvedBinding pin carried from macro expansion. This lets a binding co-introduced by a template — which carries the same intro scope as the reference — shadow a same-named global. The ResolvedBinding and library-scope fallbacks fire only when no lexical binding shadows. See plans/2026-06-15-macro-hygiene-global-shadow-fix.local.md (Change 2).

func (*CompileTimeContinuation) CompileSyntax

CompileSyntax compiles the (syntax template) form.

Unlike quote which unwraps syntax to raw values, syntax preserves the syntax structure. When used inside syntax-case, pattern variables in the template are substituted with their matched values.

For templates containing ellipsis (...), runtime expansion is used because ellipsis patterns capture variable-length lists that must be expanded dynamically.

(syntax template) -> syntax-object

func (*CompileTimeContinuation) CompileSyntaxCase

func (p *CompileTimeContinuation) CompileSyntaxCase(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileSyntaxCase compiles the (syntax-case expr (literal ...) clause ...) form.

R6RS syntax-case is a pattern matching form that provides procedural macro facilities. Unlike syntax-rules which expands to templates, syntax-case evaluates arbitrary Scheme code in the body, with pattern variables bound as local variables.

Syntax:

(syntax-case expr (literal ...)
  (pattern body)
  (pattern fender body)
  ...)

Compilation strategy:

  1. Compile expr to get the input syntax object
  2. For each clause, generate pattern matching and body code
  3. Pattern variables are bound as local variables in the body's scope
  4. If fender exists, it's evaluated as a guard condition

func (*CompileTimeContinuation) CompileUnquote

CompileUnquote errors - unquote outside of quasiquote

func (*CompileTimeContinuation) CompileUnquoteSplicing

func (p *CompileTimeContinuation) CompileUnquoteSplicing(_ CompileTimeCallContext, _ syntax.SyntaxValue) error

CompileUnquoteSplicing errors - unquote-splicing outside of quasiquote

func (*CompileTimeContinuation) CompileUnsyntax

CompileUnsyntax errors - unsyntax outside of quasisyntax

func (*CompileTimeContinuation) CompileUnsyntaxSplicing

func (p *CompileTimeContinuation) CompileUnsyntaxSplicing(_ CompileTimeCallContext, _ syntax.SyntaxValue) error

CompileUnsyntaxSplicing errors - unsyntax-splicing outside of quasisyntax

func (*CompileTimeContinuation) CompileValidatedApply

func (p *CompileTimeContinuation) CompileValidatedApply(ctctx CompileTimeCallContext, v *validate.ValidatedApply) error

CompileValidatedApply compiles a validated (apply proc arg1 ... args) form.

R7RS §6.10: apply calls proc with the arguments arg1 ... concatenated with the elements of args (the final argument, which must be a list).

Bytecode (non-tail):

SaveContinuation →after
<compile proc>          PUSH
<compile arg1>          PUSH
...
<compile argN>          PUSH
<compile finalList>              ; value = finalList
OpUnpackListToStack              ; stack: [proc, arg1, ..., argN, x1, x2, ...]
Pull                             ; value = proc
Apply                            ; calls proc(arg1, ..., argN, x1, x2, ...)
after:

Tail position: same without SaveContinuation/patch.

func (*CompileTimeContinuation) CompileValidatedBegin

func (p *CompileTimeContinuation) CompileValidatedBegin(ctctx CompileTimeCallContext, v *validate.ValidatedBegin) error

CompileValidatedBegin compiles a validated (begin expr...) form.

begin (R7RS 4.2.3) sequences expressions for side effects. All expressions are evaluated left-to-right; the value of the last expression becomes the value of the entire begin form.

R7RS §5.3.2: Internal definitions use letrec* semantics - all defined names are visible throughout the body, enabling forward references between defines.

Example: (begin (display "hello") (newline) 42) => 42 (after printing)

func (*CompileTimeContinuation) CompileValidatedCaseLambda

func (p *CompileTimeContinuation) CompileValidatedCaseLambda(ctctx CompileTimeCallContext, v *validate.ValidatedCaseLambda) error

CompileValidatedCaseLambda compiles a validated (case-lambda [clause] ...) form.

case-lambda (R7RS 4.2.9) creates a procedure that dispatches to different implementations based on the number of arguments. For example:

(case-lambda
  ((x) (* x x))           ; 1 arg: square
  ((x y) (* x y))         ; 2 args: multiply
  ((x y . rest) (apply + x y rest)))  ; 2+ args: sum all

At runtime, the VM selects the first clause whose arity matches the call.

func (*CompileTimeContinuation) CompileValidatedDefine

func (p *CompileTimeContinuation) CompileValidatedDefine(ctctx CompileTimeCallContext, v *validate.ValidatedDefine) error

CompileValidatedDefine compiles a validated define form.

func (*CompileTimeContinuation) CompileValidatedDefineFn

func (p *CompileTimeContinuation) CompileValidatedDefineFn(ctctx CompileTimeCallContext, v *validate.ValidatedDefine) error

CompileValidatedDefineFn compiles the function shorthand form of define.

Usage: (define (name param ...) body ...)

(define (name param ... . rest) body ...)

This is syntactic sugar equivalent to:

(define name (lambda (param ...) body ...))

Examples:

(define (square x) (* x x))           ; fixed arity
(define (sum . args) (apply + args))  ; variadic (all args)
(define (sum x . rest) (apply + x rest))  ; variadic (1+ args)

The function name is bound before compiling the body to enable self-recursion:

(define (fact n) (if (<= n 1) 1 (* n (fact (- n 1)))))

func (*CompileTimeContinuation) CompileValidatedDynamicWind

func (p *CompileTimeContinuation) CompileValidatedDynamicWind(ctctx CompileTimeCallContext, v *validate.ValidatedDynamicWind) error

CompileValidatedDynamicWind compiles a validated (dynamic-wind before thunk after) form.

R7RS §6.10: dynamic-wind calls thunk without arguments, returning the result(s). Before is called whenever execution enters the dynamic extent of the call to thunk, and after is called whenever it exits.

The key insight is that by compiling to bytecode, the cleanup code (calling after) is in the bytecode stream. When a continuation is captured inside the thunk and later restored, the cleanup code will run on normal completion.

Bytecode structure:

<compile before> PUSH
<compile thunk>  PUSH
<compile after>  PUSH          ; Stack: [before, thunk, after]
PEEK_K 2                       ; value = before
SAVE_CONTINUATION →after_before
APPLY                          ; call before()
after_before:                  ; Stack: [before, thunk, after]
OP_PUSH_WIND                   ; create winding frame
PEEK_K 1                       ; value = thunk
SAVE_CONTINUATION →after_thunk
APPLY                          ; call thunk()
after_thunk:                   ; Stack: [before, thunk, after]
PUSH                           ; save thunk result, Stack: [before, thunk, after, result]
OP_POP_WIND                    ; pop winding frame
PEEK_K 1                       ; value = after
SAVE_CONTINUATION →after_after
APPLY                          ; call after()
after_after:                   ; Stack: [before, thunk, after, result]
PEEK_K 0                       ; value = result (thunk's return value)
DROP DROP DROP DROP            ; clean up stack

func (*CompileTimeContinuation) CompileValidatedIf

func (p *CompileTimeContinuation) CompileValidatedIf(ctctx CompileTimeCallContext, v *validate.ValidatedIf) error

CompileValidatedIf compiles a validated (if test conseq [alt]) form. The structure is guaranteed to be valid by the validator.

Constant folding (Aho et al., Compilers §8.5): when the test is a compile-time literal, the entire if-form reduces to one branch. This is the simplest form of constant folding — evaluating known expressions at compile time rather than runtime. See BIBLIOGRAPHY.md "Constant Folding".

func (*CompileTimeContinuation) CompileValidatedLambda

func (p *CompileTimeContinuation) CompileValidatedLambda(ctctx CompileTimeCallContext, v *validate.ValidatedLambda) error

CompileValidatedLambda compiles a validated (lambda params body...) form.

func (*CompileTimeContinuation) CompileValidatedLet

func (p *CompileTimeContinuation) CompileValidatedLet(
	ctctx CompileTimeCallContext,
	v *validate.ValidatedLet,
) error

func (*CompileTimeContinuation) CompileValidatedQuasiquote

func (p *CompileTimeContinuation) CompileValidatedQuasiquote(ctctx CompileTimeCallContext, v *validate.ValidatedQuasiquote) error

CompileValidatedQuasiquote compiles a validated (quasiquote template) form. Quasiquote has complex runtime semantics, so we delegate to the existing compiler.

func (*CompileTimeContinuation) CompileValidatedQuote

CompileValidatedQuote compiles a validated (quote datum) form.

func (*CompileTimeContinuation) CompileValidatedSetBang

func (p *CompileTimeContinuation) CompileValidatedSetBang(ctctx CompileTimeCallContext, v *validate.ValidatedSetBang) error

CompileValidatedSetBang compiles a validated (set! name expr) form.

func (*CompileTimeContinuation) CompileValidatedWithContinuationMark

func (p *CompileTimeContinuation) CompileValidatedWithContinuationMark(
	ctctx CompileTimeCallContext,
	v *validate.ValidatedWithContinuationMark,
) error

CompileValidatedWithContinuationMark compiles (with-continuation-mark key val body).

Tail position:

<compile key> PUSH
<compile val>
SetContMark               ; pops key, sets marks[key] = val
<compile body in tail>

Non-tail position:

<compile key> PUSH
<compile val>
SaveContMark              ; pops key, saves (key, old) on stack, sets mark
<compile body in non-tail>
RestoreContMark           ; pops (old, key), restores mark

func (*CompileTimeContinuation) CompileWithSyntax

func (p *CompileTimeContinuation) CompileWithSyntax(ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

CompileWithSyntax compiles the (with-syntax ((pattern expr) ...) body ...) form.

with-syntax is a convenience form for binding pattern variables from expressions. It's equivalent to:

(syntax-case (list expr ...) ()
  ((pattern ...) (let () body ...)))

For now, this implements a simple transformation approach.

func (*CompileTimeContinuation) SetFileResolver

func (p *CompileTimeContinuation) SetFileResolver(r FileResolver)

SetFileResolver overrides the file resolver used by include/load. Nil resets to the environment's resolver (or OSFileResolver as fallback).

func (*CompileTimeContinuation) SetInlineThreshold added in v1.10.7

func (p *CompileTimeContinuation) SetInlineThreshold(n int)

SetInlineThreshold sets the maximum body length for procedure inlining. 0 disables inlining entirely.

func (*CompileTimeContinuation) SetLibraryCallback

func (p *CompileTimeContinuation) SetLibraryCallback(cb func(*CompiledLibrary))

SetLibraryCallback sets a callback function that will be called when a library is compiled via CompileDefineLibrary. This is used by LoadLibrary to capture the compiled library.

type CompiledLibrary

type CompiledLibrary struct {
	Name        LibraryName                   // Library name
	Description string                        // from (description ...) clause; "" if absent
	Env         *environment.EnvironmentFrame // Library's private environment
	Exports     map[string]string             // external-name -> internal-name
	SourceFile  string                        // Path to .sld file (for error messages)
	Template    *machine.NativeTemplate       // Compiled bytecode (for execution)
}

CompiledLibrary holds a loaded and compiled library.

func LoadLibrary

LoadLibrary loads a library by name, compiling and executing it if not already loaded. Returns the CompiledLibrary which can be used to import bindings.

The function: 1. Checks if already loaded (returns cached library) 2. Checks for circular dependencies 3. Resolves and opens the library file via FileResolver 4. Parses and compiles the define-library form 5. Executes the library to create runtime bindings 6. Registers the library in the registry

func NewCompiledLibrary

func NewCompiledLibrary(name LibraryName, env *environment.EnvironmentFrame) *CompiledLibrary

NewCompiledLibrary creates a new compiled library.

func (*CompiledLibrary) AddExport

func (p *CompiledLibrary) AddExport(externalName, internalName string)

AddExport adds an export to the library. If internalName is empty, it defaults to externalName (no rename).

func (*CompiledLibrary) GetInternalName

func (p *CompiledLibrary) GetInternalName(externalName string) string

GetInternalName returns the internal name for an exported external name. Returns empty string if not exported.

func (*CompiledLibrary) IsExported

func (p *CompiledLibrary) IsExported(externalName string) bool

IsExported returns true if the given external name is exported.

type CompilerFunc

CompilerFunc is the signature for Tier 2 (syntax passthrough) compiler functions. These receive ValidatedLiteral because Tier 2 forms pass through validation as literals with a FormName. Tier 1 forms (if, define, lambda, etc.) are dispatched by type switch in compileValidated and never reach the registry.

func LookupCompiler

func LookupCompiler(name string) CompilerFunc

LookupCompiler returns the compiler function for a form name, or nil.

type ERMacroTransformer

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

ERMacroTransformer wraps a 3-arg machine.MachineClosure to identify it as an explicit-renaming transformer in expandMacroInvocation. The defEnv captures the expand-time environment at the macro definition site, used by the rename closure to resolve definition-site bindings.

func NewERMacroTransformer

func NewERMacroTransformer(closure *machine.MachineClosure, defEnv *environment.EnvironmentFrame) *ERMacroTransformer

func (*ERMacroTransformer) Closure

func (*ERMacroTransformer) DefEnv

func (*ERMacroTransformer) EqualTo

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

func (*ERMacroTransformer) IsVoid

func (p *ERMacroTransformer) IsVoid() bool

func (*ERMacroTransformer) SchemeString

func (p *ERMacroTransformer) SchemeString() string

type EmbedFileResolver

type EmbedFileResolver = resolver.EmbedFileResolver

func NewEmbedFileResolver

func NewEmbedFileResolver(fsys fs.FS) *EmbedFileResolver

NewEmbedFileResolver creates a resolver backed by an embedded filesystem.

type ExpanderContext

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

ExpanderContext provides access to the macro expander from within Scheme code during macro expansion. It is set on machine.MachineContext when invoking macro transformers, enabling syntax-local-* primitives.

func NewExpanderContext

func NewExpanderContext(
	env *environment.EnvironmentFrame,
	expander *ExpanderTimeContinuation,
) *ExpanderContext

NewExpanderContext creates a new ExpanderContext.

func (*ExpanderContext) Env

Env returns the environment frame associated with this context.

func (*ExpanderContext) Expand

Expand fully expands a syntax object.

func (*ExpanderContext) ExpandOnce

ExpandOnce performs a single step of macro expansion. Returns (expanded-syntax, did-expand, error). If the input is a macro call, it expands it once and returns (result, true, nil). If the input is not a macro call, it returns (input, false, nil).

func (*ExpanderContext) IntroductionScope

func (p *ExpanderContext) IntroductionScope() *syntax.Scope

IntroductionScope returns the introduction scope for the current macro expansion. This scope is added to identifiers introduced by a macro and can be flipped using syntax-local-introduce.

func (*ExpanderContext) SetIntroductionScope

func (p *ExpanderContext) SetIntroductionScope(scope *syntax.Scope)

SetIntroductionScope sets the introduction scope for the current macro expansion.

func (*ExpanderContext) SetUseSiteScope

func (p *ExpanderContext) SetUseSiteScope(scope *syntax.Scope)

SetUseSiteScope sets the use-site scope for binding forms.

func (*ExpanderContext) UseSiteScope

func (p *ExpanderContext) UseSiteScope() *syntax.Scope

UseSiteScope returns the use-site scope for binding forms. This scope is used by syntax-local-identifier-as-binding to mark identifiers as binding sites.

type ExpanderTimeContinuation

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

ExpanderTimeContinuation is a continuation used during the expansion phase.

It walks the syntax tree, detecting and expanding macro invocations. The env field provides access to macro definitions (BindingTypeSyntax bindings).

func NewExpanderTimeContinuation

func NewExpanderTimeContinuation(ctx context.Context, env *environment.EnvironmentFrame, evaluator machine.MacroEvaluator) *ExpanderTimeContinuation

NewExpanderTimeContinuation creates a new ExpanderTimeContinuation. The returned expander begins a fresh expansion run with its own depth guard defaulted to DefaultMaxExpandDepth; override via SetMaxDepth.

func (*ExpanderTimeContinuation) Context

Context returns the context associated with this expander continuation.

func (*ExpanderTimeContinuation) ExpandBodyWithDefineSyntax

func (p *ExpanderTimeContinuation) ExpandBodyWithDefineSyntax(
	forms []syntax.SyntaxValue,
) ([]syntax.SyntaxValue, error)

ExpandBodyWithDefineSyntax expands a sequence of body forms, compiling define-syntax forms as encountered so subsequent forms can use the macros.

This unifies the expansion pattern used by: - Lambda bodies (internal define-syntax) - Library bodies (top-level define-syntax) - Include files (top-level define-syntax)

R7RS §5.3: Internal define-syntax forms must be processed before expanding subsequent body expressions so that locally-defined macros are visible.

R7RS §5.3.2: Bodies use letrec* semantics where all defined names are visible to all initializers. This enables forward references within macros - a macro can reference a definition that appears later in the same body.

func (*ExpanderTimeContinuation) ExpandExpression

func (p *ExpanderTimeContinuation) ExpandExpression(expr syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandExpression expands a syntax expression.

This is the single recursion chokepoint of the expander: every descent into a sub-expression — nested cars, argument lists, primitive-form bodies (via child expanders), and macro re-expansion — funnels through here. The depth guard therefore bounds total Go-stack recursion for the whole expansion run, returning a catchable ErrExpandDepthExceeded instead of letting deeply nested (programmatically-constructed) syntax crash the host with a fatal Go stack overflow. See expandDepthGuard and DefaultMaxExpandDepth.

func (*ExpanderTimeContinuation) ExpandOnce

ExpandOnce performs a single step of macro expansion. Returns (expanded-syntax, did-expand, error). If the input is a macro call, it expands it once and returns (result, true, nil). If the input is not a macro call, it returns (input, false, nil). Unlike ExpandExpression, this does NOT recursively expand the result.

func (*ExpanderTimeContinuation) ExpandPrimitiveForm

func (p *ExpanderTimeContinuation) ExpandPrimitiveForm(primName string, sym *syntax.SyntaxSymbol, expr syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandPrimitiveForm handles expansion within primitive forms like if, begin, lambda, define, etc. Some primitives need their subexpressions expanded (like if, begin) while others should be left unchanged (like quote, define-syntax).

This function looks up the primitive expander in the expand environment registry. If found, it invokes the expander; otherwise returns the form unchanged.

func (*ExpanderTimeContinuation) ExpandSelfEvaluating

func (p *ExpanderTimeContinuation) ExpandSelfEvaluating(expr syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandSelfEvaluating handles self-evaluating expressions.

func (*ExpanderTimeContinuation) ExpandSymbol

ExpandSymbol handles a symbol expression.

func (*ExpanderTimeContinuation) ExpandSyntaxArgumentList

func (p *ExpanderTimeContinuation) ExpandSyntaxArgumentList(args syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandSyntaxArgumentList expands each argument in the argument list. It returns a new syntax list with the expanded arguments.

func (*ExpanderTimeContinuation) ExpandSyntaxExpression

func (p *ExpanderTimeContinuation) ExpandSyntaxExpression(sym *syntax.SyntaxSymbol, expr syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandSyntaxExpression checks if sym is a macro and expands it, or returns the expression as a procedure call if not.

This is where macro invocation happens:

  1. Look up the symbol in the expand environment
  2. If bound with BindingTypeSyntax, it's a macro - invoke the transformer
  3. If it's a primitive (like quote, if, define-syntax), don't expand args
  4. Otherwise, treat as procedure call and expand arguments

The transformer closure (machine.MachineClosure from CompileSyntaxRules) is invoked by creating a machine.MachineContext and running it. The transformer:

  • Receives the full macro invocation form on the eval stack
  • Pattern matches against its clauses (OperationSyntaxRulesTransform)
  • Expands the matching template with captured bindings
  • Adds an intro scope to the expansion for hygiene
  • Returns the expanded syntax in the value register

The expanded result may itself contain macro invocations, so the caller should recursively expand it.

func (*ExpanderTimeContinuation) ExpandSyntaxOrProcedureCall

func (p *ExpanderTimeContinuation) ExpandSyntaxOrProcedureCall(car syntax.SyntaxValue, cdr syntax.SyntaxValue) (syntax.SyntaxValue, error)

ExpandSyntaxOrProcedureCall handles a list expression. The car may be a symbol (possibly a macro), a nested pair (computed procedure), or a self-evaluating value (like in quoted data or malformed expressions).

func (*ExpanderTimeContinuation) SetMaxDepth added in v1.17.0

func (p *ExpanderTimeContinuation) SetMaxDepth(n int)

SetMaxDepth sets the maximum structural recursion depth allowed during expansion for this run. A value of 0 (or negative, clamped to 0) disables the limit. Mirrors the parser's SetMaxDepth and MachineContext.SetMaxCallDepth.

type FSFileResolver

type FSFileResolver = resolver.FSFileResolver

func NewFSFileResolver

func NewFSFileResolver(fsys fs.FS, env *environment.EnvironmentFrame) *FSFileResolver

NewFSFileResolver creates a resolver backed by a virtual filesystem.

type FeatureRequirement

type FeatureRequirement interface {
	// IsSatisfied returns true if this requirement is satisfied.
	// The registry parameter is used to check if a library is already loaded.
	// The resolver parameter is used to check if a library file exists
	// (via the FileResolver chain, supporting both OS and virtual fs.FS).
	IsSatisfied(ctx context.Context, registry *LibraryRegistry, resolver FileResolver) bool
}

FeatureRequirement represents a parsed cond-expand feature requirement. Feature requirements can be:

  • A symbol (feature identifier)
  • (library <library-name>) - check if library is available
  • (and <req> ...) - all requirements must be satisfied
  • (or <req> ...) - at least one requirement must be satisfied
  • (not <req>) - requirement must NOT be satisfied

func NewAndRequirement

func NewAndRequirement(reqs ...FeatureRequirement) FeatureRequirement

NewAndRequirement creates an and requirement.

func NewElseRequirement

func NewElseRequirement() FeatureRequirement

NewElseRequirement creates an else requirement (always satisfied).

func NewFeatureIdentifier

func NewFeatureIdentifier(name string) FeatureRequirement

NewFeatureIdentifier creates a feature identifier requirement.

func NewLibraryRequirement

func NewLibraryRequirement(name LibraryName) FeatureRequirement

NewLibraryRequirement creates a library requirement.

func NewNotRequirement

func NewNotRequirement(req FeatureRequirement) FeatureRequirement

NewNotRequirement creates a not requirement.

func NewOrRequirement

func NewOrRequirement(reqs ...FeatureRequirement) FeatureRequirement

NewOrRequirement creates an or requirement.

type FileResolver

type FileResolver = environment.FileResolver

FileResolver resolves and opens files for include/load operations. The interface is defined in the environment package; this alias keeps the name available in compilation without re-declaration.

Concrete resolver types (OSFileResolver, EmbedFileResolver, FSFileResolver, ChainFileResolver) and their constructors live in the resolver sub-package. Backward-compatible aliases are provided in resolver_compat.go.

type FreeIdResolution added in v1.13.21

type FreeIdResolution struct {
	Global          *environment.GlobalIndex
	LocalScopes     []*syntax.Scope
	HasLocalBinding bool
	LibScope        *syntax.Scope
}

FreeIdResolution records how a free identifier in a syntax-rules template was bound at macro definition time. The match package uses this during template expansion to preserve hygiene.

Implements localScopesProvider, globalBindingProvider, hasLocalBindingProvider, and libraryScopeProvider from the match package.

func (*FreeIdResolution) GetGlobal added in v1.13.21

func (p *FreeIdResolution) GetGlobal() *environment.GlobalIndex

GetGlobal implements the globalBindingProvider interface.

func (*FreeIdResolution) GetHasLocalBinding added in v1.13.21

func (p *FreeIdResolution) GetHasLocalBinding() bool

GetHasLocalBinding implements the hasLocalBindingProvider interface.

func (*FreeIdResolution) GetLibraryScope added in v1.13.21

func (p *FreeIdResolution) GetLibraryScope() *syntax.Scope

GetLibraryScope implements the libraryScopeProvider interface.

func (*FreeIdResolution) GetLocalScopes added in v1.13.21

func (p *FreeIdResolution) GetLocalScopes() []*syntax.Scope

GetLocalScopes implements the localScopesProvider interface.

type ImportSet

type ImportSet struct {
	LibraryName LibraryName         // Base library to import from
	Only        map[string]struct{} // If non-nil, only import these names
	Except      map[string]struct{} // If non-nil, import all except these
	Prefix      string              // If non-empty, add this prefix to all names
	Renames     map[string]string   // old-name -> new-name
	PhaseShift  environment.Phase   // Phase offset: 0=runtime, 1=for-syntax, -1=for-template
}

ImportSet represents a parsed import specification. It can be a simple library reference or include modifiers.

PhaseShift supports Racket-style phased imports:

  • (import (scheme base)) ; Phase 0 (runtime) - default
  • (import (for-syntax (scheme base))) ; Phase +1 (expand)
  • (import (for-template (scheme base))) ; Phase -1
  • (import (for-meta 2 (scheme base))) ; Phase +2
  • (import (for-meta -1 (scheme base))) ; Phase -1 (same as for-template)

Phase shifts compose additively: (for-syntax (for-syntax lib)) = phase +2

func NewImportSet

func NewImportSet(name LibraryName) *ImportSet

NewImportSet creates a new import set for a library.

func ParseImportSetFromDatum

func ParseImportSetFromDatum(ctx context.Context, expr values.Value) (*ImportSet, error)

ParseImportSetFromDatum parses an import set from datum values. Used at both runtime (by the 'environment' procedure) and compile time (via UnwrapAll on syntax objects).

Import sets can be:

  • (<library-name>) : import all exports
  • (only <import-set> <id> ...) : import only specified identifiers
  • (except <import-set> <id> ...): import all except specified
  • (prefix <import-set> <prefix>): add prefix to all imported names
  • (rename <import-set> (<old> <new>) ...): rename specific imports
  • (for-syntax <import-set>) : import at phase +1 (macro expansion)
  • (for-template <import-set>) : import at phase -1
  • (for-meta <n> <import-set>) : import at phase +n

func (*ImportSet) ApplyToExports

func (p *ImportSet) ApplyToExports(lib *CompiledLibrary) (map[string]string, error)

ApplyToExports applies the import modifiers and returns the final bindings. Returns a map of local-name -> external-name (the name in the library).

type LibraryExportIndex added in v1.12.0

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

LibraryExportIndex maps library keys to their summaries. Immutable after construction.

func BuildExportIndex added in v1.12.0

func BuildExportIndex(ctx context.Context, res FileResolver, reg *LibraryRegistry) (*LibraryExportIndex, error)

BuildExportIndex scans all discoverable library files via the resolver's FileEnumerator, parses their exports, and returns a LibraryExportIndex. Libraries already loaded in reg are skipped (their metadata is already available via the registry). If the resolver does not implement FileEnumerator, an empty index is returned (not an error).

func NewLibraryExportIndexFromEntries added in v1.12.0

func NewLibraryExportIndexFromEntries(entries map[string]*LibrarySummary) *LibraryExportIndex

NewLibraryExportIndexFromEntries creates an index from pre-built entries.

func (*LibraryExportIndex) Entries added in v1.12.0

func (p *LibraryExportIndex) Entries() []*LibrarySummary

Entries returns all indexed summaries sorted by library key.

func (*LibraryExportIndex) Lookup added in v1.12.0

Lookup returns the summary for a library, or nil if not indexed.

type LibraryImportEvent

type LibraryImportEvent struct {
	Library    LibraryName       // imported library name, e.g., (scheme base)
	SourceFile string            // path to .sld file (empty for synthetic libraries)
	Exports    []string          // all names exported by the library
	Imported   []string          // names that actually landed in the importer (after only/except/prefix/rename)
	Importer   LibraryName       // importing library name (zero value for top-level import)
	Phase      environment.Phase // pipeline phase: environment.PhaseExpand or environment.PhaseCompile
}

LibraryImportEvent records what happened when a library was imported.

type LibraryImportObserver

type LibraryImportObserver func(LibraryImportEvent)

LibraryImportObserver is called when a library is imported. Observers are read-only — they cannot influence the import.

type LibraryName

type LibraryName struct {
	Parts []string // e.g., ["scheme", "base"]
}

LibraryName represents an R7RS library name like (scheme base) or (my lib). Library names are lists of identifiers used to uniquely identify a library.

func DiscoverAvailableLibraries

func DiscoverAvailableLibraries(res FileResolver, reg *LibraryRegistry) ([]LibraryName, error)

DiscoverAvailableLibraries returns all importable library names by combining filesystem discovery (via the resolver's FileEnumerator) with registry-known libraries (synthetic extension libraries). Returns a sorted, deduplicated list.

If the resolver does not implement FileEnumerator, only registry libraries are returned. If reg is nil, only filesystem libraries are returned.

func FilePathToLibraryName

func FilePathToLibraryName(path string) (LibraryName, error)

FilePathToLibraryName converts a forward-slash-separated file path with .sld or .scm extension to a LibraryName. This is the inverse of ToFSPath(). Returns an error if the path has no recognized extension or is empty.

func NewLibraryName

func NewLibraryName(parts ...string) LibraryName

NewLibraryName creates a LibraryName from a list of string parts.

func ParseLibraryNameFromDatum

func ParseLibraryNameFromDatum(ctx context.Context, expr values.Value) (LibraryName, error)

ParseLibraryNameFromDatum extracts a LibraryName from a datum list like (scheme base). Used at both runtime (by the 'environment' procedure) and compile time (via UnwrapAll on syntax objects).

func (LibraryName) Key

func (p LibraryName) Key() string

Key returns a unique string key for map lookups.

func (LibraryName) SchemeString

func (p LibraryName) SchemeString() string

SchemeString returns the Scheme representation like "(scheme base)".

func (LibraryName) String

func (p LibraryName) String() string

String returns a human-readable representation like "scheme/base".

func (LibraryName) ToFSPath

func (p LibraryName) ToFSPath() string

ToFSPath returns the library name as a forward-slash-separated path with .sld extension, suitable for fs.FS and FileResolver operations.

func (LibraryName) ToSchemeValue

func (p LibraryName) ToSchemeValue() values.Value

ToSchemeValue converts a LibraryName to a Scheme list. Parts that parse as nonnegative integers become exact integers; all others become symbols. Matches R7RS library name syntax.

type LibraryRegistry

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

LibraryRegistry manages loaded libraries and handles library loading.

func NewLibraryRegistry

func NewLibraryRegistry() *LibraryRegistry

NewLibraryRegistry creates a new library registry with default search paths.

func (*LibraryRegistry) All

func (p *LibraryRegistry) All() []*CompiledLibrary

All returns all loaded libraries, sorted by name key for determinism.

func (*LibraryRegistry) AllNames

func (p *LibraryRegistry) AllNames() []LibraryName

AllNames returns the names of all registered libraries, sorted by key.

func (*LibraryRegistry) FinishLoading

func (p *LibraryRegistry) FinishLoading(name LibraryName)

FinishLoading marks a library as finished loading.

func (*LibraryRegistry) GetSearchPaths

func (p *LibraryRegistry) GetSearchPaths() []string

GetSearchPaths returns the current library search paths.

func (*LibraryRegistry) ImportObserver

func (p *LibraryRegistry) ImportObserver() LibraryImportObserver

ImportObserver returns the current import observer, or nil.

func (*LibraryRegistry) IsLoading

func (p *LibraryRegistry) IsLoading(name LibraryName) bool

IsLoading returns true if the library is currently being loaded. Used to detect circular dependencies.

func (*LibraryRegistry) Lookup

func (p *LibraryRegistry) Lookup(name LibraryName) *CompiledLibrary

Lookup returns a library by name, or nil if not found.

func (*LibraryRegistry) PrependSearchPath

func (p *LibraryRegistry) PrependSearchPath(path string)

PrependSearchPath adds a path to the beginning of the search path list.

func (*LibraryRegistry) Register

func (p *LibraryRegistry) Register(lib *CompiledLibrary) error

Register adds a compiled library to the registry.

func (*LibraryRegistry) SetImportObserver

func (p *LibraryRegistry) SetImportObserver(obs LibraryImportObserver)

SetImportObserver sets an optional observer that is called each time a library is imported. The observer is read-only and cannot influence the import. Pass nil to remove the observer.

func (*LibraryRegistry) SetSearchPaths

func (p *LibraryRegistry) SetSearchPaths(paths []string)

SetSearchPaths sets the library search paths.

func (*LibraryRegistry) StartLoading

func (p *LibraryRegistry) StartLoading(name LibraryName)

StartLoading marks a library as being loaded.

type LibrarySummary added in v1.12.0

type LibrarySummary struct {
	Name        LibraryName
	Description string
	Exports     []string
	SourceFile  string
}

LibrarySummary holds the statically-extracted metadata for a single library definition file (.sld). It captures exports and description without compiling or executing any library code.

func ParseLibrarySummary added in v1.12.0

func ParseLibrarySummary(ctx context.Context, r io.Reader, filePath string, name LibraryName) (*LibrarySummary, error)

ParseLibrarySummary reads a library definition from r and extracts its export list and description without compiling the library. This is a best-effort parser: malformed export specs are silently skipped.

type OSFileResolver

type OSFileResolver = resolver.OSFileResolver

func NewOSFileResolver

func NewOSFileResolver(env *environment.EnvironmentFrame) *OSFileResolver

NewOSFileResolver creates a resolver backed by the OS filesystem.

type OperationBindPatternVars added in v1.13.21

type OperationBindPatternVars struct {
	machine.OperationBase
	PatternVars []string // Ordered list for consistent indexing
}

OperationBindPatternVars binds pattern variables from the last match into a new local environment frame that is pushed onto the current environment.

This operation creates a new environment frame with local slots for each pattern variable, binds the matched values, and makes this the current environment.

func NewOperationBindPatternVars added in v1.13.21

func NewOperationBindPatternVars(patternVars map[string]struct{}) *OperationBindPatternVars

func (*OperationBindPatternVars) Apply added in v1.13.21

func (*OperationBindPatternVars) EqualTo added in v1.13.21

func (p *OperationBindPatternVars) EqualTo(other values.Value) bool

func (*OperationBindPatternVars) OpKind added in v1.16.0

type OperationBuildSyntaxList added in v1.13.21

type OperationBuildSyntaxList struct {
	machine.OperationBase
	Count int
}

OperationBuildSyntaxList builds a syntax list from elements on the eval stack. n elements are popped from the stack (in reverse order) and consed into a list.

func NewOperationBuildSyntaxList added in v1.13.21

func NewOperationBuildSyntaxList(count int) *OperationBuildSyntaxList

NewOperationBuildSyntaxList creates a new OperationBuildSyntaxList.

func (*OperationBuildSyntaxList) Apply added in v1.13.21

Apply implements the Operation interface.

func (*OperationBuildSyntaxList) EqualTo added in v1.13.21

func (p *OperationBuildSyntaxList) EqualTo(other values.Value) bool

func (*OperationBuildSyntaxList) OpKind added in v1.16.0

type OperationClearSyntaxCaseInput added in v1.13.21

type OperationClearSyntaxCaseInput struct {
	machine.OperationBase
}

OperationClearSyntaxCaseInput clears the per-context syntax-case state. This is called at the end of a syntax-case form.

func NewOperationClearSyntaxCaseInput added in v1.13.21

func NewOperationClearSyntaxCaseInput() *OperationClearSyntaxCaseInput

func (*OperationClearSyntaxCaseInput) Apply added in v1.13.21

func (*OperationClearSyntaxCaseInput) EqualTo added in v1.13.21

func (p *OperationClearSyntaxCaseInput) EqualTo(other values.Value) bool

func (*OperationClearSyntaxCaseInput) OpKind added in v1.16.0

type OperationStoreSyntaxCaseInput added in v1.13.21

type OperationStoreSyntaxCaseInput struct {
	machine.OperationBase
}

OperationStoreSyntaxCaseInput stores the value register into the per-context syntaxCaseState for use by OperationSyntaxCaseMatch.

func NewOperationStoreSyntaxCaseInput added in v1.13.21

func NewOperationStoreSyntaxCaseInput() *OperationStoreSyntaxCaseInput

func (*OperationStoreSyntaxCaseInput) Apply added in v1.13.21

func (*OperationStoreSyntaxCaseInput) EqualTo added in v1.13.21

func (p *OperationStoreSyntaxCaseInput) EqualTo(other values.Value) bool

func (*OperationStoreSyntaxCaseInput) OpKind added in v1.16.0

type OperationSyntaxCaseMatch added in v1.13.21

type OperationSyntaxCaseMatch struct {
	machine.OperationBase
}

OperationSyntaxCaseMatch performs pattern matching for syntax-case.

Expects:

  • Value register: syntaxCaseClause with compiled pattern
  • Per-context syntaxCaseState.input: input syntax object (set by OperationStoreSyntaxCaseInput)

Results:

  • If match succeeds: value register = #t, pattern bindings stored in context
  • If match fails: value register = #f

func NewOperationSyntaxCaseMatch added in v1.13.21

func NewOperationSyntaxCaseMatch() *OperationSyntaxCaseMatch

func (*OperationSyntaxCaseMatch) Apply added in v1.13.21

func (*OperationSyntaxCaseMatch) EqualTo added in v1.13.21

func (p *OperationSyntaxCaseMatch) EqualTo(other values.Value) bool

func (*OperationSyntaxCaseMatch) OpKind added in v1.16.0

type OperationSyntaxCaseNoMatch added in v1.13.21

type OperationSyntaxCaseNoMatch struct {
	machine.OperationBase
}

OperationSyntaxCaseNoMatch is emitted at the end of syntax-case when no clause matches.

func NewOperationSyntaxCaseNoMatch added in v1.13.21

func NewOperationSyntaxCaseNoMatch() *OperationSyntaxCaseNoMatch

func (*OperationSyntaxCaseNoMatch) Apply added in v1.13.21

func (*OperationSyntaxCaseNoMatch) EqualTo added in v1.13.21

func (p *OperationSyntaxCaseNoMatch) EqualTo(other values.Value) bool

func (*OperationSyntaxCaseNoMatch) OpKind added in v1.16.0

type OperationSyntaxRulesTransform added in v1.13.21

type OperationSyntaxRulesTransform struct {
	machine.OperationBase
}

OperationSyntaxRulesTransform is a VM operation that performs macro expansion.

Execution context:

  • Value register: contains clausesWrapper with compiled pattern/template pairs
  • Local parameter 0: contains the input form (the macro invocation)

The operation is part of the transformer closure created by CompileSyntaxRules.

func NewOperationSyntaxRulesTransform added in v1.13.21

func NewOperationSyntaxRulesTransform() *OperationSyntaxRulesTransform

func (*OperationSyntaxRulesTransform) Apply added in v1.13.21

func (*OperationSyntaxRulesTransform) EqualTo added in v1.13.21

func (p *OperationSyntaxRulesTransform) EqualTo(other values.Value) bool

func (*OperationSyntaxRulesTransform) OpKind added in v1.16.0

type OperationSyntaxTemplateExpand added in v1.13.21

type OperationSyntaxTemplateExpand struct {
	machine.OperationBase
}

OperationSyntaxTemplateExpand expands a syntax template using the current pattern variable bindings. This is used for templates containing ellipsis, which require runtime expansion rather than compile-time code generation.

The template is stored in the value register (loaded from literals). The result is the expanded syntax object, left in the value register.

func NewOperationSyntaxTemplateExpand added in v1.13.21

func NewOperationSyntaxTemplateExpand() *OperationSyntaxTemplateExpand

func (*OperationSyntaxTemplateExpand) Apply added in v1.13.21

func (*OperationSyntaxTemplateExpand) EqualTo added in v1.13.21

func (p *OperationSyntaxTemplateExpand) EqualTo(other values.Value) bool

func (*OperationSyntaxTemplateExpand) OpKind added in v1.16.0

type PhaseEntry

type PhaseEntry[F any] struct {
	Name string
	Fn   F
}

PhaseEntry represents a named item to register in a phase environment.

type PrimitiveExpander

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

PrimitiveExpander wraps a PrimitiveExpanderFunc as a values.Value so it can be stored in the environment.

func LookupPrimitiveExpander

func LookupPrimitiveExpander(env *environment.EnvironmentFrame, sym *values.Symbol, scopes []*syntax.Scope) *PrimitiveExpander

LookupPrimitiveExpander looks up a primitive expander by symbol in the expand environment. Returns the PrimitiveExpander if found, or nil if the symbol does not name a primitive expander.

This function handles hygiene by using scoped lookup - it will only match bindings whose scopes are a subset of the symbol's scopes.

func NewPrimitiveExpander

func NewPrimitiveExpander(name string, fn PrimitiveExpanderFunc) *PrimitiveExpander

NewPrimitiveExpander creates a new primitive expander.

func (*PrimitiveExpander) EqualTo

func (p *PrimitiveExpander) EqualTo(other values.Value) bool

EqualTo implements values.Value interface.

func (*PrimitiveExpander) Expand

Expand invokes the primitive expander function.

func (*PrimitiveExpander) IsVoid

func (p *PrimitiveExpander) IsVoid() bool

IsVoid returns false — named handlers are never void.

func (*PrimitiveExpander) Name

func (p *PrimitiveExpander) Name() string

Name returns the handler's name.

func (*PrimitiveExpander) SchemeString

func (p *PrimitiveExpander) SchemeString() string

SchemeString returns the Scheme representation: #<prefix:name>.

type PrimitiveExpanderFunc

type PrimitiveExpanderFunc func(
	etc *ExpanderTimeContinuation,
	sym *syntax.SyntaxSymbol,
	expr syntax.SyntaxValue,
) (syntax.SyntaxValue, error)

PrimitiveExpanderFunc is the type for expand-time special form handlers. These functions handle macro expansion of primitive forms like `if`, `lambda`, `define`, `quote`, etc.

Parameters:

  • etc: The expander-time continuation (expander state, carries context)
  • sym: The keyword symbol (e.g., 'if', 'lambda')
  • expr: The expression arguments (everything after the keyword)

Returns the expanded syntax value.

type ResolvedImportSet

type ResolvedImportSet struct {
	ImportSet *ImportSet
	Library   *CompiledLibrary
	Bindings  map[string]string // localName -> externalName
}

ResolvedImportSet holds the result of parsing and loading an import set. This is the shared prefix of all import processing: parse the import set datum, load the named library, and apply modifiers (only, except, prefix, rename) to produce the final binding map.

type SourcedError added in v1.14.0

type SourcedError struct {
	Source *syntax.SourceContext
	Cause  error
}

SourcedError wraps a compilation error with the source location where it occurred. The compiler tracks source context via pushSource/popSource; wrapCompilationError attaches the current source to errors so that callers (especially the public Engine API) can report file:line:col.

Use errors.As to extract the source from an error chain:

var se *compilation.SourcedError
if errors.As(err, &se) && se.Source != nil { ... }

func (*SourcedError) Error added in v1.14.0

func (p *SourcedError) Error() string

func (*SourcedError) Unwrap added in v1.14.0

func (p *SourcedError) Unwrap() error

type SyntaxCaseClause added in v1.13.21

type SyntaxCaseClause struct {
	Bytecode       []match.SyntaxCommand
	PatternVars    map[string]struct{}
	EllipsisVars   map[int]map[string]struct{}
	EllipsisDepths map[int]int
}

SyntaxCaseClause wraps compiled pattern info for a syntax-case clause. Created by the compiler, consumed by OperationSyntaxCaseMatch at runtime.

func (*SyntaxCaseClause) EqualTo added in v1.13.21

func (p *SyntaxCaseClause) EqualTo(other values.Value) bool

func (*SyntaxCaseClause) IsVoid added in v1.13.21

func (p *SyntaxCaseClause) IsVoid() bool

func (*SyntaxCaseClause) SchemeString added in v1.13.21

func (p *SyntaxCaseClause) SchemeString() string

type SyntaxCompiler

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

SyntaxCompiler wraps a SyntaxCompilerFunc as a values.Value so it can be stored in the environment.

func LookupSyntaxCompiler

func LookupSyntaxCompiler(env *environment.EnvironmentFrame, sym *values.Symbol, scopes []*syntax.Scope) *SyntaxCompiler

LookupSyntaxCompiler looks up a syntax compiler by symbol in the compile environment. Returns the SyntaxCompiler if found, or nil if the symbol does not name a syntax compiler.

This function handles hygiene by using scoped lookup - it will only match bindings whose scopes are a subset of the symbol's scopes.

func NewSyntaxCompiler

func NewSyntaxCompiler(name string, fn SyntaxCompilerFunc) *SyntaxCompiler

NewSyntaxCompiler creates a new syntax compiler.

func (*SyntaxCompiler) Compile

Compile invokes the syntax compiler function.

func (*SyntaxCompiler) EqualTo

func (p *SyntaxCompiler) EqualTo(other values.Value) bool

EqualTo implements values.Value interface.

func (*SyntaxCompiler) IsVoid

func (p *SyntaxCompiler) IsVoid() bool

IsVoid returns false — named handlers are never void.

func (*SyntaxCompiler) Name

func (p *SyntaxCompiler) Name() string

Name returns the handler's name.

func (*SyntaxCompiler) SchemeString

func (p *SyntaxCompiler) SchemeString() string

SchemeString returns the Scheme representation: #<prefix:name>.

type SyntaxCompilerFunc

type SyntaxCompilerFunc func(ctc *CompileTimeContinuation, ctctx CompileTimeCallContext, expr syntax.SyntaxValue) error

SyntaxCompilerFunc is the type for compile-time special form handlers. These functions handle syntax-directed compilation of extension forms like `syntax-case`, `import`, `define-syntax`, `include`, etc.

Parameters:

  • ctc: The compile-time continuation (compiler state)
  • ctctx: The compile-time call context (tail position info, etc.)
  • expr: The expression arguments (everything after the keyword)

The function should emit operations via ctc.AppendOperations and return nil on success.

type SyntaxRulesClause added in v1.13.21

type SyntaxRulesClause struct {
	Template         syntax.SyntaxValue
	Bytecode         []match.SyntaxCommand
	Matcher          *match.SyntaxMatcher
	PatternVars      map[string]struct{}
	PatternVarSyntax map[string]*syntax.SyntaxSymbol
	EllipsisVars     map[int]map[string]struct{}
	FreeIds          map[string]*FreeIdResolution
	Ellipsis         string
	LiteralSyntax    map[string]*syntax.SyntaxSymbol
}

SyntaxRulesClause represents a single compiled pattern-template pair in a syntax-rules form. Created by the compiler, consumed by OperationSyntaxRulesTransform at runtime.

Directories

Path Synopsis
Package resolver provides file resolution infrastructure for the Scheme compiler.
Package resolver provides file resolution infrastructure for the Scheme compiler.
Package sourceload provides file-finding and load-stack tracking for locating source files across virtual filesystems.
Package sourceload provides file-finding and load-stack tracking for locating source files across virtual filesystems.

Jump to

Keyboard shortcuts

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