tsvsheet

package module
v0.27.9 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 3 Imported by: 0

README

Documentation

Overview

Package tsvsheet is the engine for the tsvsheet single-file spreadsheet: a .tsvt is 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.

The package parses a grid (Parse, ReadTSV), computes it with an Excel- and Google-Sheets-faithful expression evaluator that carries error values (#REF!, #DIV/0!, #CIRC!, …) through a dependency-ordered, memoized pass (Compute, ComputeWith), and inspects the result (Check diagnostics, Explain traces) before rendering it back to TSV (WriteTSV). A bare expression — the text after a formula cell's `=` — also compiles standalone (CompileExpr) into an immutable Expr that evaluates against any Grid with formula-cell semantics (Expr.Eval) and formats canonically (FormatValue). Formula compilation reuses the grammar repo's ANTLR-generated expression parser through the internal/tsvt seam; no ANTLR type escapes into the public surface.

The engine is filesystem- and network-free by construction: cross-sheet embedding (SHEET/INPUT/OUTPUT) and imports (IMPORT*) resolve only through the Loader and Fetcher a caller injects, and every allocation is bounded by an injected Limits ceiling. Errors returned to callers are the errs.Const sentinels re-exported from errors.go, matchable with errors.Is.

This package is a thin facade: every type, function, and constant it exposes re-exports the implementation in internal/engine unchanged, so the public surface is documented here while the engine stays an internal package.

Package tsvsheet's document facade: a parsed .tsvt with its physical line layout retained, and the view its own directives declare.

The sheet edit language facade (work order 011): parse an edits document, content-address a document, and apply the deterministic fold.

Package tsvsheet's expression facade: one compiled bare expression, detached from any sheet.

Index

Examples

Constants

View Source
const (
	ErrSyntax       = constants.ErrSyntax
	ErrDocTooLarge  = constants.ErrDocTooLarge
	ErrInvalidValue = constants.ErrInvalidValue
	ErrNotFound     = constants.ErrNotFound
	ErrReadInput    = constants.ErrReadInput
	ErrWriteFile    = constants.ErrWriteFile

	ErrEditsAddress = constants.ErrEditsAddress
	ErrEditsApply   = constants.ErrEditsApply
	ErrEditsArity   = constants.ErrEditsArity
	ErrEditsBase    = constants.ErrEditsBase
	ErrEditsBlock   = constants.ErrEditsBlock
	ErrEditsOp      = constants.ErrEditsOp
)

Engine error sentinels returned to callers, matchable with errors.Is.

View Source
const (
	KeyHide   = engine.KeyHide
	KeyHeader = engine.KeyHeader
	KeyFreeze = engine.KeyFreeze
)

The view-directive keys, one per class — a projection, a structure declaration, and a viewport hint.

Variables

This section is empty.

Functions

func FormatValue added in v0.5.0

func FormatValue(v Value) string

FormatValue is the canonical computed-cell text for v — byte-identical to what WriteTSV emits for that value in a computed grid. A 2-D array value reduces to its scalar-context (top-left) value before formatting.

func IsCommentLine added in v0.14.0

func IsCommentLine(at LineNumber, text SourceLine) bool

IsCommentLine reports whether the line at 1-based position at is one the grid skips rather than treating as a row: a first-line `#!` shebang, a `#.` directive-or-comment line, or a legacy `# ` hash-space comment. Everything else is data, including `#N/A` and a hash followed by a TAB.

Frontends that map document lines to grid rows — an LSP, an editor gutter — call this instead of testing the prefixes themselves, so the language has one definition of what a comment is.

func OpenSheet added in v0.27.3

func OpenSheet(src ByteSource, limits Limits) (Sheet, *WindowedSheet, error)

OpenSheet loads a source of any size under limits: one scan builds the census, and the resident cell budget decides the capability — at or under it, a fully resident Sheet identical to Parse's; over it, a WindowedSheet serving bounded viewport reads while the source stays on disk (SPECIFICATION §6 budgets; raise the budget to make any size resident).

func WriteTSV

func WriteTSV(w io.Writer, g Grid) error

WriteTSV writes the grid as tab-separated rows, each terminated by a newline. A write failure surfaces as constants.ErrWriteFile. Callers wanting buffering pass a bufio.Writer; WriteTSV writes each row directly so a write error is reported at its source.

Types

type Address

type Address = engine.Address

Address is a cell coordinate in spreadsheet notation (`F4`): column letters plus a 1-based row. It carries 0-based indices internally.

func ParseAddress

func ParseAddress(s AddressText) (Address, error)

ParseAddress parses spreadsheet notation (`A1`, `F4`, `AA10`) into an Address. The column is one or more ASCII uppercase letters, the row a positive integer; anything else is constants.ErrInvalidValue.

type AddressText

type AddressText = engine.AddressText

AddressText is spreadsheet-address source text (`A1`, `F4`) accepted by ParseAddress. It is exported so callers in other packages can convert their string input at the call site.

type BlockText added in v0.24.0

type BlockText = engine.BlockText

BlockText is clipboard-block source text: TAB-separated cells on newline-separated rows, as a copied range travels through a clipboard.

type ByteSource added in v0.27.3

type ByteSource = engine.ByteSource

ByteSource is an any-size byte source — a file, a spooled stream, or in-memory bytes — with its length.

type CellInfo

type CellInfo = engine.CellInfo

CellInfo describes one non-empty cell: its address, source text, and whether it is a formula — the projection the parse command emits.

type ComputeOptions

type ComputeOptions = engine.ComputeOptions

ComputeOptions configures a compute pass. Loader and Base enable embedded sub-sheets; a zero Loader disables SHEET (it resolves to #REF!). Tick is the recompute-pass ordinal a refreshing frontend increments so TICK()/FRAME() advance across passes.

type Diagnostic

type Diagnostic = engine.Diagnostic

Diagnostic is an advisory finding about a sheet: an unknown function call in a formula cell (which computes to #NAME?), or a view directive the parser rejects. A cell finding carries Cell; a directive finding carries Line, since a directive occupies a physical line and no grid row.

func Check

func Check(s Sheet) []Diagnostic

Check reports the static diagnostics of a parsed sheet: each unknown function call. Syntax errors are already rejected by Parse, and every reference the narrowed grammar admits is a valid A1 form, so Check never reports those.

Example

Check reports static diagnostics — unknown functions, provable arity errors, non-A1 references — without computing.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

func main() {
	sheet, _ := tsvsheet.Parse([]byte("=BOGUS(1)\n"))
	for _, d := range tsvsheet.Check(sheet) {
		fmt.Printf("%s: %s\n", d.Cell, d.Message)
	}
}
Output:
A1: unknown function: BOGUS

type Directive added in v0.20.0

type Directive = engine.Directive

Directive is one key/value pair read from a `#.` line, with the physical line it came from.

type Document added in v0.6.0

type Document = engine.Document

Document is a parsed .tsvt file with its physical line layout retained, so comment and shebang lines — which the grid drops — survive editing and are written back in position by Text. Document is immutable: every editing operation returns a new Document. It is the one sanctioned way to serialize a .tsvt; frontends must never rebuild a file from a grid.

func Apply added in v0.25.0

func Apply(d Document, e Edits, limits Limits) (Document, error)

Apply left-folds e's ops over d: atomic (a refused op rejects the whole batch), base-checked (ErrEditsBase when e names a revision that is not d's), and deterministic — nothing outside (d, e, limits) influences the result.

func ParseDocument added in v0.6.0

func ParseDocument(src []byte) (Document, error)

ParseDocument reads a .tsvt file like Parse, additionally recording the physical line layout so comment and shebang lines are preserved by Text.

func ParseDocumentWith added in v0.27.4

func ParseDocumentWith(src []byte, limits Limits) (Document, error)

ParseDocumentWith is ParseDocument under the same residency budget as ParseWith (spec 018): an over-resident document refuses with ErrDocTooLarge before the layout or any cell materializes.

type Edits added in v0.25.0

type Edits = engine.Edits

Edits is a parsed edits document (application/vnd.tsvsheet.edits+tsv): a TSV stream of semantic operations. Immutable; Text returns the exact bytes parsed.

func ParseEdits added in v0.25.0

func ParseEdits(src []byte) (Edits, error)

ParseEdits reads an edits document; any malformed line is a specific ErrEdits* sentinel carrying its 1-based line number.

func ParseEditsWith added in v0.27.5

func ParseEditsWith(src []byte, limits Limits) (Edits, error)

ParseEditsWith is ParseEdits under a residency budget (spec 018): a batch whose line count (each line is at most one op) exceeds the effective resident ceiling refuses with ErrDocTooLarge before any op parses.

type ErrorValue

type ErrorValue = engine.ErrorValue

ErrorValue is a spreadsheet error value — a cell value, not a Go error. It propagates through expressions per ADR 0003 (rules 3, 8, 12, 14).

The error values. #REF! (out-of-grid), #VALUE! (type), #NAME? (unknown function), #DIV/0! (division by zero), #CIRC! (a formula whose evaluation depends on itself), #N/A (lookup miss / NA()), #NUM! (numeric domain), #NULL! (empty range intersection), #SPILL! (blocked dynamic-array spill), #IMPORT! (a content-typed import failed — disabled, denied, or a bad handshake), #LIMIT! (a reference or result larger than the configured cell budget).

type Expr added in v0.5.0

type Expr = engine.Expr

Expr is one compiled bare expression — the text that would follow `=` in a formula cell — detached from any sheet: compile once with CompileExpr, then evaluate against any number of grids, including concurrently.

func CompileExpr added in v0.5.0

func CompileExpr(src []byte) (Expr, error)

CompileExpr parses and compiles one bare expression — the text that would follow `=` in a formula cell. A malformed expression is ErrSyntax carrying line/column detail via With. The compiled Expr is an immutable value, safe for concurrent reuse; its Eval(g, opts) evaluates 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, volatile functions from opts.At, Limits enforcement, and Loader/Fetcher gating — returning error values, never Go errors.

type Extent added in v0.20.0

type Extent = engine.Extent

Extent is a grid's size in rows and columns; edge-anchored directive items resolve against it.

type FetchResult

type FetchResult = engine.FetchResult

FetchResult is a Fetcher's response: the raw body and the media type the server declared, which must match the requested Accept for the handshake to succeed (ADR 0006 §2).

type Fetcher

type Fetcher = engine.Fetcher

Fetcher retrieves the content-typed import at url, sending accept as the requested media type. The engine holds only this interface; the concrete net/http fetcher, allowlist, and caching are injected by a frontend. A nil Fetcher disables imports (every IMPORT* is #IMPORT!).

Example

The engine is network-free: IMPORT* cells resolve only through a Fetcher injected via ComputeOptions. With none, they are #IMPORT!; with one, they resolve to the fetched value.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

// stubFetcher is a trivial Fetcher for the example below: it answers every
// request with the value 42, echoing the requested media type so the handshake
// succeeds.
type stubFetcher struct{}

func (stubFetcher) Fetch(_ tsvsheet.ImportURL, accept tsvsheet.MediaType) (tsvsheet.FetchResult, error) {
	return tsvsheet.FetchResult{ContentType: accept, Body: []byte("42")}, nil
}

func main() {
	sheet, _ := tsvsheet.Parse([]byte(`=IMPORTCELL("https://example/v")` + "\n"))
	fmt.Println(sheet.Compute()[0][0])
	fmt.Println(sheet.ComputeWith(tsvsheet.ComputeOptions{Fetcher: stubFetcher{}})[0][0])
}
Output:
#IMPORT!
42

type Grid

type Grid = engine.Grid

Grid is a rectangular value grid indexed [row][col], 0-based. Cells are raw strings: a literal's own text on input, or a formula cell's computed value after ComputeAt.

func ParseBlock added in v0.24.0

func ParseBlock(text BlockText) Grid

ParseBlock reads a clipboard block: CRLF and lone CR normalize to LF, exactly one trailing newline is ignored, rows split on LF and cells on TAB. Every line is data — a clipboard block has no comment or directive semantics, unlike a .tsvt file — so this is the one definition every frontend uses to turn pasted text back into a grid for Document.Paste. An empty text decodes to a single empty cell (its exact TSV serialization), which is how a paste clears one cell.

func ReadTSV

func ReadTSV(r io.Reader) (Grid, error)

ReadTSV reads a tab-separated value grid. Rows are newline-separated; a trailing newline does not add an empty row. Full-line comments are skipped and do not occupy a grid row, per IsCommentLine: a leading `#!` on the first line (a shebang, so a .tsvt can be `chmod +x` and run via `#!/usr/bin/env tsvsheet`), any `#.` directive-or-comment line, and any legacy `# ` hash-space line. An error-value cell like `#N/A` is data, not a comment. A read failure surfaces as ErrReadInput.

type ImportURL

type ImportURL = engine.ImportURL

ImportURL is the location an IMPORT* function fetches — the (already evaluated) string value of its single argument.

type Key added in v0.20.0

type Key = engine.Key

Key is a view-directive key: hide, header, or freeze.

type Limits

type Limits = engine.Limits

Limits bounds the sizes an untrusted sheet may drive an allocation to.

func BrowserLimits

func BrowserLimits() Limits

BrowserLimits are the tighter ceilings the WASM build applies, sized for a browser tab rather than a workstation.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are generous for real spreadsheets while still bounding OOM.

type LineNumber added in v0.14.0

type LineNumber = engine.LineNumber

LineNumber is a 1-based physical line position in a .tsvt source file, as opposed to a grid row: comment lines occupy a line but no row.

type Loader

type Loader = engine.Loader

Loader resolves the sheet referenced by ref, relative to the embedding sheet's own path base, returning the parsed sub-sheet and its resolved path (used for cycle detection and as the base for the sub-sheet's own SHEET calls). The frontend injects it, keeping the engine filesystem-free; a resolution or containment failure is reported as an error and surfaces as #REF!.

type MediaType

type MediaType = engine.MediaType

MediaType is a content-typed import's RFC 6838 media type — the Accept header an IMPORT* function requests, which the response Content-Type must match.

type Path

type Path = engine.Path

Path identifies a sheet to a Loader: the reference written in a SHEET(...) call, and (as the loader's result) the sheet's own resolved path.

type RevisionHex added in v0.25.0

type RevisionHex = engine.RevisionHex

RevisionHex is a document's content address: the lowercase-hex SHA-256 of its canonical Text bytes. Equal revision ⇔ byte-equal document.

func Revision added in v0.25.0

func Revision(d Document) RevisionHex

Revision content-addresses d.

type Selection added in v0.20.0

type Selection = engine.Selection

Selection is a set of 1-based positions on one axis.

type Sheet

type Sheet = engine.Sheet

Sheet is a parsed spreadsheet grid of literal and formula cells.

func Parse

func Parse(src []byte) (Sheet, error)

Parse reads a .tsvt grid: each TAB-separated field is a literal, or — when it begins with `=` — a formula compiled from the expression that follows. A malformed formula is a syntax error naming its cell.

Example

Parse compiles a .tsvt grid; Compute evaluates every =formula in dependency order and returns the value grid ([][]string), literals passing through.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

func main() {
	sheet, err := tsvsheet.Parse([]byte("2\t3\n=A1*B1\t=A1+B1\n"))
	if err != nil {
		fmt.Println(err)
		return
	}
	grid := sheet.Compute()
	fmt.Println(grid[1][0], grid[1][1])
}
Output:
6 5
Example (ErrorValues)

A cell that fails to evaluate carries a spreadsheet error value, which propagates through the formulas that read it — it is data, not a Go error.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

func main() {
	sheet, _ := tsvsheet.Parse([]byte("=1/0\t=A1+1\n"))
	grid := sheet.Compute()
	fmt.Println(grid[0][0], grid[0][1])
}
Output:
#DIV/0! #DIV/0!
Example (SyntaxError)

A malformed formula is reported as ErrSyntax, matchable with errors.Is.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

func main() {
	_, err := tsvsheet.Parse([]byte("=1 +\n"))
	fmt.Println(err != nil)
}
Output:
true

func ParseWith added in v0.27.4

func ParseWith(src []byte, limits Limits) (Sheet, error)

ParseWith is Parse under a residency budget (spec 018): a document whose census exceeds Limits.ResidentCells (single-ceiling fallback like its siblings) refuses with ErrDocTooLarge before anything materializes; an in-budget document parses identically to Parse (FuzzParseWith is the parity oracle). Parse stays unbounded for embedders that pre-vet their sources.

type SheetCensus added in v0.27.3

type SheetCensus = engine.SheetCensus

SheetCensus is what one open-time scan learned about a document.

func Census added in v0.27.5

func Census(src ByteSource) (SheetCensus, error)

Census reports a source's totals from one index scan — O(index) memory, nothing materialized: the cheap pre-flight a frontend runs to decide or refuse before buffering or parsing anything (spec 018).

type SourceLine added in v0.14.0

type SourceLine = engine.SourceLine

SourceLine is one physical line of .tsvt source, newline already stripped.

type Span

type Span = engine.Span

Span is a rectangular reference target resolved to 0-based addresses: a single cell (From == To) or a range (From is the top-left, To the bottom-right as written). It is the projection a frontend highlights.

type Tick added in v0.10.0

type Tick = engine.Tick

Tick is a recompute-pass ordinal read by TICK()/FRAME(); a frontend that re-renders a volatile sheet passes an incrementing value each pass.

type Trace

type Trace = engine.Trace

Trace explains how one cell was produced: its value, the formula (empty for a literal), and the resolved value of each cell the formula reads.

func Explain

func Explain(s Sheet, at Address) (Trace, error)

Explain computes the sheet and describes the cell at at: its value, and — when the cell is a formula — that formula and each reference it reads. Imports and embedded sheets are disabled on this path; use ExplainWith to trace a sheet that uses them.

Example

Explain traces how a cell was produced: its value, formula, and the inputs the formula read.

package main

import (
	"fmt"

	tsvsheet "github.com/tsvsheet/go-tsvsheet"
)

func main() {
	sheet, _ := tsvsheet.Parse([]byte("2\t3\n=A1+B1\t\n"))
	trace, _ := tsvsheet.Explain(sheet, tsvsheet.Address{Row: 1, Col: 0})
	fmt.Printf("%s = %s (from %s, %d inputs)\n", trace.Cell, trace.Value, trace.Formula, len(trace.Inputs))
}
Output:
A2 = 5 (from A1 + B1, 2 inputs)

func ExplainWith added in v0.22.0

func ExplainWith(s Sheet, at Address, opts ComputeOptions) (Trace, error)

ExplainWith is Explain with an injected compute environment (Loader, Fetcher, Limits), so a sheet whose cells embed sub-sheets or import external data traces with those resolved. The Trace's Imports report where each IMPORT* actually went and why it failed — the only place that is visible, since every import failure is the same opaque #IMPORT! in the grid.

type TraceImport added in v0.22.0

type TraceImport = engine.TraceImport

TraceImport is one IMPORT* call a traced formula performs: the source the sheet wrote, the URL it resolved to, and the reason it failed.

type TraceInput

type TraceInput = engine.TraceInput

TraceInput is one reference a formula reads, with its resolved value.

type Value

type Value = engine.Value

Value is an evaluated cell value: empty, number, string, boolean, date, error, or a 2-D array (a dynamic-array result that spills, or reduces to its top-left value in a scalar context).

type View added in v0.20.0

type View = engine.View

View is what a viewport does with a grid, as the sheet's own `#.` directives declare it: which rows and columns are hidden, carry headers, or stay anchored while the rest scrolls. Every field is a set of 1-based positions, resolved against the sheet's extent, so a host renders it without deriving anything itself.

type WindowedSheet added in v0.27.3

type WindowedSheet = engine.WindowedSheet

WindowedSheet is the over-budget capability: view/compute-only, serving bounded row windows through the index's block cache.

Directories

Path Synopsis
Command browser exposes the tsvsheet engine to the browser as a set of STATELESS functions: the caller holds the .tsvt source, and each call parses it, applies one immutable engine operation, and returns the result as a JSON string.
Command browser exposes the tsvsheet engine to the browser as a set of STATELESS functions: the caller holds the .tsvt source, and each call parses it, applies one immutable engine operation, and returns the result as a JSON string.
internal
constants
Package constants declares the tsvsheet engine's sentinel error values.
Package constants declares the tsvsheet engine's sentinel error values.
engine
Package engine's argument-shaping layer: how a call's operands are reduced to the values a builtin receives — a scalar slot, a flattened range, or the mixed shapes the recurring parameter specs describe.
Package engine's argument-shaping layer: how a call's operands are reduced to the values a builtin receives — a scalar slot, a flattened range, or the mixed shapes the recurring parameter specs describe.
index
The reader's block cache: an LRU bounded in cells, block-granular.
The reader's block cache: an LRU bounded in cells, block-granular.
tsvt
Package tsvt is the covered seam over the ANTLR-generated formula parser: it turns a cell's formula source (the text after its leading `=`) into an immutable typed AST — an Expr over A1 references and literals — or a sentinel syntax error, and hides every ANTLR type from the rest of the program.
Package tsvt is the covered seam over the ANTLR-generated formula parser: it turns a cell's formula source (the text after its leading `=`) into an immutable typed AST — an Expr over A1 references and literals — or a sentinel syntax error, and hides every ANTLR type from the rest of the program.

Jump to

Keyboard shortcuts

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