Add user-defined functions, editable exclusively via a new UI
Document-wide, named formula functions (e.g. MARGE(revenue; cost) = (revenue - cost) / revenue) callable from any cell like a built-in (=MARGE(Umsatz!B2; Umsatz!C2)). Created, edited and deleted only through a new "𝑓 Funktionen" toolbar button -> modal (list + shared edit form), never via JSON handedit. Engine: a call is inlined into the caller's AST at parse time (src/engine/userFunctions.ts's expandUserFunctions), so evaluator.ts and graph.ts need no changes -- dependencies on cells referenced only inside a function body (not in the call) are picked up for free. parser.ts gains an optional paramNames argument to parseFormula() producing a new ParamNode leaf, used only when parsing a function body. Store: addCustomFunction/updateCustomFunction/deleteCustomFunction with inline (not toast) validation -- name/parameter shape, no collision with a built-in or another function, the body parses, and a definition-time cycle check across the whole call graph. Renaming/deleting a function mirrors table rename/delete: existing calls are not rewritten, they become #NAME? on the next rebuild. Fully undo/redo-able and part of the JSON document (customFunctions: [], migrated for v1 and older v2 docs). 178 tests passing (32 new), vue-tsc and eslint clean. Manually verified end-to-end in the running app via Playwright: define/call/recalculate, rename -> #NAME?, a closing cycle rejected inline at save, undo restores the function library. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,13 @@ function evalScalar(node: Node, host: EngineHost): CellValue {
|
||||
}
|
||||
return fn(values);
|
||||
}
|
||||
|
||||
case 'param':
|
||||
// userFunctions.ts's expandUserFunctions() substitutes every ParamNode
|
||||
// away before a cell's AST is ever stored -- this should be unreachable
|
||||
// in practice. Fails as a cell error rather than however an unhandled
|
||||
// case would otherwise behave, matching the engine's "no crash" rule.
|
||||
return new FormulaError('#ERR', `unresolved parameter ${node.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user