validate

package
v1.20.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package validate validates Scheme syntax and produces typed expressions.

The package converts syntax.SyntaxValue into typed ValidatedExpr values for the compiler, accumulating multiple errors without short-circuiting:

result := validate.ValidateExpression(ctx, env, expr)
if !result.Ok() {
    for _, err := range result.Errors {
        fmt.Println(err)
    }
}
validated := result.Expr

Validated Types

Hygiene

The validator supports local variable shadowing of special forms (R7RS 4.2.2) using Flatt's scope-set model for identifier resolution.

Index

Constants

View Source
const DefaultMaxOriginDepth = 10

DefaultMaxOriginDepth is the default maximum number of macro expansions to show in error messages. Set to 0 for unlimited depth.

Variables

This section is empty.

Functions

func BodyIsFrameReleasable

func BodyIsFrameReleasable(proc ValidatedBodyAndParams, selfSym *syntax.SyntaxSymbol, env *environment.EnvironmentFrame) bool

BodyIsFrameReleasable reports whether proc's activation frame may be released to the pool at its tail calls (the OpReleaseEnvFrame optimization — fib-shaped general tail recursion). Unlike OpSelfTailCall it does NOT rebind in place or hardcode a jump: it releases the dead frame and performs a normal apply (which re-resolves the callee). So it needs neither the define's stability nor a self-tail call — only that the body can never expose its frame to a continuation:

  • no capture operator in the body (no call/cc &c.),
  • no escaping closure, and
  • every callee is the self name or a capture-safe, non-rebindable primitive (bodyCalleesAllCaptureSafe) — a callee that could capture would pin the frame.

This is a conservative, per-body approximation of the interprocedural classifier (ClassifyFrameReclaim): it covers self-recursion over capture-safe primitives (fib, tak) but refuses a tail call to another user-defined function (mutual recursion), which the full call graph would admit. Sound either way; the call-graph precision is a later refinement.

func BodyIsSelfTailReusable

func BodyIsSelfTailReusable(
	proc ValidatedBodyAndParams,
	selfSym *syntax.SyntaxSymbol,
	env *environment.EnvironmentFrame,
) bool

BodyIsSelfTailReusable is the compiler entry point: it builds the production capture-operator identity test from env (hygiene-correct: a locally-shadowed call/cc is not the primitive) and runs the self-tail-reuse safety predicate. selfSym is the closure's own binder identifier, or nil for an anonymous body.

NOTE: this is the IN-BODY safety half. The caller must additionally ensure the self BINDING is immutable against cross-unit redefinition — for a top-level define that means IsStable() at the emit site; for a lexical named-let loop the in-body ¬set! this predicate checks is the whole story.

func CallbackIsCaptureSafe

func CallbackIsCaptureSafe(arg ValidatedExpr, env *environment.EnvironmentFrame) bool

CallbackIsCaptureSafe reports whether a callback ARGUMENT at a call site is provably capture-safe — the D2 proof the inline-HOF dispatch (callback specialization Strategy A) requires before inlining a curated tail HOF's reclaiming loop with this callback substituted. Inlining a capturing callback into a frame-reclaiming loop would be a use-after-release, so this answers true only with positive proof; everything uncertain is false (the soundness asymmetry — a false negative merely forgoes the optimization).

Two positive forms:

  • a symbol resolving to a binding that is IsCaptureSafe() AND IsStable() — the same gate bodyCalleesAllCaptureSafe applies to a loop's callees: capture-safe (cannot invoke a Scheme procedure, so cannot capture) and stable (not rebindable to something that can), or
  • a lambda literal whose body ProcedureBodyIsCaptureSafe proves cannot capture the caller's continuation. The self symbol is nil — an argument callback is anonymous, so no callee is exempted as a self-recursive tail call.

Anything else — a procedure-invoking, unbound, or rebindable symbol; a lambda that runs a capture operator or a procedure-invoking callee; or a computed operator expression (neither symbol nor lambda) — returns false.

func ClassifyFrameReclaim

func ClassifyFrameReclaim(
	unit []ValidatedExpr,
	env *environment.EnvironmentFrame,
) map[ScopedBindingKey]bool

ClassifyFrameReclaim returns, for every top-level function define in unit, whether its frame is reclaimable at its tail calls. It is the exported entry point over the Layer-B/C internals: build the call graph, run the greatest-fixpoint mayCapture, and project to a per-identity verdict map. The verdict is conservative (any uncertainty ⇒ not reclaimable).

The map is keyed by ScopedBindingKey — the define name's (Sym.Key, scope fingerprint) — NOT by Sym.Key alone, and its value is the reclaimable bool. Two hygiene-distinct top-level binders of one name (a macro-introduced define and a user define) carry different scope sets, so each gets its own verdict instead of sharing one. The read side (compilation.frameReuseForDefine) looks up the identity of the very define it is compiling, an exact match against the key built from that same define here, so reference→verdict resolution cannot diverge. The identity is value-stable — it survives env.Copy and cross-library sharing — unlike the *Binding pointer a scope-keyed global slot happens to mint. A name-oriented consumer reads the define name back off the key (id.Key).

