backend

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DumpBytecode

func DumpBytecode(p *Program) string

DumpBytecode renders the whole Program.

func RunSafe

func RunSafe(m *Machine) (result value.Value)

RunSafe wraps Run with RuntimeError recovery.

func WHNF

func WHNF(v value.Value) value.Value

WHNF forces a value to weak head normal form. An already-forced thunk is followed cheaply; anything that still needs work — an unforced thunk (code thunk, stdin cell, or pattern-binding indirection) or a pending application from a composition — is handed to the full reducer, which pushes the right update frames and memoises. It is the re-entrant entry point used by show, DeepEqual, FullNormalForm, and the primitive kernels (installed as value.Force); routing every force through the same reducer keeps update-frame handling and memoisation in one place.

Types

type Capture

type Capture struct {
	FromUpvalue bool
	Slot        int
	Name        string // debug: source name of the captured variable; never read by the machine
}

A Capture says where MakeClosure reads one captured free variable: from the enclosing activation's own captured env (FromUpvalue) or from its local frame.

type ClosureTemplate

type ClosureTemplate struct {
	Code    PC               // entry point: the first case's Case instruction
	Capture []Capture        // minimal free-variable capture (closures escape, so this is worth computing)
	Frame   int              // slots to allocate for the matched case's bindings and lets
	NoMatch source.SourcePos // span of the whole pattern set, for "no pattern matched"
}

A ClosureTemplate is the compile-time description of a lambda. MakeClosure instantiates it into a *Closure, reading Capture to copy the minimal set of free variables from the enclosing activation's frames. Frame is the largest case frame, allocated once per application and reused across the cases tried.

type Instr

type Instr struct {
	Op Op
	A  int32
	B  int32
}

Instr is one instruction. The two operands are small integers (pool indices, slots, arities, jump targets), never pointers, so Code stays dense. Each opcode's use of A and B is documented on the constant.

type Machine

type Machine struct {
	Prog *Program
	// contains filtered or unexported fields
}

Machine is the STG-style push/enter reducer. It executes the flat bytecode produced by compile.go and keeps an explicit, heap-allocated reduction stack. Every strict point — forcing a thunk, applying an argument, forcing a builtin's operand or a match scrutinee — is a frame on that stack rather than a Go recursive call, so it forces values of arbitrary depth in bounded Go stack.

func NewMachine

func NewMachine(prog *Program) *Machine

func (*Machine) Run

func (m *Machine) Run() value.Value

Run initialises module environments and reduces the main body to WHNF.

type ModuleBinding

type ModuleBinding struct {
	Code  PC
	Frame int
	Name  string
}

A ModuleBinding is one public binding of a module, compiled as its own activation (its right-hand side run in a fresh frame, memoised in the module environment). Order matches the module's PublicBindings so environment slots line up.

type Op

type Op uint8
const (
	// Build (push a value; never force). The head of a body is whatever these
	// leave on the operand stack when Enter is reached.
	PushConst   Op = iota // push Consts[A]
	PushLocal             // push Locals[A]
	PushUpvalue           // push Upvalues[A]
	PushModule            // push ModuleEnvironments[ModuleNames[A]][B]
	PushStdin             // push the lazy stdin code-point stream
	PushBstdin            // push the lazy stdin byte stream
	MakeCons              // pop tail, head; push Cons{head, tail}
	MakeTuple             // pop A operands; push Tuple of arity A (A ≠ 2)
	MakeCompose           // pop second, first; push Composition{first, second}
	MakeClosure           // push a closure from Closures[A], capturing the current frames
	MakeThunk             // push a (memoising) thunk from Thunks[A] over the current frames
	StoreLet              // Locals[A] = a (memoising) thunk from Thunks[B] over the current frames

	// Apply / tail position (push/enter: push arguments, then enter the head).
	PushArg // pop an operand; push it as an argument frame (with Posns[A]) onto the reduction stack
	Enter   // terminator: the operand stack's top is this body's head — hand control back to the reducer

	// Match (force the subject, test, and bind). These run before a case's body;
	// a failed test jumps to B, the next case's Case instruction.
	Case        // reset the subject stack to hold only the closure's argument (case entry / retry point)
	MatchNumber // pop subject; force to a number; jump B unless it equals Consts[A]
	MatchTuple  // pop subject; force; if a tuple/cons of arity A push its elements, else jump B
	MatchString // pop subject; match the code-point spine against Consts[A]; jump B on mismatch
	Bind        // pop subject; Locals[A] = a named thunk over it (no force)
	NoMatch     // no case matched: raise the located "no pattern matched" error

	// Saturated primitive call (the strict arithmetic/comparison builtins). The
	// kernel forces and pops its operands and pushes the result; see prims.go.
	Prim // run primitive PrimOp(A); B = Posns index for error location
)

type PC

type PC = value.PC

PC is re-exported from the value package, where Thunk and Closure store entry points; the two definitions are the same int32 alias.

type Program

type Program struct {
	Code        []Instr
	Consts      []value.Value
	Posns       []source.SourcePos
	Names       []string
	ModuleNames []string
	Closures    []ClosureTemplate
	Thunks      []ThunkTemplate

	Entry      PC
	EntryFrame int

	Modules     map[string][]ModuleBinding
	ModuleOrder []string // stable iteration order for startup and disassembly
}

Program is the whole compiled unit: one flat instruction array plus the pools its instructions index, the closure/thunk templates, the program-body entry, and the per-module binding entries.

func Compile

func Compile(mainCore core.Expr, modCores map[string][]core.Bind, sourceProgram *syntax.Program, modules map[string]*syntax.Module) *Program

Compile translates the Core IR (program body + module bindings) into a flat Program. Bodies referenced out of line (lambdas, thunks) are queued and drained after the top-level bodies, so the layout never lets one span fall into another.

type StackFrame

type StackFrame struct {
	Kind  StackFrameKind
	Arg   value.Value      // valid when Kind == argFrame
	Pos   source.SourcePos // valid when Kind == argFrame
	Thunk *value.Thunk     // valid when Kind == updateFrame
	Cont  *contState       // valid when Kind == runFrame
	Prim  *primArgs        // valid when Kind == primArgsFrame
}

StackFrame is one entry of the reduction stack.

type StackFrameKind

type StackFrameKind uint8

StackFrameKind distinguishes the five entries the reduction stack can hold.

type ThunkTemplate

type ThunkTemplate struct {
	Code PC
	Name int              // index into Names; the binding name for let/module thunks, -1 (→ "") for anonymous
	Pos  source.SourcePos // debug: definition site; never read by the machine
}

A ThunkTemplate is the compile-time description of a thunk body. The thunk captures the whole enclosing activation by reference (Locals + Upvalues), so no capture list is needed; it addresses them with the enclosing slot numbers.

Jump to

Keyboard shortcuts

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