| 1 | package calc |
| 2 | |
| 3 | // fn_is.go: the IS* predicates. |
| 4 | // |
| 5 | // These are what somebody wrapped a formula in to stop a sheet showing |
| 6 | // an error, so they are the functions an imported workbook is most |
| 7 | // likely to use around the ones it already used. Without them the wrap |
| 8 | // itself becomes the error: `=IF(ISBLANK(A1),"",B1/A1)` imports |
| 9 | // faithfully and evaluates to #NAME? in every row. |
| 10 | // |
| 11 | // Ordinary Funcs, not nodeFuncs, and that is a real decision rather |
| 12 | // than the easy one. evalCallArgs preserves both distinctions these |
| 13 | // need: KindEmpty survives flattening (it is the zero ValueKind |
| 14 | // precisely so a blank cell stays distinguishable from a zero — see |
| 15 | // eval.go), and evalCall does NOT short-circuit an error argument, so |
| 16 | // an error reaches ISERROR as a value rather than replacing the call. |
| 17 | // The one thing flattening loses is a range's shape, and none of these |
| 18 | // takes a range. |
| 19 | |
| 20 | func init() { |
| 21 | Register("ISBLANK", fnIsBlank) |
| 22 | Register("ISNUMBER", fnIsNumber) |
| 23 | Register("ISTEXT", fnIsText) |
| 24 | Register("ISERROR", fnIsError) |
| 25 | } |
| 26 | |
| 27 | // oneArg is the shared argument check. Exactly one value, never a |
| 28 | // range: `ISBLANK(A1:A3)` flattens to three arguments here, and Excel |
| 29 | // answers that with an array this engine has no representation for. One |
| 30 | // honest #VALUE! is better than silently testing the first cell and |
| 31 | // reporting on two the caller never mentioned. |
| 32 | func oneArg(args []Value) (Value, bool) { |
| 33 | if len(args) != 1 { |
| 34 | return Value{}, false |
| 35 | } |
| 36 | return args[0], true |
| 37 | } |
| 38 | |
| 39 | // fnIsBlank is TRUE only for a genuinely empty cell. |
| 40 | // |
| 41 | // Not for the empty string, and the difference is the whole point of the |
| 42 | // function: `=IF(A1="","",…)` and `=IF(ISBLANK(A1),"",…)` disagree on a |
| 43 | // cell holding a formula that returned "", which is exactly the case |
| 44 | // somebody reached for ISBLANK to tell apart. |
| 45 | func fnIsBlank(args []Value) Value { |
| 46 | v, ok := oneArg(args) |
| 47 | if !ok { |
| 48 | return errValue(ErrValue) |
| 49 | } |
| 50 | return boolValue(v.Kind == KindEmpty) |
| 51 | } |
| 52 | |
| 53 | // fnIsNumber. A boolean is not a number here, matching Excel: TRUE |
| 54 | // coerces to 1 in arithmetic but ISNUMBER(TRUE) is FALSE. |
| 55 | func fnIsNumber(args []Value) Value { |
| 56 | v, ok := oneArg(args) |
| 57 | if !ok { |
| 58 | return errValue(ErrValue) |
| 59 | } |
| 60 | return boolValue(v.Kind == KindNum) |
| 61 | } |
| 62 | |
| 63 | func fnIsText(args []Value) Value { |
| 64 | v, ok := oneArg(args) |
| 65 | if !ok { |
| 66 | return errValue(ErrValue) |
| 67 | } |
| 68 | return boolValue(v.Kind == KindStr) |
| 69 | } |
| 70 | |
| 71 | // fnIsError is TRUE for every error kind, #N/A included. |
| 72 | // |
| 73 | // Excel has ISERR too, which is ISERROR minus #N/A. It is deliberately |
| 74 | // not here: the pair is easy to register and easy to get backwards, and |
| 75 | // nothing has asked for the distinction. Adding it later costs one line; |
| 76 | // shipping it now with the wrong one of the two doing the wrong thing |
| 77 | // costs somebody a wrong answer they have no reason to check. |
| 78 | func fnIsError(args []Value) Value { |
| 79 | v, ok := oneArg(args) |
| 80 | if !ok { |
| 81 | return errValue(ErrValue) |
| 82 | } |
| 83 | return boolValue(v.Kind == KindErr) |
| 84 | } |
| 85 | |
| 86 | // boolValue is the counterpart of errValue for the predicate family. |
| 87 | func boolValue(b bool) Value { return Value{Kind: KindBool, Bool: b} } |
| 88 | |