Same-unit-define immutability is the callee node's rebindStable (d.StableInUnit ∧ the namespace's ImmutableTopLevel), computed thread-locally from the ValidatedDefine and an engine-construction-time flag — never read from the callee's *Binding (the T1.5 decoupling). The tier-(a) vs tier-(b) distinction is therefore a property of WHAT is passed: defines whose StableInUnit is unset, or a namespace without immutable top-level, yield mutable same-unit edges (tier-(a)); StableInUnit defines under immutable top-level yield immutable edges (tier-(b)).

env is still consulted, but only for binding-side FACTS about non-same-unit callees: capture-operator identity (makeIsCaptureOp) and the capture-safe-primitive gate (Binding.IsCaptureSafe ∧ IsStable). Those reads are fail-safe on a nil/unstamped binding (⇒ unsafe), so an un-stamped env costs only primitive-callee precision, never soundness — though a measurement still wants a positive control.

func ForEachOpaqueLiveSymbol added in v1.20.0

func ForEachOpaqueLiveSymbol(expr ValidatedExpr, fn func(*syntax.SyntaxSymbol)) bool

ForEachOpaqueLiveSymbol calls fn for every symbol in a LIVE position of expr's concealed syntax, and reports whether expr was opaque at all. It does NOT descend into validated children: a caller that already walks the tree (every caller so far does) recurses itself and asks this at each node.

It is exported so that pkg/machine/compilation's boxing pass can ask the question this package already answers, instead of concluding "nothing in there" from WalkSubExprs reporting an opaque node childless. That blindness is what left a `set!` inside a quasiquote or a cond-expand invisible to the ref index, so a captured local was never boxed and the closure kept a stale copy — (let ((n 0)) (let ((f (lambda () n))) `(,(set! n 99)) f)) returned 0.

WHY NOT EXPORT forEachRawSymbol. Its second parameter is the entry depth, and the depth differs by SHAPE: a quasiquote Template has already been stepped into (quasiDepthTemplate), a passthrough form has not (quasiDepthCode). Exporting the raw walker moves that choice to the call site, where getting it wrong under-marks silently — the exact failure mode this file exists to prevent. The classification stays here, with the switch that defines it.

The boolean and fn answer DIFFERENT questions, per opaqueRawSyntax's contract: an opaque node with a nil payload calls fn zero times and still reports true, because a nil payload is when we know least.

func InternalDefineFrameReleasable added in v1.19.1

func InternalDefineFrameReleasable(
	d *ValidatedDefine,
	body []ValidatedExpr,
	env *environment.EnvironmentFrame,
) bool

InternalDefineFrameReleasable is LetBindingFrameReleasable's twin for the OTHER spelling of a local recursive binding group: internal defines. R7RS §5.3.2 gives them letrec* semantics, but they are never rewritten into a ValidatedLet — they stay ValidatedDefine nodes compiled in the enclosing lambda's own frame — so the let-binding predicate never sees them, while the shape and the hazard are identical. body is the enclosing body sequence whose internal defines form the group; d must be one of them.

This is the spelling that occurs in practice: benchmarks/larceny/src alone carries 255 internal defines in compiler.scm and 32 in earley.scm, against zero explicit letrec forms in either corpus. Mutual recursion between two internal defines allocated a frame per iteration (2.0 measured) for exactly the reason the letrec form did — a call to a sibling resolves through env.GetBinding, finds nothing because the sibling is local, and refuses.

The co-induction, the uniform group answer, and the per-binding escape clause are LetBindingFrameReleasable's, unchanged; see there for why each is shaped that way. Only the seed differs (withInternalDefines rather than withLet).

MEMBERSHIP IS CHECKED, and that check is load-bearing rather than defensive hygiene. The group arrives from the compiler, which must supply the body that predeclared d — there are three such sites. If a caller ever passes a STALE enclosing body, an unrelated define sharing a sibling's name would be recorded as evidence for a call to a different procedure entirely, which is unsound. A stale body does not contain d, so refusing when d is absent converts that whole class of plumbing error into a forgone optimization.

func IsOpaqueSubtree added in v1.20.0

func IsOpaqueSubtree(expr ValidatedExpr) bool

IsOpaqueSubtree reports whether an expression is an opaque subtree — the boolean half of opaqueRawSyntax, without the payload.

It is exported because the classification is needed OUTSIDE this package and must not be re-derived there. pkg/machine/compilation's Pass 1 asks the same question for a different reason: an opaque subtree may hold an outer reference free-variable enumeration cannot name, so the closure keeps its static link. That is "not enumerable ⇒ retain"; this package's own consumer (bodyReferencesCaptureOperator) reads the same shapes as "not analysable ⇒ may capture".

