interpreter

package
v1.12.3 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package interpreter provides the CalcMark expression interpreter.

The interpreter executes validated Abstract Syntax Trees (ASTs) and computes results while maintaining variable bindings in an environment.

Architecture

The interpreter follows a tree-walking pattern:

  1. Traverse the AST nodes
  2. Evaluate each node recursively
  3. Store variable assignments in environment
  4. Return computed values as typed results

Usage

Basic interpretation:

env := interpreter.NewEnvironment()
interp := interpreter.NewInterpreterWithEnv(env)

nodes, _ := parser.Parse("x = 5\ny = x + 3\n")
results, err := interp.Eval(nodes)
if err != nil {
    log.Fatal(err)
}

for _, result := range results {
    fmt.Println(result.String())
}

Environment

The environment tracks variable bindings:

env := interpreter.NewEnvironment()
env.Set("x", types.NewNumber(decimal.NewFromInt(5)))
value, exists := env.Get("x")

Variables persist across evaluations when using the same environment, enabling line-by-line evaluation in interactive editors.

Type System Integration

The interpreter produces typed values:

  • types.Number: Arbitrary-precision decimals
  • types.Currency: Monetary values with symbols
  • types.Quantity: Physical quantities with units
  • types.Date: Calendar dates
  • types.Time: Time of day
  • types.Duration: Time intervals
  • types.Boolean: True/false values

Operators

Supported operations:

  • Arithmetic: +, -, *, /, %, ^
  • Comparison: ==, !=, <, >, <=, >=
  • Unary: -, +

Functions

Built-in functions:

  • avg(...): Average of values (variadic)
  • sqrt(x): Square root

Unit Handling

The interpreter implements special semantics for units:

Binary operations (first-unit-wins):

5 kg + 10 lb    → ~9.54 kg (converts lb to kg, keeps first unit)
$100 + €50      → ~$155.00 (converts EUR to USD based on rates)
10 m + 5 ft     → ~11.52 m (converts ft to m)

Functions (drop mixed units):

avg($100, $200)      → $150.00 (same unit preserved)
avg($100, €200)      → 150 (mixed units → dimensionless number)
avg(5 kg, 10 kg)     → 7.5 kg (same unit preserved)
avg(5 kg, 10 lb)     →  (error: incompatible units)

Error Handling

Runtime errors include:

  • Undefined variables
  • Type mismatches
  • Division by zero
  • Incompatible units

Performance

The interpreter is designed for interactive use and completes evaluations in microseconds. See benchmark tests for metrics.

Index

Constants

This section is empty.

Variables

View Source
var BuiltinFunctions = []FunctionDef{
	{Name: "avg"},
	{Name: "sum"},
	{Name: "sqrt"},
	{Name: "number"},
	{Name: "accumulate"},
	{Name: "convert_rate"},
	{Name: "downtime"},
	{Name: "rtt"},
	{Name: "throughput"},
	{Name: "transfer_time"},
	{Name: "read"},
	{Name: "seek"},
	{Name: "compress"},
	{Name: "capacity"},
	{Name: "compound"},
	{Name: "grow"},
	{Name: "depreciate"},
}

BuiltinFunctions registers all CalcMark function implementations. Metadata (description, params, synonyms) comes from spec/features.Registry. Adding a function here requires a matching Feature in spec/features/registry.go.

Eval fields are populated in init() to break an initialization cycle: BuiltinFunctions → evalXxxFunc → evalAllArgs → evalNode → evalFunctionCall → BuiltinFunctions.

Functions

func Evaluate

func Evaluate(source string, env *Environment) error

Evaluate is a convenience wrapper for parsing and evaluating CalcMark source. This provides API compatibility with the old evaluator package.

func ExtractNumber added in v1.7.0

func ExtractNumber(val types.Type) (*types.Number, error)

ExtractNumber extracts the decimal value from any CalcMark type as a plain Number. Exported so that operator coercion (Quantity × Currency) can reuse the logic.

func GetCategoryOrder

func GetCategoryOrder() []string

GetCategoryOrder returns category names in their registration order (first-occurrence order from BuiltinFunctions).

func GetFunctionNames

func GetFunctionNames() []string

GetFunctionNames returns all function names including synonyms. Reads from BuiltinFunctions and enriches with synonyms from the features registry.

func GetFunctionsByCategory

func GetFunctionsByCategory() map[string][]FunctionInfo

GetFunctionsByCategory returns functions grouped by category. Reads from BuiltinFunctions and enriches with metadata from the features registry.

Types

type CascadingError added in v1.10.0

type CascadingError struct {
	VarName string // the errored variable that was referenced
	Cause   error  // the original error on that variable
}

CascadingError indicates that evaluation failed because a referenced variable has an error from a prior evaluation failure.

func (*CascadingError) Error added in v1.10.0

func (e *CascadingError) Error() string

func (*CascadingError) Unwrap added in v1.10.0

func (e *CascadingError) Unwrap() error

type DirectiveResolver added in v1.6.2

type DirectiveResolver interface {
	// ScaleFactor returns the scale factor and true if scale is defined.
	ScaleFactor() (decimal.Decimal, bool)
	// ResolveGlobal returns the typed value of a global and true if it exists.
	ResolveGlobal(name string) (types.Type, bool, error)
}

DirectiveResolver provides access to frontmatter values for @directive references. This interface breaks the import cycle between impl/interpreter and spec/document.

type Environment

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

Environment tracks variable bindings during interpretation. This is separate from Go's context.Context - it's simply variable storage for CalcMark variables.

func NewEnvironment

func NewEnvironment() *Environment

