Documentation
¶
Overview ¶
Package buzz is a stack-based bytecode interpreter for the Buzz scripting language embedded in magusfiles. It is a Go reimplementation of the upstream Buzz language. gopherbuzz targets Buzz 0.6.0-dev: it tracks buzz-language/buzz main (0.6.0 is unreleased), synced and validated against the exact commit pinned by UpstreamRef; see LanguageVersion. The latest published language reference is https://buzz-lang.dev/0.5.0/reference/ .
Architecture: source is lexed and parsed (Parse), type-checked, then compiled to a flat instruction stream (CompileWith) that the register-window VM ([VM.Run]) executes.
The primary embedding entry point is NewSession; host code injects globals with Session.SetGlobal and registers target callbacks that Buzz can invoke via Session.Targets.
Value equality ¶
Equality (==) is structural for scalars and strings but reference-based for collections (lists, maps, objects). Two distinct list or map values are never == even if their contents match — this avoids O(n) comparison costs in the common case. Compare elements explicitly when content equality is needed.
Heap lifetime ¶
Heap objects (strings, lists, maps, objects, userdata) are pinned for the process lifetime: the VM never collects or compacts its heap. Fine for magus's short-lived sessions; a long-running embedder should isolate workloads in separate processes.
Index ¶
- Constants
- Variables
- func AncestorsFromContext(ctx context.Context) []string
- func CompileWith(prog *ast.Program, opts CompileOptions) (*vmpackage.Chunk, error)
- func IsReservedIdent(name string) bool
- func Parse(src string) (*ast.Program, error)
- func ParseEmbedded(src string) (*ast.Program, error)
- func WithAncestors(ctx context.Context, stack []string) context.Context
- func WithObserver(ctx context.Context, obs TargetObserver) context.Context
- func WithPoolObserver(ctx context.Context, obs PoolObserver) context.Context
- func WithPoolRegistry(ctx context.Context, reg *PoolRegistry) context.Context
- func WithTargetMemo(ctx context.Context, m *TargetMemo) context.Context
- func WrapDirect(name string, fn vmpackage.Callable, obs DirectObserver) vmpackage.Callable
- type CFuncSig
- type CParam
- type CType
- type CompileObserver
- type CompileOptions
- type CompilePhase
- type Diagnostic
- type DirectObserver
- type FFIProvider
- type ImportOutcome
- type MarshalOption
- type Module
- type ModuleEnv
- type Option
- type Pool
- type PoolObserver
- type PoolRegistry
- type Semaphore
- type Session
- func (s *Session) CallDepth() int
- func (s *Session) CallValue(ctx context.Context, fn vmpackage.Value, args []vmpackage.Value) (vmpackage.Value, error)
- func (s *Session) ClearStepHook()
- func (s *Session) Close() error
- func (s *Session) Compile(code string) (*vmpackage.Chunk, error)
- func (s *Session) DeclareModuleTypes(boundName, src string)
- func (s *Session) Diagnostics(code string) []Diagnostic
- func (s *Session) DoString(code string) error
- func (s *Session) Eval(ctx context.Context, code string) (vmpackage.Value, error)
- func (s *Session) EvalChunk(ctx context.Context, chunk *vmpackage.Chunk) (vmpackage.Value, error)
- func (s *Session) Exec(ctx context.Context, code string) error
- func (s *Session) ExecBytecode(ctx context.Context, data []byte) error
- func (s *Session) ExecChunk(ctx context.Context, chunk *vmpackage.Chunk) error
- func (s *Session) Exports() map[string]vmpackage.Value
- func (s *Session) Frames() []vmpackage.DebugFrame
- func (s *Session) GetGlobal(name string) vmpackage.Value
- func (s *Session) Globals() map[string]vmpackage.Value
- func (s *Session) IncludeDirs() []string
- func (s *Session) Locals(level int) map[string]vmpackage.Value
- func (s *Session) NativeModule(importPath string) (vmpackage.Value, bool)
- func (s *Session) NewChild() *Session
- func (s *Session) Provide(env ModuleEnv, mods ...Module) error
- func (s *Session) SetCompileObserver(obs CompileObserver)
- func (s *Session) SetFaultHook(cb func(vmpackage.FaultKind))
- func (s *Session) SetGlobal(name string, v vmpackage.Value)
- func (s *Session) SetIncludeDirs(dirs []string)
- func (s *Session) SetModuleDecls(importPath, src string)
- func (s *Session) SetModuleResolver(fn func(importPath string) (vmpackage.Value, bool))
- func (s *Session) SetNativeModule(importPath string, v vmpackage.Value)
- func (s *Session) SetPromoteTopLevel(on bool)
- func (s *Session) SetStepHook(mask vmpackage.StepMask, cb func(vmpackage.StepEvent, vmpackage.DebugFrame))
- func (s *Session) Targets() map[string]vmpackage.Callable
- func (s *Session) Tests() []TestEntry
- func (s *Session) Upvalues(level int) map[string]vmpackage.Value
- func (s *Session) Warnings() []Diagnostic
- type Severity
- type TargetMemo
- type TargetObserver
- type TestEntry
- type WorkerFunc
- type WorkerSession
Constants ¶
const ( // Type-check errors (checker.go). UndefinedName diagnostics.Code = "BZZ1001" // reference to a variable or function that is not in scope UndefinedType diagnostics.Code = "BZZ1002" // reference to a type name that is not defined NonBoolCondition diagnostics.Code = "BZZ1003" // an if/while/for condition whose type is not bool ArgumentError diagnostics.Code = "BZZ1004" // a call with the wrong count, an unknown/duplicate name, or a missing argument TypeMismatch diagnostics.Code = "BZZ1005" // an assignment, return, yield, or operand whose type does not match what is expected UnhandledRaise diagnostics.Code = "BZZ1006" // a call to a !> function from a caller that neither declares !> nor catches it UnknownMember diagnostics.Code = "BZZ1007" // access to a member an imported module does not export // Session / runtime errors (session.go). UnresolvedImport diagnostics.Code = "BZZ2001" // an import that cannot be resolved to a module or file FiberMisuse diagnostics.Code = "BZZ2002" // resume/resolve called wrong: not a fiber, missing argument, or a running fiber // Warnings (parser.go). Unlike every code above, a warning never fails Exec/Compile - // see Severity. UnusedImport diagnostics.Code = "BZZ3001" // an import whose namespace binding is never referenced // Warnings (checker.go). StringAccumulation diagnostics.Code = "BZZ3002" // a string rebuilt from itself with + inside a loop )
BZZ diagnostic codes. Each names a distinct, documented buzz error kind. There is deliberately NO catch-all code: a type error the checker has not classified carries NO code at all (just its message), matching Rust and TypeScript, where an error either earns a specific code or has none. A code is a lookup handle for a documented failure, not a completeness checkbox.
const ( CVoid = vmpackage.CVoid CBool = vmpackage.CBool CInt = vmpackage.CInt CUint = vmpackage.CUint CFloat = vmpackage.CFloat CDouble = vmpackage.CDouble CCharPtr = vmpackage.CCharPtr CVoidPtr = vmpackage.CVoidPtr CAddr = vmpackage.CAddr CPoint2D = vmpackage.CPoint2D CRect4D = vmpackage.CRect4D CUnsupported = vmpackage.CUnsupported )
C type constants.
const ( // LabelUpstream marks a module that tracks upstream Buzz's standard library: a // clean-room reimplementation whose names, signatures, and semantics match it. LabelUpstream = "upstream" // LabelGopherbuzz marks a module that originates in gopherbuzz, with no // counterpart in upstream Buzz. LabelGopherbuzz = "gopherbuzz" )
Well-known module labels, classifying a module by origin. Labels are free-form strings; these are the vocabulary gopherbuzz applies to its own stdlib, and a host defines additional labels as needed (e.g. "host", "wasm").
const LanguageVersion = "0.6.0-dev"
LanguageVersion is the Buzz language version gopherbuzz targets. Buzz 0.6.0 is unreleased: gopherbuzz tracks buzz-language/buzz `main`, which sits between the released 0.5.0 and the eventual 0.6.0, so the honest label is the in-development series. gopherbuzz stays compatible with released 0.5.0 and additionally implements the 0.6.0-dev conventions present at UpstreamRef -- namespace-decl resolution, `=>` arrow-body functions, and the `buzz:` stdlib import scheme.
const UpstreamRef = "0.5.0-265-g294d8f9"
UpstreamRef pins the exact buzz-language/buzz commit gopherbuzz is measured against, as a `git describe`: the 0.5.0 tag plus the commits since (0.5.0-<N>-g<shortsha>). Because 0.6.0 is not tagged, a commit -- not a version number -- is the only precise statement of which upstream this is compared with.
It is a comparison point, NOT a compatibility claim. gopherbuzz implements a subset. Do NOT restate the score here: this comment carried "26 of 83" long after the real figure moved, and a second stale number lived in conformance_test.go at the same time, so the tree asserted three different scores at once. The authority is testdata/upstream-behavior-allowlist.txt - its line count IS the passing count, because the conformance test enforces the list in both directions. The README's parity section carries the running record in prose. Bump this ref and re-run the conformance target on every sync.
Variables ¶
var ( // CTypeLayout returns the size and alignment in bytes of a C type name. CTypeLayout = vmpackage.CTypeLayout // IsPointerCType reports whether a C/Zig type spelling is a pointer (carried // as a heap-boxed `ud` to preserve the full 64-bit address). IsPointerCType = vmpackage.IsPointerCType // StructLayout computes size, alignment, and field offsets of a C struct. StructLayout = vmpackage.StructLayout // AllocFFI pins n zeroed bytes at a fixed address and returns it. AllocFFI = vmpackage.AllocFFI // AllocCString copies a string into a NUL-terminated C block. AllocCString = vmpackage.AllocCString // ReadCString reads a NUL-terminated C string, terminator included. ReadCString = vmpackage.ReadCString // ForeignStructTypes returns a zdef struct's C field types by name. ForeignStructTypes = vmpackage.ForeignStructTypes // WriteFFIBytes copies bytes into a block returned by AllocFFI. WriteFFIBytes = vmpackage.WriteFFIBytes // FreeFFI releases a block previously returned by AllocFFI. FreeFFI = vmpackage.FreeFFI // ReadScalar reads a C scalar from an alloc block at addr+offset. ReadScalar = vmpackage.ReadScalar // WriteScalar writes a C scalar into an alloc block at addr+offset. WriteScalar = vmpackage.WriteScalar // MakeCallback wraps a Buzz function as a C function pointer (its address). MakeCallback = vmpackage.MakeCallback )
FFI memory and C-ABI type metadata, backing the `ffi` std module. These are portable (no cgo, no purego) — see vm/ffi_mem.go.
var DebugOnly = vmpackage.DebugOnly
DebugOnly makes Marshal emit the debug-info (.bdb) blob instead of the executable bytecode (.bo). See vm.DebugOnly for full documentation.
var DefaultSearchPaths = []string{
"./?.buzz",
"./?/main.buzz",
"./?/src/main.buzz",
"./?/src/?.buzz",
"/usr/share/buzz/?.buzz",
"/usr/share/buzz/?/main.buzz",
"/usr/share/buzz/?/src/main.buzz",
"/usr/share/buzz/?/src/?.buzz",
"/usr/local/share/buzz/?.buzz",
"/usr/local/share/buzz/?/main.buzz",
"/usr/local/share/buzz/?/src/main.buzz",
"/usr/local/share/buzz/?/src/?.buzz",
"$BUZZ_PATH/?.buzz",
"$BUZZ_PATH/?/main.buzz",
"$BUZZ_PATH/?/src/main.buzz",
"$BUZZ_PATH/?/src/?.buzz",
}
DefaultSearchPaths is the ordered list of path templates an unconfigured Session searches to resolve `import "<name>"` to a file. In each template `?` is replaced with the import path and environment variables are expanded (a template referencing an unset variable is skipped, so an unset $BUZZ_PATH drops its entries rather than searching the filesystem root).
It mirrors the upstream Buzz search order (https://buzz-lang.dev); module files use the `.buzz` extension. Override per-session with WithSearchPaths.
var GetFFIProvider = vmpackage.GetFFIProvider
GetFFIProvider returns the currently installed FFI provider.
var ParseCDecls = vmpackage.ParseCDecls
ParseCDecls parses one or more C function prototypes separated by semicolons.
var ParseZigDecls = vmpackage.ParseZigDecls
ParseZigDecls parses Zig-style declarations (the upstream-Buzz zdef dialect).
var RegisterFFIProvider = vmpackage.RegisterFFIProvider
RegisterFFIProvider installs p as the FFI backend used by zdef().
var SetFFIProvider = vmpackage.SetFFIProvider
SetFFIProvider sets the FFI provider (accepts nil, for tests).
var UnmarshalChunk = vmpackage.UnmarshalChunk
UnmarshalChunk deserializes a Chunk produced by Chunk.Marshal.
Functions ¶
func AncestorsFromContext ¶
AncestorsFromContext returns the current dispatch ancestor stack stored by the pool.
func CompileWith ¶
CompileWith compiles prog under opts. See CompileOptions. Pass the zero CompileOptions{} for a self-contained program whose top-level variables are slot-based locals (the one-shot fast path); set SharedGlobals for the session model. (This is the standalone counterpart to Session.Compile, which compiles source against a session's shared scope.)
func IsReservedIdent ¶
IsReservedIdent reports whether name is a word upstream Buzz reserves, so it cannot be used as a plain binding name. A code GENERATOR emitting Buzz needs this: a Go field named Type mirrors to a field named `type`, which does not parse, and the generator has to reach for a free identifier (@"type") instead. Exported so there is one list rather than a copy that silently drifts from the parser's.
func Parse ¶
Parse tokenizes src and returns a Program using upstream Buzz's rules: the program top level may contain only declarations, imports, and expression statements (no control flow), and call arguments after the first must be labeled. This is the default because it matches upstream — leniency is the deviation, not strictness, so it must be opted into explicitly (ParseEmbedded).
func ParseEmbedded ¶
ParseEmbedded relaxes the two script-conformance rules Parse enforces (top-level statements and labeled args) for gopherbuzz's embedded use: the REPL, magus eval, magusfile loading, and interactive snippets, where top-level statements are the whole point. It is the named, deliberate deviation from upstream Buzz.
func WithAncestors ¶
WithAncestors installs stack as the dispatch ancestor stack. The pool maintains it itself while dispatching; it is exported for the two boundaries the pool cannot see: the entry target (invoked directly rather than dispatched, so it must seed the stack with its own name or a dependency can cycle back into it undetected) and a cross-project dispatch (which enters a project where these names mean nothing, and passes nil to clear them).
func WithObserver ¶
func WithObserver(ctx context.Context, obs TargetObserver) context.Context
WithObserver returns ctx carrying obs, which Pool.execute notifies for each target.
func WithPoolObserver ¶
func WithPoolObserver(ctx context.Context, obs PoolObserver) context.Context
WithPoolObserver returns ctx carrying obs, which a Pool notifies as it acquires, warms, and releases sessions. Pass it on the same ctx you hand to Dispatch.
func WithPoolRegistry ¶
func WithPoolRegistry(ctx context.Context, reg *PoolRegistry) context.Context
WithPoolRegistry stores reg in ctx for retrieval inside the Buzz call stack.
func WithTargetMemo ¶
func WithTargetMemo(ctx context.Context, m *TargetMemo) context.Context
WithTargetMemo returns ctx carrying m as the invocation-scoped target memo.
func WrapDirect ¶
WrapDirect returns a Callable that runs fn and reports its duration and outcome to obs under name; register the result with vm.DirectValue as usual. When obs is nil it returns fn unchanged, so an unobserved binding pays nothing. This is the recommended way to time native calls (host bindings, stdlib directs) without touching the interpreter dispatch loop. The wrapper passes args straight through and does not retain them, preserving the Callable no-retain contract.
Types ¶
type CompileObserver ¶
type CompileObserver interface {
// Phase reports one finished compile sub-phase: which phase, how long it ran,
// and the error it produced (nil on success). Fired in pipeline order (parse,
// then check, then compile) for each compiled chunk. Diagnostics runs parse and
// check only, so it fires those two without a following compile.
Phase(phase CompilePhase, elapsed time.Duration, err error)
// Import reports one resolved import: its path (as written, e.g. "buzz:os"),
// how it resolved, how long resolution took, and any error. For a flat file or
// source import the duration includes executing the imported module, whose own
// compile phases fire separately on this same observer.
Import(importPath string, outcome ImportOutcome, elapsed time.Duration, err error)
}
CompileObserver is notified as a Session compiles source into a runnable chunk: the parse/check/compile phase timings and each import it resolves.
It is optional: attach one with Session.SetCompileObserver. With none set the session compiles unchanged, so this adds no cost and no behaviour change.
type CompileOptions ¶
type CompileOptions struct {
// (OpDefName/OpLoadName) rather than stack slots. Set it when several chunks
// execute against one shared Env and must observe each other's top-level
// definitions — the magus multi-magusfile model and the REPL both rely on
// this. When false (the default), top-level variables are slot-based locals:
// faster (no per-access map hashing), but private to a single Run. Function
// bodies always use slots regardless of this flag.
SharedGlobals bool
// DebugLines records a source line for every emitted instruction (Chunk.lines)
// so the debugger can report a paused frame's current line and drive
// line-level step hooks. Off by default — the one-shot fast path pays nothing;
// the session path (Session.Compile) turns it on so magus.pry() works.
DebugLines bool
// PromoteTopLevel, only meaningful together with SharedGlobals, slot-promotes
// a top-level var/const that is provably chunk-private: not exported and never
// referenced from inside any function/fiber body (where it would either need to
// outlive the top-level frame or change from a live-Env read to a by-value
// upvalue snapshot). Promoted vars become stack slots — the same fast path
// block-locals and function bodies already use — while exported and
// closure-captured top-level names stay Env bindings, so cross-chunk visibility
// is unchanged for them. Leave it false for the REPL/incremental path, where a
// later chunk may reference any earlier top-level name by name.
PromoteTopLevel bool
// ImportedTypes are the exported object/enum declarations of flat-imported
// modules (the same set handed to the checker). They are seeded into the
// compiler's typeDecls so an object literal of an imported type
// (`config\Config{...}`) applies that type's field defaults, exactly as a
// local-type literal does. Without this, an imported-type literal only carries
// the fields it sets and leaves the rest null — upstream Buzz applies the
// defaults, so this is a parity fix, not an extension.
ImportedTypes []ast.Node
}
CompileOptions controls how a program's top-level scope is compiled.
type CompilePhase ¶
type CompilePhase int
CompilePhase identifies a sub-phase of turning Buzz source into a runnable chunk.
const ( PhaseParse CompilePhase = iota // lexer + parser: source -> AST PhaseCheck // type checker over the AST PhaseCompile // AST -> bytecode chunk )
func (CompilePhase) String ¶
func (p CompilePhase) String() string
String names the compile phase for logs and metric labels (plain ASCII).
type Diagnostic ¶
type Diagnostic struct {
Line, Col int
Code diagnostics.Code
Msg string
Severity Severity
}
Diagnostic is a positioned diagnostic for editor tooling. Line and Col are 1-based; a zero Line means no position was recoverable (Col is only meaningful beside a nonzero Line). Msg has the "buzz: line L:C:" prefix stripped - the position travels in the fields instead. Code is the BZZ diagnostic code (empty for a parse error, which has no code). Severity's zero value is SeverityError, matching every diagnostic before this field existed (a parse error has no Severity set either, so it reads as an error, correctly). Msg/Line/Col/Code/Severity mirror the unexported checker typeError; keep the two shapes in sync if either gains a field.
func (Diagnostic) String ¶
func (d Diagnostic) String() string
String renders d the same shape typeError.Error() renders a hard error in - "[CODE] buzz: line L:C: <severity: >msg", plus a "see: <url>" line when Code is set - so a warning a caller prints reads consistently with the errors this package already produces.
type DirectObserver ¶
type DirectObserver interface {
// DirectCall reports one finished direct call: the binding name, its wall-clock
// duration, and the error it returned (nil on success).
DirectCall(name string, elapsed time.Duration, err error)
}
DirectObserver is notified when a wrapped native (direct) callable returns. It is the recommended seam for timing host calls: wrap a Callable with WrapDirect at binding registration, which leaves the VM's hot direct-dispatch arm untouched. See WrapDirect.
type FFIProvider ¶
type FFIProvider = vmpackage.FFIProvider
FFIProvider binds parsed C function signatures from a shared library into callable Buzz values.
type ImportOutcome ¶
type ImportOutcome int
ImportOutcome classifies how a Session resolved one import statement.
const ( ImportBound ImportOutcome = iota // already bound or already loaded; skipped ImportNative // a host-native module value ImportDecls // host-supplied embedded declarations ImportResolver // resolved by the host module resolver ImportFile // a .buzz file on the search path ImportNotFound // nothing resolved the import (an error) )
func (ImportOutcome) String ¶
func (o ImportOutcome) String() string
String names the import outcome for logs and metric labels (plain ASCII).
type MarshalOption ¶
type MarshalOption = vmpackage.MarshalOption
MarshalOption configures what Chunk.Marshal emits.
type Module ¶
type Module struct {
// Name is the bare string a program imports, e.g. "os" in `import "os"`.
Name string
// Labels classify the module for filtering. See the Label* constants for the
// vocabulary gopherbuzz applies to its own stdlib; a host adds its own
// (e.g. "host", "wasm").
Labels []string
// Bind wires the module onto sess. It may install a fresh module (via
// Session.SetNativeModule / SetModuleDecls) or read back and extend one an
// earlier Module already provided under Name (host methods over the stdlib).
Bind func(sess *Session, env ModuleEnv) error
}
Module describes one importable Buzz module for registration on a session: the bare name a program imports, free-form Labels that classify it (provenance, WASM-safety, ...), and a Bind hook that wires it onto a session -- installing a fresh module, or merging onto one an earlier Module provided under the same name.
It is the single shape gopherbuzz's stdlib and a host embedder (e.g. magus's os/vcs/http surface) both use to describe a module, so a session's whole import surface is one ordered, labeled list: Session.Provide applies it, and a caller filters by label to derive a subset (the WASM playground, a strict-conformance run, a docs index).
This is the *registration* descriptor -- how to install a module -- and is distinct from a host's richer *API* descriptor (magus/std.Module carries a module's methods and fields for documentation and binding codegen). A given module may have both: one says how to install it, the other what it exposes.
type ModuleEnv ¶
ModuleEnv carries what a Bind hook may need beyond the session itself: the context a host module captures, and the writer std's `print` should target. A Module ignores the fields it does not use.
type Option ¶
type Option func(*Session)
Option configures a Session at construction. See NewSession.
func WithEmbedded ¶
func WithEmbedded() Option
WithEmbedded relaxes the upstream-Buzz script-conformance rules (top-level statements and labeled args) for this session. Embedding hosts (REPL, magus eval, magusfile loading) must set it; without it a session parses strictly, matching upstream.
func WithREPL ¶
func WithREPL() Option
WithREPL marks this session as an interactive REPL, suppressing the BZZ3001 unused-import warning (see Session.repl). A REPL host should pass this alongside WithEmbedded.
func WithSearchPaths ¶
WithSearchPaths replaces the session's import search path templates (see DefaultSearchPaths for the syntax). Passing no paths is a no-op, leaving the session on DefaultSearchPaths. A host that wants to confine imports to its own layout passes its own templates here (e.g. magus restricts resolution to `magusfiles/?.buzz` under the project and workspace roots).
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool is a per-source bounded pool of pre-warmed Buzz sessions. Safe for concurrent use.
Concurrency model: each Submit spawns a goroutine that acquires one semaphore slot (bounding real parallelism), checks out a warmed session from the idle list, runs the target, and returns the session. Because there is no fixed set of worker goroutines, a target that dispatches children via Dispatch and blocks until they finish never starves the children of a goroutine to run on — nested dispatch cannot deadlock on GOROUTINE OR SEMAPHORE availability, regardless of fan-out. Parallelism is bounded by the semaphore, which Dispatch yields (via getSem.Yield) so a child can acquire the slot its parent holds, even at MAGUS_CONCURRENCY=1.
That invariant does not, by itself, rule out every deadlock: two in-flight SIBLINGS that mutually depend on each other (B needs C, C needs B) each hold a goroutine and a slot just fine, but would block forever on each other's TargetMemo entry — neither name appears in the other's static ancestor stack, so the ancestor-chain cycle check never fires. TargetMemo.TryRun detects this dynamically (its waitingFor wait-for graph) and errors instead of hanging; see TargetMemo's doc comment.
func (*Pool) Close ¶
Close shuts down all idle sessions after in-flight jobs finish and release theirs.
func (*Pool) Dispatch ¶
Dispatch fans out names concurrently, yielding the caller's buzz slot if held so that children can acquire it (deadlock-free at MAGUS_CONCURRENCY=1). TargetMemo deduplication is applied when a memo is present in ctx: a target already in-flight is subscribed to (not re-submitted); the waitFn is called without holding the slot, so it cannot deadlock.
type PoolObserver ¶
type PoolObserver interface {
// SessionAcquire fires when the pool checks out a session to run a target.
// reused is true when an idle warm session was taken; false when none was idle
// and the pool must warm a fresh one (a cold start, reported next by
// SessionWarm). idle is the idle-session count remaining right after checkout.
SessionAcquire(ctx context.Context, reused bool, idle int)
// SessionWarm fires when the pool warms a fresh session, reporting how long
// construction took and its error (nil on success). Always preceded by a
// SessionAcquire with reused=false.
SessionWarm(ctx context.Context, elapsed time.Duration, err error)
// SessionRelease fires when a finished session returns to the pool. evicted is
// true when the pool was full or closed and the session was closed instead of
// retained; idle is the idle-session count right after the release.
SessionRelease(ctx context.Context, evicted bool, idle int)
}
PoolObserver is notified of a Session pool's lifecycle as it serves target runs: session checkout (reuse vs cold warm), warm cost, and release or eviction.
It is optional: attach one with WithPoolObserver. With none set the pool runs unchanged, so this adds no cost and no behaviour change to callers that do not opt in.
type PoolRegistry ¶
type PoolRegistry struct {
// contains filtered or unexported fields
}
PoolRegistry maps string keys to per-source Pools. Safe for concurrent use.
func NewPoolRegistry ¶
func NewPoolRegistry(getSem func(ctx context.Context) Semaphore, capacity int) *PoolRegistry
NewPoolRegistry returns an empty registry. getSem is called per-execute to derive the semaphore from ctx (pass nil for no concurrency budget). capacity<=0 defaults to NumCPU.
func PoolRegistryFromContext ¶
func PoolRegistryFromContext(ctx context.Context) *PoolRegistry
PoolRegistryFromContext retrieves the PoolRegistry stored by WithPoolRegistry, or nil.
func (*PoolRegistry) Close ¶
func (r *PoolRegistry) Close() error
Close closes every Pool in the registry.
func (*PoolRegistry) Get ¶
func (r *PoolRegistry) Get(key string, newSession WorkerFunc) *Pool
Get returns the Pool for key, creating it with newSession on first call. newSession is ignored on cache hits.
type Semaphore ¶
type Semaphore interface {
Acquire(ctx context.Context) error
Release()
// Yield releases one slot for the duration of fn and re-acquires it before
// returning. The caller must hold a slot; use only when buzzSlotHeld is true.
Yield(ctx context.Context, fn func() error) error
}
Semaphore is the concurrency budget the pool draws from. *cache.Limiter satisfies this interface (Acquire/Release/Yield are defined on it).
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is a single Buzz execution context. Not safe for concurrent use; ensure one goroutine owns it at a time.
func NewSession ¶
NewSession creates a Buzz execution context. Inject globals with SetGlobal and register target callbacks via Targets. Close releases the context.
Imports resolve against DefaultSearchPaths unless WithSearchPaths overrides it. BUZZ_INCLUDE_PATH (colon-separated on Unix, semicolon-separated on Windows) is read to populate the additional include directory list, searched after the templates; the host may override it with SetIncludeDirs.
func (*Session) CallDepth ¶
CallDepth reports the number of active frames in the current VM. Used by step-over to detect frame-boundary crossings.
func (*Session) CallValue ¶
func (s *Session) CallValue(ctx context.Context, fn vmpackage.Value, args []vmpackage.Value) (vmpackage.Value, error)
CallValue invokes a Buzz function (or direct callable) Value with the given arguments. Host code (e.g. magus target dispatch) uses this to call back into Buzz.
func (*Session) ClearStepHook ¶
func (s *Session) ClearStepHook()
ClearStepHook removes any installed step hook from the session and the current VM.
func (*Session) Compile ¶
Compile parses, type-checks, and returns a runnable Chunk bound to this session's shared-globals scope. Pass the result to ExecChunk to run it, optionally multiple times without re-parsing.
func (*Session) DeclareModuleTypes ¶
DeclareModuleTypes parses src and registers its exported object/enum types under boundName's namespace immediately, without waiting for a matching `import` statement to trigger it (contrast SetModuleDecls, whose src is only collected lazily, when resolveImport processes a real `import "<importPath>";`).
Every native module (crypto, io, os, vcs, ...) can use the lazy path, because nothing binds its name into the session env before the import runs. It does NOT work for a module whose native value a host binds some OTHER way before any import is processed - e.g. a namespace meant to be callable without an explicit import, via SetGlobal. resolveImport's "already bound" check fires before it ever consults moduleDecls, so a SetModuleDecls registered under that same name would never be collected. Call DeclareModuleTypes directly instead, once, when setting up such a namespace, to get the same "import this path, get its types" outcome SetModuleDecls gives every other module.
func (*Session) Diagnostics ¶
func (s *Session) Diagnostics(code string) []Diagnostic
Diagnostics parses and type-checks code against the session's shared scope and returns every diagnostic the editor should surface: a single parse error (checking cannot proceed past it), or otherwise every type error the checker found. Unlike Exec and Compile it does not stop at the first error, so it can drive live squiggles per keystroke.
It is NOT side-effect-free. Resolving the program's imports executes each imported module's top-level code and reads its file from disk, so the checker can see the globals and types they define (there is no check-only import pass). It also mutates session state (loadedPaths, env, importedTypes). Call it on a fresh or throwaway session - the embedded playground path (dry.Diagnostics) makes a new one per call - never on a live session you still intend to Exec, or a later real import will be skipped as already-loaded.
func (*Session) DoString ¶
DoString executes code using the session's own context. Embedders needing per-call cancellation use Exec directly. Required by the cross-engine engine.Session interface (every backend implements DoString), so it stays even though it is a thin wrapper over Exec.
func (*Session) Eval ¶
Eval compiles and runs code against the session's shared scope and returns the program's result value (the value of a trailing `return <expr>`, else Null). The REPL uses it to print bare expressions.
func (*Session) EvalChunk ¶
EvalChunk runs a previously compiled Chunk and returns its result value. The REPL driver compiles first (to tell a syntax error — fall back to the statement form — from a runtime error) then runs exactly once via this, so a snippet with side effects never executes twice.
func (*Session) Exec ¶
Exec parses, type-checks, compiles, and executes Buzz source code in the session's environment. Type errors are returned as hard errors (Buzz is statically typed).
func (*Session) ExecBytecode ¶
ExecBytecode deserializes a Chunk from data and executes it in this session.
func (*Session) ExecChunk ¶
ExecChunk runs a previously compiled Chunk in the session's environment.
func (*Session) Exports ¶
Exports returns the subset of Globals() whose names were declared with export in a file executed via Exec or ExecChunk. The map is a fresh snapshot; mutations don't affect the session.
func (*Session) Frames ¶
func (s *Session) Frames() []vmpackage.DebugFrame
Frames returns the active call stack of the currently-executing VM, innermost first. Empty when no run is in progress.
func (*Session) GetGlobal ¶
GetGlobal returns the value bound to name, or Null if unbound. The signature matches the cross-engine engine.Session interface (which returns a bare Value); absence and an explicit null binding both yield Null.
func (*Session) Globals ¶
Globals returns a snapshot of the session's top-level bindings (name → value), including host-injected globals. The REPL filters host names for .globals.
func (*Session) IncludeDirs ¶
IncludeDirs returns the current include directory list.
func (*Session) Locals ¶
Locals returns the named locals of the frame at level (0 = innermost) in the current VM, read from its stack register window. Empty when the level is out of range or no debug-name info was compiled.
func (*Session) NativeModule ¶
NativeModule returns the value registered for importPath via SetNativeModule, or ok=false if none is registered. It lets a host that layers its own methods onto a stdlib module under a shared name (e.g. magus merging host methods onto Buzz's bare "os"/"fs"/"crypto") read the registered module back so it can be extended in place rather than replaced.
func (*Session) NewChild ¶
NewChild creates an isolated session that inherits this session's import resolution (search paths, include dirs, native modules, module resolver) but starts with a fresh top-level scope and its own loaded-path set. io.runFile uses it so a run file cannot see or mutate the caller's globals — parity with upstream buzz, whose runFile executes the file in its own scope, not the caller's.
func (*Session) Provide ¶
Provide binds each module onto the session in order. Order is significant: a later module may merge onto an earlier one that shares its Name (a host layering methods onto the stdlib), so lower-precedence modules come first. Provide stops at the first Bind error and returns it.
func (*Session) SetCompileObserver ¶
func (s *Session) SetCompileObserver(obs CompileObserver)
SetCompileObserver attaches obs, notified as this session compiles source (parse/check/compile phase timings) and resolves imports. Pass nil to detach. With none set the session compiles unchanged, adding no cost.
func (*Session) SetFaultHook ¶
SetFaultHook installs cb to fire when a VM executing this session's code faults (see vm.FaultKind: a recovered internal panic, or a host callable error raised as a throw). It is applied to each VM the session runs, gated exactly like the debugger step hook, so an unset hook costs nothing. Pass nil to detach.
func (*Session) SetIncludeDirs ¶
SetIncludeDirs replaces the directories searched for file-based imports. The host (e.g. internal/interp) calls this to enforce workspace sandboxing before running any user code.
func (*Session) SetModuleDecls ¶
SetModuleDecls registers src as the embedded Buzz source imported by `import "<importPath>"`. It resolves before the includeDirs file search and flat-merges (its exported object/enum types become visible to the importer's checker, which a native value module cannot provide). Use it for shipped Buzz library modules that have no file on the include path.
func (*Session) SetModuleResolver ¶
SetModuleResolver installs fn as the on-demand resolver for path-style imports (see the moduleResolver field). fn is called with the import path and binds its returned value under the path's basename (or alias) when it reports ok; a false return leaves the import for the includeDirs file search.
func (*Session) SetNativeModule ¶
SetNativeModule registers v as the module imported by `import "<importPath>"`. The import binds v under the path's basename (e.g. "util" for "magus/extra"), or under an explicit alias. Host-provided modules resolve before any file search, so they need no .buzz file on disk.
func (*Session) SetPromoteTopLevel ¶
SetPromoteTopLevel enables top-level slot promotion for every chunk this session compiles (see Session.promoteTopLevel and CompileOptions.PromoteTopLevel). The magusfile execution path turns it on for faster top-level code; the REPL must leave it off so a later prompt line can resolve earlier top-level names.
func (*Session) SetStepHook ¶
func (s *Session) SetStepHook(mask vmpackage.StepMask, cb func(vmpackage.StepEvent, vmpackage.DebugFrame))
SetStepHook installs cb to fire on the current VM for events matching mask. cb runs synchronously on the execution goroutine and may re-enter the pry REPL. It applies to the VM currently executing; if none is active the hook is stored and applied to the next run that starts.
func (*Session) Targets ¶
Targets returns the session's dispatchable target map. The embedder owns its contents: magus registers exported magusfile targets and spell ops into it.
func (*Session) Tests ¶
Tests returns the test blocks registered while executing this session's code, in source order. A normal run never executes their bodies; a test runner calls each Fn (e.g. via CallValue) and treats a returned error as a failure.
func (*Session) Upvalues ¶
Upvalues returns the captured upvalues of the frame at level (0 = innermost), keyed by name. Empty when the frame is not a closure or no names were compiled.
func (*Session) Warnings ¶
func (s *Session) Warnings() []Diagnostic
Warnings returns the non-fatal diagnostics (currently just BZZ3001 unused imports) found by the most recent Exec or Compile call on this session - see lastWarnings for why it is last-compile rather than accumulated. Nil before any compile.
Unlike Diagnostics, this is a plain read of state compileShared already computed on the run path: it does not re-resolve imports or re-execute anything, so it is safe to call right after Exec/Compile on a live session you intend to keep using.
type Severity ¶
type Severity int
Severity classifies a BZZ diagnostic. The zero value is SeverityError, so every diagnostic built before Severity existed - and every one the checker still builds without setting it explicitly - keeps its current meaning: it fails Exec/Compile exactly as before this type was introduced. Only a diagnostic that opts in (UnusedImport, StringAccumulation) is a warning, which Exec/Compile must never fail on.
type TargetMemo ¶
type TargetMemo struct {
// contains filtered or unexported fields
}
TargetMemo is a per-invocation run-once tracker. It ensures a target executes at most once within one top-level dispatch, even when concurrent `depends_on` callers name the same target (diamond dependencies). Safe for concurrent use.
It also detects the cross-dependency cycle the static ancestor-chain check in Submit/dispatchInner cannot see: two in-flight SIBLINGS that depend on each other (B needs C, C needs B) never appear in each other's ancestor stack, so without this they would each subscribe to the other's still-running entry and deadlock forever. waitingFor records the dynamic wait-for graph (which caller is blocked on which name) so a caller about to block can detect the loop closing back to itself first. See TryRun.
func NewTargetMemo ¶
func NewTargetMemo() *TargetMemo
NewTargetMemo returns a fresh, empty TargetMemo for one invocation scope.
func TargetMemoFromContext ¶
func TargetMemoFromContext(ctx context.Context) *TargetMemo
TargetMemoFromContext retrieves the TargetMemo stored by WithTargetMemo, or nil.
func (*TargetMemo) Complete ¶
func (m *TargetMemo) Complete(name string, err error)
Complete records err for name and unblocks any waiters. Must be called exactly once by the goroutine that received isNew=true from TryRun.
func (*TargetMemo) TryRun ¶
func (m *TargetMemo) TryRun(caller, name string) (isNew bool, waitFn func(ctx context.Context) error)
TryRun checks whether name has already run or is running. caller is the name of the target on whose behalf this call is being made (the last entry of its ancestor stack), or "" for a top-level dispatch with no enclosing target; it is used only to record/detect the wait-for cycle below.
Returns (true, nil) when name is new — caller must run the target then call Complete. Returns (false, waitFn) when name is already in-flight or done — caller invokes waitFn(ctx) to get the result. waitFn blocks until the in-flight execution finishes, ctx is cancelled, or a cross-dependency cycle through name is detected; call it WITHOUT holding a limiter slot.
type TargetObserver ¶
type TargetObserver interface {
// TargetEnd reports a finished target: its name, how long its function ran, and the
// error it returned (nil on success). Called after the target function returns.
TargetEnd(ctx context.Context, name string, elapsed time.Duration, err error)
}
TargetObserver is notified as the pool runs targets. The pool calls it once per target per run (the target memo collapses repeat dependents into a single run), so an observer sees each target exactly once with its wall-clock duration and outcome.
It is optional: attach one with WithObserver. With none set the pool runs unchanged, so this adds no cost and no behaviour change to callers that do not opt in.
type TestEntry ¶
TestEntry is one registered `test "Name" { … }` block: its name and the zero-argument closure that runs its body.
type WorkerFunc ¶
type WorkerFunc func(ctx context.Context) (*WorkerSession, error)
WorkerFunc creates a pre-warmed Buzz session and target map for the pool. The session is owned by the pool worker and must not be used concurrently.
type WorkerSession ¶
WorkerSession is the pre-warmed pair a WorkerFunc returns: a freshly-executed Buzz session plus the target map derived from its exports. The two are produced together and always returned together, so bundling them in one struct keeps callers from having to handle the "half-loaded" nil-nil-error shape the old three-return signature carried.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ast defines the Buzz abstract syntax tree node types.
|
Package ast defines the Buzz abstract syntax tree node types. |
|
Package buzzgen mirrors Go types into Buzz.
|
Package buzzgen mirrors Go types into Buzz. |
|
cmd
|
|
|
buzz
command
Command buzz is a standalone runner for the Buzz language, mirroring the upstream `buzz` CLI (https://buzz-lang.dev).
|
Command buzz is a standalone runner for the Buzz language, mirroring the upstream `buzz` CLI (https://buzz-lang.dev). |
|
examples
|
|
|
ffi-c
command
Command ffi-c is a runnable demonstration of gopherbuzz's C FFI.
|
Command ffi-c is a runnable demonstration of gopherbuzz's C FFI. |
|
internal
|
|
|
Package std provides Buzz's standard library modules as native modules for the magus/buzz interpreter.
|
Package std provides Buzz's standard library modules as native modules for the magus/buzz interpreter. |
|
Package token defines the lexical token types and scanner for Buzz source.
|
Package token defines the lexical token types and scanner for Buzz source. |
|
Package types defines the Buzz static type system used by the type checker.
|
Package types defines the Buzz static type system used by the type checker. |
|
Command buzz-wasm is a minimal WebAssembly entry point for the interpreter.
|
Command buzz-wasm is a minimal WebAssembly entry point for the interpreter. |