Two justifications, one classification, and the frame-release gates depend on them agreeing: a closure whose template retains the lexical env pins the creating frame, and the only thing keeping the release gates off such a body is that the capture walk refuses it too. That agreement was previously an unenforced coincidence between this function and a hand-copy in compilation. Adding a third opaque shape to opaqueRawSyntax now moves both walks at once.

func LetBindingFrameReleasable added in v1.19.1

func LetBindingFrameReleasable(v *ValidatedLet, i int, env *environment.EnvironmentFrame) bool

LetBindingFrameReleasable reports whether the i-th binding's lambda may have its OWN activation frame released at its tail calls (OpReleaseEnvFrame) — the local analogue of BodyIsFrameReleasable, and the release-path sibling of LetBindingSelfTailReusable.

WHAT THIS COVERS THAT SELF-TAIL DOES NOT. A loop whose tail call is to a SIBLING binding rather than to itself: mutual local recursion, which has no depth-0 self call and so fails bodyIsSelfTailReusable's clause (4) outright. Measured at 2 env-frame allocations per iteration before this predicate existed. A loop whose self call sits inside a further let is NOT covered — that call is at depth > 0, where the reuse disposition is cleared on the let descent (compile_let.go), so no arming here can reach it.

THE CO-INDUCTION, AND WHY IT IS A GROUP PREDICATE. Clearing a call to sibling `o` rests on `o` not capturing, which is exactly the property being proven for `o`. The shadow set therefore seeds the WHOLE letrec group (withLet), and this function discharges the resulting assumption by verifying every lambda-bound member under that same seed. Verifying only the binding asked about would leave the assumption standing on itself. The group answer is uniform — one unsafe member refuses the whole group — which loses precision when an unrelated sibling is unsafe, and is the conservative direction.

Only a lambda init is ever assumed anything: withLet records evidence solely for lambda inits (letBindingLocal), so a sibling bound to a parameter or a computed value stays opaque and refuses at its call site. A sibling set! within the let is likewise already folded in as localBinding.mutated, so a name that can be rebound to a capturing procedure is never cleared.

TWO CHECKS BELOW ARE REDUNDANT TODAY, and are kept as explicit tightenings (they can withhold a verdict, never grant one — the subsystem's kill-don't-guard rule). Both survive mutation testing, so neither is load-bearing at present and a reader must not infer that it is:

  • The InitsInScope precondition. The group seed is withLet's INIT scope, which for a plain let binds every name opaquely, so a sibling call already refuses there. The guard becomes load-bearing the moment that seed changes to the body scope.
  • The group capture-operator scan. An INVOKED capture operator is also a call operator, and call/cc carries no CaptureSafe stamp, so the callee walk refuses it first. The scan is what would catch a capture reached other than by direct invocation, and it keeps this predicate's shape identical to bodyCannotCaptureCaller's.

PRECONDITION: v.Kind must be recursive (letrec family), or the bindings are not in scope in each other's inits and the group seed would describe outer bindings.

func LetBindingSelfTailReusable

func LetBindingSelfTailReusable(v *ValidatedLet, i int, env *environment.EnvironmentFrame) (int, bool)

LetBindingSelfTailReusable reports the arity and eligibility of the i-th binding of a recursively-scoped let (letrec / letrec* / named-let) for in-place self-tail reuse — the local-binding analogue of selfTailForDefine. ok is true iff

  • the binding's Init is a lambda whose body is self-tail-reusable on the binding name (BodyIsSelfTailReusable, i.e. the in-body facts AND bodyCalleesAllCaptureSafe), AND
  • the binding name is immutable across the WHOLE let: never set! in any binding init or in the let body.

The second clause is the local immutability story. bodyIsSelfTailReusable only inspects the lambda's own body, but a sibling init or the let body could set! the name, which would make a hardcoded jump-to-pc=0 unsound. A lexical letrec binding cannot be mutated from outside the let, so set!-freedom within the let is sufficient (no IsStable() is needed, unlike a top-level define).

PRECONDITION: the caller must ensure v.Kind is recursive (letrec-family) so the lambda can see itself; in a plain let/let* the name is not in scope in the init.

func LetIsOrShaped added in v1.19.1

func LetIsOrShaped(v *ValidatedLet) (init ValidatedExpr, alt ValidatedExpr, ok bool)

LetIsOrShaped recognizes

(let ((t E)) (if t t B))

and returns E and B. This is what `or` expands to (bootstrap_macros.scm), one let per operand beyond the first, and it is the single most common macro-introduced environment frame in the tree: 340 occurrences across the stdlib and the larceny corpus, none of them written in any .scm file.

