Go Package
Use go-calcmark as a library to evaluate CalcMark expressions and documents in your Go applications.
go-calcmark is a standard Go module. Import it and evaluate CalcMark expressions in a few lines of code.
Full API documentation is on pkg.go.dev.
go get github.com/CalcMark/go-calcmark
Evaluate a Single Expression #
The simplest entry point — pass an expression, get a result:
package main
import (
"fmt"
"log"
calcmark "github.com/CalcMark/go-calcmark"
)
func main() {
result, err := calcmark.Eval("price = 100 USD * 1.08")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Value) // $108.00
}
Eval creates a fresh session for each call. The returned Result contains:
Value— the final computed value (atypes.Type)AllValues— all values when the input has multiple linesDiagnostics— any warnings or hints from semantic analysis
Sessions: Persistent Variables #
Use a Session when you need variables to persist across multiple evaluations — like a REPL or an interactive editor:
session := calcmark.NewSession()
session.Eval("base = $85000")
session.Eval("bonus_pct = 15%")
result, _ := session.Eval("total = base + base * bonus_pct")
fmt.Println(result.Value) // $97,750.00
// Look up any variable by name
val, ok := session.GetVariable("total")
if ok {
fmt.Println(val) // $97,750.00
}
Call session.Reset() to clear all variables and start fresh.
Evaluate a Full Document #
For complete CalcMark documents — with markdown, frontmatter, and multiple calc blocks — use the document-level API:
import (
"github.com/CalcMark/go-calcmark/spec/document"
implDoc "github.com/CalcMark/go-calcmark/impl/document"
)
source := `---
exchange:
USD_EUR: 0.92
---
# Budget
revenue = 500K USD
costs = revenue * 0.6
profit = revenue - costs
profit_eur = profit in EUR
`
doc, err := document.NewDocument(source)
if err != nil {
log.Fatal(err)
}
eval := implDoc.NewEvaluator()
if err := eval.Evaluate(doc); err != nil {
log.Fatal(err)
}
// Access all variables through the environment
env := eval.GetEnvironment()
profit, _ := env.Get("profit")
fmt.Println(profit) // $200,000.00
The document evaluator handles block ordering, dependency resolution, frontmatter directives (exchange rates, globals, scale), and variable interpolation in text blocks.
The Type System #
Every CalcMark value is a types.Type. The concrete types are in github.com/CalcMark/go-calcmark/spec/types:
| Type | Example | Go Type |
|---|---|---|
*Number | 42, 3.14, 15% | types.Number |
*Currency | $100, 85 EUR | types.Currency |
*Quantity | 5 kg, 100 Mbps | types.Quantity |
*Duration | 3 hours, 2 weeks | types.Duration |
*Date | 2026-03-11 | types.Date |
*Rate | 100 MB/s, $50/hour | types.Rate |
*Boolean | true, false | types.Boolean |
All numeric values use shopspring/decimal for arbitrary-precision arithmetic — no floating-point surprises.
Extracting Values #
Use types.ToDecimal to get the numeric value from any numeric type:
import "github.com/CalcMark/go-calcmark/spec/types"
result, _ := calcmark.Eval("weight = 5 kg")
d, err := types.ToDecimal(result.Value)
if err == nil {
fmt.Println(d) // 5
}
For type-specific fields, use a type assertion:
result, _ := calcmark.Eval("price = 100 USD")
if currency, ok := result.Value.(*types.Currency); ok {
fmt.Println(currency.Value) // 100
fmt.Println(currency.Code) // USD
fmt.Println(currency.Symbol) // $
}
The Environment #
The Environment holds all variable bindings and exchange rates. You can pre-populate it before evaluation:
import (
"github.com/CalcMark/go-calcmark/impl/interpreter"
"github.com/CalcMark/go-calcmark/spec/types"
"github.com/shopspring/decimal"
)
env := interpreter.NewEnvironment()
env.Set("tax_rate", types.NewNumber(decimal.NewFromFloat(0.08)))
env.SetExchangeRate("USD", "EUR", decimal.NewFromFloat(0.92))
interp := interpreter.NewInterpreterWithEnv(env)
Key Environment methods:
| Method | Description |
|---|---|
Set(name, value) | Store a variable |
Get(name) | Retrieve a variable (returns value, ok) |
Has(name) | Check if a variable exists |
GetAllVariables() | Get all variables as a map |
SetExchangeRate(from, to, rate) | Set a currency conversion rate |
Clone() | Shallow copy for isolation |
Diagnostics #
Both Eval and the document evaluator return diagnostics — structured messages with severity levels:
result, _ := calcmark.Eval("x = unknown_var + 1")
for _, d := range result.Diagnostics {
fmt.Printf("[%s] %s\n", d.Severity, d.Message)
}
Severity levels: Error, Warning, Hint.
Package Structure #
The codebase separates specification from implementation:
| Package | Purpose |
|---|---|
github.com/CalcMark/go-calcmark | Top-level Eval and Session API |
spec/types | Value types (Number, Currency, Quantity, etc.) |
spec/document | Document model (blocks, parsing) |
spec/units | Canonical unit definitions and conversions |
spec/features | Feature registry (functions, NL syntax) |
impl/interpreter | Expression evaluator and Environment |
impl/document | Document-level evaluator |
Dependencies flow one way: impl depends on spec, never the reverse.
Further Reading #
- pkg.go.dev documentation — full API reference with all exported types and methods
- Agent & API Integration — using CalcMark via stdin/stdout pipes
- Language Reference — formal specification of the CalcMark language
- Source code on GitHub —
spec/for the language spec,impl/for the interpreter