functions

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package functions is 012's function library: the FuncDef table, the evaluation of formulas (operators and calls), the argument, range and criteria helpers functions share, decimal arithmetic, and the questions JEV functions ask.

It knows nothing of sheets or storage. A formula reads cells through a Reader over a Book, which the engine (internal/sheet) implements, and the engine finds functions through the table (LookupFunc) when it parses formulas. Dependencies point one way: formula and value below, the engine above.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Pending is shown while an answer is on its way.
	Pending = Value{Kind: value.Error, Str: "Loading…"}
	// ErrNoRemote means JEV functions can't run: there is no API key.
	ErrNoRemote = Value{Kind: value.Error, Str: "#N/A"}
)
View Source
var ErrRemote = Value{Kind: value.Error, Str: "#ERROR!"}

ErrRemote is a question the model couldn't answer, e.g. a network failure; the context line says why.

Functions

func EvalParts added in v0.3.0

func EvalParts(n Node, paths [][]int, get *Reader, at Addr) (Part, []Part)

EvalParts evaluates n in the cell at, as EvalCell does, and returns what it computed, with the array it spills, and what each part at paths computed. A path is child indexes from the root, in formula.EachChild's order; the empty path is the whole formula. A path to a reference, name or literal records nothing: those are read where they stand.

func IsPending

func IsPending(v Value) bool

IsPending reports whether v is waiting for a remote answer.

func IsVolatile

func IsVolatile(n Node) bool

IsVolatile reports whether n calls a function whose result changes without its inputs changing (TODAY, NOW, RAND).

Types

type Addr

type Addr = formula.Addr

Addr identifies a cell by zero-based column and row.

func Intersect

func Intersect(r Rect, at Addr) (Addr, bool)

Intersect is the cell of r a formula in the cell at reads where it wants one value: a single cell is itself; a single column gives the cell in at's row, a single row the cell in at's column, when that lies within r (implicit intersection, on whatever sheet r is on). Any other range has no such cell, which is #VALUE!.

type Agg

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

Agg is the running aggregate of SUM-like functions (SUM, AVERAGE, COUNT, COUNTA, MIN, MAX, PRODUCT): what they read of a range, added in order. The engine keeps one per range shared by a recalculation (Book.RangeAgg).

func NewAgg

func NewAgg() Agg

NewAgg is the aggregate of nothing.

func (*Agg) Add

func (s *Agg) Add(v Value, direct bool) *Value

Add counts v into the aggregate, with SUM's rules: blanks are skipped, errors returned (counted, when counting), and text counts only when given directly.

type Array added in v0.2.0

type Array struct {
	Rows, Cols   int
	DRows, DCols int
	V            []Value
	Fill         Value
}

Array is the value of a formula that computes several: Rows by Cols entries. Only the top-left DRows by DCols hold values of their own, in V row by row; every other entry is Fill. A whole column read as an array costs what it holds: A:A is 1,048,576 rows, of which the rows holding data are stored, the rest blank.

func NewArray added in v0.2.0

func NewArray(rows, cols int) *Array

NewArray is a dense array of rows x cols values, all blank.

func (*Array) At added in v0.2.0

func (a *Array) At(r, c int) Value

At is the entry at row r and column c, counting from 0.

func (*Array) Set added in v0.2.0

func (a *Array) Set(r, c int, v Value)

Set stores v at row r and column c of a dense array.

type Book