THE FRAME IS UNOBSERVABLE, which is what makes removing it a rewrite rather than an optimization with a side condition. `t` is bound only to be tested and returned, and OpBranchOnFalseValue reads the value register without clobbering it, so the value E already left there IS the consequent's value. The form compiles to E, a branch, and B — no slot, no OpPushEnv, and no reload.

Note what this does NOT require: nothing about E's type. An earlier design lowered `or` to (if E #t B), which is only valid when E yields a boolean and would have needed the primitive return-type annotation carried across the registry/validate layering. The register passthrough returns E's own value, so (or 5 1) is still 5 and the type question does not arise.

WHY THE ALTERNATIVE IS SCANNED. Lowering evaluates B in the enclosing frame instead of the let's, so a reference to `t` there would resolve to a different binding or to none. `or`'s temp is hygienic and cannot appear in B, but the same shape written by hand can, and (cond (test => f)) produces a sibling shape whose consequent consumes the value. Both are refused.

The scan matches by name AND binderScopes ⊆ refScopes — Flatt's rule, through the same syntax.ScopesCompatible the environment's resolveLocal uses, so it cannot drift from real resolution. Over-approximating in that direction is safe: a genuine reference necessarily carries the binder's name and satisfies the subset relation, so none is missed, while an unrelated same-name occurrence at a compatible scope merely costs the lowering. Refusing is always the free direction here — the frame stays, exactly as it does today.

The head positions need no such scan. Test and Conseq must BE this binding's symbol, and no form intervenes between the let and them that could rebind the name, so a match there is exact rather than approximate.

func ProcedureBodyIsCaptureSafe

func ProcedureBodyIsCaptureSafe(proc ValidatedBodyAndParams, selfSym *syntax.SyntaxSymbol, env *environment.EnvironmentFrame) bool

ProcedureBodyIsCaptureSafe reports whether CALLING proc can never capture the caller's continuation — the property that lets the frame-reclaim classifier trust proc as a callee. It is exactly bodyCannotCaptureCaller (BodyIsFrameReleasable MINUS the escaping-closure check): a procedure that merely builds and returns a closure does not capture the caller's continuation when called, so it is safe as a callee even though its own frame is not releasable.

The compiler stamps a define's binding CaptureSafe from this verdict (compile_define.go), so a proven-safe Scheme procedure — stdlib (zero?, not) or a user helper — is trusted exactly like a capture-safe primitive, without a hand-maintained whitelist. Conservative on forward references: a callee not yet stamped reads IsCaptureSafe()==false, so proc is left unstamped (sound — a missed stamp only forgoes the optimization).

func UnitArityOf added in v1.20.0

func UnitArityOf(unit []ValidatedExpr) map[string]UnitArityInfo

UnitArityOf returns the arity of every top-level define in unit whose name the unit neither redefines nor mutates. Callers may trust each entry without re-checking a flag: an unstable name is absent rather than present-but-doubtful. Run it after validation, which is what stamps StableInUnit.

It answers the question Binding.Value() cannot for a same-unit define: the binding exists at compile time (that is what makes forward references resolve), but its closure is built by the emitted code at run time, so there is no callable to interrogate. The formals are the only compile-time evidence of that define's arity.

Gating on StableInUnit does two jobs, and the second is the non-obvious one.

The plain job: a name defined twice, or set! in unit, has no single arity, so there is nothing sound to record.

The load-bearing job: it is also what makes the string key safe. A symbol Key() drops hygiene, so two hygiene-distinct top-level bindings of one name collide here. binding_ref.go makes the parallel over-match safe for BindingRef by arguing it "can only forfeit an optimization, never wrongly apply one" — and that argument does NOT transfer to arity, where applying one h's arity to a hygiene-distinct h would be a false compile error on correct code. A collision means definedKeyCount[key] >= 2, which forfeits StableInUnit, so neither entry is recorded and the call site falls through unchecked. Do not relax this to last-definition-wins without first replacing the key with something hygiene-aware — and note a reference's scope set is a superset of its binder's, so a ScopeFingerprint cannot serve as that key.

func WalkBindingRefs

func WalkBindingRefs(
	expr ValidatedExpr,
	visit func(sym *syntax.SyntaxSymbol, role RefRole, depth int),
)

WalkBindingRefs walks expr recursively, calling visit for every *syntax.SyntaxSymbol reference encountered, with its role and closure-nesting depth.

depth is the number of escaping closure boundaries crossed (0 = same closure as expr). Immediately-applied lambdas (ValidatedLambda or ValidatedCaseLambda as the Proc of a ValidatedCall or ValidatedApply) do NOT increment depth, since the closure does not escape.

