wile

package module
v1.13.14 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

README

Wile

CI Go Reference

R7RS Scheme in pure Go. No CGo, no C toolchain, no cross-compilation pain.

Full hygienic macros, first-class continuations, numeric tower, and sandboxing. go get and it just works.

Embed Scheme in 4 lines of Go
engine, _ := wile.NewEngine(ctx)
engine.Define("width", wile.NewInteger(800))
engine.Define("height", wile.NewInteger(600))
result, _ := engine.Eval(ctx, engine.MustParse(ctx, "(* width height)"))  // => 480000

Table of Contents

Why Wile?

Embedding a Lisp in Go means tradeoffs:

Approach Problem
Chibi-Scheme, S7 via CGo Slow builds, broken cross-compilation, platform toolchain pain
Lisp subsets (Zygomys, etc.) No R7RS compliance, limited ecosystem
Lua via go-lua Not a Lisp, no macros, different semantics
JavaScript via goja Heavy runtime, no hygiene, async complexity

Wile solves this: Full R7RS Scheme in pure Go. Scheme values are Go heap objects, collected by Go's GC. No custom allocator, no FFI tax, no surprises.

Feature Wile Chibi/S7 (CGo) Goja (JS) Starlark Lua
Pure Go
Hygienic macros
R7RS compliance N/A N/A N/A
First-class continuations
Cross-compilation
Go GC integration
Performance

Gabriel benchmarks (16 programs), cumulative gains from v1.3.0 through opcode promotion and pool optimizations:

Benchmark v1.3.0 v1.9.0 Change
tak 0.381s 0.117s -69%
fib 1.262s 0.381s -70%
sumfp 4.271s 1.068s -75%
peval 0.168s 0.079s -53%

Full results and methodology in examples/benchmarks/.

Embedding in Go

The wile package provides the public API for embedding Scheme in Go.

Basic Usage
import "github.com/aalpar/wile"

// Create an engine
engine, err := wile.NewEngine(ctx)
if err != nil {
    log.Fatal(err)
}

// Evaluate a single expression
result, err := engine.Eval(ctx, engine.MustParse(ctx, "(+ 1 2 3)"))
fmt.Println(result.SchemeString()) // "6"

// Evaluate multiple expressions (returns last result)
result, err = engine.EvalMultiple(ctx, `
  (define x 10)
  (define y 20)
  (+ x y)
`)
Compile Once, Run Many Times
compiled, err := engine.Compile(ctx, engine.MustParse(ctx, "(+ x 1)"))
result, err := engine.Run(ctx, compiled)
Bridging Go and Scheme

Define Go values in Scheme's environment:

engine.Define("my-var", wile.NewInteger(100))
val, ok := engine.Get("my-var")

Register a Go function as a Scheme primitive:

import "github.com/aalpar/wile/values"

engine.RegisterPrimitive(wile.PrimitiveSpec{
    Name:       "go-add",
    ParamCount: 2,
    Impl: func(mc wile.CallContext) error {
        a := mc.Arg(0).(*values.Integer).Value
        b := mc.Arg(1).(*values.Integer).Value
        mc.SetValue(values.NewInteger(a + b))
        return nil
    },
})
// Now callable from Scheme: (go-add 3 4) => 7

Call a Scheme procedure from Go:

proc, _ := engine.Get("my-scheme-function")
result, err := engine.Call(ctx, proc, wile.NewInteger(42))
Value Constructors
Constructor Creates
wile.NewInteger(n) Exact integer
wile.NewBigInteger(n) Exact arbitrary-precision integer (*big.Int)
wile.NewFloat(f) Inexact real
wile.NewBigFloat(f) Inexact arbitrary-precision float (*big.Float)
wile.NewRational(num, den) Exact rational
wile.NewComplex(v) Complex number (complex128)
wile.NewString(s) String
wile.NewSymbol(s) Symbol
wile.NewBoolean(b) #t / #f
wile.True / wile.False Boolean constants
wile.NewVector(vals...) Vector
wile.NewList(vals...) Proper list
wile.EmptyList Empty list '()
wile.Void Void value

The values package provides additional constructors (e.g., NewRationalFromBigInt, NewComplexFromParts).

Engine Options
Option Description
wile.WithExtension(ext) Add a single extension
wile.WithExtensions(exts...) Add multiple extensions
wile.WithSafeExtensions() Add safe extension set (no filesystem, eval, system, threads, Go interop)
wile.WithoutCore() Skip core primitives — bare engine with only explicit extensions
wile.WithLibraryPaths(paths...) Enable R7RS library system with search paths
wile.WithMaxCallDepth(n) Set maximum VM recursion depth
wile.WithAuthorizer(auth) Set fine-grained runtime authorization policy
wile.WithRegistry(r) Use a custom registry instead of the default core primitives
wile.WithSourceFS(fsys) Add a virtual fs.FS layer to the source resolver chain
wile.WithSourceOS() Add OS filesystem to the source resolver chain
wile.WithNamespace(ns) Use a pre-built namespace (see NewNamespace)

Quick Start

# Install as a command-line tool
go install github.com/aalpar/wile/cmd/wile@latest

# Or download a prebuilt binary from releases
# https://github.com/aalpar/wile/releases

# Run the REPL
wile

# Try an example
wile --file examples/basics/hello.scm

# See all examples
ls examples/

Explore:

Key Features in Action

Logic Programming — Full Prolog embedded in Scheme

(load "examples/logic/schelog/schelog.scm")

(%rel (append xs ys zs)
  ((append () ?ys ?ys))
  ((append (?x . ?xs) ?ys (?x . ?zs))
   (append ?xs ?ys ?zs)))

(%which (zs)
  (append '(1 2) '(3 4) zs))
;; ⇒ ((zs 1 2 3 4))

See examples/logic/schelog/ for a self-contained Prolog implementation in Scheme.

Numeric Tower — Exact rationals, complex numbers, and arbitrary precision

(/ 1 3)              ; ⇒ 1/3 (exact rational, not 0.333...)
(* 1/3 3)            ; ⇒ 1 (exact)
(make-rectangular 0 1) ; ⇒ 0+1i (exact complex)
(expt 2 1000)        ; ⇒ 10715086071862673209484250490...

Hygienic Macros — Build DSLs without variable capture

(load "examples/macros/state-machine.scm")

(define-state-machine traffic-light
  (states: red yellow green)
  (initial: red)
  (transitions:
   (red -> green)
   (green -> yellow)
   (yellow -> red)))

Go-Native Concurrency — Goroutines and channels from Scheme

(let ((ch (make-channel)))
  (thread-start!
   (make-thread
    (lambda () (channel-send! ch 42))))
  (channel-receive ch))  ; ⇒ 42

First-Class Continuations — Non-local control flow

