@affino/datagrid-formula-engine 0.1.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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/analysis/index.d.ts +2 -0
  4. package/dist/analysis/index.d.ts.map +1 -0
  5. package/dist/analysis/index.js +1 -0
  6. package/dist/analysis/types.d.ts +44 -0
  7. package/dist/analysis/types.d.ts.map +1 -0
  8. package/dist/analysis/types.js +1 -0
  9. package/dist/contracts.d.ts +177 -0
  10. package/dist/contracts.d.ts.map +1 -0
  11. package/dist/contracts.js +140 -0
  12. package/dist/coreTypes.d.ts +2 -0
  13. package/dist/coreTypes.d.ts.map +1 -0
  14. package/dist/coreTypes.js +1 -0
  15. package/dist/dependency/index.d.ts +2 -0
  16. package/dist/dependency/index.d.ts.map +1 -0
  17. package/dist/dependency/index.js +1 -0
  18. package/dist/evaluators/columnar.d.ts +6 -0
  19. package/dist/evaluators/columnar.d.ts.map +1 -0
  20. package/dist/evaluators/columnar.js +39 -0
  21. package/dist/evaluators/index.d.ts +5 -0
  22. package/dist/evaluators/index.d.ts.map +1 -0
  23. package/dist/evaluators/index.js +4 -0
  24. package/dist/evaluators/interpreter.d.ts +6 -0
  25. package/dist/evaluators/interpreter.d.ts.map +1 -0
  26. package/dist/evaluators/interpreter.js +156 -0
  27. package/dist/evaluators/jit.d.ts +7 -0
  28. package/dist/evaluators/jit.d.ts.map +1 -0
  29. package/dist/evaluators/jit.js +158 -0
  30. package/dist/evaluators/shared.d.ts +36 -0
  31. package/dist/evaluators/shared.d.ts.map +1 -0
  32. package/dist/evaluators/shared.js +242 -0
  33. package/dist/evaluators/vector.d.ts +6 -0
  34. package/dist/evaluators/vector.d.ts.map +1 -0
  35. package/dist/evaluators/vector.js +228 -0
  36. package/dist/formulaEngine/compile.d.ts +2 -0
  37. package/dist/formulaEngine/compile.d.ts.map +1 -0
  38. package/dist/formulaEngine/compile.js +1 -0
  39. package/dist/formulaEngine/core.d.ts +2 -0
  40. package/dist/formulaEngine/core.d.ts.map +1 -0
  41. package/dist/formulaEngine/core.js +1 -0
  42. package/dist/formulaEngine/evaluators.d.ts +2 -0
  43. package/dist/formulaEngine/evaluators.d.ts.map +1 -0
  44. package/dist/formulaEngine/evaluators.js +1 -0
  45. package/dist/formulaEngine/index.d.ts +5 -0
  46. package/dist/formulaEngine/index.d.ts.map +1 -0
  47. package/dist/formulaEngine/index.js +2 -0
  48. package/dist/formulaExecutionPlan.d.ts +2 -0
  49. package/dist/formulaExecutionPlan.d.ts.map +1 -0
  50. package/dist/formulaExecutionPlan.js +1 -0
  51. package/dist/graph/executionPlan.d.ts +66 -0
  52. package/dist/graph/executionPlan.d.ts.map +1 -0
  53. package/dist/graph/executionPlan.js +534 -0
  54. package/dist/graph/index.d.ts +2 -0
  55. package/dist/graph/index.d.ts.map +1 -0
  56. package/dist/graph/index.js +3 -0
  57. package/dist/index.d.ts +8 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +4 -0
  60. package/dist/runtime/compile.d.ts +7 -0
  61. package/dist/runtime/compile.d.ts.map +1 -0
  62. package/dist/runtime/compile.js +453 -0
  63. package/dist/runtime/evaluators.d.ts +5 -0
  64. package/dist/runtime/evaluators.d.ts.map +1 -0
  65. package/dist/runtime/evaluators.js +6 -0
  66. package/dist/runtime/index.d.ts +3 -0
  67. package/dist/runtime/index.d.ts.map +1 -0
  68. package/dist/runtime/index.js +2 -0
  69. package/dist/runtime/types.d.ts +63 -0
  70. package/dist/runtime/types.d.ts.map +1 -0
  71. package/dist/runtime/types.js +1 -0
  72. package/dist/syntax/analysis.d.ts +14 -0
  73. package/dist/syntax/analysis.d.ts.map +1 -0
  74. package/dist/syntax/analysis.js +159 -0
  75. package/dist/syntax/ast.d.ts +18 -0
  76. package/dist/syntax/ast.d.ts.map +1 -0
  77. package/dist/syntax/ast.js +54 -0
  78. package/dist/syntax/core.d.ts +4 -0
  79. package/dist/syntax/core.d.ts.map +1 -0
  80. package/dist/syntax/core.js +1 -0
  81. package/dist/syntax/functionGroups/advancedFunctions.d.ts +2 -0
  82. package/dist/syntax/functionGroups/advancedFunctions.d.ts.map +1 -0
  83. package/dist/syntax/functionGroups/advancedFunctions.js +252 -0
  84. package/dist/syntax/functionGroups/dateFunctions.d.ts +2 -0
  85. package/dist/syntax/functionGroups/dateFunctions.d.ts.map +1 -0
  86. package/dist/syntax/functionGroups/dateFunctions.js +144 -0
  87. package/dist/syntax/functionGroups/logicFunctions.d.ts +2 -0
  88. package/dist/syntax/functionGroups/logicFunctions.d.ts.map +1 -0
  89. package/dist/syntax/functionGroups/logicFunctions.js +140 -0
  90. package/dist/syntax/functionGroups/numericFunctions.d.ts +2 -0
  91. package/dist/syntax/functionGroups/numericFunctions.d.ts.map +1 -0
  92. package/dist/syntax/functionGroups/numericFunctions.js +268 -0
  93. package/dist/syntax/functionGroups/textFunctions.d.ts +2 -0
  94. package/dist/syntax/functionGroups/textFunctions.d.ts.map +1 -0
  95. package/dist/syntax/functionGroups/textFunctions.js +118 -0
  96. package/dist/syntax/functionHelpers.d.ts +45 -0
  97. package/dist/syntax/functionHelpers.d.ts.map +1 -0
  98. package/dist/syntax/functionHelpers.js +553 -0
  99. package/dist/syntax/functions.d.ts +9 -0
  100. package/dist/syntax/functions.d.ts.map +1 -0
  101. package/dist/syntax/functions.js +139 -0
  102. package/dist/syntax/index.d.ts +10 -0
  103. package/dist/syntax/index.d.ts.map +1 -0
  104. package/dist/syntax/index.js +7 -0
  105. package/dist/syntax/legacy.d.ts +29 -0
  106. package/dist/syntax/legacy.d.ts.map +1 -0
  107. package/dist/syntax/legacy.js +188 -0
  108. package/dist/syntax/optimizer.d.ts +6 -0
  109. package/dist/syntax/optimizer.d.ts.map +1 -0
  110. package/dist/syntax/optimizer.js +362 -0
  111. package/dist/syntax/parser.d.ts +7 -0
  112. package/dist/syntax/parser.d.ts.map +1 -0
  113. package/dist/syntax/parser.js +239 -0
  114. package/dist/syntax/tokenizer.d.ts +14 -0
  115. package/dist/syntax/tokenizer.d.ts.map +1 -0
  116. package/dist/syntax/tokenizer.js +852 -0
  117. package/dist/syntax/types.d.ts +120 -0
  118. package/dist/syntax/types.d.ts.map +1 -0
  119. package/dist/syntax/types.js +1 -0
  120. package/dist/syntax/values.d.ts +25 -0
  121. package/dist/syntax/values.d.ts.map +1 -0
  122. package/dist/syntax/values.js +270 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -0
  124. package/package.json +42 -0