ValidatedSetBang emits a synthetic RefSetBangTarget visit for its target name, then recurses into its value expression as a normal walk (the value's symbol references appear as RefInBody at the same depth).

There is no closure-body RefRole; "is this inside a closure?" is the depth > 0 predicate. (RoleClosureBody is a ChildRole, consumed internally by walkBindingRefsAt to bump depth.)

Role tagging is shallow: RefInCallProc and RefSetBangTarget are reported only on direct symbol children of the surrounding form. A symbol nested inside a non-leaf call-proc child — for example, the inner `f` and `g` in `((if c f g) x)`, or the inner symbols of `((f x) y)` — is reported as RefInBody, not RefInCallProc. The role describes the slot the symbol IS, not the slot of any enclosing non-symbol expression.

func WalkSubExprs

func WalkSubExprs(expr ValidatedExpr, fn func(child ValidatedExpr, role ChildRole))

WalkSubExprs calls fn for every direct sub-expression of expr, reporting each child's structural role:

  • RoleCallProc: the operator of ValidatedCall and ValidatedApply
  • RoleClosureBody: body of ValidatedLambda, ValidatedCaseLambda clause, or ValidatedDefine (function form)
  • RoleNormal: everything else (arguments, inits, branches, sequence bodies)

ValidatedSetBang: walks only the value expression (RoleNormal). The set! target is mutation (tracked by Mutable), not a reference.

ValidatedSymbol has no children — fn is not called. The caller handles symbols directly before calling WalkSubExprs.

Types

type ChildRole

type ChildRole int

ChildRole describes the structural position of a sub-expression within its parent validated form.

const (
	// RoleNormal is the default: arguments, init expressions, branch arms,
	// body of begin/let/dynamic-wind/with-continuation-mark, define-variable value.
	RoleNormal ChildRole = iota

	// RoleCallProc is the operator position of ValidatedCall and ValidatedApply.
	RoleCallProc

	// RoleClosureBody is a body expression inside a closure boundary:
	// ValidatedLambda, ValidatedCaseLambda clause, or ValidatedDefine (function form).
	RoleClosureBody
)

type LetKind

type LetKind int

LetKind encodes the two orthogonal dimensions of binding form semantics: init-visibility (do inits see the bindings?) and evaluation order (all-then-store vs sequential store-as-you-go).

| Kind       | Inits see bindings? | Eval order      |
|------------|---------------------|-----------------|
| Let        | No (outer scope)    | All-then-store  |
| LetStar    | Preceding only      | Sequential      |
| Letrec     | All (full scope)    | All-then-store  |
| LetrecStar | All (full scope)    | Sequential      |
const (
	LetKindLet        LetKind = iota // R7RS §4.2.2: inits in outer scope, all-then-store
	LetKindLetStar                   // R7RS §4.2.2: inits see preceding, sequential
	LetKindLetrec                    // R7RS §4.2.2: inits in full scope, all-then-store
	LetKindLetrecStar                // R7RS §4.2.2: inits in full scope, sequential
)

func (LetKind) InitsInScope

func (p LetKind) InitsInScope() bool

InitsInScope reports whether init expressions see the let bindings.

func (LetKind) Sequential

func (p LetKind) Sequential() bool

Sequential reports whether init expressions are stored immediately (left-to-right) rather than all-then-store.

func (LetKind) String

func (p LetKind) String() string

String returns the Scheme keyword for this kind.

type RefRole

type RefRole int

RefRole describes the structural slot a symbol reference occupies. It is the "what role does this use play" axis, orthogonal to depth.

const (
	// RefInBody is a normal-position reference: argument, return value,
	// init expression, branch arm, sequence body, set! value, etc.
	// This is the default for any non-call-proc, non-set!-target reference.
	RefInBody RefRole = iota

	// RefInCallProc is the operator position of a ValidatedCall or
	// ValidatedApply (the "callee" slot).
	RefInCallProc

	// RefSetBangTarget is the target name of a ValidatedSetBang (the
	// symbol being mutated). The value expression of the set! is walked
	// separately as RefInBody.
	RefSetBangTarget
)

type ScopedBindingKey added in v1.19.0

type ScopedBindingKey struct {
	Key      string
	ScopeKey string
}

ScopedBindingKey uniquely identifies a binding by name and scope set. Two identifiers with the same name but different scope sets (e.g. introduced by different macro expansions) are distinct. This is the scope-discriminated identity findDuplicateSymbols dedups by and the frame-reclaim classifier keys its verdict on — the same identity BASIS as match.FreeIdKey ((name, ScopeFingerprint)), though FreeIdKey encodes it as a concatenated string and this as a struct; a binding is named the same way wherever Wile reasons about identity.

func ScopedBindingKeyOf added in v1.19.0

func ScopedBindingKeyOf(sym *syntax.SyntaxSymbol) ScopedBindingKey

ScopedBindingKeyOf returns the scope-discriminated identity of sym: its name key paired with a deterministic fingerprint of its scope set. Producer and consumers must build the identity through this one function so their keys cannot drift (cf. match.FreeIdKey's identical producer/consumer contract).

type UnitArityInfo added in v1.20.0

type UnitArityInfo struct {
	RequiredCount int
	Variadic      bool
}

UnitArityInfo is a top-level define's arity as parsed from its formals. RequiredCount excludes any rest parameter; Variadic is true when one exists. That is ValidatedParams' convention, NOT NativeTemplate's, which counts the rest parameter in its parameterCount — the two meet at exactly one conversion point in the compiler.

type ValidatedApply

type ValidatedApply struct {
	Proc       ValidatedExpr
	PrefixArgs []ValidatedExpr
	FinalList  ValidatedExpr
	// contains filtered or unexported fields
}

ValidatedApply represents (apply proc arg1 ... args)

R7RS §6.10: apply calls proc with the elements of the list (append (list arg1 ...) args) as arguments.

func (*ValidatedApply) FormName

func (p *ValidatedApply) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedApply) SetFormName

func (p *ValidatedApply) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedApply) Source

