interp

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: GPL-3.0 Imports: 32 Imported by: 0

Documentation

Overview

Package interp compiles and runs magusfile sources via the Buzz scripting backend. Owns the host-binding seam, REPL, and compiled-source cache.

Index

Constants

View Source
const (
	ModeMagusfile = "magusfile"
	ModeSpell     = "spell"
	ModeRepl      = "repl"
)

Buzz execution modes, the "mode" attribute on magus.buzz.exec/compile.

View Source
const TargetContextGlobal = "__magus_target_context"

TargetContextGlobal is the session-global name under which the bindings layer stashes the shared magus.Context value (see bindings.registerAllBuzz). A target function receives it as its first argument; execBuzzSrc fetches it with GetGlobal and prepends it at dispatch. The double underscore keeps it out of the way of any magusfile identifier.

Variables

View Source
var ErrNoMagusfile = errors.New("magusfile: not found (no magusfile.buzz or magusfiles/*.buzz in this project); run `magus init` to add one")
View Source
var ErrUnknownTarget = errors.New("magusfile: unknown target")

Functions

func AttachSessionObservers added in v0.2.0

func AttachSessionObservers(ctx context.Context, sess *buzz.Session, mode string)

AttachSessionObservers wires the session-scoped compile observer and VM fault hook so sess's parse/check/compile phases, resolved imports, and faults feed the spine under mode. A no-op when telemetry is disabled, leaving sess unobserved.

func Available

func Available() bool

Available reports whether the interp layer can run magusfiles: the Buzz engine is registered and Buzz host bindings are installed. Both are always present in a real magus binary (blank-imported from cmd/magus).

func CtxFormTargetKeys added in v0.3.0

func CtxFormTargetKeys(src string) map[string]bool

CtxFormTargetKeys returns the normalized keys of the exported functions in src whose FIRST parameter is annotated `magus\Context` (types.ContextParamAnnotation) - the target contract. execBuzzSrc uses it to enforce that contract at load (an exported function missing the context is rejected with MGS1008) and to prepend the context at dispatch. It does NOT build the graph: the dependency graph is read statically by describe.Extract, which sees ctx-form declarations directly. Best-effort: a parse failure yields nil, matching the extractor's never-error contract.

func NewBuzzReplSession

func NewBuzzReplSession(ctx context.Context, autoloadDir string) (engine.Session, error)

NewBuzzReplSession creates a Buzz session with host bindings installed, ready for the shared REPL. When autoloadDir is non-empty and a magusfile.buzz is found in or above it, its files are executed first so their top-level definitions are available at the prompt. The returned engine.Session also satisfies the optional REPL/debug interfaces.

func NewBuzzWorkerFunc

func NewBuzzWorkerFunc(src *Source) buzz.WorkerFunc

NewBuzzWorkerFunc returns the buzz.WorkerFunc that creates a pre-warmed Buzz session for src. Safe to call from multiple goroutines because execBuzzSrc reads sources by absolute path and does not acquire chdirMu.

func NewHostCallObserver added in v0.2.0

func NewHostCallObserver(ctx context.Context) buzz.DirectObserver

NewHostCallObserver returns a DirectObserver that records magus.buzz.host.call for every wrapped native callable, or nil when telemetry is disabled so buzz.WrapDirect returns the callable unchanged and the VM's hot native-dispatch arm is untouched.

func NewPoolObserver added in v0.2.0

func NewPoolObserver(ctx context.Context) buzz.PoolObserver

NewPoolObserver returns a PoolObserver that reports Buzz session-pool lifecycle (reuse, warm, eviction, idle) to the spine, or nil when telemetry is disabled so the pool runs unobserved. Thread the result onto the dispatch ctx with buzz.WithPoolObserver.

func PrettyPrint

func PrettyPrint(w io.Writer, v engine.Value, opts PrettyOpts)

PrettyPrint writes a human-readable rendering of v to w with cycle detection.

func ProjectPathFromContext

func ProjectPathFromContext(ctx context.Context) (string, bool)

ProjectPathFromContext returns the project path stored by WithProjectPath, and whether one was set.

func RegisterBuzzHostBindings

func RegisterBuzzHostBindings(fn BuzzHostBindingsFn)

RegisterBuzzHostBindings stores the Buzz host-binding function. Called from bindings init().

func RegisterBuzzSpellImportCheck

func RegisterBuzzSpellImportCheck(fn func(handles []string) error)

RegisterBuzzSpellImportCheck stores the validator for `magus/spell/*` imports. Called from bindings init(), the same seam as RegisterBuzzHostBindings.