type Book interface {
	// Cell is the current value of a cell, evaluated first if it's
	// dirty; #REF! on a sheet that doesn't exist.
	Cell(sheet string, a Addr) Value
	// Scan reads the cells of r that hold something, row by row, from
	// the cell from on (those before it in that order are skipped): their
	// addresses into addrs and, unless vals is nil, their values into
	// vals, evaluating them as Cell does. It stops when addrs is full or
	// just after a value that is an error, so no cell past the first
	// error is evaluated, and returns how many it read: -1 when the sheet
	// doesn't exist.
	Scan(sheet string, r Rect, from Addr, addrs []Addr, vals []Value) int
	// Bounds is the smallest range holding every stored cell of r, with
	// false if r holds none; exists is false when the sheet doesn't.
	Bounds(sheet string, r Rect) (b Rect, any, exists bool)
	// RangeAgg is the aggregate of r that SUM-like functions share within
	// a recalculation (see Agg), with the first error in it, or false
	// when r should be read directly.
	RangeAgg(sheet string, r Rect) (Agg, *Value, bool)
	// Fold adds the cells of r that hold something to s, row by row, as
	// SUM-like functions read a range (Agg.Add), evaluating them as Cell
	// does and stopping at the first error, which it returns. On a sheet
	// that doesn't exist the range is one #REF!.
	Fold(sheet string, r Rect, s Agg) (Agg, *Value)
	// Ask looks up the answer to a remote question (JEV functions). The
	// value is an error when there is none to give: Pending while it is
	// on its way, ErrNoRemote when nothing answers them.
	Ask(call RemoteCall) (RemoteAnswer, Value)
}

Book is how formulas reach the workbook: the engine implements it, once for each sheet whose formulas it evaluates. Sheets are named as references write them; "" is the formula's own sheet.

Every method takes and returns plain values, never a callback: a function value or pointer passed through an interface escapes to the heap, so a callback per range read would cost an allocation that an engine reading its own cells doesn't pay. Ranges are read instead in chunks, into buffers the Reader keeps and reuses (Scan), and each function walks them with callbacks of its own that stay on the stack.

type Format

type Format = value.Format

Format is a cell's number format.

func InferFormat

func InferFormat(n Node, at func(string, Addr) Format) Format

InferFormat picks the format Sheets shows a formula's result in when the cell is Automatic: dates from date functions, and otherwise the format of the first formatted input, so =B2+B3 of currency shows currency and a date plus days shows a date.

type FuncDef

type FuncDef struct {
	Name string
	Args string // signature shown to users, e.g. "value1, [value2, ...]"
	Desc string
	Min  int
	Max  int // -1 for variadic

	// Volatile functions (TODAY, NOW, RAND) change without their inputs
	// changing, so every recalculation recomputes them.
	Volatile bool
	// contains filtered or unexported fields
}

FuncDef describes a spreadsheet function. The table drives parsing (arity checks), evaluation, autocomplete and help.

func Funcs

func Funcs() []*FuncDef

Funcs returns every function, sorted by name.

func LookupFunc

func LookupFunc(name string) (*FuncDef, bool)

LookupFunc finds a function by name or alias, case-insensitively (callers pass upper case).

func Of

func Of(c formula.Call) *FuncDef

Of is the function a parsed call calls: always one of the table's, as long as the parser found functions through LookupFunc.

func (*FuncDef) Question

func (f *FuncDef) Question(args []Node, get *Reader) (RemoteCall, error)

Question is the remote question a call of f asks with its current arguments, or an error when they don't make one.

func (*FuncDef) Remote

func (f *FuncDef) Remote() bool

Remote reports whether f asks a remote question (JEV functions).

func (*FuncDef) Signature

func (f *FuncDef) Signature() formula.Signature

Signature is how the parser checks calls to f.

type Node

type Node = formula.Node

Node is a parsed formula expression.

func Decimalize

func Decimalize(n Node) Node

Decimalize returns a copy of n that evaluates in decimal: operators are marked, and functions with a decimal twin call it.

func ReplaceAt added in v0.3.0

func ReplaceAt(n Node, path []int, f func(Node) Node) Node

ReplaceAt returns a copy of n with the node at path, child indexes in formula.EachChild's order, replaced by what f makes of it. The nodes on the way are copied; n itself is left as it is.

type Part added in v0.3.0

type Part struct {
	Value Value
	// Array holds the first rows and columns of an array the part
	// computed, with Rows and Cols its whole size; nil otherwise.
	Array *Array
	// Lambda is set when the part computed a LAMBDA.
	Lambda bool
	// Reached is false for a part the formula never computed.
	Reached bool
}