func (p *ValidatedApply) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedBegin

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

ValidatedBegin represents (begin expr...)

func (*ValidatedBegin) Body

func (p *ValidatedBegin) Body() []ValidatedExpr

Body returns the sequence of expressions in this begin form.

func (*ValidatedBegin) FormName

func (p *ValidatedBegin) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedBegin) SetFormName

func (p *ValidatedBegin) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedBegin) Source

func (p *ValidatedBegin) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedBodyAndParams

type ValidatedBodyAndParams interface {
	Params() *ValidatedParams
	Body() []ValidatedExpr
	Docstring() string
}

ValidatedBodyAndParams provides access to parameters and body for procedure forms.

type ValidatedCall

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

ValidatedCall represents (proc arg...)

func (*ValidatedCall) Body

func (p *ValidatedCall) Body() []ValidatedExpr

Body returns the argument expressions for this call.

func (*ValidatedCall) FormName

func (p *ValidatedCall) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedCall) Proc

func (p *ValidatedCall) Proc() ValidatedExpr

Proc returns the procedure expression being called.

func (*ValidatedCall) SetFormName

func (p *ValidatedCall) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedCall) Source

func (p *ValidatedCall) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedCaseLambda

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

ValidatedCaseLambda represents (case-lambda [clause] ...)

func (*ValidatedCaseLambda) Clauses

Clauses returns the list of case-lambda clauses.

func (*ValidatedCaseLambda) FormName

func (p *ValidatedCaseLambda) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedCaseLambda) SetFormName

func (p *ValidatedCaseLambda) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedCaseLambda) Source

func (p *ValidatedCaseLambda) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedCaseLambdaClause

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

ValidatedCaseLambdaClause represents a single clause in case-lambda

func (*ValidatedCaseLambdaClause) Body

func (p *ValidatedCaseLambdaClause) Body() []ValidatedExpr

Body returns the body expressions.

func (*ValidatedCaseLambdaClause) Docstring

func (p *ValidatedCaseLambdaClause) Docstring() string

Docstring returns the Guile-style docstring extracted from the body, or "" if no docstring was present.

func (*ValidatedCaseLambdaClause) FormName

func (p *ValidatedCaseLambdaClause) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedCaseLambdaClause) Params

func (p *ValidatedCaseLambdaClause) Params() *ValidatedParams

Params returns the parameter list.

func (*ValidatedCaseLambdaClause) SetFormName

func (p *ValidatedCaseLambdaClause) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedCaseLambdaClause) Source

func (p *ValidatedCaseLambdaClause) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedDefine

type ValidatedDefine struct {
	IsFunction bool // True for (define (name ...) ...)

	// StableInUnit reports that this define's name is defined exactly once and
	// never set! within the compilation unit the validator saw, computed
	// syntactically by symbol Key (conservative over-approximation: a same-Key
	// local set! or shadowing define marks it non-stable — a false match costs
	// optimization, never soundness). It is the in-unit evidence the compiler
	// consumes (only for top-level/global defines) to populate
	// BindingMeta.Stable when top-level immutability is enabled (the default). Stamped
	// by finalizeStability after the whole unit is validated.
	StableInUnit bool
	// contains filtered or unexported fields
}

ValidatedDefine represents both forms: (define name expr) and (define (name params...) body...)

func (*ValidatedDefine) Body

func (p *ValidatedDefine) Body() []ValidatedExpr

Body returns the body expressions.

func (*ValidatedDefine) Docstring

func (p *ValidatedDefine) Docstring() string

Docstring returns the Guile-style docstring extracted from the body, or "" if no docstring was present.

func (*ValidatedDefine) FormName

func (p *ValidatedDefine) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedDefine) Name

func (p *ValidatedDefine) Name() *syntax.SyntaxSymbol

Name returns the name being defined.

func (*ValidatedDefine) Params