NewEnvironment creates a new empty environment with built-in constants.

func (*Environment) ClearError added in v1.10.0

func (e *Environment) ClearError(name string)

ClearError removes a single variable's error state.

func (*Environment) ClearErrors added in v1.10.0

func (e *Environment) ClearErrors()

ClearErrors removes all tracked variable errors.

func (*Environment) Clone

func (e *Environment) Clone() *Environment

Clone creates a shallow copy of the environment.

func (*Environment) Get

func (e *Environment) Get(name string) (types.Type, bool)

Get retrieves a variable binding. Returns the value and true if found, nil and false if not found.

func (*Environment) GetAllErroredVars added in v1.10.0

func (e *Environment) GetAllErroredVars() map[string]error

GetAllErroredVars returns a copy of the errored variables map.

func (*Environment) GetAllVariables

func (e *Environment) GetAllVariables() map[string]types.Type

GetAllVariables returns a snapshot copy of all variables. The returned map is safe to iterate without concern for concurrent mutation.

func (*Environment) GetError added in v1.10.0

func (e *Environment) GetError(name string) (error, bool)

GetError checks if a variable has a recorded evaluation error. Returns the error and true if found, nil and false otherwise.

func (*Environment) GetExchangeRate

func (e *Environment) GetExchangeRate(from, to string) (decimal.Decimal, bool)

GetExchangeRate retrieves an exchange rate for currency conversion. Returns the rate and true if found, zero and false if not defined.

func (*Environment) Has

func (e *Environment) Has(name string) bool

Has checks if a variable is defined.

func (*Environment) HasExchangeRates

func (e *Environment) HasExchangeRates() bool

HasExchangeRates returns true if any exchange rates are defined.

func (*Environment) Set

func (e *Environment) Set(name string, value types.Type)

Set stores a variable binding.

func (*Environment) SetError added in v1.10.0

func (e *Environment) SetError(name string, err error)

SetError marks a variable as having failed evaluation.

func (*Environment) SetExchangeRate

func (e *Environment) SetExchangeRate(from, to string, rate decimal.Decimal)

SetExchangeRate sets an exchange rate for currency conversion. Key format: "FROM_TO" (e.g., "USD_EUR").

type FunctionDef

type FunctionDef struct {
	Name string // Primary name — must match a Feature in spec/features/registry.go
	// Eval is the function implementation. It receives the interpreter (for evaluating
	// arguments) and the AST node (for accessing raw arguments when needed).
	// Populated in init() to break an initialization cycle (see BuiltinFunctions).
	Eval func(interp *Interpreter, f *ast.FunctionCall) (types.Type, error)
}

FunctionDef pairs a function name with its implementation. All metadata (description, syntax, params, synonyms, category) lives in spec/features.Feature — the single source of truth for presentation.

type FunctionInfo

type FunctionInfo struct {
	Name        string   // Primary function name (e.g., "avg")
	Synonyms    []string // Alternative names (e.g., ["average", "mean"])
	Description string   // Human-readable description
	Signature   string   // Usage pattern (e.g., "avg(value1, value2, ...)")
	Category    string   // Grouping for help display (e.g., "Math", "Network", "Storage")
}

FunctionInfo contains metadata about a CalcMark function for help output.

func GetAllFunctions

func GetAllFunctions() []FunctionInfo

GetAllFunctions returns all registered functions sorted by name. Reads from BuiltinFunctions (single source of truth for which functions exist) and enriches with metadata from the features registry.

func GetFunctionByName

func GetFunctionByName(name string) (FunctionInfo, bool)

GetFunctionByName looks up a function by name or synonym. Returns the FunctionInfo and true if found, or empty FunctionInfo and false if not.

type Interpreter

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

Interpreter executes validated AST nodes and produces typed results. This is a Go-specific implementation of CalcMark execution.

func NewInterpreter

func NewInterpreter() *Interpreter

NewInterpreter creates a new interpreter with an empty environment.

func NewInterpreterWithEnv

func NewInterpreterWithEnv(env *Environment) *Interpreter

NewInterpreterWithEnv creates a new interpreter with a pre-populated environment.

func (*Interpreter) Eval

func (interp *Interpreter) Eval(nodes []ast.Node) ([]types.Type, error)

Eval executes a list of AST nodes and returns the results. Each node produces a typed value.

func (*Interpreter) GetEnvironment

func (interp *Interpreter) GetEnvironment() *Environment

GetEnvironment returns the interpreter's environment.

func (*Interpreter) SetDirectiveResolver added in v1.6.2

func (interp *Interpreter) SetDirectiveResolver(dr DirectiveResolver)

SetDirectiveResolver provides frontmatter context for resolving @directive references.

func (*Interpreter) SetFiscalYearStarts added in v1.11.0

func (interp *Interpreter) SetFiscalYearStarts(month, day int)

SetFiscalYearStarts configures the start of the fiscal year. Month is 1-12 (January-December), day is the start day (1-31, typically 1).

func (*Interpreter) SetMeasurement added in v1.8.4

func (interp *Interpreter) SetMeasurement(mc *units.MeasurementConfig)

SetMeasurement configures how bare ambiguous unit names are resolved. This is a pre-interpreter directive: it affects how unit names are interpreted before any evaluation occurs (unlike convert_to which is post-evaluation).

func (*Interpreter) SetTimeFunc added in v1.11.0

func (interp *Interpreter) SetTimeFunc(f func() time.Time)

SetTimeFunc overrides the clock used for date evaluation. Useful for deterministic testing with pinned dates.

Jump to

Keyboard shortcuts

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