Part is what one part of a formula computed the first time it was computed.

type Reader

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

Reader is what a function is given to read the cells its arguments refer to: a Book, and the buffers ranges are read through. It is a concrete type so the callbacks functions pass to it stay on the stack.

func NewReader

func NewReader(book Book, depth *int, dense bool) *Reader

NewReader reads cells through book. depth counts how deeply evaluation nests (cells, operators and calls), for the engine to put off cells past its limit. With dense, functions read every address of their ranges, as they did before ranges were clipped to what they hold, so tests can check that the two agree.

func (*Reader) Forget added in v0.2.0

func (rd *Reader) Forget()

Forget drops the ranges read as arrays. The engine calls it as each recalculation starts and ends: within one, a range's values don't change once read, since reading it evaluates its dirty cells first (a range holding a cell being evaluated is a cycle, an error either way), and spilled cells are written between passes.

func (*Reader) Reset added in v0.2.0

func (rd *Reader) Reset()

Reset forgets the evaluations in progress, which the engine abandoned part way (its evaluate.go), so the next starts afresh.

func (*Reader) Spilled added in v0.2.0

func (rd *Reader) Spilled() *Array

Spilled returns the array the formula EvalCell computed last spills, or nil when it computed one value.

type Rect

type Rect = formula.Rect

Rect is an inclusive rectangular range of cells.

type RemoteAnswer

type RemoteAnswer struct {
	Noul       float64 // probability of yes
	Choice     string
	Score      float64
	Confidence float64 // in Choice or Score, not a probability of truth
	Failed     string
}

RemoteAnswer is the model's answer. Which fields are set depends on the call's Kind; Failed explains an answer that couldn't be had.

type RemoteCall

type RemoteCall struct {
	Kind         string `json:"kind"` // "noul", "choice" or "score"
	State        any    `json:"state"`
	Instructions string `json:"instructions"`
	// Criteria depends on Kind: noul is [2]string{yes, no} (either may be
	// empty), choice is map[label]description, score is []string levels.
	Criteria any `json:"criteria"`
}

RemoteCall is one question for the model, built from a function's arguments. It isn't comparable: Key identifies it by its content.

func (RemoteCall) Key

func (c RemoteCall) Key() string

Key identifies a question by its content: the answer cache and the engine's list of cells waiting for answers are keyed by it. JEV cells are volatile, so every recalculation looks each one up: with thousands of them, encoding the key with encoding/json took most of the time an answer spent in recalc. The shapes a RemoteCall holds are encoded by hand, each value tagged and each string length-prefixed so different calls never share a key; anything else falls back to JSON.

type RemoteSource

type RemoteSource interface {
	Lookup(RemoteCall) (RemoteAnswer, bool)
}

RemoteSource answers RemoteCalls. Lookup returns false while an answer isn't known, and queues the call.

type Value

type Value = value.Value

Value is the computed contents of a cell.

func Eval

func Eval(n Node, get *Reader) Value

Eval computes a formula where one value is wanted, reading the cells it refers to through get. A range where one value is wanted reads as the cell in the row or column last given to EvalAt (see intersect), and an array as its first entry.

func EvalAt

func EvalAt(n Node, get *Reader, at Addr) Value

EvalAt computes the formula in the cell at as one value, so that a range used where one value is wanted reads as the cell in at's row or column (implicit intersection, as Sheets and Excel do). Evaluations nest, one cell's formula reading another's through the same Reader, so the cell before is given back afterwards.

func EvalCell added in v0.2.0

func EvalCell(n Node, get *Reader, at Addr) Value

EvalCell computes the formula in the cell at, as EvalAt, keeping the array it computes when that is several values for Spilled: the cell shows the first, and the engine spills the array from it. A 1x1 array is one value. A range that is the whole formula is an array, as Sheets spills =B2:B9.

Jump to

Keyboard shortcuts

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