func (p *ValidatedDefine) Params() *ValidatedParams

Params returns the parameter list.

func (*ValidatedDefine) SetFormName

func (p *ValidatedDefine) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedDefine) Source

func (p *ValidatedDefine) Source() *syntax.SourceContext

Source returns the source context for error reporting.

func (*ValidatedDefine) SubExp

func (p *ValidatedDefine) SubExp() ValidatedExpr

SubExp returns the value expression for simple definitions.

type ValidatedDynamicWind

type ValidatedDynamicWind struct {
	Before ValidatedExpr
	Thunk  ValidatedExpr
	After  ValidatedExpr
	// contains filtered or unexported fields
}

ValidatedDynamicWind represents (dynamic-wind before thunk after)

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.

func (*ValidatedDynamicWind) FormName

func (p *ValidatedDynamicWind) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedDynamicWind) SetFormName

func (p *ValidatedDynamicWind) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedDynamicWind) Source

func (p *ValidatedDynamicWind) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedExpr

type ValidatedExpr = forms.ValidatedExpr

ValidatedExpr is the interface for all validated expressions. The canonical definition lives in forms.ValidatedExpr to break the validate → forms ← machine import cycle.

type ValidatedIf

type ValidatedIf struct {
	Test   ValidatedExpr
	Conseq ValidatedExpr
	Alt    ValidatedExpr // nil if no alternative (will produce void)
	// contains filtered or unexported fields
}

ValidatedIf represents (if test conseq [alt])

func (*ValidatedIf) FormName

func (p *ValidatedIf) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedIf) SetFormName

func (p *ValidatedIf) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedIf) Source

func (p *ValidatedIf) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedLambda

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

ValidatedLambda represents (lambda (params...) body...)

func (*ValidatedLambda) Body

func (p *ValidatedLambda) Body() []ValidatedExpr

Body returns the body expressions.

func (*ValidatedLambda) Docstring

func (p *ValidatedLambda) Docstring() string

Docstring returns the Guile-style docstring extracted from the body, or "" if no docstring was present.

func (*ValidatedLambda) FormName

func (p *ValidatedLambda) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedLambda) Params

func (p *ValidatedLambda) Params() *ValidatedParams

Params returns the parameter list.

func (*ValidatedLambda) SetFormName

func (p *ValidatedLambda) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedLambda) Source

func (p *ValidatedLambda) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedLet

type ValidatedLet struct {
	Kind     LetKind
	Bindings []ValidatedLetBinding
	Tag      *syntax.SyntaxSymbol
	// contains filtered or unexported fields
}

ValidatedLet represents all four R7RS binding forms: let, let*, letrec, letrec*. The Kind field encodes which form this is — the type is shared because the four forms differ only in two orthogonal dimensions (init visibility and evaluation order), not in structure. Tag is non-nil for named let (compiled with letrec semantics).

func NewValidatedLet

func NewValidatedLet(
	formName string,
	source *syntax.SourceContext,
	kind LetKind,
	bindings []ValidatedLetBinding,
	body []ValidatedExpr,
) *ValidatedLet

NewValidatedLet constructs a ValidatedLet from pre-validated components. Used by the compiler for synthetic let forms (e.g., procedure inlining). Callers must ensure all bindings and body expressions are individually validated — this constructor does not re-validate its inputs.

This is the only exported constructor in the validate package. Other validated types are constructed exclusively within the package. Add exported constructors for other types only when an external consumer requires them.

func (*ValidatedLet) Body

func (p *ValidatedLet) Body() []ValidatedExpr

Body returns the body expressions.

func (*ValidatedLet) FormName

func (p *ValidatedLet) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedLet) SetFormName

func (p *ValidatedLet) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedLet) Source

func (p *ValidatedLet) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedLetBinding

type ValidatedLetBinding struct {
	Name    *syntax.SyntaxSymbol
	Init    ValidatedExpr
	Mutable bool
	Escapes bool
	// CaptureSafe requests that the compiler stamp this binding CaptureSafe+Stable
	// when it creates it (callback specialization Strategy A, the unify path). Set
	// only on a synthetic let-binding for a callback the call site has already
	// proven capture-safe via CallbackIsCaptureSafe, so the inlined HOF loop calling
	// it passes bodyCalleesAllCaptureSafe and reclaims. Never set by the validator
	// on user source — only by the inline-HOF dispatch.
	CaptureSafe bool
}

ValidatedLetBinding represents a single (name init-expr) binding pair. Mutable is true if the binding is targeted by set! in the body. Escapes is true if the binding is referenced in a non-call position.

type ValidatedLiteral

type ValidatedLiteral struct {
	Value syntax.SyntaxValue
	// contains filtered or unexported fields
}

ValidatedLiteral represents self-evaluating data (numbers, strings, booleans, etc.) It's also used for passthrough forms like define-syntax, syntax-case, etc.

