@molecule/api-formula-engine 1.0.0

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.
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Type definitions for the formula-engine.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Spreadsheet error codes. Errors propagate through expressions: any
8
+ * arithmetic or function call involving a `FormulaError` returns the
9
+ * left-most error unchanged (Excel-compatible semantics).
10
+ */
11
+ export type FormulaErrorCode = '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A' | '#CIRC!';
12
+ /**
13
+ * Spreadsheet error value. Returned (never thrown) by `evaluate`/`getValue`
14
+ * to allow downstream cells to propagate it like Excel.
15
+ */
16
+ export interface FormulaError {
17
+ readonly type: 'error';
18
+ readonly code: FormulaErrorCode;
19
+ readonly message?: string;
20
+ }
21
+ /**
22
+ * Possible cell values. `null` represents an empty cell; numbers, strings,
23
+ * booleans, and `Date`s are all valid scalar values. `FormulaError`
24
+ * represents a propagating error.
25
+ */
26
+ export type CellValue = number | string | boolean | Date | null | FormulaError;
27
+ /**
28
+ * Cell coordinate as a 0-indexed `{ col, row }`. Column 0 = "A", row 0 = 1
29
+ * (i.e. `A1` is `{ col: 0, row: 0 }`).
30
+ */
31
+ export interface CellCoord {
32
+ readonly col: number;
33
+ readonly row: number;
34
+ }
35
+ /**
36
+ * A rectangular range of cells, half-inclusive of both corners.
37
+ */
38
+ export interface CellRange {
39
+ readonly start: CellCoord;
40
+ readonly end: CellCoord;
41
+ }
42
+ /**
43
+ * AST node kinds emitted by the parser.
44
+ */
45
+ export type AstNode = NumberLiteralNode | StringLiteralNode | BooleanLiteralNode | ErrorLiteralNode | ReferenceNode | RangeNode | UnaryOpNode | BinaryOpNode | FunctionCallNode;
46
+ /** AST: numeric literal. */
47
+ export interface NumberLiteralNode {
48
+ readonly kind: 'number';
49
+ readonly value: number;
50
+ }
51
+ /** AST: string literal (double-quoted). */
52
+ export interface StringLiteralNode {
53
+ readonly kind: 'string';
54
+ readonly value: string;
55
+ }
56
+ /** AST: boolean literal (TRUE / FALSE). */
57
+ export interface BooleanLiteralNode {
58
+ readonly kind: 'boolean';
59
+ readonly value: boolean;
60
+ }
61
+ /** AST: error literal (`#DIV/0!`, etc.). */
62
+ export interface ErrorLiteralNode {
63
+ readonly kind: 'errorLiteral';
64
+ readonly code: FormulaErrorCode;
65
+ }
66
+ /** AST: cell reference (e.g. `A1`, `$A$1`). */
67
+ export interface ReferenceNode {
68
+ readonly kind: 'reference';
69
+ readonly coord: CellCoord;
70
+ /** Original textual representation, preserved for diagnostics. */
71
+ readonly text: string;
72
+ }
73
+ /** AST: cell range (e.g. `A1:B5`). */
74
+ export interface RangeNode {
75
+ readonly kind: 'range';
76
+ readonly range: CellRange;
77
+ readonly text: string;
78
+ }
79
+ /**
80
+ * Unary operators supported by the parser.
81
+ */
82
+ export type UnaryOperator = '+' | '-' | '%';
83
+ /** AST: unary operation (e.g. `-A1`, `A1%`). */
84
+ export interface UnaryOpNode {
85
+ readonly kind: 'unary';
86
+ readonly op: UnaryOperator;
87
+ readonly operand: AstNode;
88
+ }
89
+ /**
90
+ * Binary operators. `^` = power, `&` = string concat,
91
+ * `=`/`<>` = equality, `<`/`<=`/`>`/`>=` = ordering.
92
+ */
93
+ export type BinaryOperator = '+' | '-' | '*' | '/' | '^' | '&' | '=' | '<>' | '<' | '<=' | '>' | '>=';
94
+ /** AST: binary operation. */
95
+ export interface BinaryOpNode {
96
+ readonly kind: 'binary';
97
+ readonly op: BinaryOperator;
98
+ readonly left: AstNode;
99
+ readonly right: AstNode;
100
+ }
101
+ /** AST: function call (e.g. `SUM(A1:A10)`, `IF(A1>0, "y", "n")`). */
102
+ export interface FunctionCallNode {
103
+ readonly kind: 'call';
104
+ readonly name: string;
105
+ readonly args: AstNode[];
106
+ }
107
+ /**
108
+ * Function implementation signature. Functions receive already-evaluated
109
+ * argument expressions: ranges are expanded into flat `CellValue[]`
110
+ * arrays. Functions return a `CellValue` (including `FormulaError` for
111
+ * domain errors).
112
+ */
113
+ export type FormulaFunction = (args: ReadonlyArray<CellValue | ReadonlyArray<CellValue>>, ctx: FunctionContext) => CellValue;
114
+ /**
115
+ * Context handed to function implementations. Currently exposes the
116
+ * `now` function so date/time helpers can be made deterministic for
117
+ * tests via `evaluateOptions.now`.
118
+ */
119
+ export interface FunctionContext {
120
+ /** Returns the current date-time. */
121
+ readonly now: () => Date;
122
+ }
123
+ /**
124
+ * Options shared by `evaluate` and the `Sheet.recompute` family.
125
+ */
126
+ export interface EvaluateOptions {
127
+ /** Override the clock used by `TODAY()` / `NOW()`. Defaults to `Date.now()`. */
128
+ readonly now?: () => Date;
129
+ }
130
+ /**
131
+ * Sheet-level configuration. `numCols` / `numRows` are advisory bounds
132
+ * used when validating references; out-of-bounds references produce
133
+ * `#REF!`.
134
+ */
135
+ export interface SheetConfig {
136
+ /** Maximum number of columns. Default: 16384 (Excel's XFD). */
137
+ readonly numCols?: number;
138
+ /** Maximum number of rows. Default: 1048576 (Excel's row count). */
139
+ readonly numRows?: number;
140
+ }
141
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Type definitions for the formula-engine.
3
+ *
4
+ * @module
5
+ */
6
+ export {};
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Value coercion + error helpers shared by the evaluator and built-in
3
+ * function implementations.
4
+ *
5
+ * @module
6
+ */
7
+ import type { CellValue, FormulaError, FormulaErrorCode } from './types.js';
8
+ /**
9
+ * Construct a `FormulaError` value.
10
+ *
11
+ * @param code - Error code.
12
+ * @param message - Optional human-readable description.
13
+ * @returns The error value.
14
+ */
15
+ export declare function makeError(code: FormulaErrorCode, message?: string): FormulaError;
16
+ /**
17
+ * Type-guard for `FormulaError`.
18
+ *
19
+ * @param value - Cell value to inspect.
20
+ * @returns `true` if the value is a propagating error.
21
+ */
22
+ export declare function isError(value: unknown): value is FormulaError;
23
+ /**
24
+ * Returns the first error encountered in a flat list, or `null` if none.
25
+ *
26
+ * @param values - Cell values to scan.
27
+ * @returns The first error value, or `null`.
28
+ */
29
+ export declare function firstError(values: ReadonlyArray<CellValue>): FormulaError | null;
30
+ /**
31
+ * Coerce a cell value to a number (Excel-compatible).
32
+ *
33
+ * - `null` → 0
34
+ * - boolean → 0/1
35
+ * - `Date` → days since 1899-12-30 (Excel's epoch)
36
+ * - string → parsed as number, `#VALUE!` if not parseable
37
+ * - error → propagated unchanged
38
+ *
39
+ * @param value - Cell value.
40
+ * @returns Number, or `FormulaError` on failure.
41
+ */
42
+ export declare function toNumber(value: CellValue): number | FormulaError;
43
+ /**
44
+ * Coerce a cell value to a string for display / `&` concatenation.
45
+ *
46
+ * @param value - Cell value.
47
+ * @returns String, or `FormulaError` for propagating errors.
48
+ */
49
+ export declare function toStringValue(value: CellValue): string | FormulaError;
50
+ /**
51
+ * Coerce a cell value to a boolean (Excel-compatible).
52
+ *
53
+ * @param value - Cell value.
54
+ * @returns Boolean, or `FormulaError` on failure.
55
+ */
56
+ export declare function toBoolean(value: CellValue): boolean | FormulaError;
57
+ /**
58
+ * Convert a `Date` to an Excel serial number (days since 1899-12-30).
59
+ *
60
+ * @param date - Date instance.
61
+ * @returns Excel serial date.
62
+ */
63
+ export declare function dateToSerial(date: Date): number;
64
+ /**
65
+ * Convert an Excel serial number to a `Date` (UTC).
66
+ *
67
+ * @param serial - Excel serial date.
68
+ * @returns Date instance.
69
+ */
70
+ export declare function serialToDate(serial: number): Date;
71
+ /**
72
+ * Compare two scalar cell values (Excel ordering: number < string,
73
+ * boolean compared as 0/1 within numbers).
74
+ *
75
+ * @param left - Left operand.
76
+ * @param right - Right operand.
77
+ * @returns -1, 0, 1, or `FormulaError` if uncomparable.
78
+ */
79
+ export declare function compareValues(left: CellValue, right: CellValue): number | FormulaError;
80
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE3E;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAI7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,YAAY,GAAG,IAAI,CAKhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,YAAY,CAahE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,YAAY,CAWrE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,CAYlE;AAKD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,YAAY,CAoCtF"}
package/dist/values.js ADDED
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Value coercion + error helpers shared by the evaluator and built-in
3
+ * function implementations.
4
+ *
5
+ * @module
6
+ */
7
+ /**
8
+ * Construct a `FormulaError` value.
9
+ *
10
+ * @param code - Error code.
11
+ * @param message - Optional human-readable description.
12
+ * @returns The error value.
13
+ */
14
+ export function makeError(code, message) {
15
+ return message === undefined ? { type: 'error', code } : { type: 'error', code, message };
16
+ }
17
+ /**
18
+ * Type-guard for `FormulaError`.
19
+ *
20
+ * @param value - Cell value to inspect.
21
+ * @returns `true` if the value is a propagating error.
22
+ */
23
+ export function isError(value) {
24
+ return (typeof value === 'object' && value !== null && value.type === 'error');
25
+ }
26
+ /**
27
+ * Returns the first error encountered in a flat list, or `null` if none.
28
+ *
29
+ * @param values - Cell values to scan.
30
+ * @returns The first error value, or `null`.
31
+ */
32
+ export function firstError(values) {
33
+ for (const v of values) {
34
+ if (isError(v))
35
+ return v;
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Coerce a cell value to a number (Excel-compatible).
41
+ *
42
+ * - `null` → 0
43
+ * - boolean → 0/1
44
+ * - `Date` → days since 1899-12-30 (Excel's epoch)
45
+ * - string → parsed as number, `#VALUE!` if not parseable
46
+ * - error → propagated unchanged
47
+ *
48
+ * @param value - Cell value.
49
+ * @returns Number, or `FormulaError` on failure.
50
+ */
51
+ export function toNumber(value) {
52
+ if (isError(value))
53
+ return value;
54
+ if (value === null)
55
+ return 0;
56
+ if (typeof value === 'number')
57
+ return value;
58
+ if (typeof value === 'boolean')
59
+ return value ? 1 : 0;
60
+ if (value instanceof Date)
61
+ return dateToSerial(value);
62
+ if (typeof value === 'string') {
63
+ if (value.trim() === '')
64
+ return 0;
65
+ const n = Number(value);
66
+ if (Number.isFinite(n))
67
+ return n;
68
+ return makeError('#VALUE!', `Cannot coerce "${value}" to number`);
69
+ }
70
+ return makeError('#VALUE!');
71
+ }
72
+ /**
73
+ * Coerce a cell value to a string for display / `&` concatenation.
74
+ *
75
+ * @param value - Cell value.
76
+ * @returns String, or `FormulaError` for propagating errors.
77
+ */
78
+ export function toStringValue(value) {
79
+ if (isError(value))
80
+ return value;
81
+ if (value === null)
82
+ return '';
83
+ if (typeof value === 'string')
84
+ return value;
85
+ if (typeof value === 'number') {
86
+ if (!Number.isFinite(value))
87
+ return makeError('#NUM!');
88
+ return String(value);
89
+ }
90
+ if (typeof value === 'boolean')
91
+ return value ? 'TRUE' : 'FALSE';
92
+ if (value instanceof Date)
93
+ return value.toISOString();
94
+ return makeError('#VALUE!');
95
+ }
96
+ /**
97
+ * Coerce a cell value to a boolean (Excel-compatible).
98
+ *
99
+ * @param value - Cell value.
100
+ * @returns Boolean, or `FormulaError` on failure.
101
+ */
102
+ export function toBoolean(value) {
103
+ if (isError(value))
104
+ return value;
105
+ if (typeof value === 'boolean')
106
+ return value;
107
+ if (value === null)
108
+ return false;
109
+ if (typeof value === 'number')
110
+ return value !== 0;
111
+ if (typeof value === 'string') {
112
+ const u = value.toUpperCase().trim();
113
+ if (u === 'TRUE')
114
+ return true;
115
+ if (u === 'FALSE')
116
+ return false;
117
+ return makeError('#VALUE!', `Cannot coerce "${value}" to boolean`);
118
+ }
119
+ return makeError('#VALUE!');
120
+ }
121
+ const EXCEL_EPOCH = Date.UTC(1899, 11, 30);
122
+ const MS_PER_DAY = 86_400_000;
123
+ /**
124
+ * Convert a `Date` to an Excel serial number (days since 1899-12-30).
125
+ *
126
+ * @param date - Date instance.
127
+ * @returns Excel serial date.
128
+ */
129
+ export function dateToSerial(date) {
130
+ return (date.getTime() - EXCEL_EPOCH) / MS_PER_DAY;
131
+ }
132
+ /**
133
+ * Convert an Excel serial number to a `Date` (UTC).
134
+ *
135
+ * @param serial - Excel serial date.
136
+ * @returns Date instance.
137
+ */
138
+ export function serialToDate(serial) {
139
+ return new Date(EXCEL_EPOCH + serial * MS_PER_DAY);
140
+ }
141
+ /**
142
+ * Compare two scalar cell values (Excel ordering: number < string,
143
+ * boolean compared as 0/1 within numbers).
144
+ *
145
+ * @param left - Left operand.
146
+ * @param right - Right operand.
147
+ * @returns -1, 0, 1, or `FormulaError` if uncomparable.
148
+ */
149
+ export function compareValues(left, right) {
150
+ if (isError(left))
151
+ return left;
152
+ if (isError(right))
153
+ return right;
154
+ // null normalizes to 0 / "" depending on the other side
155
+ if (typeof left === 'number' || typeof left === 'boolean' || left === null) {
156
+ const ln = toNumber(left);
157
+ if (isError(ln))
158
+ return ln;
159
+ if (typeof right === 'string') {
160
+ // Excel: numbers always sort before strings
161
+ return -1;
162
+ }
163
+ const rn = toNumber(right);
164
+ if (isError(rn))
165
+ return rn;
166
+ return ln === rn ? 0 : ln < rn ? -1 : 1;
167
+ }
168
+ if (left instanceof Date) {
169
+ const ln = dateToSerial(left);
170
+ const rn = toNumber(right);
171
+ if (isError(rn))
172
+ return rn;
173
+ return ln === rn ? 0 : ln < rn ? -1 : 1;
174
+ }
175
+ if (typeof left === 'string') {
176
+ if (typeof right === 'number' || typeof right === 'boolean' || right === null) {
177
+ return 1;
178
+ }
179
+ if (right instanceof Date) {
180
+ return 1;
181
+ }
182
+ if (typeof right === 'string') {
183
+ // Case-insensitive comparison, like Excel.
184
+ const a = left.toUpperCase();
185
+ const b = right.toUpperCase();
186
+ return a === b ? 0 : a < b ? -1 : 1;
187
+ }
188
+ }
189
+ return makeError('#VALUE!');
190
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@molecule/api-formula-engine",
3
+ "version": "1.0.0",
4
+ "description": "Spreadsheet-style formula evaluator: parser, evaluator, dependency tracker for cell graphs with arithmetic, comparison, string ops, references, aggregations, conditionals, date/text functions, and circular-ref detection.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "keywords": [
23
+ "molecule",
24
+ "formula",
25
+ "engine"
26
+ ],
27
+ "license": "Apache-2.0",
28
+ "devDependencies": {
29
+ "@types/node": "26.1.2",
30
+ "typescript": "6.0.3",
31
+ "vitest": "4.1.10"
32
+ }
33
+ }