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:
- Traverse the AST nodes
- Evaluate each node recursively
- Store variable assignments in environment
- 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 ¶
- Variables
- func Evaluate(source string, env *Environment) error
- func ExtractNumber(val types.Type) (*types.Number, error)
- func GetCategoryOrder() []string
- func GetFunctionNames() []string
- func GetFunctionsByCategory() map[string][]FunctionInfo
- type CascadingError
- type DirectiveResolver
- type Environment
- func (e *Environment) ClearError(name string)
- func (e *Environment) ClearErrors()
- func (e *Environment) Clone() *Environment
- func (e *Environment) Get(name string) (types.Type, bool)
- func (e *Environment) GetAllDefinedLines() map[string]int
- func (e *Environment) GetAllErroredVars() map[string]error
- func (e *Environment) GetAllVariables() map[string]types.Type
- func (e *Environment) GetError(name string) (error, bool)
- func (e *Environment) GetExchangeRate(from, to string) (decimal.Decimal, bool)
- func (e *Environment) Has(name string) bool
- func (e *Environment) HasExchangeRates() bool
- func (e *Environment) Set(name string, value types.Type)
- func (e *Environment) SetDefinedLine(name string, line int)
- func (e *Environment) SetError(name string, err error)
- func (e *Environment) SetExchangeRate(from, to string, rate decimal.Decimal)
- type FunctionDef
- type FunctionInfo
- type Interpreter
- func (interp *Interpreter) Eval(nodes []ast.Node) ([]types.Type, error)
- func (interp *Interpreter) GetEnvironment() *Environment
- func (interp *Interpreter) SetDirectiveResolver(dr DirectiveResolver)
- func (interp *Interpreter) SetFiscalYearStarts(month, day int)
- func (interp *Interpreter) SetLineOffset(offset int)
- func (interp *Interpreter) SetMeasurement(mc *units.MeasurementConfig)
- func (interp *Interpreter) SetTimeFunc(f func() time.Time)
Constants ¶
This section is empty.
Variables ¶
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
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) GetAllDefinedLines ¶ added in v1.14.0
func (e *Environment) GetAllDefinedLines() map[string]int
GetAllDefinedLines returns a snapshot of variable → defined-line. Variables without a recorded line (built-in constants, frontmatter globals) are absent from the map, which signals "no position constraint" to the consumer.
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) SetDefinedLine ¶ added in v1.14.0
func (e *Environment) SetDefinedLine(name string, line int)
SetDefinedLine records the doc-absolute, 0-indexed line where a variable was assigned. Used by the LSP to filter completion items to variables defined ABOVE the cursor — calcmark is strictly ordered.
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 ¶
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) SetLineOffset ¶ added in v1.14.0
func (interp *Interpreter) SetLineOffset(offset int)
SetLineOffset sets the doc-absolute, 0-indexed line offset used to translate block-relative AST line numbers into doc-absolute lines when recording variable assignment positions.
For a parser run over a single block's source starting at doc line 5 (0-indexed), pass 5. The interpreter then records `flour` defined on AST line 2 as `definedLines[flour] = 5 + (2-1) = 6` (0-indexed, doc-absolute), which the LSP can compare directly to its 0-indexed cursor line.
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.
Source Files
¶
- availability_functions.go
- capacity_functions.go
- compression_functions.go
- datetime.go
- doc.go
- environment.go
- errors.go
- evaluate.go
- fraction_ops.go
- functions.go
- growth_functions.go
- helpers.go
- interpreter.go
- literals.go
- napkin.go
- napkin_eval.go
- network_functions.go
- operators.go
- percentage_of_eval.go
- precise_eval.go
- rate_eval.go
- rate_functions.go
- registry.go
- speed_bridge.go
- storage_functions.go
- unit_conversion.go
- unit_conversion_eval.go
- variables.go