func (*ValidatedLiteral) FormName

func (p *ValidatedLiteral) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedLiteral) SetFormName

func (p *ValidatedLiteral) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedLiteral) Source

func (p *ValidatedLiteral) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedParams

type ValidatedParams struct {
	Required []*syntax.SyntaxSymbol
	Rest     *syntax.SyntaxSymbol // nil if no rest parameter
	// contains filtered or unexported fields
}

ValidatedParams represents a parameter list Handles: (a b c), (a b . rest), and just rest

type ValidatedProcedure

type ValidatedProcedure interface {
	ValidatedExpr
	ValidatedBodyAndParams
}

ValidatedProcedure represents a validated procedure form with parameters and body.

type ValidatedQuasiquote

type ValidatedQuasiquote struct {
	Template syntax.SyntaxValue // The raw template - quasiquote has complex runtime semantics
	// contains filtered or unexported fields
}

ValidatedQuasiquote represents (quasiquote template)

func (*ValidatedQuasiquote) FormName

func (p *ValidatedQuasiquote) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedQuasiquote) SetFormName

func (p *ValidatedQuasiquote) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedQuasiquote) Source

func (p *ValidatedQuasiquote) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedQuote

type ValidatedQuote struct {
	Datum syntax.SyntaxValue
	// contains filtered or unexported fields
}

ValidatedQuote represents (quote datum)

func (*ValidatedQuote) FormName

func (p *ValidatedQuote) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedQuote) SetFormName

func (p *ValidatedQuote) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedQuote) Source

func (p *ValidatedQuote) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedSetBang

type ValidatedSetBang struct {
	Name *syntax.SyntaxSymbol
	// contains filtered or unexported fields
}

ValidatedSetBang represents (set! name expr)

func (*ValidatedSetBang) FormName

func (p *ValidatedSetBang) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedSetBang) SetFormName

func (p *ValidatedSetBang) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedSetBang) Source

func (p *ValidatedSetBang) Source() *syntax.SourceContext

Source returns the source context for error reporting.

func (*ValidatedSetBang) SubExp

func (p *ValidatedSetBang) SubExp() ValidatedExpr

SubExp returns the value expression to be assigned.

type ValidatedSymbol

type ValidatedSymbol struct {
	Symbol *syntax.SyntaxSymbol
	// contains filtered or unexported fields
}

ValidatedSymbol represents a variable reference

func (*ValidatedSymbol) FormName

func (p *ValidatedSymbol) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedSymbol) SetFormName

func (p *ValidatedSymbol) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedSymbol) Source

func (p *ValidatedSymbol) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidatedWithContinuationMark

type ValidatedWithContinuationMark struct {
	Key  ValidatedExpr
	Val  ValidatedExpr
	Body ValidatedExpr
	// contains filtered or unexported fields
}

ValidatedWithContinuationMark represents (with-continuation-mark key val body)

Sets a continuation mark on the current frame during body evaluation. In tail position, the mark replaces any existing mark with the same key on the current frame. In non-tail position, the mark is removed after body completes.

func (*ValidatedWithContinuationMark) FormName

func (p *ValidatedWithContinuationMark) FormName() string

FormName returns the name of the form for error messages.

func (*ValidatedWithContinuationMark) SetFormName

func (p *ValidatedWithContinuationMark) SetFormName(nm string)

SetFormName sets the form name for error messages.

func (*ValidatedWithContinuationMark) Source

func (p *ValidatedWithContinuationMark) Source() *syntax.SourceContext

Source returns the source context for error reporting.

type ValidationError

type ValidationError struct {
	Source  *syntax.SourceContext
	Message string
	Form    string // e.g., "if", "define", "lambda"
}

ValidationError captures location and details for error reporting

func (ValidationError) Error

func (p ValidationError) Error() string

func (ValidationError) ErrorWithMaxOriginDepth

func (p ValidationError) ErrorWithMaxOriginDepth(maxDepth int) string

ErrorWithMaxOriginDepth returns the error message with a configurable origin chain depth.

type ValidationResult

type ValidationResult struct {
	Expr   ValidatedExpr     // nil if validation failed
	Errors []ValidationError // All errors encountered
	// contains filtered or unexported fields
}

ValidationResult collects all errors from validation

func ValidateExpression

func ValidateExpression(ctx context.Context, env *environment.EnvironmentFrame, expr syntax.SyntaxValue) *ValidationResult

ValidateExpression validates a syntax expression and returns a validated form or a list of errors. The env parameter provides the environment context for checking local variable shadowing of special forms (R7RS §4.2.2).

func (*ValidationResult) Error

func (p *ValidationResult) Error() string

func (*ValidationResult) Ok

func (p *ValidationResult) Ok() bool

Ok returns true if no validation errors were encountered.

Jump to

Keyboard shortcuts

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