Documentation
¶
Overview ¶
Package types defines the CalcMark type system.
CalcMark supports several value types for calculations and data representation. All types implement the Type interface which provides string representation.
Core Types ¶
- Number: Arbitrary-precision decimal numbers
- Currency: Monetary values with currency symbol/code
- Quantity: Physical quantities with units (e.g., "5 kg", "10 meters")
- Date: Calendar dates with date arithmetic
- Time: Time of day with timezone support
- Duration: Time durations (e.g., "5 days", "3 hours")
- Boolean: True/false values
Number Type ¶
Numbers use arbitrary-precision decimals via shopspring/decimal:
num := types.NewNumber(decimal.NewFromFloat(3.14159)) fmt.Println(num.String()) // "3.14159"
Supports thousands separators:
1,000 1_000_000
Currency Type ¶
Currency values combine a decimal amount with a symbol or ISO code:
usd := types.NewCurrency(decimal.NewFromInt(100), "$") eur := types.NewCurrencyFromCode(decimal.NewFromInt(50), "EUR")
Supported symbols: $, €, £, ¥ Supports ISO 4217 currency codes (e.g., USD, EUR, GBP, JPY)
Quantity Type ¶
Physical quantities with units:
mass := types.NewQuantity(decimal.NewFromInt(5), "kg") length := types.NewQuantity(decimal.NewFromInt(10), "meters")
Supports SI and US customary units:
- Length: meters, feet, kilometers, miles, etc.
- Mass: kilograms, pounds, grams, etc.
- Volume: liters, gallons, cups, etc.
Date Type ¶
Calendar dates with arithmetic operations:
today := types.NewDateFromTime(time.Now()) nextWeek := today.AddDays(7) daysBetween := today.DaysBetween(nextWeek) // 7
Time Type ¶
Time of day with timezone support:
morning := types.NewTime(9, 30, 0, false, 0) // 9:30 AM UTC evening := types.NewTime(6, 0, 0, true, -300) // 6:00 PM EST
Duration Type ¶
Time durations for date arithmetic:
week := types.NewDuration(7, types.DurationUnitDay) hour := types.NewDuration(1, types.DurationUnitHour)
Boolean Type ¶
Simple true/false values:
t := types.NewBoolean(true) f := types.NewBoolean(false)
Type Conversions ¶
The type system maintains type safety while allowing sensible conversions:
- Currency + Number → Currency (preserves currency)
- Currency + Currency (different) → Number (drops units)
- Quantity + Quantity (compatible) → Quantity (first unit wins)
Performance ¶
All type operations are designed for speed and use efficient decimal arithmetic. String formatting is optimized for display/debugging.
Index ¶
- Constants
- Variables
- func GetCurrencySymbol(code string) string
- func GetExamplesForType(argType ArgType) []string
- func IsCurrencyCode(s string) bool
- func IsTimeUnit(unit string) bool
- func IsValidDurationUnit(unit string) bool
- func NormalizeCurrencyCode(s string) string
- func NormalizeTimeUnit(unit string) string
- func PeriodToPeriodsPerYear(period string) (int, bool)
- func TimeUnitToSeconds(unit string) (decimal.Decimal, error)
- func ToDecimal(t Type) (decimal.Decimal, error)
- type ArgType
- type Boolean
- type Currency
- type Date
- type Duration
- type Fraction
- type FunctionSpec
- type Number
- type ParamSpec
- type Percentage
- type Quantity
- type Rate
- type Time
- type Type
- type TypeError
Constants ¶
const MaxDisplayDenominator = 1000
MaxDisplayDenominator is the threshold above which fractions fall back to decimal display.
Variables ¶
var ArgTypeExamples = map[ArgType][]string{ ArgTypeNumber: {"42", "3.14", "1000", "1e6"}, ArgTypeQuantity: {"10 meters", "5 kg", "100 MB", "2.5 liters"}, ArgTypeRate: {"10 MB/s", "60 km/h", "100 requests/second", "5 GB/day"}, ArgTypeDuration: {"2 hours", "30 minutes", "1 day", "500 ms"}, ArgTypePercentage: {"99.9%", "15%", "50%", "0.1%"}, ArgTypeString: {"ssd", "gzip", "gigabit"}, ArgTypeAny: {"<any value>"}, }
ArgTypeExamples provides example values for each argument type.
var CodeToSymbol = map[string]string{
"USD": "$",
"EUR": "€",
"GBP": "£",
"JPY": "¥",
}
CodeToSymbol maps ISO 4217 codes to display symbols. This is the reverse of SymbolToCode.
var DurationToSeconds = map[string]int64{
"second": 1,
"seconds": 1,
"minute": 60,
"minutes": 60,
"hour": 3600,
"hours": 3600,
"day": 86400,
"days": 86400,
"week": 604800,
"weeks": 604800,
"month": 2592000,
"months": 2592000,
"year": 31536000,
"years": 31536000,
}
DurationToSeconds provides conversion factors to seconds (approximate for months/years). Uses int64 for whole-second units. Milliseconds are handled separately in conversion functions.
var FunctionSpecs = map[string]FunctionSpec{ "avg": { Name: "avg", Params: []ParamSpec{ {Name: "values", Type: ArgTypeAny, Variadic: true, Examples: []string{"1, 2, 3", "10, 20", "$100, $200"}}, }, }, "sum": { Name: "sum", Params: []ParamSpec{ {Name: "values", Type: ArgTypeAny, Variadic: true, Examples: []string{"1, 2, 3", "$100, $200", "1 kg, 500 g"}}, }, }, "sqrt": { Name: "sqrt", Params: []ParamSpec{ {Name: "value", Type: ArgTypeNumber, Examples: []string{"16", "2", "100"}}, }, }, "number": { Name: "number", Params: []ParamSpec{ {Name: "value", Type: ArgTypeAny, Examples: []string{"10 kg", "$100", "25%", "42"}}, }, }, "accumulate": { Name: "accumulate", Params: []ParamSpec{ {Name: "rate", Type: ArgTypeRate, Examples: []string{"10 MB/s", "100 requests/second", "5 GB/day"}}, {Name: "duration", Type: ArgTypeDuration, Examples: []string{"1 hour", "30 minutes", "1 day"}}, }, }, "convert_rate": { Name: "convert_rate", Params: []ParamSpec{ {Name: "rate", Type: ArgTypeRate, Examples: []string{"10 MB/s", "1 GB/hour", "100 req/s"}}, {Name: "time_unit", Type: ArgTypeString, Examples: identifiers.TimeUnits, EnumValues: identifiers.TimeUnits}, }, }, "downtime": { Name: "downtime", Params: []ParamSpec{ {Name: "availability", Type: ArgTypePercentage, Examples: []string{"99.9%", "99.99%", "99%"}}, {Name: "duration", Type: ArgTypeDuration, Examples: []string{"1 year", "1 month", "1 day"}}, }, }, "rtt": { Name: "rtt", Params: []ParamSpec{ {Name: "scope", Type: ArgTypeString, Examples: identifiers.NetworkScopes, EnumValues: identifiers.NetworkScopes}, }, }, "throughput": { Name: "throughput", Params: []ParamSpec{ {Name: "network_type", Type: ArgTypeString, Examples: identifiers.NetworkTypes, EnumValues: identifiers.NetworkTypes}, }, }, "transfer_time": { Name: "transfer_time", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 GB", "100 MB", "10 TB"}}, {Name: "scope", Type: ArgTypeString, Examples: identifiers.NetworkScopes, EnumValues: identifiers.NetworkScopes}, {Name: "network_type", Type: ArgTypeString, Examples: identifiers.NetworkTypes, EnumValues: identifiers.NetworkTypes}, }, }, "read": { Name: "read", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 MB", "4 KB", "1 GB"}}, {Name: "storage_type", Type: ArgTypeString, Examples: identifiers.StorageTypes, EnumValues: identifiers.StorageTypes}, }, }, "seek": { Name: "seek", Params: []ParamSpec{ {Name: "storage_type", Type: ArgTypeString, Examples: identifiers.StorageTypes, EnumValues: identifiers.StorageTypes}, }, }, "compress": { Name: "compress", Params: []ParamSpec{ {Name: "size", Type: ArgTypeQuantity, Examples: []string{"1 MB", "100 KB", "1 GB"}}, {Name: "compression_type", Type: ArgTypeString, Examples: identifiers.CompressionTypes, EnumValues: identifiers.CompressionTypes}, }, }, "compound": { Name: "compound", Params: []ParamSpec{ {Name: "principal", Type: ArgTypeAny, Examples: []string{"1000", "$1000", "100 users"}}, {Name: "rate", Type: ArgTypePercentage, Examples: []string{"5%", "0.05", "12%"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"10", "12", "5"}}, {Name: "period", Type: ArgTypeString, Optional: true, Examples: []string{"monthly", "quarterly", "compounded:monthly"}}, }, }, "grow": { Name: "grow", Params: []ParamSpec{ {Name: "amount", Type: ArgTypeAny, Examples: []string{"100", "50 GB", "1000 users"}}, {Name: "increment", Type: ArgTypeAny, Examples: []string{"20 GB", "100 users", "5"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"12", "6", "24"}}, }, }, "depreciate": { Name: "depreciate", Params: []ParamSpec{ {Name: "value", Type: ArgTypeAny, Examples: []string{"10000", "$50000", "1000 units"}}, {Name: "rate", Type: ArgTypePercentage, Examples: []string{"20%", "0.15", "10%"}}, {Name: "periods", Type: ArgTypeNumber, Examples: []string{"5", "10", "7"}}, {Name: "salvage", Type: ArgTypeAny, Optional: true, Examples: []string{"$5000", "1000"}}, }, }, "capacity": { Name: "capacity", Params: []ParamSpec{ {Name: "demand", Type: ArgTypeRate, Examples: []string{"1000 requests/second", "10 GB/day"}}, {Name: "capacity_per_unit", Type: ArgTypeRate, Examples: []string{"100 requests/second", "1 GB/day"}}, {Name: "unit", Type: ArgTypeString, Examples: []string{"server", "instance", "pod"}}, {Name: "buffer", Type: ArgTypePercentage, Optional: true, Examples: []string{"20%", "50%"}}, }, }, }
FunctionSpecs defines the type signatures for all built-in functions. This is the authoritative source for parameter type information.
var SymbolToCode = map[string]string{
"$": "USD",
"€": "EUR",
"£": "GBP",
"¥": "JPY",
}
SymbolToCode maps currency symbols to their ISO 4217 codes. This allows normalization of common symbols to standard codes.
Functions ¶
func GetCurrencySymbol ¶
GetCurrencySymbol returns the display symbol for a currency code. Returns the code itself if no symbol is defined.
func GetExamplesForType ¶ added in v1.8.13
GetExamplesForType returns example values for a given argument type.
func IsCurrencyCode ¶
IsCurrencyCode checks if a string is a known currency code or symbol.
func IsTimeUnit ¶
IsTimeUnit returns true if the given unit is a valid time unit.
func IsValidDurationUnit ¶
IsValidDurationUnit checks if the unit is a valid duration unit.
func NormalizeCurrencyCode ¶
NormalizeCurrencyCode converts a symbol or code to its ISO code. Returns the original string if not recognized.
func NormalizeTimeUnit ¶
NormalizeTimeUnit converts various time unit formats to canonical form. Handles abbreviations, plurals, and adjectival forms (e.g., "monthly" → "month"). Examples: "s" → "second", "seconds" → "second", "sec" → "second", "daily" → "day"
func PeriodToPeriodsPerYear ¶
PeriodToPeriodsPerYear returns how many of the given period fit in one year. Used by growth functions to convert between annual rates and per-period rates. Returns 0 and false for unrecognized periods.
func TimeUnitToSeconds ¶
TimeUnitToSeconds returns the number of seconds in a time unit. Used for accumulation and conversion calculations. Uses the shared DurationToSeconds map from duration.go
Types ¶
type ArgType ¶ added in v1.8.13
type ArgType string
ArgType represents the semantic type expected for a function argument. This defines what kinds of values are valid for each parameter position.
const ( ArgTypeNumber ArgType = "number" // Plain number: 42, 3.14, 1e6 ArgTypeQuantity ArgType = "quantity" // Number with unit: 10 meters, 5 kg, 100 MB ArgTypeRate ArgType = "rate" // Quantity per time: 10 MB/s, 60 km/h ArgTypeDuration ArgType = "duration" // Time span: 2 hours, 30 minutes ArgTypePercentage ArgType = "percentage" // Percentage: 99.9%, 15% ArgTypeString ArgType = "string" // String literal: "datacenter", "ssd" ArgTypeAny ArgType = "any" // Any value type )
type Boolean ¶
type Boolean struct {
Value bool
}
Boolean represents a boolean value (true/false). CalcMark supports multiple boolean representations: true/false, yes/no, t/f, y/n.
func NewBoolean ¶
NewBoolean creates a new Boolean with the given value.
type Currency ¶
type Currency struct {
Value decimal.Decimal
Symbol string // Display symbol: "$", "€", "£", "¥", or ISO code like "USD"
Code string // Normalized ISO 4217 code: "USD", "EUR", "GBP", "JPY"
IsNapkin bool // true when produced by `as napkin` conversion
}
Currency represents a monetary value with a currency symbol and ISO code. It preserves both the display symbol (e.g., "$") and the normalized ISO code (e.g., "USD").
func NewCurrency ¶
NewCurrency creates a new Currency with the given value and symbol/code. If symbolOrCode is a known symbol ($, €, £, ¥), it's mapped to the ISO code. Otherwise, it's used as both the symbol and code.
func NewCurrencyFromString ¶
NewCurrencyFromString creates a Currency from a string value and symbol/code. Returns an error if the string cannot be parsed as a valid number.
func (*Currency) IsSameCurrency ¶
IsSameCurrency checks if two currencies have the same ISO code. This is used for validating currency arithmetic operations.
func (*Currency) String ¶
String returns the precise string representation with symbol and value. This is the machine-readable form used by the JSON formatter. Unlike FormatCurrency() in format/display, String() does NOT add a space between ISO codes and amounts (e.g., "CNY1000.00" not "CNY 1,000.00").
type Date ¶
Date represents a calendar date, optionally with time-of-day precision. Uses Go's time.Time internally for proper date handling (leap years, etc.). When HasTime is false, the time component is midnight and should not be displayed. When HasTime is true, the time was explicitly set and should be displayed.
func NewDate ¶
NewDate creates a Date from year, month, and day. If year is 0, uses the current year.
func NewDateFromTime ¶
NewDateFromTime creates a date-only Date from a time.Time value. The time component is normalized to midnight UTC and HasTime is false.
func NewDateTime ¶ added in v1.11.0
NewDateTime creates a Date with full time precision from a time.Time value. The time component is preserved and HasTime is true.
func (*Date) DaysBetween ¶
DaysBetween returns the number of days from other to this date (d - other).
func (*Date) ShortString ¶
ShortString returns a shorter date format
type Duration ¶
type Duration struct {
Value decimal.Decimal
Unit string // "days", "hours", "minutes", "seconds", "weeks", "months", "years"
}
Duration represents a time duration with a specific unit. Supports: days, hours, minutes, seconds, weeks, months, years.
func NewDuration ¶
NewDuration creates a new Duration with the given value and unit.
func NewDurationFromString ¶
NewDurationFromString creates a Duration from string value and unit.
type Fraction ¶ added in v1.8.2
type Fraction struct {
Value *big.Rat
IsNapkin bool // true if this value was rounded by "as napkin"
Unit string // empty for dimensionless fractions
}
Fraction represents an exact rational number using Go's math/big.Rat. Fractions are always stored in lowest terms (big.Rat normalizes automatically).
func NewFraction ¶ added in v1.8.2
NewFraction creates a new Fraction from numerator and denominator. Returns an error if denominator is zero. The fraction is automatically reduced to lowest terms by big.Rat.
func NewFractionFromRat ¶ added in v1.8.2
NewFractionFromRat creates a Fraction from an existing big.Rat.
func (*Fraction) Denom ¶ added in v1.8.2
Denom returns the denominator of the fraction (after GCD reduction).
func (*Fraction) ExceedsComputationLimit ¶ added in v1.8.2
ExceedsComputationLimit returns true if the denominator exceeds 10^9 after GCD reduction, or if |numerator| exceeds 10^18 (int64 safety).
func (*Fraction) Num ¶ added in v1.8.2
Num returns the numerator of the fraction (after GCD reduction).
func (*Fraction) String ¶ added in v1.8.2
String returns the display representation of the fraction. Rules:
- denominator == 1 → integer
- denominator > 1000 → decimal fallback
- |numerator| > denominator → mixed number (e.g., "2 1/3")
- else → simple fraction (e.g., "2/3")
- IsNapkin → prefix "~"
- Unit → append " cup" etc.
type FunctionSpec ¶ added in v1.8.13
type FunctionSpec struct {
Name string // Function name
Params []ParamSpec // Parameter specifications
}
FunctionSpec describes a function's type signature for semantic analysis.
func GetFunctionSpec ¶ added in v1.8.13
func GetFunctionSpec(name string) *FunctionSpec
GetFunctionSpec returns the type specification for a function by name. Returns nil if the function is not found.
func (*FunctionSpec) GetParamAtIndex ¶ added in v1.8.13
func (f *FunctionSpec) GetParamAtIndex(index int) *ParamSpec
GetParamAtIndex returns the parameter spec for the given argument position. Handles variadic parameters (last param applies to all subsequent args).
type Number ¶
type Number struct {
Value decimal.Decimal
IsNapkin bool // true when produced by `as napkin` conversion
}
Number represents an arbitrary-precision decimal number. It uses the shopspring/decimal package for accurate decimal arithmetic.
func NewNumberFromString ¶
NewNumberFromString creates a Number from a string representation. Returns an error if the string cannot be parsed as a valid number.
type ParamSpec ¶ added in v1.8.13
type ParamSpec struct {
Name string // Parameter name (e.g., "rate", "duration")
Type ArgType // Expected argument type
Optional bool // True if parameter has a default value
Variadic bool // True if parameter accepts multiple values
Examples []string // Example values showing how to create this type
EnumValues []string // Canonical unquoted enum values when the parameter accepts a finite identifier set (nil for free-form params)
}
ParamSpec describes a function parameter with type constraints and examples.
type Percentage ¶ added in v1.7.0
Percentage represents a percentage value. The Value field stores the fractional form (e.g., 0.32 for 32%). Percentage preserves its identity through variables and applies widening on + and - operators: value + pct = value * (1 + pct).
func NewPercentage ¶ added in v1.7.0
func NewPercentage(value decimal.Decimal) *Percentage
NewPercentage creates a Percentage from a fractional decimal value. For 32%, pass 0.32.
func (*Percentage) String ¶ added in v1.7.0
func (p *Percentage) String() string
String returns the percentage in human-readable form (e.g., "32%").
type Quantity ¶
type Quantity struct {
Value decimal.Decimal
Unit string
IsNapkin bool // True if this is a napkin estimate (approximate value)
IsExplicit bool // True if unit was explicitly chosen via `in`/`as` conversion
IsPrecise bool // True if full precision requested via `as precise`
}
Quantity represents a value with a physical unit. This is a stub for future implementation with full unit support. For now, it simply stores the value and unit string.
func NewQuantity ¶
NewQuantity creates a new Quantity with the given value and unit.
func NewQuantityFromString ¶
NewQuantityFromString creates a Quantity from a string value and unit.
type Rate ¶
type Rate struct {
// Amount is the numerator (quantity per time unit)
// Can be any Quantity type (GB, requests, dollars, etc.)
Amount *Quantity
// PerUnit is the time unit denominator
// Valid units: "second", "minute", "hour", "day", "week", "month", "year"
PerUnit string
}
Rate represents a rate type: quantity per time period. Examples: 100 MB/s, $0.10/hour, 5 GB/day, 60 meters per second
Rate syntax:
- Slash (no spaces): "100 MB/s", "5 GB/day"
- Word "per" (with spaces): "100 MB per second", "5 GB per day"
Rates are first-class types that enable:
- Accumulation: rate * time = quantity
- Conversion: rate in different time units
- Arithmetic: adding/subtracting compatible rates
func (*Rate) CompoundUnit ¶
CompoundUnit returns the compound unit string for this rate (e.g., "MB/s", "USD/h"). The numerator uses the amount's unit and the denominator uses the abbreviated time unit.
func (*Rate) IsCompatible ¶
IsCompatible checks if two rates can be added/subtracted. Rates are compatible if their amounts have compatible units and same time periods.
type Time ¶
Time represents a time of day with optional timezone offset. Uses Go's time.Time internally.
func NewTime ¶
NewTime creates a Time from hour, minute, second (optional), and UTC offset (optional). hour: 0-23 for 24-hour format, or 1-12 for 12-hour with isPM minute: 0-59 second: 0-59 (use -1 if not specified) isPM: true for PM in 12-hour format, false for AM or 24-hour format utcOffsetMinutes: offset from UTC in minutes (0 for UTC/local time)
type Type ¶
type Type interface {
// String returns a human-readable representation of the value.
// This is used for display purposes and debugging.
String() string
}
Type is the interface that all CalcMark value types implement. All types can be converted to a human-readable string representation.