;; Early return
(call/cc (lambda (return)
  (for-each (lambda (x)
              (if (negative? x)
                  (return x)))
            '(1 2 -3 4))
  'not-found))  ; ⇒ -3

;; See examples/control/ for generators, coroutines, and backtracking

Installation

Requires Go 1.24 or later.

As a library
go get github.com/aalpar/wile@latest
As a standalone interpreter

Download a prebuilt binary from Releases, or build from source:

git clone https://github.com/aalpar/wile.git
cd wile
make build

The binary is built to ./dist/{os}/{arch}/wile.

Usage

# Start REPL
wile

# Run a Scheme file
wile example.scm
wile --file example.scm
wile -f example.scm

# With library search path
wile -L /path/to/libs example.scm

# Enter REPL after loading file
wile -f example.scm -i

# Print version
wile --version

The SCHEME_LIBRARY_PATH environment variable provides additional library search paths (colon-separated).

REPL Debugger

The REPL includes a debugger. Commands start with ,:

Command Description
,break FILE:LINE Set breakpoint
,delete ID Delete breakpoint
,list List breakpoints
,step Step into next expression
,next Step over (same frame)
,finish Step out (return from function)
,continue Continue execution
,backtrace Show stack trace
,where Show current source location

R7RS Standard Libraries

Library Description
(scheme base) Core language: arithmetic, pairs, lists, strings, vectors, control
(scheme case-lambda) case-lambda form
(scheme char) Character predicates and case conversion
(scheme complex) Complex number operations
(scheme cxr) Compositions of car and cdr
(scheme eval) eval and environment
(scheme file) File I/O
(scheme inexact) Transcendental functions (sin, cos, exp, log, sqrt, etc.)
(scheme lazy) Promises (delay, force, make-promise)
(scheme load) load
(scheme read) read
(scheme write) write, display
(scheme repl) interaction-environment
(scheme process-context) command-line, exit, get-environment-variable
(scheme time) current-second, current-jiffy, jiffies-per-second
(scheme r5rs) R5RS compatibility
Additional Libraries
Library Description
(srfi 1) List library (constructors, predicates, fold, search, set operations)
(chibi test) Minimal test framework (for R7RS test compatibility)
(chibi diff) Diff utilities
(chibi optional) Optional value handling
(chibi term ansi) ANSI terminal escape codes
Extension Libraries

With the library system enabled (WithLibraryPaths), Go extensions import as (wile <name>):

Library Description
(wile math) Transcendental functions, numeric utilities
(wile files) File I/O
(wile threads) SRFI-18 multithreading (threads, mutexes, condition variables)
(wile system) System interaction (environment, process)
(wile gointerop) Go concurrency primitives (channels, wait groups, atomics)
(wile introspection) Reflection and introspection
See docs/EXTENSION_LIBRARIES.md for import syntax and modifiers.

Go static analysis extensions (AST, SSA, CFG, callgraph, lint) have been extracted to wile-goast.

Architecture

Source → Tokenizer → Parser → Expander → Compiler → VM
  1. Tokenizer — Lexical analysis with comprehensive R7RS token support
  2. Parser — Builds syntax tree with source location tracking
  3. Expander — Macro expansion using syntax-rules/syntax-case with scope sets
  4. Compiler — Generates bytecode operations
  5. VM — Executes bytecode with stack-based evaluation
Package Structure
Package Purpose
wile (root) Public embedding API
machine/ Virtual machine, compiler, macro expander
values/ Scheme value types (numbers, pairs, ports, threads, etc.)
werr/ Error infrastructure (sentinel errors, contextual wrapping)
environment/ Variable binding, scope chains, phase hierarchy
registry/ Extension registration and primitives
registry/core/ Essential primitives and bootstrap macros
security/ Fine-grained runtime authorization
registry/helpers/ Shared utilities for primitive implementations
extensions/ Public extension packages (files, math, threads, system, etc.)
runtime/ Compile/Run API for embedding
internal/syntax/ First-class syntax objects with scope sets
internal/match/ Pattern matching engine for macros
internal/parser/ Scheme parser
internal/tokenizer/ Lexer
internal/validate/ Syntax validation
internal/forms/ Compiled form definitions
internal/schemeutil/ Scheme utility functions
internal/repl/ Interactive REPL with debugger
internal/bootstrap/ Environment initialization
internal/extensions/ Internal extension wiring (io, eval, aggregate registration)
API Stability

These packages form the public API and follow Go module versioning:

  • wile (root) — Engine, RegisterFunc, Eval/Compile/Run, error types
  • values — Scheme value types, Value interface, numeric tower
  • werr — Sentinel errors, WrapForeignErrorf, error infrastructure
  • registryRegistry, Extension, PrimitiveSpec, phase constants
  • securityAuthorizer, AccessRequest, built-in authorizers
  • extensions/* — Public extensions (files, math, threads, system, etc.)

All other packages (machine/, environment/, internal/) are implementation details and may change without notice. The machine package is importable but carries no compatibility guarantees.

Hygiene Model

Wile uses the "sets of scopes" approach from Flatt's 2016 paper. Each identifier carries a set of scopes, and variable resolution checks that the binding's scopes are a subset of the use site's scopes:

bindingScopes ⊆ useScopes

This prevents unintended variable capture in macros:

(define-syntax swap!
  (syntax-rules ()
    ((swap! x y)
     (let ((tmp x))    ; tmp gets macro's scope
       (set! x y)
       (set! y tmp)))))

(let ((tmp 5) (a 1) (b 2))  ; this tmp has different scope
  (swap! a b)
  tmp)  ; => 5, not captured by macro's tmp

Types

Numeric Tower
Type Description Example
Integer Exact 64-bit signed 42, -17
BigInteger Exact arbitrary precision #z12345678901234567890
Rational Exact fraction 3/4, -1/2
Float Inexact IEEE 754 double 3.14, 1e10
BigFloat Inexact arbitrary precision #m3.14159265358979323846
Complex Inexact complex (float64 parts) 1+2i, 3@1.57 (polar)
BigComplex Arbitrary-precision complex Exact or inexact parts
Concurrency Types
Type Description
Thread SRFI-18 thread
Mutex SRFI-18 mutex
Condition Variable SRFI-18 condition variable
Channel Go channel wrapper
WaitGroup Go sync.WaitGroup wrapper
RWMutex Go sync.RWMutex wrapper
Atomic Thread-safe mutable value

Sandboxing

Wile sandboxes embedded engines with two independent, composable layers.

Layer 1: Extension-based (compile-time)

Primitives not loaded into the engine don't exist. Attempts to use them produce compile-time errors — there are no runtime checks to bypass.

// Safe sandbox: no filesystem, eval, system, threading, or Go interop
engine, err := wile.NewEngine(ctx, wile.WithSafeExtensions())

Compose with specific privileged extensions:

engine, err := wile.NewEngine(ctx,
    append(wile.SafeExtensions(),
        wile.WithExtension(files.Extension),
    )...,
)

Library environments inherit the engine's registry, so restrictions propagate transitively to loaded libraries.

Layer 2: Fine-grained authorization (runtime)

The security.Authorizer interface gates privileged operations with K8s-style resource+action vocabulary:

engine, err := wile.NewEngine(ctx,
    wile.WithSafeExtensions(),
    wile.WithExtension(files.Extension),
    wile.WithAuthorizer(security.All(
        security.ReadOnly(),
        security.FilesystemRoot("/app/data"),
    )),
)
// Can read files under /app/data, nothing else

Built-in authorizers: DenyAll(), ReadOnly(), FilesystemRoot(path), All(authorizers...) (AND-composition).

See docs/SANDBOXING.md for the full security model, extension classification, known gaps, and custom authorizer examples.

Documentation

Document Description
docs/SCHEME_REFERENCE.md Complete Scheme language reference
docs/SANDBOXING.md Sandboxing and security model
docs/EXTENSIONS.md Extension system architecture and authoring guide
docs/EXTENSION_LIBRARIES.md R7RS library integration for extensions
PRIMITIVES.md Complete reference of types and primitives
docs/design/DESIGN.md Macro system design
docs/design/EMBEDDING.md Embedding API design
docs/design/DELIMITED_CONTINUATIONS.md Delimited continuation implementation
docs/dev/NUMERIC_TOWER.md Numeric tower architecture
docs/dev/ENVIRONMENT_SYSTEM.md Environment system architecture
docs/dev/R7RS_SEMANTIC_DIFFERENCES.md Documented differences from R7RS
BIBLIOGRAPHY.md Academic references
CHANGELOG.md Release history

References

Contributing

Wile welcomes contributions. Help wanted:

  • Documentation — Examples, guides, tutorials
  • Standard library — R7RS-small features, SRFI implementations
  • Test coverage — Broader coverage across packages
  • Performance — Allocation reduction, targeted optimizations
  • Tooling — REPL improvements, debugging tools, IDE integration

Get started:

License

This project is licensed under the Apache License 2.0 — see the LICENSE file for details.

Documentation

Overview

Package wile provides the public API for embedding the Wile Scheme interpreter.

Basic usage:

engine, err := wile.NewEngine(ctx)
if err != nil {
    log.Fatal(err)
}
result, err := engine.Eval(ctx, engine.MustParse(ctx, "(+ 1 2 3)"))
fmt.Println(result) // 6

With extensions:

engine, err := wile.NewEngine(ctx,
    wile.WithExtension(io.Extension),
    wile.WithExtension(system.Extension),
)

Sandboxed engine (no filesystem, eval, system, or Go interop):

engine, err := wile.NewEngine(ctx, wile.WithSafeExtensions())

Custom primitives:

engine, _ := wile.NewEngine(ctx)
engine.RegisterPrimitive(wile.PrimitiveSpec{
    Name:       "my-func",
    ParamCount: 1,
    Impl:       myFuncImpl,
})

Index

Examples

Constants

View Source
const (
	PhaseExpand  = environment.PhaseExpand
	PhaseCompile = environment.PhaseCompile
)

Phase constants for LibraryImportEvent.Phase. Re-exported from environment for embedder convenience.

View Source
const DefaultMaxCallDepth uint64 = 10000

DefaultMaxCallDepth is the default call depth limit for new engines. At ~500 bytes per frame, 10000 frames ≈ 5MB. Use WithMaxCallDepth(0) to opt out of the limit explicitly.

Variables

View Source
var EmptyList = wrapValue(values.EmptyList)

EmptyList is the empty list.

View Source
var ErrEngineClosed = werr.NewStaticError("engine is closed")

ErrEngineClosed is returned when Close is called on an already-closed engine.

View Source
var False = wrapValue(values.FalseValue)

False is the #f value.

View Source
var StdLibFS = mustSub(stdlibRaw, "stdlib")

StdLibFS provides the standard Scheme libraries shipped with wile (e.g., (wile algebra), (wile match), etc.) as an embedded filesystem. The filesystem is rooted at stdlib/, so library paths resolve as "lib/...". Consumers add it to the engine with WithSourceFS(StdLibFS).

View Source
var True = wrapValue(values.TrueValue)

True is the #t value.

View Source
var Void = wrapValue(values.Void)

Void is the void value.

Functions

func IsBoolean added in v1.1.0

func IsBoolean(v Value) bool

IsBoolean returns true if v is a boolean.

func IsIncompleteInput added in v1.11.0

func IsIncompleteInput(err error) bool

IsIncompleteInput reports whether a parse error indicates the input is a valid prefix of an expression that needs more input to complete. This is useful for REPL implementations that accumulate multi-line input.

Detection uses errors.Is where possible (wrapped io.EOF for truncated input). String matching is used only for tokenizer/parser errors whose types are internal and cannot be matched structurally from public code. Returns false for nil and bare io.EOF.

func IsList added in v1.1.0

func IsList(v Value) bool

IsList returns true if v is a proper list (including the empty list).

func IsNull added in v1.1.0

func IsNull(v Value) bool

IsNull returns true if v is the empty list.

func IsNumber added in v1.1.0

func IsNumber(v Value) bool

IsNumber returns true if v is a number.

func IsPair added in v1.1.0

func IsPair(v Value) bool

IsPair returns true if v is a non-empty pair (cons cell). EmptyList is not a *Pair (it's a separate type), so the type assertion handles the distinction without an explicit IsEmptyList check.

func IsProcedure added in v1.1.0

func IsProcedure(v Value) bool

IsProcedure returns true if v is a callable procedure (lambda, foreign closure, case-lambda, parameter, or composable continuation).

func IsString added in v1.1.0

func IsString(v Value) bool

IsString returns true if v is a string.

func IsSymbol added in v1.1.0

func IsSymbol(v Value) bool

IsSymbol returns true if v is a symbol.

func NewNamespace added in v1.7.0

func NewNamespace(ctx context.Context, opts ...EngineOption) (*environment.Namespace, error)

NewNamespace creates a fully initialized namespace with a registry, base environment bindings, syntax compilers, expanders, and bootstrap macros. The namespace can be passed to NewEngine via WithNamespace.

Options are shared with NewEngine: WithExtension, WithRegistry, WithoutCore, WithAuthorizer all work. Engine-specific options (WithMaxCallDepth, WithLibraryPaths, etc.) are accepted but ignored.

Example:

ns, err := wile.NewNamespace(ctx,
    wile.WithExtension(math.Extension),
    wile.WithAuthorizer(security.ReadOnly()),
)
eng, err := wile.NewEngine(ctx, wile.WithNamespace(ns))

func ToGoBool added in v1.1.0

func ToGoBool(v Value) (bool, bool)

ToGoBool extracts a Go bool from a Scheme boolean value. Returns (false, false) if v is not a boolean.

func ToGoFloat added in v1.1.0

func ToGoFloat(v Value) (float64, bool)

ToGoFloat extracts a float64 from an inexact real value. Returns (0, false) if v is not a Float.

func ToGoInt added in v1.1.0

func ToGoInt(v Value) (int64, bool)

ToGoInt extracts an int64 from an exact integer value. Returns (0, false) if v is not an exact integer or does not fit in int64.

func ToGoString added in v1.1.0

func ToGoString(v Value) (string, bool)

ToGoString extracts the Go string from a Scheme string value. Returns ("", false) if v is not a string.

Types

type BreakpointInfo added in v1.13.14

type BreakpointInfo struct {
	ID       int
	File     string
	Line     int
	Column   int
	Enabled  bool
	HitCount int
}

BreakpointInfo holds read-only breakpoint state for display.

type CallContext added in v1.10.7

type CallContext = machine.CallContext

CallContext is the extension-facing subset of MachineContext. This is a re-export of machine.CallContext for convenience.

type CompilationError added in v1.1.0

type CompilationError struct {
	Message string
	Cause   error
}

CompilationError wraps errors from parsing, expanding, or compiling Scheme code.

func (*CompilationError) Error added in v1.1.0

func (p *CompilationError) Error() string

func (*CompilationError) Unwrap added in v1.1.0

func (p *CompilationError) Unwrap() error

type CompiledCode

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

CompiledCode represents compiled Scheme code ready for execution.

CompiledCode captures the environment from the Engine that compiled it and always executes using that captured environment, regardless of which Engine is used to run it. Using a different Engine instance affects only that Engine's own bookkeeping (for example, evaluation counters), not the environment bindings or symbol interning.

CompiledCode can be run multiple times. It is not safe for concurrent execution (the underlying Engine is not goroutine-safe).

func (*CompiledCode) String

func (p *CompiledCode) String() string

String returns a string representation of the compiled code.

type Debugger added in v1.13.14

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

Debugger controls breakpoints and stepping for an Engine. It wraps the internal machine.Debugger to avoid exposing VM types.

func NewDebugger added in v1.13.14

func NewDebugger() *Debugger

NewDebugger creates a new Debugger.

func (*Debugger) Breakpoints added in v1.13.14

func (p *Debugger) Breakpoints() []BreakpointInfo

Breakpoints returns all breakpoints.

func (*Debugger) Continue added in v1.13.14

func (p *Debugger) Continue()

Continue resumes execution.

func (*Debugger) CurrentState added in v1.13.14

func (p *Debugger) CurrentState() values.DebugState

CurrentState returns the DebugState from the most recent break, or nil if no break has occurred.

func (*Debugger) DisableBreakpoint added in v1.13.14

func (p *Debugger) DisableBreakpoint(id int) bool

DisableBreakpoint disables a breakpoint by ID.

func (*Debugger) EnableBreakpoint added in v1.13.14

func (p *Debugger) EnableBreakpoint(id int) bool

EnableBreakpoint enables a breakpoint by ID.

func (*Debugger) IsStepping added in v1.13.14

func (p *Debugger) IsStepping() bool

IsStepping returns true if the debugger is in step mode.

func (*Debugger) OnBreak added in v1.13.14

func (p *Debugger) OnBreak(fn func(state values.DebugState, bp *BreakpointInfo))

OnBreak sets the callback invoked when a breakpoint is hit or a step completes. The DebugState provides source location and stack trace access without exposing VM internals.

func (*Debugger) RemoveBreakpoint added in v1.13.14

func (p *Debugger) RemoveBreakpoint(id int) bool

RemoveBreakpoint removes a breakpoint by ID.

func (*Debugger) SetBreakpoint added in v1.13.14

func (p *Debugger) SetBreakpoint(file string, line, col int) int

SetBreakpoint adds a breakpoint at the given source location. Returns the breakpoint ID.

func (*Debugger) StepInto added in v1.13.14

func (p *Debugger) StepInto()

StepInto enables step-into mode.

func (*Debugger) StepOut added in v1.13.14

func (p *Debugger) StepOut()

StepOut enables step-out mode using the stored break context.

func (*Debugger) StepOver added in v1.13.14

func (p *Debugger) StepOver()

StepOver enables step-over mode using the stored break context.

type Engine

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

Engine is the main entry point for embedding Wile.

An Engine is NOT safe for concurrent use from multiple goroutines. Most methods that parse, compile, or evaluate code mutate the environment. Each goroutine should use its own Engine, or synchronize externally.

SRFI-18 threads within a single Engine are safe — the VM handles thread coordination internally.

func NewEngine

func NewEngine(ctx context.Context, opts ...EngineOption) (*Engine, error)

NewEngine creates a new Wile engine. By default, only core primitives are included. Use WithExtension to add optional extensions.

When WithNamespace is used, the engine uses the pre-built namespace and ignores registry/extension/core options (they were applied when the namespace was created). Library paths and other engine-specific options still apply.

Initialization Order Invariant

NewEngine performs 6 initialization steps that MUST execute in this order. Each step depends on prior steps; reordering causes silent failures or panics.

  1. Config — build engineConfig from options
  2. Registry — buildRegistry(cfg): register core + extension primitives
  3. Namespace — NewNamespace() + SetRegistry + SetAuthorizer
  4. Bootstrap — applyBaseEnvironment: bind primitives, syntax compilers, expanders, bootstrap macros (uses EmbedFileResolver, NOT the runtime file resolver)
  5. File resolver — env.SetFileResolver: runtime include/load resolver. Must come AFTER bootstrap (step 4) so bootstrap uses its own EmbedFileResolver, not the runtime resolver.
  6. Library system — setupLibrarySystem: search paths, extension libraries, library env factory. Requires file resolver (step 5) and bootstrap macros (step 4) for define-library parsing.

The WithNamespace path (pre-built namespace) skips steps 2-5 and trusts that the caller bootstrapped correctly. NewNamespace() performs steps 2-4.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	result, err := engine.Eval(ctx, engine.MustParse(ctx, "(+ 1 2 3)"))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
6
Example (WithExtension)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
	"github.com/aalpar/wile/internal/extensions/io"
)

func main() {
	_, err := wile.NewEngine(context.Background(),
		wile.WithExtension(io.Extension),
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("engine created with I/O extension")
}
Output:
engine created with I/O extension

func (*Engine) AvailableLibraries added in v1.10.3

func (p *Engine) AvailableLibraries(ctx context.Context) ([]compilation.LibraryName, error)

AvailableLibraries returns all importable library names by combining filesystem discovery with registry-known libraries (synthetic extensions). Returns a sorted, deduplicated list. If the library system is not enabled (no WithLibraryPaths call), returns an empty list.

func (*Engine) Call

func (p *Engine) Call(ctx context.Context, proc Value, args ...Value) (Value, error)

Call invokes a Scheme procedure with arguments. Supports lambdas, foreign closures, case-lambdas, and parameters. Composable continuations cannot be called from Go (they require the VM winding stack) and return an error.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Define a Scheme function.
	ctx := context.Background()
	_, err = engine.EvalMultiple(ctx, `
		(define (square x) (* x x))
	`)
	if err != nil {
		log.Fatal(err)
	}

	// Retrieve and call it from Go.
	proc, ok := engine.Get("square")
	if !ok {
		log.Fatal("square not found")
	}

	result, err := engine.Call(ctx, proc, wile.NewInteger(12))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
144

func (*Engine) Close added in v1.3.0

func (p *Engine) Close() error

Close releases resources held by closeable extensions. Extensions that implement registry.Closeable have their Close method called. Errors from individual closers are collected and returned via errors.Join. Calling Close on an already-closed engine returns ErrEngineClosed.

func (*Engine) Compile

func (p *Engine) Compile(ctx context.Context, expr *Expression) (*CompiledCode, error)

Compile compiles a parsed expression without executing. The result can be executed later with Engine.Run.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Define a variable, then compile an expression that uses it.
	ctx := context.Background()
	_, err = engine.Eval(ctx, engine.MustParse(ctx, "(define x 0)"))
	if err != nil {
		log.Fatal(err)
	}

	compiled, err := engine.Compile(context.Background(), engine.MustParse(context.Background(), "(* x x)"))
	if err != nil {
		log.Fatal(err)
	}

	// Run the same compiled code with different values of x.
	for _, n := range []int64{3, 5, 7} {
		err = engine.Define("x", wile.NewInteger(n))
		if err != nil {
			log.Fatal(err)
		}

		result, err := engine.Run(ctx, compiled)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(result.SchemeString())
	}
}
Output:
9
25
49

func (*Engine) CurrentLoadDirectory added in v1.3.0

func (p *Engine) CurrentLoadDirectory() string

CurrentLoadDirectory returns the directory of the file currently being loaded, or empty string if no file is being loaded.

func (*Engine) CurrentLoadPath added in v1.3.0

func (p *Engine) CurrentLoadPath() string

CurrentLoadPath returns the path of the file currently being loaded, or empty string if no file is being loaded.

func (*Engine) Define

func (p *Engine) Define(name string, value Value) error

Define binds a value to a name in the top-level environment.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	err = engine.Define("width", wile.NewInteger(800))
	if err != nil {
		log.Fatal(err)
	}

	err = engine.Define("height", wile.NewInteger(600))
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	result, err := engine.Eval(ctx, engine.MustParse(ctx, "(* width height)"))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
480000

func (*Engine) DisassembleValue added in v1.13.14

func (p *Engine) DisassembleValue(v Value) (string, error)

DisassembleValue returns the formatted disassembly of a callable value. For compiled closures, shows bytecode instructions. For case-lambda, shows each clause separately. For foreign closures, shows name, arity, and documentation. Returns an error for non-procedure values.

func (*Engine) Environment

func (p *Engine) Environment() *environment.EnvironmentFrame

Environment returns the underlying environment for advanced use.

func (*Engine) Eval

func (p *Engine) Eval(ctx context.Context, expr *Expression) (Value, error)

Eval compiles and executes a parsed expression, returning the result. Use Engine.Parse to obtain an Expression from source code. For evaluating multi-expression strings, use Engine.EvalMultiple.

func (*Engine) EvalIn added in v1.7.0

func (p *Engine) EvalIn(ctx context.Context, expr *Expression, ns *environment.Namespace) (Value, error)

EvalIn compiles and executes a parsed expression in the given namespace, rather than the engine's own namespace.

The target namespace's authorizer governs security checks during execution. If the target namespace has no authorizer, the engine's authorizer is propagated to it before evaluation.

func (*Engine) EvalMultiple

func (p *Engine) EvalMultiple(ctx context.Context, code string) (Value, error)

EvalMultiple evaluates multiple expressions, returning the last result.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	result, err := engine.EvalMultiple(ctx, `
		(define x 10)
		(define y 20)
		(+ x y)
	`)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
30

func (*Engine) EvalMultipleWithSource added in v1.1.0

func (p *Engine) EvalMultipleWithSource(ctx context.Context, code string, source string) (Value, error)

EvalMultipleWithSource evaluates multiple expressions, returning the last result. The source parameter identifies where the code came from (e.g. a filename) and appears in error messages and stack traces.

func (*Engine) FormLabel added in v1.13.14

func (p *Engine) FormLabel(v Value) string

FormLabel returns a human-readable type label for a value: "primitive" for foreign (Go-implemented) closures, "procedure" for compiled Scheme closures, "" for non-callable values (including typed nils).

func (*Engine) Get

func (p *Engine) Get(name string) (Value, bool)

Get retrieves a value by name from the environment.

func (*Engine) LastCounters added in v1.1.0

func (p *Engine) LastCounters() machine.VMCounters

LastCounters returns the VM performance counters from the most recent Run or Eval call. Sub-context counters are not aggregated.

func (*Engine) LoadedLibraries added in v1.13.14

func (p *Engine) LoadedLibraries() ([]*LibraryInfo, error)

LoadedLibraries returns metadata for all currently loaded libraries, sorted by name. Returns (nil, nil) if no library registry is configured.

func (*Engine) LookupLibrary added in v1.13.14

func (p *Engine) LookupLibrary(parts ...string) (*LibraryInfo, error)

LookupLibrary returns info for a loaded library identified by its name parts (e.g., "scheme", "base"). Returns (nil, nil) if no library registry is configured. Returns a non-nil error if the registry has an unexpected type.

func (*Engine) MustParse added in v1.8.0

func (p *Engine) MustParse(ctx context.Context, code string) *Expression

MustParse is like Parse but panics on error.

func (*Engine) MustParseWithSource added in v1.8.0

func (p *Engine) MustParseWithSource(ctx context.Context, code string, source string) *Expression

MustParseWithSource is like ParseWithSource but panics on error.

func (*Engine) Namespace added in v1.7.0

func (p *Engine) Namespace() *environment.Namespace

Namespace returns the Namespace for advanced use. This provides access to per-instance symbol interning and phase management.

func (*Engine) Parse added in v1.8.0

func (p *Engine) Parse(ctx context.Context, code string) (*Expression, error)

Parse parses a single Scheme expression from code.

Parse returns a CompilationError if the input is empty, malformed, or contains more than one expression.

func (*Engine) ParseWithSource added in v1.8.0

func (p *Engine) ParseWithSource(ctx context.Context, code string, source string) (*Expression, error)

ParseWithSource parses a single Scheme expression from code. The source parameter identifies where the code came from (e.g. a filename) and appears in error messages.

func (*Engine) PopLoadPath added in v1.3.0

func (p *Engine) PopLoadPath()

PopLoadPath removes the top path from the load path stack. Does nothing if the stack is empty.

Advanced embedders who need fine-grained control can use Push/Pop directly, but most should use WithLoadPath for automatic cleanup.

func (*Engine) PushLoadPath added in v1.3.0

func (p *Engine) PushLoadPath(filePath string) error

PushLoadPath pushes a path onto the load path stack. Returns an error if the path is empty.

Advanced embedders who need fine-grained control can use Push/Pop directly, but most should use WithLoadPath for automatic cleanup.

func (*Engine) ReadExpression added in v1.11.0

func (p *Engine) ReadExpression(ctx context.Context, r io.Reader) (*Expression, error)

ReadExpression reads a single complete expression from r.

Unlike Engine.Parse, ReadExpression does not require the reader to contain exactly one expression — it reads the first complete expression and stops. Trailing input in the reader is ignored (the reader position advances past the consumed expression).

Use IsIncompleteInput to check whether a returned error indicates the input is a valid prefix of an expression that needs more input to complete. This is the intended pattern for REPL implementations:

expr, err := eng.ReadExpression(ctx, r)
if err != nil {
    if wile.IsIncompleteInput(err) {
        // prompt for more input
    }
    // real parse error
}

func (*Engine) RegisterFunc added in v1.1.0

func (p *Engine) RegisterFunc(name string, fn any) error

RegisterFunc registers a Go function as a Scheme primitive using natural Go signatures.

Reflection-based FFI bridging: pre-computes argument and return converters at registration time using Go's reflect package. Each call uses the cached converters to translate between Scheme values and Go types, avoiding per-call reflection overhead. See BIBLIOGRAPHY.md "Reflection-Based FFI Bridging".

Supported Types

Parameter types: int64, int, float64, string, bool, []byte, []T (typed slices), map[K]V, structs (exported fields), func(...) (callbacks), Value, and context.Context (first param only).

Return types: int64, int, float64, string, bool, []byte, []T, map[K]V, structs, Value, error (last return only), and void.

Variadic Functions

Variadic Go functions are supported. The variadic parameter receives all excess arguments from Scheme, converted element-by-element.

Context Forwarding

If the first parameter is context.Context, the VM's context is forwarded automatically and does not count toward the Scheme parameter count.

Callbacks

Callback parameters (func types) receive a Go closure that invokes a Scheme procedure through a VM sub-context. Callbacks must be called synchronously during the registered function's execution. Storing a callback for later invocation or calling it from another goroutine is unsafe — the closure captures VM state that is not goroutine-safe.

Returns an error wrapping werr.ErrFFIRegistration if fn is not a function or uses unsupported types.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Register a Go function with a natural signature — no MachineContext needed.
	err = engine.RegisterFunc("double", func(n int64) int64 {
		return n * 2
	})
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	result, err := engine.Eval(ctx, engine.MustParse(ctx, "(map double '(1 2 3 4 5))"))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
(2 4 6 8 10)

func (*Engine) RegisterFuncs added in v1.4.0

func (p *Engine) RegisterFuncs(funcs map[string]any) error

RegisterFuncs registers multiple Go functions as Scheme primitives. Each key in the map is the Scheme name; each value must be a Go function with a signature supported by [RegisterFunc].

Registration stops on the first error. The error message includes the binding name that failed first. When multiple functions are invalid, the particular binding that fails first is non-deterministic because Go map iteration order is unspecified. Functions registered before the failure remain registered.

func (*Engine) RegisterPrimitive

func (p *Engine) RegisterPrimitive(spec PrimitiveSpec) error

RegisterPrimitive adds a Go function as a Scheme primitive.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aalpar/wile"
	"github.com/aalpar/wile/values"
)

func main() {
	engine, err := wile.NewEngine(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Register a Go function that doubles an integer.
	err = engine.RegisterPrimitive(wile.PrimitiveSpec{
		Name:       "double",
		ParamCount: 1,
		Impl: func(mc wile.CallContext) error {
			n := mc.Arg(0).(*values.Integer).Value
			mc.SetValue(values.NewInteger(n * 2))
			return nil
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	result, err := engine.Eval(ctx, engine.MustParse(ctx, "(double 21)"))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.SchemeString())
}
Output:
42

func (*Engine) Registry added in v1.5.0

func (p *Engine) Registry() *registry.Registry

Registry returns a clone of the engine's registry. The returned registry can be filtered with Without, WithoutCategory, or WithoutBindings and passed to NewEngine via WithRegistry to create a restricted engine.

func (*Engine) Run

func (p *Engine) Run(ctx context.Context, cc *CompiledCode) (Value, error)

Run executes previously compiled code.

func (*Engine) SetDebugger added in v1.11.0

func (p *Engine) SetDebugger(d *Debugger)

SetDebugger attaches a debugger to the engine. Subsequent Engine.Run calls will execute with the debugger active, enabling breakpoints and stepping. Pass nil to detach the debugger.

func (*Engine) UnloadedLibraries added in v1.13.14

func (p *Engine) UnloadedLibraries(ctx context.Context) []*LibraryInfo

UnloadedLibraries returns metadata for libraries discoverable via the file resolver but not yet imported. Returns nil if no resolver is available. Thread-safe via lazy initialization with retry on failure.

func (*Engine) WithLoadPath added in v1.3.0

func (p *Engine) WithLoadPath(filePath string, fn func() error) error

WithLoadPath executes fn with filePath pushed onto the load path stack. This is the recommended API for embedders — it guarantees balanced push/pop via defer even if fn panics or returns an error.

Returns an error if filePath is empty.

Example:

err := engine.WithLoadPath("/app/scripts/main.scm", func() error {
    _, err := engine.EvalMultiple(ctx, "(load \"helper.scm\")") // resolves relative to /app/scripts/
    return err
})

type EngineOption

type EngineOption func(*engineConfig)

EngineOption configures an Engine.

func AllExtensions added in v1.9.7

func AllExtensions() []EngineOption

AllExtensions returns the complete set of engine options that add every available extension. This matches the extension set loaded by the CLI binary (io, files, math, introspection, eval, namespace, threads, gointerop, all, system, process).

Use WithAllExtensions when no additional options need to be appended.

Example:

eng, err := wile.NewEngine(ctx,
    append(wile.AllExtensions(),
        wile.WithLibraryPaths("./stdlib/lib"),
    )...,
)

func SafeExtensions added in v1.5.0

func SafeExtensions() []EngineOption

SafeExtensions returns engine options that add extensions suitable for sandboxed engines: io, exceptions, math, introspection, and the safe subset of all (records, promises, strings, characters).

These provide R7RS functionality without filesystem, eval, system, Go interop, or threading access. Core primitives are still added by default unless WithoutCore is also used.

Principle of Least Authority (Saltzer & Schroeder 1975).

authority(engine) = ∪ { caps(ext) : ext ∈ extensions }

SafeExtensions() ⊂ AllExtensions() — the safe set excludes
filesystem, eval, system, Go interop, and threading capabilities.
WithoutCore() produces authority(engine) = ∅.

Invariant: absent capabilities produce compile-time errors, not
  runtime checks. If a name has no binding, compilation fails.
  No runtime check can be bypassed because no runtime check exists.
Constrains: NewEngine (applies the registry), LibraryEnvFactory
  (closes over registry — transitive confinement per Lampson 1973).
Constrained by: Registry.Without/WithoutCategory (capability
  attenuation — derived registries never gain authority).

See BIBLIOGRAPHY.md "Saltzer & Schroeder".

Example:

eng, err := wile.NewEngine(ctx,
    append(wile.SafeExtensions(),
        wile.WithLibraryPaths("./stdlib/lib"),
    )...,
)

func WithAllExtensions added in v1.9.7

func WithAllExtensions() EngineOption

WithAllExtensions adds every available extension to the engine. This is a convenience wrapper around AllExtensions for the common case where no additional options need to be appended.

Example:

eng, err := wile.NewEngine(ctx, wile.WithAllExtensions())

func WithAuthorizer added in v1.5.0

func WithAuthorizer(auth security.Authorizer) EngineOption

WithAuthorizer sets the Authorizer for the engine. The authorizer is injected into every context passed to Eval, Compile, Run, and Call, gating runtime primitives and compile-time code loading.

Without this option, all operations are allowed (open by default). The authorizer is immutable after engine construction.

Example:

eng, err := wile.NewEngine(ctx,
    wile.WithAuthorizer(security.ReadOnly()),
)

func WithExtension

func WithExtension(ext registry.Extension) EngineOption

WithExtension adds an extension to the engine.

func WithExtensions

func WithExtensions(exts ...registry.Extension) EngineOption

WithExtensions adds multiple extensions to the engine.

func WithImportObserver added in v1.5.0

func WithImportObserver(obs func(LibraryImportEvent)) EngineOption

WithImportObserver sets a callback that is invoked each time a library is imported. The observer is read-only — it cannot influence the import. Requires WithLibraryPaths to be effective (no libraries loaded without it).

func WithInlineThreshold added in v1.10.7

func WithInlineThreshold(n int) EngineOption

WithInlineThreshold sets the maximum body length (in top-level expressions) for procedure inlining. Procedures with bodies longer than this threshold are not inlined. A value of 0 disables inlining entirely. When not called, the engine uses compilation.DefaultInlineThreshold (5).

func WithLibraryPaths added in v1.4.0

func WithLibraryPaths(paths ...string) EngineOption

WithLibraryPaths enables the R7RS library system (define-library / import) and configures directories to search for .sld library files.

Without this option, (import ...) raises a configuration error.

Paths are searched in order: user-supplied paths first, then the defaults ("." and "./stdlib/lib"). An empty call WithLibraryPaths() enables library support with defaults only.

Example:

eng, err := wile.NewEngine(ctx,
    wile.WithLibraryPaths("/app/libs", "./vendor"),
)
// search order: /app/libs, ./vendor, ., ./stdlib/lib

func WithMaxCallDepth added in v1.3.0

func WithMaxCallDepth(n uint64) EngineOption

WithMaxCallDepth sets the maximum recursion depth for the VM. When the continuation stack exceeds this depth, ErrCallDepthExceeded is returned. A value of 0 means unlimited (no depth check). When not called, the engine uses DefaultMaxCallDepth (10000).

func WithMaxStackSize added in v1.13.14

func WithMaxStackSize(n uint64) EngineOption

WithMaxStackSize sets the maximum eval stack size for the VM. When the eval stack exceeds this size, ErrStackOverflow is returned. This is opt-in: a value of 0 means unlimited (no stack size check). There is no default — when not called, the stack is unlimited.

func WithNamespace added in v1.7.0

func WithNamespace(ns *environment.Namespace) EngineOption

WithNamespace uses a pre-built namespace instead of building one from extension options. When set, registry/extension/core options are ignored by NewEngine (they were already applied when the namespace was created).

This enables sharing a namespace across engines or pre-configuring namespaces with specific capabilities.

func WithRegistry

func WithRegistry(r *registry.Registry) EngineOption

WithRegistry uses a custom registry instead of the default. When set, core primitives are NOT automatically added.

func WithSafeExtensions added in v1.5.0

func WithSafeExtensions() EngineOption

WithSafeExtensions adds the safe extension set to the engine. This is a convenience wrapper around SafeExtensions for the common case where no additional options need to be appended.

Example:

eng, err := wile.NewEngine(ctx, wile.WithSafeExtensions())

func WithSourceFS added in v1.7.1

func WithSourceFS(fsys fs.FS) EngineOption

WithSourceFS adds a virtual filesystem layer to the source file resolver chain. Multiple calls add layers searched in call order. When no resolver options are used, the engine defaults to the OS filesystem. Once any resolver option is used (WithSourceFS or WithSourceOS), only the explicitly configured resolvers are active.

Bootstrap macros are unaffected — they always load from the embedded bootstrap filesystem.

Example:

//go:embed scheme
var schemeFS embed.FS

eng, err := wile.NewEngine(ctx,
    wile.WithSourceFS(schemeFS),  // searched first
    wile.WithSourceOS(),          // OS filesystem searched last
)

func WithSourceOS added in v1.7.2

func WithSourceOS() EngineOption

WithSourceOS adds the OS filesystem to the source file resolver chain. This is typically called last so that virtual filesystems are searched first. When no resolver options are used, the engine defaults to the OS filesystem; WithSourceOS is only needed when building an explicit chain with WithSourceFS.

Example:

eng, err := wile.NewEngine(ctx,
    wile.WithSourceFS(embedFS),  // virtual FS first
    wile.WithSourceOS(),         // OS fallback last
)

func WithoutCore added in v1.5.0

func WithoutCore() EngineOption

WithoutCore creates an engine with an empty registry — no core primitives (arithmetic, pairs, control flow, etc.) are added. Extensions added via WithExtension are still applied.

This is useful for building minimal engines where only specific extensions are needed, or for testing extension isolation.

type Expression added in v1.8.0

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

Expression represents a single parsed Scheme expression.

Expression wraps a syntax value produced by the parser, before any macro expansion or compilation. It captures the source name (if any) for use in error messages.

Expression is not safe for concurrent use.

func (*Expression) Source added in v1.8.0

func (p *Expression) Source() string

Source returns the source name associated with this expression. Returns the empty string if no source was specified at parse time.

func (*Expression) String added in v1.8.0

func (p *Expression) String() string

String returns a string representation of the expression.

type ForeignFunction

type ForeignFunction = machine.ForeignFunction

ForeignFunction is the signature for primitive implementations. This is a re-export of machine.ForeignFunction for convenience.

type LibraryImportEvent added in v1.5.0

type LibraryImportEvent = compilation.LibraryImportEvent

LibraryImportEvent records what happened when a library was imported. See compilation.LibraryImportEvent for field documentation.

type LibraryInfo added in v1.13.14

type LibraryInfo struct {
	Name        string // Scheme representation, e.g. "(scheme base)"
	Description string
	SourceFile  string
	Exports     []string // sorted export names
}

LibraryInfo holds read-only metadata about a Scheme library.

type MachineContext

type MachineContext = machine.MachineContext

MachineContext provides access to the VM during primitive execution. This is a re-export of machine.MachineContext for convenience.

type PrimitiveSpec

type PrimitiveSpec = registry.PrimitiveSpec

PrimitiveSpec defines a primitive to be registered. This is a re-export of registry.PrimitiveSpec for convenience.

type RuntimeError added in v1.1.0

type RuntimeError struct {
	Message    string
	Cause      error
	Condition  Value  // non-nil when Scheme raise produced the error; nil for VM/primitive errors
	Source     string // formatted source location ("file:line:col"), empty if unavailable
	StackTrace string // formatted VM stack trace, empty if unavailable
}

RuntimeError wraps errors from executing Scheme code.

Condition

When the error originated from a Scheme raise or raise-continuable, Condition holds the raised value and RuntimeError.IsSchemeException returns true. When the error originated from Go code (VM errors, primitive failures, type mismatches), Condition is nil.

Source and Stack Trace

Source and StackTrace provide the source location and VM stack trace at the point of the error. Both are empty strings when per-operation source tracking is unavailable.

Cause

Cause may contain internal machine types. Callers should treat it as an opaque error suitable for logging and errors.Is/errors.As matching, not for direct type inspection.

func (*RuntimeError) Error added in v1.1.0

func (p *RuntimeError) Error() string

func (*RuntimeError) IsSchemeException added in v1.3.0

func (p *RuntimeError) IsSchemeException() bool

IsSchemeException reports whether this error originated from a Scheme raise or raise-continuable expression. When true, Condition holds the raised value.

func (*RuntimeError) Unwrap added in v1.1.0

func (p *RuntimeError) Unwrap() error

type Value

type Value interface {
	// SchemeString returns the Scheme representation.
	SchemeString() string
	// IsVoid returns true if this is the void value.
	IsVoid() bool
	// Internal returns the underlying values.Value for advanced use.
	// This is exported for use by testing packages and advanced embedding scenarios.
	Internal() values.Value
	// contains filtered or unexported methods
}

Value represents a Scheme value in the public API.

func Car added in v1.1.0

func Car(v Value) (Value, bool)

Car returns the car of a pair or other Tuple type. Returns (value, true) on success, or (nil, false) if v is not a non-empty Tuple.

func Cdr added in v1.1.0

func Cdr(v Value) (Value, bool)

Cdr returns the cdr of a pair or other Tuple type. Returns (value, true) on success, or (nil, false) if v is not a non-empty Tuple.

func NewBigFloat

func NewBigFloat(f *big.Float) Value

NewBigFloat creates a big float value from a big.Float.

func NewBigFloatFromFloat64

func NewBigFloatFromFloat64(f float64) Value

NewBigFloatFromFloat64 creates a big float value from a float64.

func NewBigFloatFromString

func NewBigFloatFromString(s string) Value

NewBigFloatFromString creates a big float from a string. Returns nil if the string is not a valid float.

func NewBigInteger

func NewBigInteger(n *big.Int) Value

NewBigInteger creates a big integer value from a big.Int.

func NewBigIntegerFromInt64

func NewBigIntegerFromInt64(n int64) Value

NewBigIntegerFromInt64 creates a big integer value from an int64.

func NewBigIntegerFromString

func NewBigIntegerFromString(s string, base int) Value

NewBigIntegerFromString creates a big integer from a string in the given base. Returns nil if the string is not a valid integer.

func NewBoolean

func NewBoolean(b bool) Value

NewBoolean creates a Scheme boolean.

func NewComplex added in v1.2.0

func NewComplex(v complex128) Value

NewComplex creates a Scheme complex number from a Go complex128.

func NewComplexFromParts added in v1.2.0

func NewComplexFromParts(realPart, imagPart float64) Value

NewComplexFromParts creates a Scheme complex number from real and imaginary parts.

func NewFloat

func NewFloat(f float64) Value

NewFloat creates a Scheme inexact real.

func NewInteger

func NewInteger(n int64) Value

NewInteger creates a Scheme integer.

func NewList

func NewList(vals ...Value) Value

NewList creates a Scheme list from values.

func NewRational added in v1.2.0

func NewRational(num, denom int64) Value

NewRational creates a Scheme exact rational number.

func NewRationalFromBigInt added in v1.2.0

func NewRationalFromBigInt(num, denom *big.Int) Value

NewRationalFromBigInt creates a Scheme exact rational from big.Int numerator and denominator.

func NewString

func NewString(s string) Value

NewString creates a Scheme string.

func NewSymbol

func NewSymbol(s string) Value

NewSymbol creates a Scheme symbol.

func NewVector added in v1.2.0

func NewVector(vals ...Value) Value

NewVector creates a Scheme vector from values.

func ToSlice added in v1.1.0

func ToSlice(ctx context.Context, v Value) ([]Value, bool)

ToSlice converts a proper list to a Go slice. Returns (slice, true) on success, or (nil, false) if v is not a proper list.

func WrapValue added in v1.13.14

func WrapValue(v values.Value) Value

WrapValue wraps a values.Value as a wile.Value for use with Engine methods that accept Value (FormLabel, DisassembleValue, etc.). Returns nil if v is nil.

Directories

Path Synopsis
cmd
typeswitchlint command
Command typeswitchlint finds type switch statements on values.Value or values.Number that may be missing cases for concrete types.
Command typeswitchlint finds type switch statements on values.Value or values.Number that may be missing cases for concrete types.
wile command
Package main provides the entry point for the Wile Scheme interpreter binary.
Package main provides the entry point for the Wile Scheme interpreter binary.
Package docparse parses structured metadata from Guile-style docstrings.
Package docparse parses structured metadata from Guile-style docstrings.
Package environment provides variable binding and scoping for the Scheme compiler.
Package environment provides variable binding and scoping for the Scheme compiler.
examples
embedding command
basic demonstrates embedding the Wile Scheme interpreter in a Go program.
basic demonstrates embedding the Wile Scheme interpreter in a Go program.
embedding/source-tracking command
source-tracking demonstrates the ParseWithSource/MustParseWithSource API for embedding Wile with per-operation source location tracking.
source-tracking demonstrates the ParseWithSource/MustParseWithSource API for embedding Wile with per-operation source location tracking.
extensions
files
Package files provides file I/O primitives.
Package files provides file I/O primitives.
gointerop
Package gointerop provides Go concurrency primitive wrappers.
Package gointerop provides Go concurrency primitive wrappers.
introspection
Package introspection provides read-only environment introspection primitives.
Package introspection provides read-only environment introspection primitives.
math
Package math provides transcendental and advanced mathematical primitives.
Package math provides transcendental and advanced mathematical primitives.
process
Package process provides subprocess execution primitives.
Package process provides subprocess execution primitives.
system
Package system provides system interface primitives.
Package system provides system interface primitives.
threads
Package threads provides SRFI-18 multithreading primitives.
Package threads provides SRFI-18 multithreading primitives.
internal
bootstrap
Package bootstrap initializes the top-level Scheme environment.
Package bootstrap initializes the top-level Scheme environment.
extensions/all
Package all provides additional primitives for records, promises, and extended string/character operations.
Package all provides additional primitives for records, promises, and extended string/character operations.
extensions/eval
Package eval provides evaluation and environment primitives.
Package eval provides evaluation and environment primitives.
extensions/io
Package io provides I/O primitives for reading and writing.
Package io provides I/O primitives for reading and writing.
extensions/namespace
Package namespace provides namespace manipulation primitives.
Package namespace provides namespace manipulation primitives.
forms
Package forms provides a shared registry for special form validators.
Package forms provides a shared registry for special form validators.
match
Package match implements the pattern matching engine for syntax-rules and syntax-case.
Package match implements the pattern matching engine for syntax-rules and syntax-case.
parser
Package parser implements R7RS Scheme syntax parsing.
Package parser implements R7RS Scheme syntax parsing.
schemeutil
Package schemeutil provides conversion utilities between syntax, datum, and Go types.
Package schemeutil provides conversion utilities between syntax, datum, and Go types.
syntax
Package syntax implements Scheme syntax representation with hygiene support.
Package syntax implements Scheme syntax representation with hygiene support.
syntax/syntaxtest
Package syntaxtest provides test helpers for the syntax package.
Package syntaxtest provides test helpers for the syntax package.
tokenizer
Package tokenizer implements R7RS Scheme lexical analysis.
Package tokenizer implements R7RS Scheme lexical analysis.
validate
Package validate validates Scheme syntax and produces typed expressions.
Package validate validates Scheme syntax and produces typed expressions.
ADDING A NEW PROMOTED OP
ADDING A NEW PROMOTED OP
Package registry provides a plugin architecture for registering Scheme primitives.
Package registry provides a plugin architecture for registering Scheme primitives.
core
Package core provides the essential primitives required for Scheme to function.
Package core provides the essential primitives required for Scheme to function.
helpers
Package helpers provides shared utility functions for primitive implementations.
Package helpers provides shared utility functions for primitive implementations.
testhelpers
Package testhelpers provides shared test infrastructure for Scheme primitive tests.
Package testhelpers provides shared test infrastructure for Scheme primitive tests.
Package repl provides composable components for building interactive Scheme REPLs on top of the Wile engine.
Package repl provides composable components for building interactive Scheme REPLs on top of the Wile engine.
Package gorules defines custom lint rules for the Wile project.
Package gorules defines custom lint rules for the Wile project.
Package security provides fine-grained authorization for Scheme runtime operations.
Package security provides fine-grained authorization for Scheme runtime operations.
Package stdlib provides the embedded R7RS standard library files.
Package stdlib provides the embedded R7RS standard library files.
Package values implements all Scheme runtime value types.
Package values implements all Scheme runtime value types.
valuestest
Package valuestest provides test helpers for the values package.
Package valuestest provides test helpers for the values package.

Jump to

Keyboard shortcuts

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