func RemovedAPICall added in v0.4.0

func RemovedAPICall(src string) (call, replacement string, ok bool)

RemovedAPICall reports the first removed magusfile API call in src, with the call that replaced it. ok is false when src uses none of them.

Two readings, because a stale magusfile fails in two different places. When src parses, the AST is authoritative and a mention inside a comment or string literal cannot fake a hit. When src does NOT parse, the removed shape may be the very reason (`magus.project.register(fun(p, cb) ...)` predates required parameter annotations, so it dies in the parser with a message about parameter "p"), and a textual scan is the only thing left; it is reached only for a file that is already failing, so at worst it re-explains a broken magusfile with the wrong migration.

func RemovedAPINames added in v0.4.0

func RemovedAPINames() []string

RemovedAPINames returns the dotted member path of every removed call, without the `magus.` root (e.g. "project.register"). The surface lock test uses it to assert the table never names something the namespace still binds.

func Repl

func Repl(ctx context.Context, sess engine.Session, opts ReplOptions) error

Repl runs an interactive REPL on sess until EOF or .exit.

func RunDir

func RunDir(ctx context.Context, dir, target string, extraArgs []string) (any, error)

RunDir runs target for the project in dir. Returns ErrNoMagusfile or ErrUnknownTarget when not found.

Buzz is the only engine today, so FindAll yields a single source. The loop is the seam a second engine would extend: each source is fully executed (including top-level declarations such as magus.project) before its target registry is consulted, and an unknown target falls through to the next source.

func TimeCall added in v0.2.0

func TimeCall(ctx context.Context, mode string, fn func() (vm.Value, error)) (vm.Value, error)

TimeCall is TimeExec for a value-returning call (Session.CallValue), the target and spell-handler dispatch boundary.

func TimeExec added in v0.2.0

func TimeExec(ctx context.Context, mode string, fn func() error) error

TimeExec times fn as one Buzz execution under mode, recording exec duration, outcome, and the JIT-run delta when telemetry is active on ctx. With none it runs fn directly, adding no timing to the hot path.

func WithCrossDispatch

func WithCrossDispatch(ctx context.Context, c *CrossDispatch) context.Context

WithCrossDispatch stores c in ctx; bindings retrieve it to run external deps.

func WithOverlay added in v0.4.0

func WithOverlay(ctx context.Context, files map[string]string) context.Context

WithOverlay supplies magusfile CONTENT for absolute paths, read instead of the file on disk. Paths not named here are read normally, and no overlay is the ordinary load.

It exists for the one caller that has to load a workspace whose magusfile it cannot read: `magus vcs resolve`, during a merge that left conflict markers in the magusfile itself. Everything that command does rests on the declarations - which target rebuilds which output - so a magusfile it cannot parse leaves it unable to settle even the conflicts it does own. The committed side of the merge is a complete, parseable copy of exactly those declarations, and reading it here is what lets the command do its half of the work while the hand-written conflict waits for a human.

An overlay, rather than swapping the file on disk and restoring it: a crash between the two would leave the user's conflict replaced by one side of it, and the merge state is not something a tool should be able to lose on the way to reporting an error.

func WithProjectPath

func WithProjectPath(ctx context.Context, path string) context.Context

WithProjectPath stores the workspace-relative path of the project whose magusfile is being parsed, so magus.project(fn) (the contextual form with no explicit path) can default to "this project".

func WithSource

func WithSource(ctx context.Context, src *Source) context.Context

WithSource stores src in ctx so that bindings (e.g. ctx.needs) can retrieve the active magusfile source for pool lookup.

Types

type BuzzHostBindingsFn

type BuzzHostBindingsFn func(ctx context.Context, sess *buzz.Session, targets map[string]vm.Callable, exports map[string]vm.Value, parseMode bool)

BuzzHostBindingsFn registers Go-backed host modules into a Buzz session. targets is the session's dispatchable target registry; exports maps each canonical target key to the exported function value itself, so ctx.needs can verify a passed function IS the exported target (nil when the session has no export discovery, e.g. the REPL). parseMode=true collects names only.

type CrossDispatch

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

CrossDispatch runs cross-project target dependencies (declared via a project import, then referenced as <alias>.<target>) at most once per run and detects cross-project cycles. One instance is installed in the run context and shared across every target, so two targets that both need the same remote target run it once; Dispatch is safe for concurrent use.

func CrossDispatchFromContext

func CrossDispatchFromContext(ctx context.Context) *CrossDispatch

