| 1 | package calc |
| 2 | |
| 3 | // Func is a built-in spreadsheet function: it receives every argument's |
| 4 | // value already evaluated, with a range argument flattened to one Value |
| 5 | // per cell (row-major), and returns the call's result. |
| 6 | // |
| 7 | // Task 7 wires the dispatch only; funcTable started empty. Task 8 owns the |
| 8 | // actual function bodies (SUM, IF, VLOOKUP, ...) in fn_*.go and |
| 9 | // registry.go, and populates funcTable from there via the exported |
| 10 | // Register — same package, so registry.go needs no access to funcTable |
| 11 | // beyond what's already unexported here. |
| 12 | // |
| 13 | // A few functions (NOW/TODAY, VLOOKUP/INDEX/MATCH) cannot be expressed as |
| 14 | // a plain Func — see clockFuncTable and nodeFuncTable below — so |
| 15 | // evalCall checks three tables, not one; Func itself keeps this exact |
| 16 | // signature for the ~150 ordinary functions the plan describes. |
| 17 | type Func func(args []Value) Value |
| 18 | |
| 19 | var funcTable = map[string]Func{} |
| 20 | |
| 21 | // evalCall dispatches a function call: look up t.Name (already upper-cased |
| 22 | // by the parser) in whichever of the three function tables has it, and |
| 23 | // invoke it. An unresolved name — including a reference-shaped word the |
| 24 | // lexer treated as a call because '(' followed it (token.go's |
| 25 | // followedByCallParen — e.g. A1(2) parses as Call{Name:"A1"}) — is |
| 26 | // #NAME?. |
| 27 | // |
| 28 | // Argument values reach an ordinary Func exactly as evaluated, including |
| 29 | // any that are KindErr — evalCall does not short-circuit on an error |
| 30 | // argument the way evalBinary and evalUnary short-circuit on an error |
| 31 | // operand. That is deliberate, not an oversight: IFERROR (task 8) needs |
| 32 | // the raw error value to inspect, so blanket propagation here would break |
| 33 | // the one place a formula is allowed to look at an error instead of |
| 34 | // becoming one. Each registered Func decides for itself whether an error |
| 35 | // argument means "become that error" (most functions) or something else |
| 36 | // (IFERROR). |
| 37 | func evalCall(t *Call, env Env) Value { |
| 38 | if fn, ok := funcTable[t.Name]; ok { |
| 39 | return fn(evalCallArgs(t.Args, env)) |
| 40 | } |
| 41 | if fn, ok := clockFuncTable[t.Name]; ok { |
| 42 | return fn(env, evalCallArgs(t.Args, env)) |
| 43 | } |
| 44 | if fn, ok := nodeFuncTable[t.Name]; ok { |
| 45 | return fn(t.Args, env) |
| 46 | } |
| 47 | return Value{Kind: KindErr, Err: ErrName} |
| 48 | } |
| 49 | |
| 50 | // evalCallArgs flattens every argument node into the shared []Value an |
| 51 | // ordinary Func or clockFunc receives — the same eager, error-preserving |
| 52 | // evaluation evalCall has always done, just factored out so both dispatch |
| 53 | // branches that need it don't repeat the loop. |
| 54 | func evalCallArgs(nodes []Node, env Env) []Value { |
| 55 | var args []Value |
| 56 | for _, a := range nodes { |
| 57 | args = append(args, evalCallArg(a, env)...) |
| 58 | } |
| 59 | return args |
| 60 | } |
| 61 | |
| 62 | // evalCallArg evaluates one call argument into one or more Values: a |
| 63 | // range expands to its cells in row-major order (via env.CellValue |
| 64 | // directly — not by re-entering Eval, but env.CellValue is the same |
| 65 | // dependency-aware lookup a *Ref goes through, so this is not a way to |
| 66 | // dodge cycle detection or a graph's dirty-set scoping), anything else |
| 67 | // evaluates to exactly one Value through Eval. |
| 68 | // |
| 69 | // A range over maxRangeCells resolves to a single #REF! argument rather |
| 70 | // than materializing it — see rangeCells. |
| 71 | func evalCallArg(n Node, env Env) []Value { |
| 72 | rng, ok := n.(*Range) |
| 73 | if !ok { |
| 74 | return []Value{Eval(n, env)} |
| 75 | } |
| 76 | cells, ok := rangeCells(rng) |
| 77 | if !ok { |
| 78 | return []Value{{Kind: KindErr, Err: ErrRef}} |
| 79 | } |
| 80 | out := make([]Value, len(cells)) |
| 81 | for i, k := range cells { |
| 82 | out[i] = env.CellValue(k) |
| 83 | } |
| 84 | return out |
| 85 | } |
| 86 | |