@devmm/puredocs-excel-formula 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1635 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +234 -7
- package/dist/index.d.ts +234 -7
- package/dist/index.js +1627 -48
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -10,7 +10,32 @@ declare const enum FormulaError {
|
|
|
10
10
|
Calc = 8,
|
|
11
11
|
Spill = 9
|
|
12
12
|
}
|
|
13
|
-
type FormulaValueKind = 'blank' | 'number' | 'text' | 'boolean' | 'error' | 'array';
|
|
13
|
+
type FormulaValueKind = 'blank' | 'number' | 'text' | 'boolean' | 'error' | 'array' | 'lambda';
|
|
14
|
+
/**
|
|
15
|
+
* A callable value produced by LAMBDA. Kept as an opaque interface so
|
|
16
|
+
* formula-value.ts stays decoupled from the AST/context modules.
|
|
17
|
+
*/
|
|
18
|
+
interface LambdaLike {
|
|
19
|
+
/** Number of declared parameters. */
|
|
20
|
+
readonly arity: number;
|
|
21
|
+
/** Invokes the lambda with the given argument values (runs to completion). */
|
|
22
|
+
call(args: FormulaValue[]): FormulaValue;
|
|
23
|
+
/**
|
|
24
|
+
* Runs the lambda body ONCE in tail-aware mode. Returns either the final value
|
|
25
|
+
* or a TailCall describing the next tail call, so a trampoline can iterate
|
|
26
|
+
* without growing the JS stack. Optional; call() is the fallback.
|
|
27
|
+
*/
|
|
28
|
+
step?(args: FormulaValue[]): FormulaValue | TailCall;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A pending tail call produced by evaluating a node in tail position. The
|
|
32
|
+
* trampoline in a lambda's call() loops on these instead of recursing.
|
|
33
|
+
*/
|
|
34
|
+
declare class TailCall {
|
|
35
|
+
readonly lambda: LambdaLike;
|
|
36
|
+
readonly args: FormulaValue[];
|
|
37
|
+
constructor(lambda: LambdaLike, args: FormulaValue[]);
|
|
38
|
+
}
|
|
14
39
|
declare class FormulaValue {
|
|
15
40
|
#private;
|
|
16
41
|
readonly kind: FormulaValueKind;
|
|
@@ -34,17 +59,20 @@ declare class FormulaValue {
|
|
|
34
59
|
static boolean(v: boolean): FormulaValue;
|
|
35
60
|
static error(e: FormulaError): FormulaValue;
|
|
36
61
|
static array(a: ArrayValue): FormulaValue;
|
|
62
|
+
static lambda(fn: LambdaLike): FormulaValue;
|
|
37
63
|
get isBlank(): boolean;
|
|
38
64
|
get isNumber(): boolean;
|
|
39
65
|
get isText(): boolean;
|
|
40
66
|
get isBoolean(): boolean;
|
|
41
67
|
get isError(): boolean;
|
|
42
68
|
get isArray(): boolean;
|
|
69
|
+
get isLambda(): boolean;
|
|
43
70
|
get isNumeric(): boolean;
|
|
44
71
|
get numberValue(): number;
|
|
45
72
|
get textValue(): string;
|
|
46
73
|
get booleanValue(): boolean;
|
|
47
74
|
get arrayVal(): ArrayValue;
|
|
75
|
+
get lambdaVal(): LambdaLike;
|
|
48
76
|
coerceToNumber(): FormulaValue;
|
|
49
77
|
tryAsDouble(): {
|
|
50
78
|
ok: true;
|
|
@@ -121,10 +149,11 @@ declare const enum TokenType {
|
|
|
121
149
|
GreaterThanOrEqual = 21,
|
|
122
150
|
SheetReference = 22,
|
|
123
151
|
NamedRange = 23,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
SpillReference = 24,
|
|
153
|
+
Exclamation = 25,
|
|
154
|
+
AtSign = 26,
|
|
155
|
+
ErrorLiteral = 27,
|
|
156
|
+
EOF = 28
|
|
128
157
|
}
|
|
129
158
|
interface Token {
|
|
130
159
|
readonly type: TokenType;
|
|
@@ -220,10 +249,25 @@ declare namespace FormulaHelper {
|
|
|
220
249
|
interface WorksheetLike {
|
|
221
250
|
getCellValue(reference: string): unknown;
|
|
222
251
|
name: string;
|
|
252
|
+
/** Optional: whether a 1-based row is hidden (used by SUBTOTAL/AGGREGATE). */
|
|
253
|
+
isRowHidden?(row: number): boolean;
|
|
254
|
+
/** Optional: formula text of a cell (used by SUBTOTAL to skip nested calls). */
|
|
255
|
+
getCellFormula?(reference: string): string | null;
|
|
256
|
+
/** Optional: spill range (e.g. "A1:A3") of a dynamic-array anchor, for A1#. */
|
|
257
|
+
getSpillRange?(reference: string): string | null;
|
|
223
258
|
}
|
|
224
259
|
interface WorkbookLike {
|
|
225
260
|
worksheets: readonly WorksheetLike[];
|
|
226
261
|
getWorksheet(name: string): WorksheetLike;
|
|
262
|
+
/**
|
|
263
|
+
* Optional resolver for defined names (named ranges). Returns the refers-to
|
|
264
|
+
* expression (without leading '='), following Excel scoping: a name scoped to
|
|
265
|
+
* `sheetName` wins over the workbook-global name. When this method is absent
|
|
266
|
+
* (or returns undefined), named-range references evaluate to #NAME?.
|
|
267
|
+
*
|
|
268
|
+
* @devmm/puredocs-excel's Workbook implements this.
|
|
269
|
+
*/
|
|
270
|
+
getDefinedName?(name: string, sheetName?: string): string | undefined;
|
|
227
271
|
}
|
|
228
272
|
declare class FormulaContext {
|
|
229
273
|
#private;
|
|
@@ -233,19 +277,46 @@ declare class FormulaContext {
|
|
|
233
277
|
formulaCol: number;
|
|
234
278
|
/** Optional workbook reference for cross-sheet references. */
|
|
235
279
|
workbook?: WorkbookLike;
|
|
280
|
+
/**
|
|
281
|
+
* Optional named-range resolver, installed by the evaluator when the workbook
|
|
282
|
+
* exposes getDefinedName. Given a name, returns its evaluated value. Left
|
|
283
|
+
* undefined when no resolver is available, in which case named ranges are
|
|
284
|
+
* #NAME? (unchanged legacy behaviour).
|
|
285
|
+
*/
|
|
286
|
+
nameResolver?: (name: string) => FormulaValue;
|
|
287
|
+
/**
|
|
288
|
+
* Shared recursion budget for LAMBDA calls, threaded across all contexts in a
|
|
289
|
+
* single evaluation. Guards non-tail recursion (which grows the JS stack);
|
|
290
|
+
* tail recursion is trampolined and does not count against it.
|
|
291
|
+
*/
|
|
292
|
+
recursionGuard?: {
|
|
293
|
+
depth: number;
|
|
294
|
+
max: number;
|
|
295
|
+
};
|
|
296
|
+
/** Pushes a new binding frame (used by LET/LAMBDA). */
|
|
297
|
+
pushScope(bindings: Map<string, FormulaValue>): void;
|
|
298
|
+
/** Pops the innermost binding frame. */
|
|
299
|
+
popScope(): void;
|
|
236
300
|
constructor(sheet: WorksheetLike, formulaRow?: number, formulaCol?: number, evaluating?: Set<string>, registry?: FunctionRegistry);
|
|
237
301
|
get worksheet(): WorksheetLike;
|
|
238
302
|
getCellValue(cellReference: string): FormulaValue;
|
|
239
303
|
getRangeValues(startRef: string, endRef: string): FormulaValue;
|
|
240
304
|
getSheetCellValue(sheetName: string, cellReference: string): FormulaValue;
|
|
241
305
|
getSheetRangeValues(sheetName: string, startRef: string, endRef: string): FormulaValue;
|
|
242
|
-
resolveNamedRange(
|
|
306
|
+
resolveNamedRange(name: string): FormulaValue;
|
|
307
|
+
/**
|
|
308
|
+
* Resolves a spill-range reference (A1#) to the array spilling from the anchor
|
|
309
|
+
* cell. Requires the worksheet to expose getSpillRange; otherwise #REF!.
|
|
310
|
+
*/
|
|
311
|
+
resolveSpillRange(anchorRef: string): FormulaValue;
|
|
243
312
|
getRangeBounds(startRef: string, endRef: string): {
|
|
244
313
|
startRow: number;
|
|
245
314
|
startCol: number;
|
|
246
315
|
endRow: number;
|
|
247
316
|
endCol: number;
|
|
248
317
|
};
|
|
318
|
+
/** True if a built-in function with this name is registered. */
|
|
319
|
+
hasFunction(name: string): boolean;
|
|
249
320
|
evaluateFunction(name: string, args: FormulaNode[]): FormulaValue;
|
|
250
321
|
}
|
|
251
322
|
|
|
@@ -256,6 +327,13 @@ declare class FormulaContext {
|
|
|
256
327
|
|
|
257
328
|
declare abstract class FormulaNode {
|
|
258
329
|
abstract evaluate(ctx: FormulaContext): FormulaValue;
|
|
330
|
+
/**
|
|
331
|
+
* Evaluates this node in tail position. Returns a TailCall when the node's
|
|
332
|
+
* value is a call to a lambda in tail position (so the caller's trampoline can
|
|
333
|
+
* iterate without growing the stack); otherwise the final value. The default
|
|
334
|
+
* is a plain evaluate — only control-flow and call nodes override it.
|
|
335
|
+
*/
|
|
336
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
259
337
|
}
|
|
260
338
|
declare class NumberNode extends FormulaNode {
|
|
261
339
|
readonly value: number;
|
|
@@ -310,6 +388,12 @@ declare class NamedRangeNode extends FormulaNode {
|
|
|
310
388
|
constructor(name: string);
|
|
311
389
|
evaluate(ctx: FormulaContext): FormulaValue;
|
|
312
390
|
}
|
|
391
|
+
/** Spill-range reference (A1#): the array that spills from an anchor cell. */
|
|
392
|
+
declare class SpillReferenceNode extends FormulaNode {
|
|
393
|
+
readonly anchorRef: string;
|
|
394
|
+
constructor(anchorRef: string);
|
|
395
|
+
evaluate(ctx: FormulaContext): FormulaValue;
|
|
396
|
+
}
|
|
313
397
|
declare const enum BinaryOperator {
|
|
314
398
|
Add = 0,
|
|
315
399
|
Subtract = 1,
|
|
@@ -354,6 +438,18 @@ declare class FunctionCallNode extends FormulaNode {
|
|
|
354
438
|
readonly args: FormulaNode[];
|
|
355
439
|
constructor(functionName: string, args: FormulaNode[]);
|
|
356
440
|
evaluate(ctx: FormulaContext): FormulaValue;
|
|
441
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Calls the value produced by an expression (e.g. an inline LAMBDA:
|
|
445
|
+
* `LAMBDA(x,x+1)(5)`). The callee must evaluate to a lambda, else #VALUE!.
|
|
446
|
+
*/
|
|
447
|
+
declare class CallNode extends FormulaNode {
|
|
448
|
+
readonly callee: FormulaNode;
|
|
449
|
+
readonly args: FormulaNode[];
|
|
450
|
+
constructor(callee: FormulaNode, args: FormulaNode[]);
|
|
451
|
+
evaluate(ctx: FormulaContext): FormulaValue;
|
|
452
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
357
453
|
}
|
|
358
454
|
|
|
359
455
|
/**
|
|
@@ -438,6 +534,129 @@ declare class LruCache<K, V> {
|
|
|
438
534
|
resize(newCapacity: number): void;
|
|
439
535
|
}
|
|
440
536
|
|
|
537
|
+
/** A rectangular precedent region on a specific sheet (1-based, inclusive). */
|
|
538
|
+
interface PrecedentRef {
|
|
539
|
+
/** Upper-cased sheet name. */
|
|
540
|
+
readonly sheet: string;
|
|
541
|
+
readonly startRow: number;
|
|
542
|
+
readonly startCol: number;
|
|
543
|
+
readonly endRow: number;
|
|
544
|
+
readonly endCol: number;
|
|
545
|
+
}
|
|
546
|
+
interface ExtractResult {
|
|
547
|
+
readonly refs: PrecedentRef[];
|
|
548
|
+
readonly volatile: boolean;
|
|
549
|
+
}
|
|
550
|
+
/** Resolves a defined name to its refers-to expression, for name expansion. */
|
|
551
|
+
type NameResolver = (name: string, sheet: string) => string | undefined;
|
|
552
|
+
/**
|
|
553
|
+
* Extracts the precedents and volatility of a formula AST.
|
|
554
|
+
*
|
|
555
|
+
* @param ast The parsed formula.
|
|
556
|
+
* @param formulaSheet Upper-cased name of the sheet the formula lives on;
|
|
557
|
+
* unqualified references resolve against it.
|
|
558
|
+
* @param registry Function registry used to test volatility.
|
|
559
|
+
* @param resolveName Optional resolver so NamedRangeNode precedents expand to
|
|
560
|
+
* the references inside their refers-to expression.
|
|
561
|
+
*/
|
|
562
|
+
declare function extractReferences(ast: FormulaNode, formulaSheet: string, registry?: FunctionRegistry, resolveName?: NameResolver): ExtractResult;
|
|
563
|
+
/** True if the 1-based cell (sheet,row,col) falls inside a precedent region. */
|
|
564
|
+
declare function refContains(ref: PrecedentRef, sheet: string, row: number, col: number): boolean;
|
|
565
|
+
|
|
566
|
+
interface CoreCellLike {
|
|
567
|
+
setComputedValue(value: unknown): unknown;
|
|
568
|
+
setFormula(formula: string): unknown;
|
|
569
|
+
setArrayRef(spillRef: string | null): unknown;
|
|
570
|
+
clear(): void;
|
|
571
|
+
}
|
|
572
|
+
interface CoreSheetLike {
|
|
573
|
+
getCellValue(ref: string): unknown;
|
|
574
|
+
getCell(ref: string): CoreCellLike;
|
|
575
|
+
getCellFormula?(ref: string): string | null;
|
|
576
|
+
getSpillRange?(ref: string): string | null;
|
|
577
|
+
isRowHidden?(row: number): boolean;
|
|
578
|
+
}
|
|
579
|
+
interface CoreWorkbookLike {
|
|
580
|
+
getWorksheet(name: string): CoreSheetLike;
|
|
581
|
+
getDefinedName(name: string, sheet?: string): string | undefined;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Adapts a @devmm/puredocs-excel Workbook to a RecalcModel so the engine can
|
|
585
|
+
* read cell values and write computed results back into the document (via
|
|
586
|
+
* Cell.setComputedValue, which preserves the formula). Named ranges defined on
|
|
587
|
+
* the workbook are resolved automatically.
|
|
588
|
+
*/
|
|
589
|
+
declare function createWorkbookRecalcModel(workbook: CoreWorkbookLike): RecalcModel;
|
|
590
|
+
/** Minimal storage abstraction the engine reads inputs from and writes results to. */
|
|
591
|
+
interface RecalcModel {
|
|
592
|
+
/** Current value of a cell (raw input value, or last computed value). */
|
|
593
|
+
getCellValue(sheet: string, ref: string): unknown;
|
|
594
|
+
/** Store a computed formula result. Must NOT clear the cell's formula. */
|
|
595
|
+
setCellValue(sheet: string, ref: string, value: unknown): void;
|
|
596
|
+
/**
|
|
597
|
+
* Optional: persist a formula string on a cell so it serialises with the
|
|
598
|
+
* document. When present, the engine calls it from setCellFormula so the
|
|
599
|
+
* model stays the source of truth for the formula text.
|
|
600
|
+
*/
|
|
601
|
+
setCellFormula?(sheet: string, ref: string, formula: string): void;
|
|
602
|
+
/** Optional: clear a cell (value + formula) — used by clearCellFormula. */
|
|
603
|
+
clearCell?(sheet: string, ref: string): void;
|
|
604
|
+
/** Optional: mark/unmark a cell as a dynamic-array anchor with its spill range. */
|
|
605
|
+
setCellArrayRef?(sheet: string, ref: string, spillRef: string | null): void;
|
|
606
|
+
/** Optional: formula text of a cell (lets SUBTOTAL skip nested calls). */
|
|
607
|
+
getCellFormula?(sheet: string, ref: string): string | null;
|
|
608
|
+
/** Optional: spill range of a dynamic-array anchor (enables A1#). */
|
|
609
|
+
getSpillRange?(sheet: string, ref: string): string | null;
|
|
610
|
+
/** Optional: whether a 1-based row is hidden (SUBTOTAL/AGGREGATE). */
|
|
611
|
+
isRowHidden?(sheet: string, row: number): boolean;
|
|
612
|
+
/** Optional defined-name resolver (enables named ranges in formulas). */
|
|
613
|
+
getDefinedName?(name: string, sheet?: string): string | undefined;
|
|
614
|
+
}
|
|
615
|
+
/** One recomputed cell, in evaluation order. */
|
|
616
|
+
interface RecalcResult {
|
|
617
|
+
readonly sheet: string;
|
|
618
|
+
readonly ref: string;
|
|
619
|
+
/** The written JS value (null/number/string/boolean). For a dynamic array,
|
|
620
|
+
* this is the anchor's top-left value. */
|
|
621
|
+
readonly value: unknown;
|
|
622
|
+
/** True when the cell is part of a circular reference. */
|
|
623
|
+
readonly circular?: boolean;
|
|
624
|
+
/** For a dynamic-array anchor: the spilled block size (rows × cols). */
|
|
625
|
+
readonly spill?: {
|
|
626
|
+
rows: number;
|
|
627
|
+
cols: number;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
declare class RecalcEngine {
|
|
631
|
+
#private;
|
|
632
|
+
constructor(model: RecalcModel, registry?: FunctionRegistry);
|
|
633
|
+
/**
|
|
634
|
+
* Registers (or replaces) a formula for a cell and recalculates that cell and
|
|
635
|
+
* everything downstream of it. Returns the recomputed cells in the order they
|
|
636
|
+
* were evaluated. The leading '=' of the formula is optional.
|
|
637
|
+
*/
|
|
638
|
+
setCellFormula(sheet: string, ref: string, formula: string): RecalcResult[];
|
|
639
|
+
/**
|
|
640
|
+
* Removes a formula from a cell and recalculates its dependents (which may now
|
|
641
|
+
* read a plain input value instead). Returns the recomputed dependent cells.
|
|
642
|
+
*/
|
|
643
|
+
clearCellFormula(sheet: string, ref: string): RecalcResult[];
|
|
644
|
+
/**
|
|
645
|
+
* Writes a raw input value to a cell and recalculates every formula that
|
|
646
|
+
* depends on it. Use this for non-formula edits so dependents stay current.
|
|
647
|
+
*/
|
|
648
|
+
setCellValue(sheet: string, ref: string, value: unknown): RecalcResult[];
|
|
649
|
+
/**
|
|
650
|
+
* Signals that a cell changed by some external means (the model was already
|
|
651
|
+
* updated) and recalculates its dependents.
|
|
652
|
+
*/
|
|
653
|
+
onCellChanged(sheet: string, ref: string): RecalcResult[];
|
|
654
|
+
/** Recalculates every registered formula in dependency order. */
|
|
655
|
+
recalcAll(): RecalcResult[];
|
|
656
|
+
/** The precedents recorded for a formula cell (mainly for tests/inspection). */
|
|
657
|
+
getPrecedents(sheet: string, ref: string): readonly PrecedentRef[];
|
|
658
|
+
}
|
|
659
|
+
|
|
441
660
|
declare function registerMath(r: FunctionRegistry): void;
|
|
442
661
|
|
|
443
662
|
declare function registerText(r: FunctionRegistry): void;
|
|
@@ -452,4 +671,12 @@ declare function registerStatistical(r: FunctionRegistry): void;
|
|
|
452
671
|
|
|
453
672
|
declare function registerInfo(r: FunctionRegistry): void;
|
|
454
673
|
|
|
455
|
-
|
|
674
|
+
declare function registerFinance(r: FunctionRegistry): void;
|
|
675
|
+
|
|
676
|
+
declare function registerMeta(r: FunctionRegistry): void;
|
|
677
|
+
|
|
678
|
+
declare function registerAggregate(r: FunctionRegistry): void;
|
|
679
|
+
|
|
680
|
+
declare function registerArray(r: FunctionRegistry): void;
|
|
681
|
+
|
|
682
|
+
export { ArrayValue, BinaryOpNode, BinaryOperator, BlankNode, BooleanNode, CallNode, CellReferenceNode, ErrorNode, type ExtractResult, FormulaContext, FormulaError, FormulaException, type FormulaFunction, FormulaHelper, FormulaLexer, FormulaNode, FormulaParser, FormulaValue, type FormulaValueKind, FunctionCallNode, type FunctionDef, FunctionRegistry, ImplicitIntersectionNode, type LambdaLike, LruCache, type NameResolver, NamedRangeNode, NumberNode, type PrecedentRef, RangeReferenceNode, RecalcEngine, type RecalcModel, type RecalcResult, SheetCellReferenceNode, SheetRangeReferenceNode, SpillReferenceNode, StringNode, type Token, TokenType, UnaryOpNode, UnaryOperator, type WorkbookLike, type WorksheetLike, clearAstCache, createWorkbookRecalcModel, evaluateFormula, extractReferences, getAstCacheStats, parseFormula, refContains, registerAggregate, registerArray, registerDate, registerFinance, registerInfo, registerLogical, registerLookup, registerMath, registerMeta, registerStatistical, registerText, setAstCacheCapacity };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,32 @@ declare const enum FormulaError {
|
|
|
10
10
|
Calc = 8,
|
|
11
11
|
Spill = 9
|
|
12
12
|
}
|
|
13
|
-
type FormulaValueKind = 'blank' | 'number' | 'text' | 'boolean' | 'error' | 'array';
|
|
13
|
+
type FormulaValueKind = 'blank' | 'number' | 'text' | 'boolean' | 'error' | 'array' | 'lambda';
|
|
14
|
+
/**
|
|
15
|
+
* A callable value produced by LAMBDA. Kept as an opaque interface so
|
|
16
|
+
* formula-value.ts stays decoupled from the AST/context modules.
|
|
17
|
+
*/
|
|
18
|
+
interface LambdaLike {
|
|
19
|
+
/** Number of declared parameters. */
|
|
20
|
+
readonly arity: number;
|
|
21
|
+
/** Invokes the lambda with the given argument values (runs to completion). */
|
|
22
|
+
call(args: FormulaValue[]): FormulaValue;
|
|
23
|
+
/**
|
|
24
|
+
* Runs the lambda body ONCE in tail-aware mode. Returns either the final value
|
|
25
|
+
* or a TailCall describing the next tail call, so a trampoline can iterate
|
|
26
|
+
* without growing the JS stack. Optional; call() is the fallback.
|
|
27
|
+
*/
|
|
28
|
+
step?(args: FormulaValue[]): FormulaValue | TailCall;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A pending tail call produced by evaluating a node in tail position. The
|
|
32
|
+
* trampoline in a lambda's call() loops on these instead of recursing.
|
|
33
|
+
*/
|
|
34
|
+
declare class TailCall {
|
|
35
|
+
readonly lambda: LambdaLike;
|
|
36
|
+
readonly args: FormulaValue[];
|
|
37
|
+
constructor(lambda: LambdaLike, args: FormulaValue[]);
|
|
38
|
+
}
|
|
14
39
|
declare class FormulaValue {
|
|
15
40
|
#private;
|
|
16
41
|
readonly kind: FormulaValueKind;
|
|
@@ -34,17 +59,20 @@ declare class FormulaValue {
|
|
|
34
59
|
static boolean(v: boolean): FormulaValue;
|
|
35
60
|
static error(e: FormulaError): FormulaValue;
|
|
36
61
|
static array(a: ArrayValue): FormulaValue;
|
|
62
|
+
static lambda(fn: LambdaLike): FormulaValue;
|
|
37
63
|
get isBlank(): boolean;
|
|
38
64
|
get isNumber(): boolean;
|
|
39
65
|
get isText(): boolean;
|
|
40
66
|
get isBoolean(): boolean;
|
|
41
67
|
get isError(): boolean;
|
|
42
68
|
get isArray(): boolean;
|
|
69
|
+
get isLambda(): boolean;
|
|
43
70
|
get isNumeric(): boolean;
|
|
44
71
|
get numberValue(): number;
|
|
45
72
|
get textValue(): string;
|
|
46
73
|
get booleanValue(): boolean;
|
|
47
74
|
get arrayVal(): ArrayValue;
|
|
75
|
+
get lambdaVal(): LambdaLike;
|
|
48
76
|
coerceToNumber(): FormulaValue;
|
|
49
77
|
tryAsDouble(): {
|
|
50
78
|
ok: true;
|
|
@@ -121,10 +149,11 @@ declare const enum TokenType {
|
|
|
121
149
|
GreaterThanOrEqual = 21,
|
|
122
150
|
SheetReference = 22,
|
|
123
151
|
NamedRange = 23,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
SpillReference = 24,
|
|
153
|
+
Exclamation = 25,
|
|
154
|
+
AtSign = 26,
|
|
155
|
+
ErrorLiteral = 27,
|
|
156
|
+
EOF = 28
|
|
128
157
|
}
|
|
129
158
|
interface Token {
|
|
130
159
|
readonly type: TokenType;
|
|
@@ -220,10 +249,25 @@ declare namespace FormulaHelper {
|
|
|
220
249
|
interface WorksheetLike {
|
|
221
250
|
getCellValue(reference: string): unknown;
|
|
222
251
|
name: string;
|
|
252
|
+
/** Optional: whether a 1-based row is hidden (used by SUBTOTAL/AGGREGATE). */
|
|
253
|
+
isRowHidden?(row: number): boolean;
|
|
254
|
+
/** Optional: formula text of a cell (used by SUBTOTAL to skip nested calls). */
|
|
255
|
+
getCellFormula?(reference: string): string | null;
|
|
256
|
+
/** Optional: spill range (e.g. "A1:A3") of a dynamic-array anchor, for A1#. */
|
|
257
|
+
getSpillRange?(reference: string): string | null;
|
|
223
258
|
}
|
|
224
259
|
interface WorkbookLike {
|
|
225
260
|
worksheets: readonly WorksheetLike[];
|
|
226
261
|
getWorksheet(name: string): WorksheetLike;
|
|
262
|
+
/**
|
|
263
|
+
* Optional resolver for defined names (named ranges). Returns the refers-to
|
|
264
|
+
* expression (without leading '='), following Excel scoping: a name scoped to
|
|
265
|
+
* `sheetName` wins over the workbook-global name. When this method is absent
|
|
266
|
+
* (or returns undefined), named-range references evaluate to #NAME?.
|
|
267
|
+
*
|
|
268
|
+
* @devmm/puredocs-excel's Workbook implements this.
|
|
269
|
+
*/
|
|
270
|
+
getDefinedName?(name: string, sheetName?: string): string | undefined;
|
|
227
271
|
}
|
|
228
272
|
declare class FormulaContext {
|
|
229
273
|
#private;
|
|
@@ -233,19 +277,46 @@ declare class FormulaContext {
|
|
|
233
277
|
formulaCol: number;
|
|
234
278
|
/** Optional workbook reference for cross-sheet references. */
|
|
235
279
|
workbook?: WorkbookLike;
|
|
280
|
+
/**
|
|
281
|
+
* Optional named-range resolver, installed by the evaluator when the workbook
|
|
282
|
+
* exposes getDefinedName. Given a name, returns its evaluated value. Left
|
|
283
|
+
* undefined when no resolver is available, in which case named ranges are
|
|
284
|
+
* #NAME? (unchanged legacy behaviour).
|
|
285
|
+
*/
|
|
286
|
+
nameResolver?: (name: string) => FormulaValue;
|
|
287
|
+
/**
|
|
288
|
+
* Shared recursion budget for LAMBDA calls, threaded across all contexts in a
|
|
289
|
+
* single evaluation. Guards non-tail recursion (which grows the JS stack);
|
|
290
|
+
* tail recursion is trampolined and does not count against it.
|
|
291
|
+
*/
|
|
292
|
+
recursionGuard?: {
|
|
293
|
+
depth: number;
|
|
294
|
+
max: number;
|
|
295
|
+
};
|
|
296
|
+
/** Pushes a new binding frame (used by LET/LAMBDA). */
|
|
297
|
+
pushScope(bindings: Map<string, FormulaValue>): void;
|
|
298
|
+
/** Pops the innermost binding frame. */
|
|
299
|
+
popScope(): void;
|
|
236
300
|
constructor(sheet: WorksheetLike, formulaRow?: number, formulaCol?: number, evaluating?: Set<string>, registry?: FunctionRegistry);
|
|
237
301
|
get worksheet(): WorksheetLike;
|
|
238
302
|
getCellValue(cellReference: string): FormulaValue;
|
|
239
303
|
getRangeValues(startRef: string, endRef: string): FormulaValue;
|
|
240
304
|
getSheetCellValue(sheetName: string, cellReference: string): FormulaValue;
|
|
241
305
|
getSheetRangeValues(sheetName: string, startRef: string, endRef: string): FormulaValue;
|
|
242
|
-
resolveNamedRange(
|
|
306
|
+
resolveNamedRange(name: string): FormulaValue;
|
|
307
|
+
/**
|
|
308
|
+
* Resolves a spill-range reference (A1#) to the array spilling from the anchor
|
|
309
|
+
* cell. Requires the worksheet to expose getSpillRange; otherwise #REF!.
|
|
310
|
+
*/
|
|
311
|
+
resolveSpillRange(anchorRef: string): FormulaValue;
|
|
243
312
|
getRangeBounds(startRef: string, endRef: string): {
|
|
244
313
|
startRow: number;
|
|
245
314
|
startCol: number;
|
|
246
315
|
endRow: number;
|
|
247
316
|
endCol: number;
|
|
248
317
|
};
|
|
318
|
+
/** True if a built-in function with this name is registered. */
|
|
319
|
+
hasFunction(name: string): boolean;
|
|
249
320
|
evaluateFunction(name: string, args: FormulaNode[]): FormulaValue;
|
|
250
321
|
}
|
|
251
322
|
|
|
@@ -256,6 +327,13 @@ declare class FormulaContext {
|
|
|
256
327
|
|
|
257
328
|
declare abstract class FormulaNode {
|
|
258
329
|
abstract evaluate(ctx: FormulaContext): FormulaValue;
|
|
330
|
+
/**
|
|
331
|
+
* Evaluates this node in tail position. Returns a TailCall when the node's
|
|
332
|
+
* value is a call to a lambda in tail position (so the caller's trampoline can
|
|
333
|
+
* iterate without growing the stack); otherwise the final value. The default
|
|
334
|
+
* is a plain evaluate — only control-flow and call nodes override it.
|
|
335
|
+
*/
|
|
336
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
259
337
|
}
|
|
260
338
|
declare class NumberNode extends FormulaNode {
|
|
261
339
|
readonly value: number;
|
|
@@ -310,6 +388,12 @@ declare class NamedRangeNode extends FormulaNode {
|
|
|
310
388
|
constructor(name: string);
|
|
311
389
|
evaluate(ctx: FormulaContext): FormulaValue;
|
|
312
390
|
}
|
|
391
|
+
/** Spill-range reference (A1#): the array that spills from an anchor cell. */
|
|
392
|
+
declare class SpillReferenceNode extends FormulaNode {
|
|
393
|
+
readonly anchorRef: string;
|
|
394
|
+
constructor(anchorRef: string);
|
|
395
|
+
evaluate(ctx: FormulaContext): FormulaValue;
|
|
396
|
+
}
|
|
313
397
|
declare const enum BinaryOperator {
|
|
314
398
|
Add = 0,
|
|
315
399
|
Subtract = 1,
|
|
@@ -354,6 +438,18 @@ declare class FunctionCallNode extends FormulaNode {
|
|
|
354
438
|
readonly args: FormulaNode[];
|
|
355
439
|
constructor(functionName: string, args: FormulaNode[]);
|
|
356
440
|
evaluate(ctx: FormulaContext): FormulaValue;
|
|
441
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Calls the value produced by an expression (e.g. an inline LAMBDA:
|
|
445
|
+
* `LAMBDA(x,x+1)(5)`). The callee must evaluate to a lambda, else #VALUE!.
|
|
446
|
+
*/
|
|
447
|
+
declare class CallNode extends FormulaNode {
|
|
448
|
+
readonly callee: FormulaNode;
|
|
449
|
+
readonly args: FormulaNode[];
|
|
450
|
+
constructor(callee: FormulaNode, args: FormulaNode[]);
|
|
451
|
+
evaluate(ctx: FormulaContext): FormulaValue;
|
|
452
|
+
evaluateTail(ctx: FormulaContext): FormulaValue | TailCall;
|
|
357
453
|
}
|
|
358
454
|
|
|
359
455
|
/**
|
|
@@ -438,6 +534,129 @@ declare class LruCache<K, V> {
|
|
|
438
534
|
resize(newCapacity: number): void;
|
|
439
535
|
}
|
|
440
536
|
|
|
537
|
+
/** A rectangular precedent region on a specific sheet (1-based, inclusive). */
|
|
538
|
+
interface PrecedentRef {
|
|
539
|
+
/** Upper-cased sheet name. */
|
|
540
|
+
readonly sheet: string;
|
|
541
|
+
readonly startRow: number;
|
|
542
|
+
readonly startCol: number;
|
|
543
|
+
readonly endRow: number;
|
|
544
|
+
readonly endCol: number;
|
|
545
|
+
}
|
|
546
|
+
interface ExtractResult {
|
|
547
|
+
readonly refs: PrecedentRef[];
|
|
548
|
+
readonly volatile: boolean;
|
|
549
|
+
}
|
|
550
|
+
/** Resolves a defined name to its refers-to expression, for name expansion. */
|
|
551
|
+
type NameResolver = (name: string, sheet: string) => string | undefined;
|
|
552
|
+
/**
|
|
553
|
+
* Extracts the precedents and volatility of a formula AST.
|
|
554
|
+
*
|
|
555
|
+
* @param ast The parsed formula.
|
|
556
|
+
* @param formulaSheet Upper-cased name of the sheet the formula lives on;
|
|
557
|
+
* unqualified references resolve against it.
|
|
558
|
+
* @param registry Function registry used to test volatility.
|
|
559
|
+
* @param resolveName Optional resolver so NamedRangeNode precedents expand to
|
|
560
|
+
* the references inside their refers-to expression.
|
|
561
|
+
*/
|
|
562
|
+
declare function extractReferences(ast: FormulaNode, formulaSheet: string, registry?: FunctionRegistry, resolveName?: NameResolver): ExtractResult;
|
|
563
|
+
/** True if the 1-based cell (sheet,row,col) falls inside a precedent region. */
|
|
564
|
+
declare function refContains(ref: PrecedentRef, sheet: string, row: number, col: number): boolean;
|
|
565
|
+
|
|
566
|
+
interface CoreCellLike {
|
|
567
|
+
setComputedValue(value: unknown): unknown;
|
|
568
|
+
setFormula(formula: string): unknown;
|
|
569
|
+
setArrayRef(spillRef: string | null): unknown;
|
|
570
|
+
clear(): void;
|
|
571
|
+
}
|
|
572
|
+
interface CoreSheetLike {
|
|
573
|
+
getCellValue(ref: string): unknown;
|
|
574
|
+
getCell(ref: string): CoreCellLike;
|
|
575
|
+
getCellFormula?(ref: string): string | null;
|
|
576
|
+
getSpillRange?(ref: string): string | null;
|
|
577
|
+
isRowHidden?(row: number): boolean;
|
|
578
|
+
}
|
|
579
|
+
interface CoreWorkbookLike {
|
|
580
|
+
getWorksheet(name: string): CoreSheetLike;
|
|
581
|
+
getDefinedName(name: string, sheet?: string): string | undefined;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Adapts a @devmm/puredocs-excel Workbook to a RecalcModel so the engine can
|
|
585
|
+
* read cell values and write computed results back into the document (via
|
|
586
|
+
* Cell.setComputedValue, which preserves the formula). Named ranges defined on
|
|
587
|
+
* the workbook are resolved automatically.
|
|
588
|
+
*/
|
|
589
|
+
declare function createWorkbookRecalcModel(workbook: CoreWorkbookLike): RecalcModel;
|
|
590
|
+
/** Minimal storage abstraction the engine reads inputs from and writes results to. */
|
|
591
|
+
interface RecalcModel {
|
|
592
|
+
/** Current value of a cell (raw input value, or last computed value). */
|
|
593
|
+
getCellValue(sheet: string, ref: string): unknown;
|
|
594
|
+
/** Store a computed formula result. Must NOT clear the cell's formula. */
|
|
595
|
+
setCellValue(sheet: string, ref: string, value: unknown): void;
|
|
596
|
+
/**
|
|
597
|
+
* Optional: persist a formula string on a cell so it serialises with the
|
|
598
|
+
* document. When present, the engine calls it from setCellFormula so the
|
|
599
|
+
* model stays the source of truth for the formula text.
|
|
600
|
+
*/
|
|
601
|
+
setCellFormula?(sheet: string, ref: string, formula: string): void;
|
|
602
|
+
/** Optional: clear a cell (value + formula) — used by clearCellFormula. */
|
|
603
|
+
clearCell?(sheet: string, ref: string): void;
|
|
604
|
+
/** Optional: mark/unmark a cell as a dynamic-array anchor with its spill range. */
|
|
605
|
+
setCellArrayRef?(sheet: string, ref: string, spillRef: string | null): void;
|
|
606
|
+
/** Optional: formula text of a cell (lets SUBTOTAL skip nested calls). */
|
|
607
|
+
getCellFormula?(sheet: string, ref: string): string | null;
|
|
608
|
+
/** Optional: spill range of a dynamic-array anchor (enables A1#). */
|
|
609
|
+
getSpillRange?(sheet: string, ref: string): string | null;
|
|
610
|
+
/** Optional: whether a 1-based row is hidden (SUBTOTAL/AGGREGATE). */
|
|
611
|
+
isRowHidden?(sheet: string, row: number): boolean;
|
|
612
|
+
/** Optional defined-name resolver (enables named ranges in formulas). */
|
|
613
|
+
getDefinedName?(name: string, sheet?: string): string | undefined;
|
|
614
|
+
}
|
|
615
|
+
/** One recomputed cell, in evaluation order. */
|
|
616
|
+
interface RecalcResult {
|
|
617
|
+
readonly sheet: string;
|
|
618
|
+
readonly ref: string;
|
|
619
|
+
/** The written JS value (null/number/string/boolean). For a dynamic array,
|
|
620
|
+
* this is the anchor's top-left value. */
|
|
621
|
+
readonly value: unknown;
|
|
622
|
+
/** True when the cell is part of a circular reference. */
|
|
623
|
+
readonly circular?: boolean;
|
|
624
|
+
/** For a dynamic-array anchor: the spilled block size (rows × cols). */
|
|
625
|
+
readonly spill?: {
|
|
626
|
+
rows: number;
|
|
627
|
+
cols: number;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
declare class RecalcEngine {
|
|
631
|
+
#private;
|
|
632
|
+
constructor(model: RecalcModel, registry?: FunctionRegistry);
|
|
633
|
+
/**
|
|
634
|
+
* Registers (or replaces) a formula for a cell and recalculates that cell and
|
|
635
|
+
* everything downstream of it. Returns the recomputed cells in the order they
|
|
636
|
+
* were evaluated. The leading '=' of the formula is optional.
|
|
637
|
+
*/
|
|
638
|
+
setCellFormula(sheet: string, ref: string, formula: string): RecalcResult[];
|
|
639
|
+
/**
|
|
640
|
+
* Removes a formula from a cell and recalculates its dependents (which may now
|
|
641
|
+
* read a plain input value instead). Returns the recomputed dependent cells.
|
|
642
|
+
*/
|
|
643
|
+
clearCellFormula(sheet: string, ref: string): RecalcResult[];
|
|
644
|
+
/**
|
|
645
|
+
* Writes a raw input value to a cell and recalculates every formula that
|
|
646
|
+
* depends on it. Use this for non-formula edits so dependents stay current.
|
|
647
|
+
*/
|
|
648
|
+
setCellValue(sheet: string, ref: string, value: unknown): RecalcResult[];
|
|
649
|
+
/**
|
|
650
|
+
* Signals that a cell changed by some external means (the model was already
|
|
651
|
+
* updated) and recalculates its dependents.
|
|
652
|
+
*/
|
|
653
|
+
onCellChanged(sheet: string, ref: string): RecalcResult[];
|
|
654
|
+
/** Recalculates every registered formula in dependency order. */
|
|
655
|
+
recalcAll(): RecalcResult[];
|
|
656
|
+
/** The precedents recorded for a formula cell (mainly for tests/inspection). */
|
|
657
|
+
getPrecedents(sheet: string, ref: string): readonly PrecedentRef[];
|
|
658
|
+
}
|
|
659
|
+
|
|
441
660
|
declare function registerMath(r: FunctionRegistry): void;
|
|
442
661
|
|
|
443
662
|
declare function registerText(r: FunctionRegistry): void;
|
|
@@ -452,4 +671,12 @@ declare function registerStatistical(r: FunctionRegistry): void;
|
|
|
452
671
|
|
|
453
672
|
declare function registerInfo(r: FunctionRegistry): void;
|
|
454
673
|
|
|
455
|
-
|
|
674
|
+
declare function registerFinance(r: FunctionRegistry): void;
|
|
675
|
+
|
|
676
|
+
declare function registerMeta(r: FunctionRegistry): void;
|
|
677
|
+
|
|
678
|
+
declare function registerAggregate(r: FunctionRegistry): void;
|
|
679
|
+
|
|
680
|
+
declare function registerArray(r: FunctionRegistry): void;
|
|
681
|
+
|
|
682
|
+
export { ArrayValue, BinaryOpNode, BinaryOperator, BlankNode, BooleanNode, CallNode, CellReferenceNode, ErrorNode, type ExtractResult, FormulaContext, FormulaError, FormulaException, type FormulaFunction, FormulaHelper, FormulaLexer, FormulaNode, FormulaParser, FormulaValue, type FormulaValueKind, FunctionCallNode, type FunctionDef, FunctionRegistry, ImplicitIntersectionNode, type LambdaLike, LruCache, type NameResolver, NamedRangeNode, NumberNode, type PrecedentRef, RangeReferenceNode, RecalcEngine, type RecalcModel, type RecalcResult, SheetCellReferenceNode, SheetRangeReferenceNode, SpillReferenceNode, StringNode, type Token, TokenType, UnaryOpNode, UnaryOperator, type WorkbookLike, type WorksheetLike, clearAstCache, createWorkbookRecalcModel, evaluateFormula, extractReferences, getAstCacheStats, parseFormula, refContains, registerAggregate, registerArray, registerDate, registerFinance, registerInfo, registerLogical, registerLookup, registerMath, registerMeta, registerStatistical, registerText, setAstCacheCapacity };
|