cat / 012
clone

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

cat/module-path
012 / internal / functions / logic.go
logic.go 3.9K
1package functions23import (4 "strings"56 "src.rest/cat/012/internal/value"7)89func init() {10 define(11 &FuncDef{Name: "IFS", Args: "condition1, value1, [condition2, value2, ...]", Desc: "The value of the first true condition", Min: 2, Max: -1, step: 2, arrays: liftPass,12 eval: ifs, format: ifsFormat},13 &FuncDef{Name: "SWITCH", Args: "expression, case1, value1, [case2, value2, ...], [default]", Desc: "The value for the first case equal to an expression", Min: 3, Max: -1, arrays: liftPass,14 eval: switchCase},15 &FuncDef{Name: "XOR", Args: "logical1, [logical2, ...]", Desc: "TRUE if an odd number of arguments are true", Min: 1, Max: -1,16 eval: logical(func(t, _ int) bool { return t%2 == 1 })},17 &FuncDef{Name: "IFNA", Args: "value, value_if_na", Desc: "A fallback when a value is #N/A", Min: 2, Max: 2, arrays: liftPass,18 eval: ifNA, format: inherit},19 &FuncDef{Name: "ISBLANK", Args: "value", Desc: "TRUE if a cell is empty", Min: 1, Max: 1,20 eval: is(func(v Value) bool { return v.Kind == value.Empty })},21 &FuncDef{Name: "ISNUMBER", Args: "value", Desc: "TRUE if a value is a number", Min: 1, Max: 1,22 eval: is(func(v Value) bool { return v.Kind == value.Number })},23 &FuncDef{Name: "ISTEXT", Args: "value", Desc: "TRUE if a value is text", Min: 1, Max: 1,24 eval: is(func(v Value) bool { return v.Kind == value.Text })},25 &FuncDef{Name: "ISLOGICAL", Args: "value", Desc: "TRUE if a value is TRUE or FALSE", Min: 1, Max: 1,26 eval: is(func(v Value) bool { return v.Kind == value.Bool })},27 &FuncDef{Name: "ISERROR", Args: "value", Desc: "TRUE if a value is any error", Min: 1, Max: 1,28 eval: is(func(v Value) bool { return v.Kind == value.Error })},29 &FuncDef{Name: "ISERR", Args: "value", Desc: "TRUE if a value is an error other than #N/A", Min: 1, Max: 1,30 eval: is(func(v Value) bool { return v.Kind == value.Error && v != value.ErrNA })},31 &FuncDef{Name: "ISNA", Args: "value", Desc: "TRUE if a value is #N/A", Min: 1, Max: 1,32 eval: is(func(v Value) bool { return v == value.ErrNA })},33 )34}3536// Evaluators for the table above, in its order.3738func ifs(args []Node, get lookup) Value {39 for i := 0; i+1 < len(args); i += 2 {40 c, err := truth(eval1(args[i], get))41 if err != nil {42 return *err43 }44 if c {45 return eval(args[i+1], get)46 }47 }48 return value.ErrNA49}5051func ifsFormat(args []Node, infer func(Node) Format) Format {52 var vals []Node53 for i := 1; i < len(args); i += 2 {54 vals = append(vals, args[i])55 }56 return inherit(vals, infer)57}5859func switchCase(args []Node, get lookup) Value {60 x := eval1(args[0], get)61 if x.Kind == value.Error {62 return x63 }64 i := 165 for ; i+1 < len(args); i += 2 {66 c := eval1(args[i], get)67 if c.Kind == value.Error {68 return c69 }70 if sameKind(x, c) && compare(x, c) == 0 {71 return eval(args[i+1], get)72 }73 }74 if i < len(args) {75 return eval(args[i], get)76 }77 return value.ErrNA78}7980func ifNA(args []Node, get lookup) Value {81 v := eval(args[0], get)82 if v == value.ErrNA {83 return eval(args[1], get)84 }85 return v86}8788// is builds the IS functions, which never return an error themselves.89func is(test func(Value) bool) func([]Node, lookup) Value {90 return func(args []Node, get lookup) Value {91 return boolean(test(eval(args[0], get)))92 }93}9495// sameKind reports whether two values can be equal in SWITCH and exact96// lookups: numbers only equal numbers, text only text. A blank is 0 or "".97func sameKind(a, b Value) bool {98 ka, kb := a.Kind, b.Kind99 if ka == value.Empty || kb == value.Empty {100 return true101 }102 return ka == kb103}104105// truth reads v as a condition, as Sheets does: a number is true unless106// 0, a blank or "" is false, and text reads as TRUE, FALSE or a number,107// so IF("TRUE", 1, 2) is 1; other text is #VALUE!.108func truth(v Value) (bool, *Value) {109 switch v.Kind {110 case value.Error:111 return false, errOf(v)112 case value.Text:113 switch {114 case v.Str == "", strings.EqualFold(v.Str, "FALSE"):115 return false, nil116 case strings.EqualFold(v.Str, "TRUE"):117 return true, nil118 }119 }120 f, err := toNum(v)121 return f != 0, err122}