Name
go-tsvsheet — go-tsvsheet — the tsvsheet single-file-spreadsheet engine as an importable Go library.
The tsvsheet engine as an importable Go library. A .tsvt file is a spreadsheet — a single TAB-separated grid whose cells are literal values or =formulas that address other cells in A1 notation (B2, D2:D5), computed in place. go-tsvsheet parses that grid, evaluates every formula in dependency order, and hands you the computed values — with no filesystem or network access unless you inject it.
The library is the one engine behind the tsvsheet CLI, its browser editor, and its TUI. Import it to compute .tsvt sheets from your own Go programs, services, or tools.
Install
go get github.com/tsvsheet/go-tsvsheetAPI reference: pkg.go.dev/github.com/tsvsheet/go-tsvsheet.
Quickstart
package main
import (
"fmt"
tsvsheet "github.com/tsvsheet/go-tsvsheet"
)
func main() {
src := []byte("1\t2\n=A1+B1\t=A1*B1\n")
sheet, err := tsvsheet.Parse(src)
if err != nil {
panic(err) // a formula syntax error, matchable with errors.Is(err, tsvsheet.ErrSyntax)
}
grid := sheet.Compute() // grid is a [][]string of computed cell text
fmt.Println(grid[1][0], grid[1][1]) // 3 2
}Parse compiles each =formula cell (literals pass through untouched); Compute evaluates them in dependency order and returns the value grid. A bad reference yields #REF!, a cycle yields #CIRC!, division by zero yields #DIV/0! — the error propagates as the cell’s value, exactly like a conventional spreadsheet.
The model
A .tsvt is the whole spreadsheet: one grid, each cell a literal or an =formula. Formulas address other cells in A1 notation — a single cell (B2), a range (D2:D5), or, when you inject a loader, a cross-sheet reference ("prices"!A1). The expression sublanguage is Excel-faithful: ^ (power), & (concatenation), postfix %, TRUE/FALSE, error-value literals, and a broad function library. Because the file is plain TSV, a sheet versions as text and diffs line by line. The language itself is specified in the tsvsheet repo.
Pipes — shell-style formula composition
Formulas can compose like a pipeline: expr | fn(arg, …) is sugar for the call with the piped expression as its first argument — =A1 | round(2) is exactly =round(A1, 2), and =A2:A10 | sort() | unique() | count() folds left into the composed calls. The engine desugars the pipe at parse time (SPECIFICATION §5.4), so evaluation, Check, Explain, and the dependency graph see only ordinary function application — there is no second execution path — while rendering (Explain traces, structural edits) preserves the author’s pipe spelling.
Core API
Parse(src []byte) (Sheet, error)— compile a.tsvtgrid into an immutableSheet. A malformed formula isErrSyntax, naming the offending cell.Sheet.Compute() Grid/ComputeAt(t time.Time) Grid/ComputeAtTick(t time.Time, tick Tick) Grid— evaluate every formula and return theGrid([][]string).ComputeAtinjects the clock (and seeds the per-pass PRNG from it), so the clock reads (TODAY,NOW,ISNOW) and generators (RAND,RANDBETWEEN,RANDARRAY) are deterministic and testable;ComputeAtTickalso injects the recompute-pass ordinal read byTICK/FRAME.Sheet.IsVolatile() bool/VolatileSchedules() []string— whether any cell is wrapped involatile(…), and each such cell’s refresh-cadence spec (an isnow pattern, a duration, or empty for the default) — a frontend unions these into its auto-refresh loop. Nothing is volatile withoutvolatile().Sheet.ComputeWith(ComputeOptions) Grid— compute with injected collaborators (below).Check(s Sheet) []Diagnostic— static diagnostics (unknown functions, provable arity errors, non-A1 references) without computing.Sheet.Explain(...) Trace— trace how a cell’s value was produced: its formula, the inputs it read, and the result.ReadTSV(io.Reader) (Grid, error)/WriteTSV(io.Writer, Grid) error— the tab-separated wire format.- Structural edits —
Set,InsertRow/DeleteRow/InsertCol/DeleteColreturn a newSheetwith every A1 reference rewritten to follow the move.Precedents/Dependentsexpose the dependency graph.
Sheet is immutable and every method is a value receiver: computing, editing, or inspecting a sheet never mutates it, so sheets are safe to copy, alias, and share across goroutines.
The document layer — comments and shebang round-trip
Parse reads the grid; ParseDocument(src []byte) (Document, error) reads the whole file, additionally recording its physical line layout. A Document is a Sheet plus that layout, so full-line comments (# …) and a #! shebang — which the grid drops — survive editing and are written back in position:
Document.Sheet() Sheet— the parsed sheet the document wraps.Document.Text() []byte— serialize the document canonically: lines in layout order, comments verbatim, grid rows tab-joined, every line newline-terminated. For input already in canonical form,ParseDocumentfollowed byTextis byte-identity.Document.SetCell,InsertRow/DeleteRow/InsertCol/DeleteCol— the structural edits at the document level; each returns a newDocumentwith the layout kept in step.
Text is the one sanctioned way to serialize a sheet back to disk — writing a grid out by hand loses every comment line and the shebang. Like Sheet, Document is immutable: every editing operation returns a new Document.
Standalone expressions — the engine without a sheet
A formula’s expression can be compiled and evaluated on its own, detached from any sheet:
CompileExpr(src []byte) (Expr, error)— compile one bare expression, the text that would follow=in a formula cell. A malformed expression isErrSyntaxcarrying line/column detail.Expr.Eval(g Grid, opts ComputeOptions) Value— evaluate against a grid with the exact semantics of a formula cell in a sheet over that grid: reference resolution, literal coercion, ranges, dynamic arrays, error-value propagation, clock and generator reads seeded fromopts.At,Limitsenforcement, andLoader/Fetchergating. It returns error values, never Go errors.FormatValue(v Value) string— the canonical computed-cell text for a value, byte-identical to whatWriteTSVemits for it in a computed grid; a 2-D array value reduces to its scalar-context (top-left) value before formatting.
A compiled Expr is an immutable value: compile once, then evaluate it against any number of grids, including concurrently. This is the surface the tq query language’s engine (go-tq) evaluates every embedded expression through.
Injected collaborators — the engine stays pure
The engine touches neither the filesystem nor the network on its own. Cross-file features work only through collaborators you inject via ComputeOptions, which keeps go-tsvsheet deterministic and trivially testable:
Loader— resolvesSHEET("file")/"file"!A1cross-sheet references. Without a loader, those cells resolve to#REF!. You control the base directory and containment.Fetcher— resolves content-typedIMPORT*cells (fetching values from an endpoint that speaks the tsvsheet media type). Anilfetcher disables imports (everyIMPORT*is#IMPORT!). The concrete HTTP fetcher, allowlist, and caching live in the host program, not the engine.Limits— bounds every allocation (array-result cells, string bytes, grid dimensions) so an untrusted sheet cannot exhaust memory.DefaultLimits()is generous for a workstation;BrowserLimits()is sized for a WASM tab.
grid := sheet.ComputeWith(tsvsheet.ComputeOptions{
At: time.Now(),
Loader: myLoader, // enable SHEET(...) / "file"!A1
Fetcher: myFetcher, // enable IMPORT*(...)
Limits: tsvsheet.DefaultLimits(),
})Error values and sentinels
Two distinct kinds of error:
- Spreadsheet error values — a cell that fails to evaluate carries an
ErrorValue:#REF!,#VALUE!,#NAME?,#DIV/0!,#CIRC!,#N/A,#NUM!,#NULL!,#SPILL!,#IMPORT!. These propagate through formulas as data; they are not Go errors. - Go error sentinels — functions return
errs.Constsentinels matchable witherrors.Is:ErrSyntax(a malformed formula),ErrInvalidValue,ErrReadInput,ErrWriteFile,ErrNotFound.
Design
The public surface is one package — github.com/tsvsheet/go-tsvsheet — a thin, documented facade. The evaluator, the A1 resolver, the function library, and the generated parser are all internal; no parser or grammar type escapes into the public API. Every allocation is bounded, every collaborator is injected, and the engine is filesystem- and network-free by construction.
Related
- tsvsheet — the language and grammar specification.
- tsvsheet.go — the CLI, browser editor, and TUI built on this engine.