cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / functions / format.go
format.go 3.8K
1package functions23import (4 "src.rest/cat/012/internal/formula"5 "src.rest/cat/012/internal/value"6)78// IsVolatile reports whether n calls a function whose result changes9// without its inputs changing (TODAY, NOW, RAND).10func IsVolatile(n Node) bool {11 switch n := n.(type) {12 case formula.Unary:13 return IsVolatile(n.X)14 case formula.Binary:15 return IsVolatile(n.L) || IsVolatile(n.R)16 case formula.Call:17 if funcOf(n).Volatile {18 return true19 }20 for _, a := range n.Args {21 if IsVolatile(a) {22 return true23 }24 }25 case formula.Array, formula.Invoke:26 volatile := false27 formula.EachChild(n, func(k Node) { volatile = volatile || IsVolatile(k) })28 return volatile29 }30 return false31}3233// InferFormat picks the format Sheets shows a formula's result in when34// the cell is Automatic: dates from date functions, and otherwise the35// format of the first formatted input, so =B2+B3 of currency shows36// currency and a date plus days shows a date. at is the format a cell37// shows in, a linked source's column the one its type says; paged38// reports whether a sheet is a linked source's tab, whose counts show as39// whole numbers with thousands separators.40func InferFormat(n Node, at func(string, Addr) Format, paged func(string) bool) Format {41 switch n := n.(type) {42 case formula.Ref:43 return at(n.Sheet, n.Addr)44 case formula.Range:45 return at(n.Sheet, n.Rect.From)46 case formula.Unary:47 if n.Op == "-" || n.Op == "+" {48 return InferFormat(n.X, at, paged)49 }50 case formula.Binary:51 l, r := InferFormat(n.L, at, paged), InferFormat(n.R, at, paged)52 switch n.Op {53 case "+", "-":54 if n.Op == "-" && l.Kind.IsTime() && r.Kind.IsTime() {55 return Format{} // days between two dates56 }57 return firstFormat(l, r)58 case "*", "/":59 if l.Kind.IsTime() || r.Kind.IsTime() {60 return Format{}61 }62 return firstFormat(l, r)63 }64 case formula.Call:65 f := funcOf(n)66 if counting[f.Name] && paged != nil && countsPaged(n.Args, paged) {67 return wholeNumber68 }69 if f.format != nil {70 return f.format(n.Args, func(n Node) Format { return InferFormat(n, at, paged) })71 }72 case formula.Array:73 return InferFormat(n.Rows[0][0], at, paged)74 }75 return Format{}76}7778// counting are the functions that count cells or rows.79var counting = map[string]bool{"COUNT": true, "COUNTA": true, "COUNTIF": true, "COUNTIFS": true,80 "COUNTBLANK": true, "ROWS": true, "COLUMNS": true}8182// wholeNumber is a count's format over a linked source: the locale's83// thousands separators and no decimals, so two million rows read84// 2,000,000.85var wholeNumber = Format{Kind: value.FmtNumber}8687// countsPaged reports whether a count's arguments name a range of a88// linked source.89func countsPaged(args []Node, paged func(string) bool) bool {90 for _, a := range args {91 sheet := ""92 switch r := a.(type) {93 case formula.Range:94 sheet = r.Sheet95 case formula.Ref:96 sheet = r.Sheet97 }98 if sheet != "" && paged(sheet) {99 return true100 }101 }102 return false103}104105func firstFormat(fs ...Format) Format {106 for _, f := range fs {107 if !f.IsZero() && f.Kind != value.FmtText {108 return f109 }110 }111 return Format{}112}113114// Result formats for the function table.115116// returns makes a function's result show in format f.117func returns(f Format) func([]Node, func(Node) Format) Format {118 return func([]Node, func(Node) Format) Format { return f }119}120121// inherit makes a function's result take the format of its first122// formatted argument (SUM, MIN, ROUND...).123func inherit(args []Node, infer func(Node) Format) Format {124 for _, a := range args {125 if f := infer(a); !f.IsZero() && f.Kind != value.FmtText {126 return f127 }128 }129 return Format{}130}131132// inheritFrom takes the format of argument i only.133func inheritFrom(i int) func([]Node, func(Node) Format) Format {134 return func(args []Node, infer func(Node) Format) Format {135 if i < len(args) {136 return infer(args[i])137 }138 return Format{}139 }140}