@@ -0,0 +1,362 @@
1
+ import { createFormulaSourceSpan, throwFormulaError, } from "./ast.js";
2
+ import { normalizeFormulaFunctionName, } from "./functions.js";
3
+ import { areFormulaValuesEqual, coerceFormulaValueToBoolean, coerceFormulaValueToNumber, compareFormulaValues, isFormulaErrorValue, isFormulaValuePresent, normalizeFormulaValue, } from "./values.js";
4
+ function validateFormulaFunctionArity(functionDefinition, argsCount) {
5
+ const { arity } = functionDefinition;
6
+ if (typeof arity === "undefined") {
7
+ return;
8
+ }
9
+ if (typeof arity === "number") {
10
+ if (argsCount !== arity) {
11
+ throwFormulaError(`Function '${functionDefinition.name}' expects ${arity} argument(s), got ${argsCount}.`);
12
+ }
13
+ return;
14
+ }
15
+ if (argsCount < arity.min) {
16
+ throwFormulaError(`Function '${functionDefinition.name}' expects at least ${arity.min} argument(s), got ${argsCount}.`);
17
+ }
18
+ if (typeof arity.max === "number" && argsCount > arity.max) {
19
+ throwFormulaError(`Function '${functionDefinition.name}' expects at most ${arity.max} argument(s), got ${argsCount}.`);
20
+ }
21
+ }
22
+ export function collectFormulaIdentifiers(root, output) {
23
+ if (root.kind === "identifier") {
24
+ output.push(root.name);
25
+ return;
26
+ }
27
+ if (root.kind === "call") {
28
+ for (const arg of root.args) {
29
+ collectFormulaIdentifiers(arg, output);
30
+ }
31
+ return;
32
+ }
33
+ if (root.kind === "unary") {
34
+ collectFormulaIdentifiers(root.value, output);
35
+ return;
36
+ }
37
+ if (root.kind === "binary") {
38
+ collectFormulaIdentifiers(root.left, output);
39
+ collectFormulaIdentifiers(root.right, output);
40
+ }
41
+ }
42
+ export function validateFormulaFunctions(root, functionRegistry) {
43
+ if (root.kind === "call") {
44
+ const functionName = normalizeFormulaFunctionName(root.name);
45
+ const functionDefinition = functionRegistry.get(functionName);
46
+ if (!functionDefinition) {
47
+ throwFormulaError(`Unknown function '${root.name}'.`, root.span);
48
+ }
49
+ validateFormulaFunctionArity(functionDefinition, root.args.length);
50
+ if (functionName === "IFS" && root.args.length % 2 !== 0) {
51
+ throwFormulaError("Function 'IFS' expects an even number of arguments (condition/value pairs).", root.span);
52
+ }
53
+ for (const arg of root.args) {
54
+ validateFormulaFunctions(arg, functionRegistry);
55
+ }
56
+ return;
57
+ }
58
+ if (root.kind === "unary") {
59
+ validateFormulaFunctions(root.value, functionRegistry);
60
+ return;
61
+ }
62
+ if (root.kind === "binary") {
63
+ validateFormulaFunctions(root.left, functionRegistry);
64
+ validateFormulaFunctions(root.right, functionRegistry);
65
+ }
66
+ }
67
+ function createFormulaNumberNode(value) {
68
+ return {
69
+ kind: "number",
70
+ value: Number.isFinite(value) ? value : 0,
71
+ span: createFormulaSourceSpan(0, 0),
72
+ };
73
+ }
74
+ function createFormulaLiteralNode(value, span) {
75
+ const normalized = normalizeFormulaValue(value);
76
+ if (typeof normalized === "number") {
77
+ return {
78
+ kind: "number",
79
+ value: normalized,
80
+ span: span !== null && span !== void 0 ? span : createFormulaSourceSpan(0, 0),
81
+ };
82
+ }
83
+ return {
84
+ kind: "literal",
85
+ value: normalized,
86
+ span: span !== null && span !== void 0 ? span : createFormulaSourceSpan(0, 0),
87
+ };
88
+ }
89
+ function isFormulaLiteralNode(node) {
90
+ return node.kind === "number" || node.kind === "literal";
91
+ }
92
+ function getFormulaLiteralNodeValue(node) {
93
+ return node.kind === "number" ? node.value : node.value;
94
+ }
95
+ function coerceFormulaValueToDate(value) {
96
+ var _a;
97
+ const scalarValue = Array.isArray(value) ? ((_a = value[0]) !== null && _a !== void 0 ? _a : null) : value;
98
+ if (scalarValue === null) {
99
+ return null;
100
+ }
101
+ if (isFormulaErrorValue(scalarValue)) {
102
+ throwFormulaError(scalarValue.message);
103
+ }
104
+ if (scalarValue instanceof Date) {
105
+ return Number.isNaN(scalarValue.getTime()) ? null : scalarValue;
106
+ }
107
+ if (typeof scalarValue === "number") {
108
+ if (!Number.isFinite(scalarValue)) {
109
+ return null;
110
+ }
111
+ const date = new Date(scalarValue);
112
+ return Number.isNaN(date.getTime()) ? null : date;
113
+ }
114
+ if (typeof scalarValue === "string") {
115
+ const text = scalarValue.trim();
116
+ if (text.length === 0) {
117
+ return null;
118
+ }
119
+ const timestamp = Date.parse(text);
120
+ if (!Number.isFinite(timestamp)) {
121
+ return null;
122
+ }
123
+ const date = new Date(timestamp);
124
+ return Number.isNaN(date.getTime()) ? null : date;
125
+ }
126
+ return null;
127
+ }
128
+ export function foldFormulaConstants(root) {
129
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
130
+ if (root.kind === "number" || root.kind === "literal" || root.kind === "identifier") {
131
+ return root;
132
+ }
133
+ if (root.kind === "unary") {
134
+ const value = foldFormulaConstants(root.value);
135
+ if (!isFormulaLiteralNode(value)) {
136
+ return {
137
+ kind: "unary",
138
+ operator: root.operator,
139
+ value,
140
+ span: root.span,
141
+ };
142
+ }
143
+ const valueLiteral = getFormulaLiteralNodeValue(value);
144
+ if (root.operator === "-") {
145
+ return createFormulaLiteralNode(-coerceFormulaValueToNumber(valueLiteral), root.span);
146
+ }
147
+ if (root.operator === "+") {
148
+ return createFormulaLiteralNode(+coerceFormulaValueToNumber(valueLiteral), root.span);
149
+ }
150
+ return createFormulaLiteralNode(coerceFormulaValueToBoolean(valueLiteral) ? 0 : 1, root.span);
151
+ }
152
+ if (root.kind === "call") {
153
+ const args = root.args.map(arg => foldFormulaConstants(arg));
154
+ const normalizedFunctionName = normalizeFormulaFunctionName(root.name);
155
+ if (normalizedFunctionName === "IF") {
156
+ const condition = (_a = args[0]) !== null && _a !== void 0 ? _a : createFormulaNumberNode(0);
157
+ const whenTrue = (_b = args[1]) !== null && _b !== void 0 ? _b : createFormulaNumberNode(0);
158
+ const whenFalse = (_c = args[2]) !== null && _c !== void 0 ? _c : createFormulaNumberNode(0);
159
+ if (isFormulaLiteralNode(condition)) {
160
+ return coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(condition))
161
+ ? whenTrue
162
+ : whenFalse;
163
+ }
164
+ return { kind: "call", name: root.name, args, span: root.span };
165
+ }
166
+ if (normalizedFunctionName === "IFS") {
167
+ for (let index = 0; index < args.length; index += 2) {
168
+ const condition = args[index];
169
+ if (!condition || !isFormulaLiteralNode(condition)) {
170
+ return { kind: "call", name: root.name, args, span: root.span };
171
+ }
172
+ const value = (_d = args[index + 1]) !== null && _d !== void 0 ? _d : createFormulaNumberNode(0);
173
+ if (coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(condition))) {
174
+ return value;
175
+ }
176
+ }
177
+ return createFormulaNumberNode(0);
178
+ }
179
+ if (normalizedFunctionName === "COALESCE") {
180
+ for (const value of args) {
181
+ if (!isFormulaLiteralNode(value)) {
182
+ return { kind: "call", name: root.name, args, span: root.span };
183
+ }
184
+ if (isFormulaValuePresent(getFormulaLiteralNodeValue(value))) {
185
+ return value;
186
+ }
187
+ }
188
+ return createFormulaNumberNode(0);
189
+ }
190
+ if (!args.every(isFormulaLiteralNode)) {
191
+ return { kind: "call", name: root.name, args, span: root.span };
192
+ }
193
+ const literalArgs = args.map(arg => getFormulaLiteralNodeValue(arg));
194
+ if (normalizedFunctionName === "ABS") {
195
+ return createFormulaLiteralNode(Math.abs(coerceFormulaValueToNumber((_e = literalArgs[0]) !== null && _e !== void 0 ? _e : null)), root.span);
196
+ }
197
+ if (normalizedFunctionName === "AVG") {
198
+ if (literalArgs.length === 0) {
199
+ return createFormulaLiteralNode(0, root.span);
200
+ }
201
+ return createFormulaLiteralNode(literalArgs.reduce((sum, value) => sum + coerceFormulaValueToNumber(value), 0) / literalArgs.length, root.span);
202
+ }
203
+ if (normalizedFunctionName === "CEIL") {
204
+ return createFormulaLiteralNode(Math.ceil(coerceFormulaValueToNumber((_f = literalArgs[0]) !== null && _f !== void 0 ? _f : null)), root.span);
205
+ }
206
+ if (normalizedFunctionName === "CONCAT") {
207
+ return createFormulaLiteralNode(literalArgs
208
+ .map((value) => {
209
+ if (value === null) {
210
+ return "";
211
+ }
212
+ if (value instanceof Date) {
213
+ return Number.isNaN(value.getTime()) ? "" : value.toISOString();
214
+ }
215
+ return String(value);
216
+ })
217
+ .join(""), root.span);
218
+ }
219
+ if (normalizedFunctionName === "COUNT") {
220
+ return createFormulaLiteralNode(literalArgs.reduce((count, value) => (isFormulaValuePresent(value) ? count + 1 : count), 0), root.span);
221
+ }
222
+ if (normalizedFunctionName === "DATE") {
223
+ const year = Math.trunc(coerceFormulaValueToNumber((_g = literalArgs[0]) !== null && _g !== void 0 ? _g : null));
224
+ const month = Math.trunc(coerceFormulaValueToNumber((_h = literalArgs[1]) !== null && _h !== void 0 ? _h : null));
225
+ const day = Math.trunc(coerceFormulaValueToNumber((_j = literalArgs[2]) !== null && _j !== void 0 ? _j : null));
226
+ return createFormulaLiteralNode(new Date(Date.UTC(year, month - 1, day)), root.span);
227
+ }
228
+ if (normalizedFunctionName === "DAY") {
229
+ const date = coerceFormulaValueToDate((_k = literalArgs[0]) !== null && _k !== void 0 ? _k : null);
230
+ return createFormulaLiteralNode(date ? date.getUTCDate() : 0, root.span);
231
+ }
232
+ if (normalizedFunctionName === "FLOOR") {
233
+ return createFormulaLiteralNode(Math.floor(coerceFormulaValueToNumber((_l = literalArgs[0]) !== null && _l !== void 0 ? _l : null)), root.span);
234
+ }
235
+ if (normalizedFunctionName === "LEN") {
236
+ const value = (_m = literalArgs[0]) !== null && _m !== void 0 ? _m : null;
237
+ return createFormulaLiteralNode(value === null ? 0 : value instanceof Date ? (Number.isNaN(value.getTime()) ? 0 : value.toISOString().length) : String(value).length, root.span);
238
+ }
239
+ if (normalizedFunctionName === "LEFT") {
240
+ const value = (_o = literalArgs[0]) !== null && _o !== void 0 ? _o : null;
241
+ const text = value === null ? "" : String(value);
242
+ const count = Math.max(0, Math.trunc(coerceFormulaValueToNumber((_p = literalArgs[1]) !== null && _p !== void 0 ? _p : text.length)));
243
+ return createFormulaLiteralNode(text.slice(0, count), root.span);
244
+ }
245
+ if (normalizedFunctionName === "LOWER") {
246
+ const value = (_q = literalArgs[0]) !== null && _q !== void 0 ? _q : null;
247
+ return createFormulaLiteralNode(value === null ? "" : String(value).toLowerCase(), root.span);
248
+ }
249
+ if (normalizedFunctionName === "MID") {
250
+ const value = (_r = literalArgs[0]) !== null && _r !== void 0 ? _r : null;
251
+ const text = value === null ? "" : String(value);
252
+ const start = Math.max(1, Math.trunc(coerceFormulaValueToNumber((_s = literalArgs[1]) !== null && _s !== void 0 ? _s : 1)));
253
+ const count = Math.max(0, Math.trunc(coerceFormulaValueToNumber((_t = literalArgs[2]) !== null && _t !== void 0 ? _t : 0)));
254
+ return createFormulaLiteralNode(text.slice(start - 1, start - 1 + count), root.span);
255
+ }
256
+ if (normalizedFunctionName === "MOD") {
257
+ const left = coerceFormulaValueToNumber((_u = literalArgs[0]) !== null && _u !== void 0 ? _u : null);
258
+ const right = coerceFormulaValueToNumber((_v = literalArgs[1]) !== null && _v !== void 0 ? _v : null);
259
+ return createFormulaLiteralNode(right === 0 ? 0 : left % right, root.span);
260
+ }
261
+ if (normalizedFunctionName === "MONTH") {
262
+ const date = coerceFormulaValueToDate((_w = literalArgs[0]) !== null && _w !== void 0 ? _w : null);
263
+ return createFormulaLiteralNode(date ? date.getUTCMonth() + 1 : 0, root.span);
264
+ }
265
+ if (normalizedFunctionName === "POW") {
266
+ return createFormulaLiteralNode(Math.pow(coerceFormulaValueToNumber((_x = literalArgs[0]) !== null && _x !== void 0 ? _x : null), coerceFormulaValueToNumber((_y = literalArgs[1]) !== null && _y !== void 0 ? _y : null)), root.span);
267
+ }
268
+ if (normalizedFunctionName === "ROUND") {
269
+ const sourceValue = coerceFormulaValueToNumber((_z = literalArgs[0]) !== null && _z !== void 0 ? _z : null);
270
+ const digits = Math.max(0, Math.trunc(coerceFormulaValueToNumber((_0 = literalArgs[1]) !== null && _0 !== void 0 ? _0 : null)));
271
+ const factor = 10 ** digits;
272
+ return createFormulaLiteralNode(Math.round(sourceValue * factor) / factor, root.span);
273
+ }
274
+ if (normalizedFunctionName === "MIN") {
275
+ return createFormulaLiteralNode(literalArgs.length === 0 ? 0 : Math.min(...literalArgs.map(value => coerceFormulaValueToNumber(value))), root.span);
276
+ }
277
+ if (normalizedFunctionName === "MAX") {
278
+ return createFormulaLiteralNode(literalArgs.length === 0 ? 0 : Math.max(...literalArgs.map(value => coerceFormulaValueToNumber(value))), root.span);
279
+ }
280
+ if (normalizedFunctionName === "SUM") {
281
+ return createFormulaLiteralNode(literalArgs.reduce((sum, value) => sum + coerceFormulaValueToNumber(value), 0), root.span);
282
+ }
283
+ if (normalizedFunctionName === "RIGHT") {
284
+ const value = (_1 = literalArgs[0]) !== null && _1 !== void 0 ? _1 : null;
285
+ const text = value === null ? "" : String(value);
286
+ const count = Math.max(0, Math.trunc(coerceFormulaValueToNumber((_2 = literalArgs[1]) !== null && _2 !== void 0 ? _2 : text.length)));
287
+ return createFormulaLiteralNode(count >= text.length ? text : text.slice(text.length - count), root.span);
288
+ }
289
+ if (normalizedFunctionName === "TRIM") {
290
+ const value = (_3 = literalArgs[0]) !== null && _3 !== void 0 ? _3 : null;
291
+ return createFormulaLiteralNode(value === null ? "" : String(value).trim(), root.span);
292
+ }
293
+ if (normalizedFunctionName === "UPPER") {
294
+ const value = (_4 = literalArgs[0]) !== null && _4 !== void 0 ? _4 : null;
295
+ return createFormulaLiteralNode(value === null ? "" : String(value).toUpperCase(), root.span);
296
+ }
297
+ if (normalizedFunctionName === "YEAR") {
298
+ const date = coerceFormulaValueToDate((_5 = literalArgs[0]) !== null && _5 !== void 0 ? _5 : null);
299
+ return createFormulaLiteralNode(date ? date.getUTCFullYear() : 0, root.span);
300
+ }
301
+ return { kind: "call", name: root.name, args, span: root.span };
302
+ }
303
+ const left = foldFormulaConstants(root.left);
304
+ const right = foldFormulaConstants(root.right);
305
+ if (root.operator === "AND" && isFormulaLiteralNode(left)) {
306
+ if (!coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(left))) {
307
+ return createFormulaLiteralNode(0, root.span);
308
+ }
309
+ if (isFormulaLiteralNode(right)) {
310
+ return createFormulaLiteralNode(coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(right)) ? 1 : 0, root.span);
311
+ }
312
+ return { kind: "binary", operator: "AND", left, right, span: root.span };
313
+ }
314
+ if (root.operator === "OR" && isFormulaLiteralNode(left)) {
315
+ if (coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(left))) {
316
+ return createFormulaLiteralNode(1, root.span);
317
+ }
318
+ if (isFormulaLiteralNode(right)) {
319
+ return createFormulaLiteralNode(coerceFormulaValueToBoolean(getFormulaLiteralNodeValue(right)) ? 1 : 0, root.span);
320
+ }
321
+ return { kind: "binary", operator: "OR", left, right, span: root.span };
322
+ }
323
+ if (!isFormulaLiteralNode(left) || !isFormulaLiteralNode(right)) {
324
+ return { kind: "binary", operator: root.operator, left, right, span: root.span };
325
+ }
326
+ const leftValue = getFormulaLiteralNodeValue(left);
327
+ const rightValue = getFormulaLiteralNodeValue(right);
328
+ if (root.operator === "+") {
329
+ return createFormulaLiteralNode(coerceFormulaValueToNumber(leftValue) + coerceFormulaValueToNumber(rightValue), root.span);
330
+ }
331
+ if (root.operator === "-") {
332
+ return createFormulaLiteralNode(coerceFormulaValueToNumber(leftValue) - coerceFormulaValueToNumber(rightValue), root.span);
333
+ }
334
+ if (root.operator === "*") {
335
+ return createFormulaLiteralNode(coerceFormulaValueToNumber(leftValue) * coerceFormulaValueToNumber(rightValue), root.span);
336
+ }
337
+ if (root.operator === "/") {
338
+ if (coerceFormulaValueToNumber(rightValue) === 0) {
339
+ return { kind: "binary", operator: "/", left, right, span: root.span };
340
+ }
341
+ return createFormulaLiteralNode(coerceFormulaValueToNumber(leftValue) / coerceFormulaValueToNumber(rightValue), root.span);
342
+ }
343
+ if (root.operator === ">") {
344
+ return createFormulaLiteralNode(compareFormulaValues(leftValue, rightValue) > 0 ? 1 : 0, root.span);
345
+ }
346
+ if (root.operator === "<") {
347
+ return createFormulaLiteralNode(compareFormulaValues(leftValue, rightValue) < 0 ? 1 : 0, root.span);
348
+ }
349
+ if (root.operator === ">=") {
350
+ return createFormulaLiteralNode(compareFormulaValues(leftValue, rightValue) >= 0 ? 1 : 0, root.span);
351
+ }
352
+ if (root.operator === "<=") {
353
+ return createFormulaLiteralNode(compareFormulaValues(leftValue, rightValue) <= 0 ? 1 : 0, root.span);
354
+ }
355
+ if (root.operator === "==") {
356
+ return createFormulaLiteralNode(areFormulaValuesEqual(leftValue, rightValue) ? 1 : 0, root.span);
357
+ }
358
+ if (root.operator === "!=") {
359
+ return createFormulaLiteralNode(areFormulaValuesEqual(leftValue, rightValue) ? 0 : 1, root.span);
360
+ }
361
+ return { kind: "binary", operator: root.operator, left, right, span: root.span };
362
+ }
@@ -0,0 +1,7 @@
1
+ import type { DataGridFormulaAstNode } from "./ast.js";
2
+ import type { DataGridFormulaToken } from "./tokenizer.js";
3
+ import type { DataGridFormulaReferenceParserOptions } from "./types.js";
4
+ export type { DataGridFormulaParseResult, DataGridFormulaDiagnosticsResult, DataGridFormulaExplainDependencyDomain, DataGridFormulaExplainDependency, DataGridFormulaExplainResult, DataGridFormulaFieldExplainResult, } from "./analysis.js";
5
+ export { parseDataGridFormulaExpression, diagnoseDataGridFormulaExpression, explainDataGridFormulaExpression, explainDataGridFormulaFieldDefinition, } from "./analysis.js";
6
+ export declare function parseFormula(tokens: readonly DataGridFormulaToken[], referenceParserOptions?: DataGridFormulaReferenceParserOptions): DataGridFormulaAstNode;
7
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/syntax/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,YAAY,CAAA;AAOvE,YAAY,EACV,0BAA0B,EAC1B,gCAAgC,EAChC,sCAAsC,EACtC,gCAAgC,EAChC,4BAA4B,EAC5B,iCAAiC,GAClC,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,eAAe,CAAA;AAEtB,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,oBAAoB,EAAE,EACvC,sBAAsB,GAAE,qCAA0C,GACjE,sBAAsB,CAuQxB"}
@@ -0,0 +1,239 @@
1
+ import { createFormulaSourceSpan, throwFormulaError, } from "./ast.js";
2
+ import { parseDataGridFormulaIdentifier } from "./tokenizer.js";
3
+ export { parseDataGridFormulaExpression, diagnoseDataGridFormulaExpression, explainDataGridFormulaExpression, explainDataGridFormulaFieldDefinition, } from "./analysis.js";
4
+ export function parseFormula(tokens, referenceParserOptions = {}) {
5
+ let cursor = 0;
6
+ const peek = () => tokens[cursor];
7
+ const consume = () => {
8
+ const next = tokens[cursor];
9
+ if (!next) {
10
+ return undefined;
11
+ }
12
+ cursor += 1;
13
+ return next;
14
+ };
15
+ const parseExpression = () => parseOr();
16
+ const parsePrimary = () => {
17
+ const token = consume();
18
+ if (!token) {
19
+ throwFormulaError("Unexpected end of formula.", createFormulaSourceSpan(0, 0));
20
+ }
21
+ if (token.kind === "number") {
22
+ return {
23
+ kind: "number",
24
+ value: token.value,
25
+ span: createFormulaSourceSpan(token.position, token.end),
26
+ };
27
+ }
28
+ if (token.kind === "string") {
29
+ return {
30
+ kind: "literal",
31
+ value: token.value,
32
+ span: createFormulaSourceSpan(token.position, token.end),
33
+ };
34
+ }
35
+ if (token.kind === "identifier") {
36
+ const next = peek();
37
+ if (!next || next.kind !== "paren" || next.value !== "(") {
38
+ const keyword = token.value.toUpperCase();
39
+ if (keyword === "TRUE") {
40
+ return {
41
+ kind: "literal",
42
+ value: true,
43
+ span: createFormulaSourceSpan(token.position, token.end),
44
+ };
45
+ }
46
+ if (keyword === "FALSE") {
47
+ return {
48
+ kind: "literal",
49
+ value: false,
50
+ span: createFormulaSourceSpan(token.position, token.end),
51
+ };
52
+ }
53
+ if (keyword === "NULL") {
54
+ return {
55
+ kind: "literal",
56
+ value: null,
57
+ span: createFormulaSourceSpan(token.position, token.end),
58
+ };
59
+ }
60
+ const parsedIdentifier = parseDataGridFormulaIdentifier(token.value, referenceParserOptions);
61
+ return {
62
+ kind: "identifier",
63
+ name: parsedIdentifier.name,
64
+ sheetReference: parsedIdentifier.sheetReference,
65
+ referenceName: parsedIdentifier.referenceName,
66
+ rangeReferenceName: parsedIdentifier.rangeReferenceName,
67
+ rowSelector: parsedIdentifier.rowSelector,
68
+ span: createFormulaSourceSpan(token.position, token.end),
69
+ };
70
+ }
71
+ consume();
72
+ const args = [];
73
+ const possibleClose = peek();
74
+ if (possibleClose && possibleClose.kind === "paren" && possibleClose.value === ")") {
75
+ consume();
76
+ return {
77
+ kind: "call",
78
+ name: token.value,
79
+ args,
80
+ span: createFormulaSourceSpan(token.position, possibleClose.end),
81
+ };
82
+ }
83
+ while (true) {
84
+ args.push(parseExpression());
85
+ const delimiter = peek();
86
+ if (!delimiter || delimiter.kind !== "comma") {
87
+ break;
88
+ }
89
+ consume();
90
+ }
91
+ const close = consume();
92
+ if (!close || close.kind !== "paren" || close.value !== ")") {
93
+ throwFormulaError(`Missing ')' for function call '${token.value}' at position ${token.position + 1}.`, createFormulaSourceSpan(token.position, token.end));
94
+ }
95
+ return {
96
+ kind: "call",
97
+ name: token.value,
98
+ args,
99
+ span: createFormulaSourceSpan(token.position, close.end),
100
+ };
101
+ }
102
+ if (token.kind === "paren" && token.value === "(") {
103
+ const nested = parseExpression();
104
+ const close = consume();
105
+ if (!close || close.kind !== "paren" || close.value !== ")") {
106
+ throwFormulaError(`Missing ')' for group at position ${token.position + 1}.`, createFormulaSourceSpan(token.position, token.end));
107
+ }
108
+ return nested;
109
+ }
110
+ throwFormulaError(`Unexpected token at position ${token.position + 1}.`, createFormulaSourceSpan(token.position, token.end));
111
+ };
112
+ const parseUnary = () => {
113
+ const token = peek();
114
+ if (token
115
+ && token.kind === "operator"
116
+ && (token.value === "-" || token.value === "+" || token.value === "NOT")) {
117
+ consume();
118
+ const value = parseUnary();
119
+ return {
120
+ kind: "unary",
121
+ operator: token.value,
122
+ value,
123
+ span: createFormulaSourceSpan(token.position, value.span.end),
124
+ };
125
+ }
126
+ return parsePrimary();
127
+ };
128
+ const parseMulDiv = () => {
129
+ let node = parseUnary();
130
+ while (true) {
131
+ const token = peek();
132
+ if (!token || token.kind !== "operator" || (token.value !== "*" && token.value !== "/")) {
133
+ break;
134
+ }
135
+ consume();
136
+ const right = parseUnary();
137
+ node = {
138
+ kind: "binary",
139
+ operator: token.value,
140
+ left: node,
141
+ right,
142
+ span: createFormulaSourceSpan(node.span.start, right.span.end),
143
+ };
144
+ }
145
+ return node;
146
+ };
147
+ const parseAddSub = () => {
148
+ let node = parseMulDiv();
149
+ while (true) {
150
+ const token = peek();
151
+ if (!token || token.kind !== "operator" || (token.value !== "+" && token.value !== "-")) {
152
+ break;
153
+ }
154
+ consume();
155
+ const right = parseMulDiv();
156
+ node = {
157
+ kind: "binary",
158
+ operator: token.value,
159
+ left: node,
160
+ right,
161
+ span: createFormulaSourceSpan(node.span.start, right.span.end),
162
+ };
163
+ }
164
+ return node;
165
+ };
166
+ const parseComparison = () => {
167
+ let node = parseAddSub();
168
+ while (true) {
169
+ const token = peek();
170
+ if (!token
171
+ || token.kind !== "operator"
172
+ || (token.value !== ">"
173
+ && token.value !== "<"
174
+ && token.value !== ">="
175
+ && token.value !== "<="
176
+ && token.value !== "=="
177
+ && token.value !== "!=")) {
178
+ break;
179
+ }
180
+ consume();
181
+ const right = parseAddSub();
182
+ node = {
183
+ kind: "binary",
184
+ operator: token.value,
185
+ left: node,
186
+ right,
187
+ span: createFormulaSourceSpan(node.span.start, right.span.end),
188
+ };
189
+ }
190
+ return node;
191
+ };
192
+ const parseAnd = () => {
193
+ let node = parseComparison();
194
+ while (true) {
195
+ const token = peek();
196
+ if (!token || token.kind !== "operator" || token.value !== "AND") {
197
+ break;
198
+ }
199
+ consume();
200
+ const right = parseComparison();
201
+ node = {
202
+ kind: "binary",
203
+ operator: "AND",
204
+ left: node,
205
+ right,
206
+ span: createFormulaSourceSpan(node.span.start, right.span.end),
207
+ };
208
+ }
209
+ return node;
210
+ };
211
+ const parseOr = () => {
212
+ let node = parseAnd();
213
+ while (true) {
214
+ const token = peek();
215
+ if (!token || token.kind !== "operator" || token.value !== "OR") {
216
+ break;
217
+ }
218
+ consume();
219
+ const right = parseAnd();
220
+ node = {
221
+ kind: "binary",
222
+ operator: "OR",
223
+ left: node,
224
+ right,
225
+ span: createFormulaSourceSpan(node.span.start, right.span.end),
226
+ };
227
+ }
228
+ return node;
229
+ };
230
+ const root = parseExpression();
231
+ if (cursor < tokens.length) {
232
+ const token = tokens[cursor];
233
+ if (!token) {
234
+ throwFormulaError("Unexpected trailing expression.", createFormulaSourceSpan(0, 0));
235
+ }
236
+ throwFormulaError(`Unexpected token at position ${token.position + 1}.`, createFormulaSourceSpan(token.position, token.end));
237
+ }
238
+ return root;
239
+ }
@@ -0,0 +1,14 @@
1
+ import type { DataGridFormulaReferenceParserOptions, DataGridFormulaReferenceSegment, DataGridFormulaRowSelector, DataGridFormulaToken } from "./types.js";
2
+ export type { DataGridFormulaToken, DataGridFormulaOperator, DataGridFormulaReferenceSegment, DataGridFormulaRowSelector, } from "./types.js";
3
+ export interface DataGridParsedFormulaIdentifier {
4
+ name: string;
5
+ sheetReference: string | null;
6
+ referenceName: string;
7
+ rangeReferenceName: string | null;
8
+ rowSelector: DataGridFormulaRowSelector;
9
+ }
10
+ export declare function normalizeFormulaReference(reference: string): string;
11
+ export declare function parseDataGridFormulaIdentifier(reference: string, options?: DataGridFormulaReferenceParserOptions): DataGridParsedFormulaIdentifier;
12
+ export declare function parseFormulaReferenceSegments(reference: string): readonly DataGridFormulaReferenceSegment[];
13
+ export declare function tokenizeFormula(formula: string, options?: DataGridFormulaReferenceParserOptions): readonly DataGridFormulaToken[];
14
+ //# sourceMappingURL=tokenizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../src/syntax/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,qCAAqC,EACrC,+BAA+B,EAE/B,0BAA0B,EAC1B,oBAAoB,EACrB,MAAM,YAAY,CAAA;AAGnB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,WAAW,EAAE,0BAA0B,CAAA;CACxC;AA+ID,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUnE;AAqsBD,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qCAA0C,GAClD,+BAA+B,CA6BjC;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,GAChB,SAAS,+BAA+B,EAAE,CAwB5C;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,qCAA0C,GAClD,SAAS,oBAAoB,EAAE,CAqIjC"}