CrossDispatchFromContext returns the coordinator stored by WithCrossDispatch, or nil — e.g. in describe/parse, where external deps stay graph-only and must not run.

func NewCrossDispatch

func NewCrossDispatch() *CrossDispatch

NewCrossDispatch returns an empty coordinator for one run.

func (*CrossDispatch) Dispatch

func (c *CrossDispatch) Dispatch(ctx context.Context, dir, target string) error

Dispatch runs target in the project rooted at dir, at most once per run. A second caller for the same (dir, target) blocks on and shares the first run's result. A (dir, target) already on the current call stack is a cross-project cycle and errors instead of deadlocking.

The caller is responsible for yielding any concurrency slot it holds before calling Dispatch (the remote run needs slots of its own); see the binding's use of proc.RunChildSync.

type PrettyOpts

type PrettyOpts struct {
	MaxDepth int    // recursion limit; default 4
	Indent   string // per-level indent; default "  "
	Color    bool   // ANSI color; REPL sets based on stream + NO_COLOR
}

PrettyOpts configures the pretty-printer.

type PryContext

type PryContext struct {
	File   string // source file of the pry() call
	Line   int
	Func   string         // enclosing function name when known
	Frames []engine.Frame // call stack, innermost first
}

PryContext describes the call site and stack at a magus.pry() breakpoint.

type PryResume

type PryResume int

PryResume tells the debugger how to proceed after a breakpoint.

const (
	ResumeContinue PryResume = iota
	ResumeStep               // step into next line
	ResumeNext               // step over current line
	ResumeFinish             // run until current frame returns
)

func Pry

func Pry(ctx context.Context, sess engine.Session, pctx PryContext, opts ReplOptions) (PryResume, error)

Pry runs the pry REPL on sess. On .step/.next/.finish the REPL exits and the caller re-enters Pry when the engine's one-shot step hook fires.

type ReplOptions

type ReplOptions struct {
	WorkDir string // working directory for the session; defaults to process cwd
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Banner  string                  // printed once before the first prompt
	Locals  map[string]engine.Value // injected as globals before the loop
	// Candidates supplies completion candidates the interpreter cannot know:
	// workspace target names, project paths, host module names. Optional.
	//
	// It is a callback rather than a slice because the workspace is the caller's to
	// read, and reading it once at startup would go stale in a session long enough
	// to matter - which is most of them, since a REPL is where you sit while editing
	// the magusfile it is describing.
	Candidates func() []string
}

ReplOptions configures a REPL session.

type Source

type Source struct {
	Dir    string   // absolute directory containing the magusfile
	Files  []string // absolute source paths in load order
	Engine string   // engine name ("buzz"); inferred from file extensions by Find
}

Source describes a located magusfile source.

func Find

func Find(dir string) (*Source, error)

Find returns the primary magusfile source in dir, or ErrNoMagusfile.

func FindAll

func FindAll(dir string) ([]*Source, error)

FindAll locates every magusfile source in dir grouped by engine, in priority order. Returns ErrNoMagusfile when nothing is found; errors when single-file and magusfiles/ forms coexist.

func SourceFromContext

func SourceFromContext(ctx context.Context) *Source

SourceFromContext retrieves the Source stored by WithSource, or nil.

type Target

type Target struct {
	Key  string // lowercase dispatch identifier
	Name string // mixed-case display name
}

Target is a single invocable target discovered in a magusfile.

func Parse

func Parse(ctx context.Context, src *Source) ([]Target, error)

Parse executes src in parse mode (name discovery only) and returns discovered targets.

Directories

Path Synopsis
Package bindings registers the Go-backed modules (magus, std: os, platform, fs, vcs, env, crypto, json, log, http, archive) available to every magusfile script.
Package bindings registers the Go-backed modules (magus, std: os, platform, fs, vcs, env, crypto, json, log, http, archive) available to every magusfile script.
gen
Package gen is the generated native-to-Buzz adapter layer.
Package gen is the generated native-to-Buzz adapter layer.
Package engine defines the Engine and Session interfaces that all scripting engine implementations must satisfy, along with the engine registry.
Package engine defines the Engine and Session interfaces that all scripting engine implementations must satisfy, along with the engine registry.
buzz
Package buzz adapts the standalone Buzz interpreter (magus/gopherbuzz) to magus's engine.Engine/engine.Session interfaces and registers it under the "buzz" key.
Package buzz adapts the standalone Buzz interpreter (magus/gopherbuzz) to magus's engine.Engine/engine.Session interfaces and registers it under the "buzz" key.

Jump to

Keyboard shortcuts

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