reamkit 1.13.0 → 1.15.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.
Files changed (80) hide show
  1. package/README.md +34 -8
  2. package/dist/esm/core/converter/facade.d.ts +8 -1
  3. package/dist/esm/core/converter/facade.js +34 -8
  4. package/dist/esm/core/converter/ream.d.ts +1 -0
  5. package/dist/esm/core/converter/ream.js +13 -8
  6. package/dist/esm/core/document-model/types.d.ts +2 -0
  7. package/dist/esm/core/ir/features.d.ts +1 -0
  8. package/dist/esm/core/ir/features.js +1 -0
  9. package/dist/esm/core/ir/sheet.d.ts +42 -3
  10. package/dist/esm/core/ole/cfb.d.ts +19 -0
  11. package/dist/esm/core/ole/cfb.js +158 -0
  12. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  13. package/dist/esm/core/spreadsheet-model/types.d.ts +81 -1
  14. package/dist/esm/excel/activex-parser.d.ts +9 -0
  15. package/dist/esm/excel/activex-parser.js +188 -0
  16. package/dist/esm/excel/comments-parser.d.ts +4 -0
  17. package/dist/esm/excel/comments-parser.js +104 -0
  18. package/dist/esm/excel/conditional-format.d.ts +5 -3
  19. package/dist/esm/excel/conditional-format.js +248 -12
  20. package/dist/esm/excel/form-control-parser.d.ts +6 -0
  21. package/dist/esm/excel/form-control-parser.js +29 -0
  22. package/dist/esm/excel/formula/context.d.ts +11 -0
  23. package/dist/esm/excel/formula/dates.d.ts +11 -0
  24. package/dist/esm/excel/formula/dates.js +46 -0
  25. package/dist/esm/excel/formula/eval.d.ts +11 -0
  26. package/dist/esm/excel/formula/eval.js +196 -0
  27. package/dist/esm/excel/formula/functions.d.ts +7 -0
  28. package/dist/esm/excel/formula/functions.js +1359 -0
  29. package/dist/esm/excel/formula/index.d.ts +14 -0
  30. package/dist/esm/excel/formula/index.js +17 -0
  31. package/dist/esm/excel/formula/lexer.d.ts +8 -0
  32. package/dist/esm/excel/formula/lexer.js +163 -0
  33. package/dist/esm/excel/formula/parser.d.ts +57 -0
  34. package/dist/esm/excel/formula/parser.js +274 -0
  35. package/dist/esm/excel/formula/value.d.ts +52 -0
  36. package/dist/esm/excel/formula/value.js +102 -0
  37. package/dist/esm/excel/header-footer.d.ts +2 -0
  38. package/dist/esm/excel/header-footer.js +127 -0
  39. package/dist/esm/excel/index.d.ts +1 -1
  40. package/dist/esm/excel/number-format.d.ts +2 -0
  41. package/dist/esm/excel/number-format.js +5 -1
  42. package/dist/esm/excel/print-model.d.ts +7 -2
  43. package/dist/esm/excel/print-model.js +123 -16
  44. package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
  45. package/dist/esm/excel/shared-strings-parser.js +91 -12
  46. package/dist/esm/excel/sheet-drawing.d.ts +13 -1
  47. package/dist/esm/excel/sheet-drawing.js +47 -14
  48. package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
  49. package/dist/esm/excel/sheet-shape-parser.js +96 -0
  50. package/dist/esm/excel/sheet-to-flow.d.ts +4 -1
  51. package/dist/esm/excel/sheet-to-flow.js +170 -3
  52. package/dist/esm/excel/styles-parser.js +37 -0
  53. package/dist/esm/excel/worksheet-parser.js +191 -1
  54. package/dist/esm/excel/xls/biff-chart.d.ts +8 -0
  55. package/dist/esm/excel/xls/biff-chart.js +121 -0
  56. package/dist/esm/excel/xls/biff-reader.d.ts +4 -0
  57. package/dist/esm/excel/xls/biff-reader.js +1418 -0
  58. package/dist/esm/excel/xls/biff-styles.d.ts +8 -0
  59. package/dist/esm/excel/xls/biff-styles.js +299 -0
  60. package/dist/esm/excel/xls/escher.d.ts +24 -0
  61. package/dist/esm/excel/xls/escher.js +171 -0
  62. package/dist/esm/excel/xls/xls-reader.d.ts +3 -0
  63. package/dist/esm/excel/xls/xls-reader.js +30 -0
  64. package/dist/esm/excel/xlsx-reader.d.ts +2 -1
  65. package/dist/esm/excel/xlsx-reader.js +134 -16
  66. package/dist/esm/excel/xlsx-to-pdf.d.ts +1 -0
  67. package/dist/esm/excel/xlsx-to-pdf.js +2 -2
  68. package/dist/esm/excel/xlsx-writer.js +12 -2
  69. package/dist/esm/layout/styled-layout.js +29 -0
  70. package/dist/esm/pptx/ppt/ppt-reader.d.ts +4 -0
  71. package/dist/esm/pptx/ppt/ppt-reader.js +337 -0
  72. package/dist/esm/pptx/ppt/ppt-text.d.ts +69 -0
  73. package/dist/esm/pptx/ppt/ppt-text.js +756 -0
  74. package/dist/esm/pptx/slide-parser.d.ts +4 -2
  75. package/dist/esm/pptx/slide-parser.js +1 -1
  76. package/dist/esm/word/doc/doc-reader.d.ts +4 -0
  77. package/dist/esm/word/doc/doc-reader.js +421 -0
  78. package/dist/esm/word/doc/doc-text.d.ts +79 -0
  79. package/dist/esm/word/doc/doc-text.js +706 -0
  80. package/package.json +1 -1
@@ -1,5 +1,9 @@
1
+ import { serialFromDate, timePeriodMatches } from "./formula/dates.js";
2
+ import { BLANK, bool, err, num, str } from "./formula/value.js";
3
+ import { NO_SHIFT, evaluate } from "./formula/eval.js";
4
+ import { compileFormula, evaluateToBool } from "./formula/index.js";
1
5
  //#region src/excel/conditional-format.ts
2
- function buildConditionalFormatter(conditionalFormats, styles, cells) {
6
+ function buildConditionalFormatter(conditionalFormats, styles, cells, resolveText, date1904 = false, now, sheetGrids, currentSheet, definedNames) {
3
7
  if (!conditionalFormats || conditionalFormats.length === 0) return void 0;
4
8
  const dxfs = styles.dxfs ?? [];
5
9
  const flat = [];
@@ -18,41 +22,107 @@ function buildConditionalFormatter(conditionalFormats, styles, cells) {
18
22
  rule,
19
23
  iconThresholds: resolveIconThresholds(rule, cf.ranges, cells)
20
24
  });
21
- else flat.push({
25
+ else if (rule.type === "top10") flat.push({
26
+ ranges: cf.ranges,
27
+ rule,
28
+ threshold: resolveTop10(rule, cf.ranges, cells)
29
+ });
30
+ else if (rule.type === "aboveAverage") flat.push({
31
+ ranges: cf.ranges,
32
+ rule,
33
+ threshold: resolveAboveAverage(rule, cf.ranges, cells)
34
+ });
35
+ else if (rule.type === "duplicateValues" || rule.type === "uniqueValues") flat.push({
36
+ ranges: cf.ranges,
37
+ rule,
38
+ dupKeys: resolveDupUnique(rule, cf.ranges, cells, resolveText)
39
+ });
40
+ else if (rule.type === "expression") {
41
+ const first = cf.ranges[0];
42
+ flat.push({
43
+ ranges: cf.ranges,
44
+ rule,
45
+ compiled: compileFormula(rule.formula),
46
+ ...first ? { origin: {
47
+ row: first.startRow,
48
+ col: first.startColumn
49
+ } } : {}
50
+ });
51
+ } else flat.push({
22
52
  ranges: cf.ranges,
23
53
  rule
24
54
  });
25
55
  flat.sort((a, b) => a.rule.priority - b.rule.priority);
26
- return (row, col, value) => {
27
- if (value === void 0) return void 0;
56
+ const hasExpr = flat.some((f) => f.rule.type === "expression");
57
+ const nowSerial = now !== void 0 ? serialFromDate(now, date1904) : void 0;
58
+ const cross = hasExpr && sheetGrids ? {
59
+ sheets: [...sheetGrids].map(([name, ws]) => ({
60
+ name,
61
+ cells: ws.cells
62
+ })),
63
+ ...currentSheet !== void 0 ? { currentSheet } : {},
64
+ definedNames: definedNames ?? []
65
+ } : void 0;
66
+ const ctx = hasExpr ? buildEvalContext(cells, resolveText, nowSerial, date1904, cross) : void 0;
67
+ return (row, col, value, text) => {
68
+ if (!hasExpr && value === void 0 && (text === void 0 || text.length === 0)) return;
69
+ const dupKey = dupKeyOf(value, text);
28
70
  let textFmt;
29
71
  let textClaimed = false;
30
72
  let bar;
31
73
  let icon;
32
- for (const { ranges, rule, scale, bar: resolved, iconThresholds } of flat) {
74
+ const claim = (dxfId) => {
75
+ textFmt = dxfToOverride(dxfs[dxfId]);
76
+ textClaimed = true;
77
+ };
78
+ for (const { ranges, rule, scale, bar: resolved, iconThresholds, threshold, dupKeys, compiled, origin } of flat) {
33
79
  if (!coversCell(ranges, row, col)) continue;
34
80
  switch (rule.type) {
35
81
  case "cellIs":
36
- if (!textClaimed && cellIsMatches(rule.operator, value, rule.formulas)) {
37
- textFmt = dxfToOverride(dxfs[rule.dxfId]);
38
- textClaimed = true;
39
- }
82
+ if (!textClaimed && value !== void 0 && cellIsMatches(rule.operator, value, rule.formulas)) claim(rule.dxfId);
40
83
  break;
41
84
  case "colorScale":
42
- if (!textClaimed && scale) {
85
+ if (!textClaimed && value !== void 0 && scale) {
43
86
  textFmt = { fillHex: rgbHex(colorScaleColor(scale, value)) };
44
87
  textClaimed = true;
45
88
  }
46
89
  break;
47
90
  case "dataBar":
48
- if (!bar && resolved) bar = dataBarBar(resolved, value);
91
+ if (!bar && value !== void 0 && resolved) bar = dataBarBar(resolved, value);
49
92
  break;
50
93
  case "iconSet":
51
- if (!icon && iconThresholds) {
94
+ if (!icon && value !== void 0 && iconThresholds) {
52
95
  const bucket = iconBucket(iconThresholds, value);
53
96
  icon = iconToCell(rule.iconSet, iconThresholds.length, bucket, rule.reverse ?? false);
54
97
  }
55
98
  break;
99
+ case "top10":
100
+ case "aboveAverage":
101
+ if (!textClaimed && value !== void 0 && threshold && thresholdMatches(threshold, value)) claim(rule.dxfId);
102
+ break;
103
+ case "duplicateValues":
104
+ case "uniqueValues":
105
+ if (!textClaimed && dupKey !== void 0 && dupKeys?.has(dupKey)) claim(rule.dxfId);
106
+ break;
107
+ case "containsText":
108
+ case "notContainsText":
109
+ case "beginsWith":
110
+ case "endsWith":
111
+ if (!textClaimed && text !== void 0 && textRuleMatches(rule, text)) claim(rule.dxfId);
112
+ break;
113
+ case "expression":
114
+ if (!textClaimed && compiled && ctx && origin) {
115
+ if (evaluateToBool(compiled, ctx, {
116
+ dRow: row - origin.row,
117
+ dCol: col - origin.col,
118
+ curRow: row,
119
+ curCol: col
120
+ })) claim(rule.dxfId);
121
+ }
122
+ break;
123
+ case "timePeriod":
124
+ if (!textClaimed && value !== void 0 && nowSerial !== void 0 && timePeriodMatches(rule.timePeriod, value, nowSerial, date1904)) claim(rule.dxfId);
125
+ break;
56
126
  }
57
127
  }
58
128
  if (!bar && !icon) return textFmt;
@@ -63,6 +133,121 @@ function buildConditionalFormatter(conditionalFormats, styles, cells) {
63
133
  };
64
134
  };
65
135
  }
136
+ var COLS = 16384;
137
+ function indexCells(cells, resolveText) {
138
+ const byKey = /* @__PURE__ */ new Map();
139
+ const populated = [];
140
+ for (const cell of cells) {
141
+ const s = cellToScalar(cell, resolveText);
142
+ if (s.t === "blank") continue;
143
+ byKey.set(cell.row * COLS + cell.column, s);
144
+ populated.push({
145
+ row: cell.row,
146
+ col: cell.column,
147
+ value: s
148
+ });
149
+ }
150
+ return {
151
+ byKey,
152
+ populated
153
+ };
154
+ }
155
+ function eachInMap(map, rect, visit) {
156
+ for (const c of map.populated) if (c.row >= rect.r0 && c.row <= rect.r1 && c.col >= rect.c0 && c.col <= rect.c1) visit(c.row, c.col, c.value);
157
+ }
158
+ function buildEvalContext(cells, resolveText, nowSerial, date1904, cross) {
159
+ const self = indexCells(cells, resolveText);
160
+ const base = {
161
+ nowSerial,
162
+ date1904,
163
+ getCell: (row, col) => self.byKey.get(row * COLS + col) ?? BLANK,
164
+ eachCell: (rect, visit) => eachInMap(self, rect, visit)
165
+ };
166
+ if (!cross) return base;
167
+ const sheetByName = /* @__PURE__ */ new Map();
168
+ cross.sheets.forEach((s, i) => sheetByName.set(s.name.toLowerCase(), i));
169
+ const maps = new Array(cross.sheets.length);
170
+ const sheetData = (idx) => maps[idx] ??= indexCells(cross.sheets[idx].cells, resolveText);
171
+ const currentIdx = cross.currentSheet !== void 0 ? sheetByName.get(cross.currentSheet.toLowerCase()) : void 0;
172
+ const nameMap = /* @__PURE__ */ new Map();
173
+ for (const dn of cross.definedNames) {
174
+ if (dn.localSheetId !== void 0 && dn.localSheetId !== currentIdx) continue;
175
+ const key = dn.name.toLowerCase();
176
+ if (dn.localSheetId !== void 0 || !nameMap.has(key)) nameMap.set(key, dn);
177
+ }
178
+ const resolving = /* @__PURE__ */ new Set();
179
+ const ctx = {
180
+ ...base,
181
+ sheetIndex: (name) => sheetByName.get(name.toLowerCase()),
182
+ getCellOn: (sheet, row, col) => sheetData(sheet).byKey.get(row * COLS + col) ?? BLANK,
183
+ eachCellOn: (sheet, rect, visit) => eachInMap(sheetData(sheet), rect, visit),
184
+ resolveName: (rawName) => {
185
+ const key = rawName.toLowerCase();
186
+ const dn = nameMap.get(key);
187
+ if (!dn || resolving.has(key)) return void 0;
188
+ const compiled = compileFormula(dn.value.startsWith("=") ? dn.value.slice(1) : dn.value);
189
+ if (!compiled) return void 0;
190
+ const ast = compiled.ast;
191
+ if (ast.k === "num") return num(ast.v);
192
+ if (ast.k === "str") return str(ast.v);
193
+ if (ast.k === "bool") return bool(ast.v);
194
+ if (ast.k !== "cell" && ast.k !== "range") return void 0;
195
+ resolving.add(key);
196
+ try {
197
+ return evaluate(ast, ctx, NO_SHIFT);
198
+ } finally {
199
+ resolving.delete(key);
200
+ }
201
+ }
202
+ };
203
+ return ctx;
204
+ }
205
+ function cellToScalar(cell, resolveText) {
206
+ switch (cell.type) {
207
+ case "n": {
208
+ const n = Number(cell.rawValue);
209
+ return Number.isFinite(n) ? num(n) : BLANK;
210
+ }
211
+ case "b": return bool(cell.rawValue === "1" || cell.rawValue.toUpperCase() === "TRUE");
212
+ case "e": return err(toFErr(cell.rawValue));
213
+ case "s":
214
+ case "str":
215
+ case "inlineStr": return str(resolveText ? resolveText(cell) : cell.inlineText ?? cell.rawValue);
216
+ default: return cell.rawValue.length > 0 ? str(cell.rawValue) : BLANK;
217
+ }
218
+ }
219
+ var FERRS = new Set([
220
+ "#NULL!",
221
+ "#DIV/0!",
222
+ "#VALUE!",
223
+ "#REF!",
224
+ "#NAME?",
225
+ "#NUM!",
226
+ "#N/A"
227
+ ]);
228
+ function toFErr(raw) {
229
+ return FERRS.has(raw) ? raw : "#VALUE!";
230
+ }
231
+ function dupKeyOf(value, text) {
232
+ if (value !== void 0) return `n:${value}`;
233
+ if (text === void 0) return void 0;
234
+ const k = text.trim().toLowerCase();
235
+ return k.length > 0 ? `s:${k}` : void 0;
236
+ }
237
+ function textRuleMatches(rule, text) {
238
+ const hay = text.toLowerCase();
239
+ const needle = rule.text.toLowerCase();
240
+ switch (rule.type) {
241
+ case "containsText": return hay.includes(needle);
242
+ case "notContainsText": return !hay.includes(needle);
243
+ case "beginsWith": return hay.startsWith(needle);
244
+ case "endsWith": return hay.endsWith(needle);
245
+ }
246
+ }
247
+ function thresholdMatches(t, value) {
248
+ if (t.below) return t.orEqual ? value <= t.threshold : value < t.threshold;
249
+ return t.orEqual ? value >= t.threshold : value > t.threshold;
250
+ }
66
251
  function coversCell(ranges, row, col) {
67
252
  for (const r of ranges) if (row >= r.startRow && row <= r.endRow && col >= r.startColumn && col <= r.endColumn) return true;
68
253
  return false;
@@ -323,5 +508,56 @@ function iconColor(count, e) {
323
508
  const ramp = ICON_RAMPS[count] ?? ICON_RAMPS[3];
324
509
  return ramp[Math.max(0, Math.min(ramp.length - 1, e))];
325
510
  }
511
+ function resolveTop10(rule, ranges, cells) {
512
+ const vals = collectRangeValues(cells, ranges);
513
+ if (vals.length === 0) return void 0;
514
+ const n = vals.length;
515
+ const raw = rule.percent ? Math.floor(n * rule.rank / 100) : rule.rank;
516
+ const count = Math.max(1, Math.min(n, raw));
517
+ return {
518
+ threshold: [...vals].sort((a, b) => rule.bottom ? a - b : b - a)[count - 1],
519
+ below: rule.bottom,
520
+ orEqual: true
521
+ };
522
+ }
523
+ function resolveAboveAverage(rule, ranges, cells) {
524
+ const vals = collectRangeValues(cells, ranges);
525
+ if (vals.length === 0) return void 0;
526
+ const mean = vals.reduce((s, v) => s + v, 0) / vals.length;
527
+ let threshold = mean;
528
+ if (rule.stdDev !== void 0 && rule.stdDev > 0) {
529
+ const variance = vals.reduce((s, v) => s + (v - mean) * (v - mean), 0) / vals.length;
530
+ const sd = Math.sqrt(variance);
531
+ threshold = rule.aboveAverage ? mean + rule.stdDev * sd : mean - rule.stdDev * sd;
532
+ }
533
+ return {
534
+ threshold,
535
+ below: !rule.aboveAverage,
536
+ orEqual: rule.equalAverage
537
+ };
538
+ }
539
+ function resolveDupUnique(rule, ranges, cells, resolveText) {
540
+ const counts = /* @__PURE__ */ new Map();
541
+ for (const cell of cells) {
542
+ if (!coversCell(ranges, cell.row, cell.column)) continue;
543
+ const key = cellDupKey(cell, resolveText);
544
+ if (key === void 0) continue;
545
+ counts.set(key, (counts.get(key) ?? 0) + 1);
546
+ }
547
+ if (counts.size === 0) return void 0;
548
+ const qualifies = rule.type === "duplicateValues" ? (c) => c >= 2 : (c) => c === 1;
549
+ const out = /* @__PURE__ */ new Set();
550
+ for (const [k, c] of counts) if (qualifies(c)) out.add(k);
551
+ return out;
552
+ }
553
+ function cellDupKey(cell, resolveText) {
554
+ if (cell.type === "n") {
555
+ const n = Number(cell.rawValue);
556
+ return Number.isFinite(n) ? `n:${n}` : void 0;
557
+ }
558
+ if (!resolveText) return void 0;
559
+ const k = resolveText(cell).trim().toLowerCase();
560
+ return k.length > 0 ? `s:${k}` : void 0;
561
+ }
326
562
  //#endregion
327
563
  export { buildConditionalFormatter };
@@ -0,0 +1,6 @@
1
+ export interface FormControlProps {
2
+ readonly objectType?: string;
3
+ readonly checked?: boolean;
4
+ readonly value?: number;
5
+ }
6
+ export declare function parseFormControlProps(data: Uint8Array): FormControlProps;
@@ -0,0 +1,29 @@
1
+ import { XMLParser } from "fast-xml-parser";
2
+ //#region src/excel/form-control-parser.ts
3
+ var decoder = new TextDecoder("utf-8");
4
+ var parser = new XMLParser({
5
+ ignoreAttributes: false,
6
+ attributeNamePrefix: "@_",
7
+ parseAttributeValue: false,
8
+ removeNSPrefix: true
9
+ });
10
+ function parseFormControlProps(data) {
11
+ const pr = parser.parse(decoder.decode(data))["formControlPr"];
12
+ const obj = pr && typeof pr === "object" ? pr : void 0;
13
+ if (!obj) return {};
14
+ const objectType = strAttr(obj, "objectType");
15
+ const checkedRaw = strAttr(obj, "checked");
16
+ const valRaw = strAttr(obj, "val");
17
+ const value = valRaw !== void 0 && /^-?\d+$/.test(valRaw) ? Number(valRaw) : void 0;
18
+ return {
19
+ ...objectType ? { objectType } : {},
20
+ ...checkedRaw !== void 0 ? { checked: checkedRaw === "Checked" || checkedRaw === "1" } : {},
21
+ ...value !== void 0 ? { value } : {}
22
+ };
23
+ }
24
+ function strAttr(obj, key) {
25
+ const v = obj[`@_${key}`];
26
+ return typeof v === "string" ? v : void 0;
27
+ }
28
+ //#endregion
29
+ export { parseFormControlProps };
@@ -0,0 +1,11 @@
1
+ import { FValue, Rect, Scalar } from './value.js';
2
+ export interface EvalContext {
3
+ readonly getCell: (row: number, col: number) => Scalar;
4
+ readonly eachCell: (rect: Rect, visit: (row: number, col: number, value: Scalar) => void) => void;
5
+ readonly nowSerial: number | undefined;
6
+ readonly date1904: boolean;
7
+ readonly sheetIndex?: (name: string) => number | undefined;
8
+ readonly getCellOn?: (sheet: number, row: number, col: number) => Scalar;
9
+ readonly eachCellOn?: (sheet: number, rect: Rect, visit: (row: number, col: number, value: Scalar) => void) => void;
10
+ readonly resolveName?: (name: string) => FValue | undefined;
11
+ }
@@ -0,0 +1,11 @@
1
+ import { TimePeriodKind } from '../../core/spreadsheet-model/index.js';
2
+ export interface DateParts {
3
+ readonly year: number;
4
+ readonly month: number;
5
+ readonly day: number;
6
+ readonly dow: number;
7
+ }
8
+ export declare function serialToParts(serial: number, date1904: boolean): DateParts;
9
+ export declare function serialFromDate(d: Date, date1904: boolean): number;
10
+ export declare function serialFromYmd(year: number, month1: number, day: number, date1904: boolean): number;
11
+ export declare function timePeriodMatches(period: TimePeriodKind, cellSerial: number, nowSerial: number, date1904: boolean): boolean;
@@ -0,0 +1,46 @@
1
+ import { excelSerialFromUtcParts, excelSerialToDate } from "../number-format.js";
2
+ //#region src/excel/formula/dates.ts
3
+ function serialToParts(serial, date1904) {
4
+ const d = excelSerialToDate(Math.floor(serial), date1904);
5
+ return {
6
+ year: d.getUTCFullYear(),
7
+ month: d.getUTCMonth() + 1,
8
+ day: d.getUTCDate(),
9
+ dow: d.getUTCDay()
10
+ };
11
+ }
12
+ function serialFromDate(d, date1904) {
13
+ return excelSerialFromUtcParts(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), date1904);
14
+ }
15
+ function serialFromYmd(year, month1, day, date1904) {
16
+ return excelSerialFromUtcParts(year, month1 - 1, day, date1904);
17
+ }
18
+ function timePeriodMatches(period, cellSerial, nowSerial, date1904) {
19
+ const cell = Math.floor(cellSerial);
20
+ const today = Math.floor(nowSerial);
21
+ switch (period) {
22
+ case "today": return cell === today;
23
+ case "yesterday": return cell === today - 1;
24
+ case "tomorrow": return cell === today + 1;
25
+ case "last7Days": return today - cell >= 0 && today - cell <= 6;
26
+ case "thisWeek":
27
+ case "lastWeek":
28
+ case "nextWeek": {
29
+ const lo = today - serialToParts(today, date1904).dow + (period === "lastWeek" ? -7 : period === "nextWeek" ? 7 : 0);
30
+ return cell >= lo && cell <= lo + 6;
31
+ }
32
+ case "thisMonth":
33
+ case "lastMonth":
34
+ case "nextMonth": {
35
+ const t = serialToParts(today, date1904);
36
+ const delta = period === "lastMonth" ? -1 : period === "nextMonth" ? 1 : 0;
37
+ const idx = t.year * 12 + (t.month - 1) + delta;
38
+ const targetYear = Math.floor(idx / 12);
39
+ const targetMonth = idx % 12 + 1;
40
+ const c = serialToParts(cell, date1904);
41
+ return c.year === targetYear && c.month === targetMonth;
42
+ }
43
+ }
44
+ }
45
+ //#endregion
46
+ export { serialFromDate, serialFromYmd, serialToParts, timePeriodMatches };
@@ -0,0 +1,11 @@
1
+ import { Ast } from './parser.js';
2
+ import { EvalContext } from './context.js';
3
+ import { FValue } from './value.js';
4
+ export interface Shift {
5
+ readonly dRow: number;
6
+ readonly dCol: number;
7
+ readonly curRow?: number;
8
+ readonly curCol?: number;
9
+ }
10
+ export declare const NO_SHIFT: Shift;
11
+ export declare function evaluate(ast: Ast, ctx: EvalContext, shift: Shift): FValue;
@@ -0,0 +1,196 @@
1
+ import { bool, deref, err, num, str, toNumber, toText } from "./value.js";
2
+ import { callFn } from "./functions.js";
3
+ //#region src/excel/formula/eval.ts
4
+ var NO_SHIFT = {
5
+ dRow: 0,
6
+ dCol: 0
7
+ };
8
+ function evaluate(ast, ctx, shift) {
9
+ switch (ast.k) {
10
+ case "num": return num(ast.v);
11
+ case "str": return str(ast.v);
12
+ case "bool": return bool(ast.v);
13
+ case "err": return err(ast.v);
14
+ case "cell": {
15
+ const sheet = sheetIdx(ast.sheet, ctx);
16
+ if (sheet === false) return err("#REF!");
17
+ const r = resolveRef(ast.ref, shift);
18
+ if (!r) return err("#REF!");
19
+ return {
20
+ t: "ref",
21
+ rect: {
22
+ r0: r.row,
23
+ c0: r.col,
24
+ r1: r.row,
25
+ c1: r.col
26
+ },
27
+ ...sheet !== void 0 ? { sheet } : {}
28
+ };
29
+ }
30
+ case "range": {
31
+ const sheet = sheetIdx(ast.sheet, ctx);
32
+ if (sheet === false) return err("#REF!");
33
+ const a = resolveRef(ast.a, shift);
34
+ const b = resolveRef(ast.b, shift);
35
+ if (!a || !b) return err("#REF!");
36
+ return {
37
+ t: "ref",
38
+ rect: {
39
+ r0: Math.min(a.row, b.row),
40
+ c0: Math.min(a.col, b.col),
41
+ r1: Math.max(a.row, b.row),
42
+ c1: Math.max(a.col, b.col)
43
+ },
44
+ ...sheet !== void 0 ? { sheet } : {}
45
+ };
46
+ }
47
+ case "name": return ctx.resolveName?.(ast.name) ?? err("#NAME?");
48
+ case "array": return {
49
+ t: "arr",
50
+ rows: ast.rows.map((row) => row.map((el) => deref(evaluate(el, ctx, shift), ctx)))
51
+ };
52
+ case "unary": return evalUnary(ast.op, evaluate(ast.x, ctx, shift), ctx);
53
+ case "pct": {
54
+ const n = toNumber(evaluate(ast.x, ctx, shift), ctx);
55
+ return typeof n === "string" ? err(n) : num(n / 100);
56
+ }
57
+ case "bin": return evalBin(ast.op, ast.a, ast.b, ctx, shift);
58
+ case "call": return callFn(ast.name, ast.args, (a) => evaluate(a, ctx, shift), ctx, shift);
59
+ }
60
+ }
61
+ function resolveRef(ref, shift) {
62
+ const row = ref.row.abs ? ref.row.index : ref.row.index + shift.dRow;
63
+ const col = ref.col.abs ? ref.col.index : ref.col.index + shift.dCol;
64
+ if (row < 0 || col < 0) return void 0;
65
+ return {
66
+ row,
67
+ col
68
+ };
69
+ }
70
+ function sheetIdx(name, ctx) {
71
+ if (name === void 0) return void 0;
72
+ const idx = ctx.sheetIndex?.(name);
73
+ return idx === void 0 ? false : idx;
74
+ }
75
+ function evalUnary(op, x, ctx) {
76
+ const n = toNumber(x, ctx);
77
+ if (typeof n === "string") return err(n);
78
+ return num(op === "-" ? -n : n);
79
+ }
80
+ function evalBin(op, aAst, bAst, ctx, shift) {
81
+ const a = evaluate(aAst, ctx, shift);
82
+ const b = evaluate(bAst, ctx, shift);
83
+ if (a.t === "arr" || b.t === "arr") return broadcastBin(op, a, b, ctx);
84
+ return applyBin(op, a, b, ctx);
85
+ }
86
+ function applyBin(op, a, b, ctx) {
87
+ if (op === "&") {
88
+ const ta = toText(a, ctx);
89
+ if (typeof ta !== "string") return err(ta);
90
+ const tb = toText(b, ctx);
91
+ if (typeof tb !== "string") return err(tb);
92
+ return str(ta + tb);
93
+ }
94
+ if (op === "=" || op === "<>" || op === "<" || op === ">" || op === "<=" || op === ">=") return compareOp(op, deref(a, ctx), deref(b, ctx));
95
+ const na = toNumber(a, ctx);
96
+ if (typeof na === "string") return err(na);
97
+ const nb = toNumber(b, ctx);
98
+ if (typeof nb === "string") return err(nb);
99
+ switch (op) {
100
+ case "+": return num(na + nb);
101
+ case "-": return num(na - nb);
102
+ case "*": return num(na * nb);
103
+ case "/": return nb === 0 ? err("#DIV/0!") : num(na / nb);
104
+ case "^": {
105
+ const r = Math.pow(na, nb);
106
+ return Number.isNaN(r) ? err("#NUM!") : num(r);
107
+ }
108
+ }
109
+ }
110
+ function broadcastBin(op, a, b, ctx) {
111
+ const ar = a.t === "arr" ? a.rows : [[deref(a, ctx)]];
112
+ const br = b.t === "arr" ? b.rows : [[deref(b, ctx)]];
113
+ const nRows = Math.max(ar.length, br.length);
114
+ const out = [];
115
+ for (let r = 0; r < nRows; r++) {
116
+ const arow = ar[ar.length === 1 ? 0 : r];
117
+ const brow = br[br.length === 1 ? 0 : r];
118
+ if (!arow || !brow) return err("#VALUE!");
119
+ const nCols = Math.max(arow.length, brow.length);
120
+ const row = [];
121
+ for (let c = 0; c < nCols; c++) {
122
+ const ea = arow[arow.length === 1 ? 0 : c];
123
+ const eb = brow[brow.length === 1 ? 0 : c];
124
+ if (ea === void 0 || eb === void 0) return err("#VALUE!");
125
+ const res = applyBin(op, ea, eb, ctx);
126
+ row.push(res.t === "ref" || res.t === "arr" ? err("#VALUE!") : res);
127
+ }
128
+ out.push(row);
129
+ }
130
+ return {
131
+ t: "arr",
132
+ rows: out
133
+ };
134
+ }
135
+ function compareOp(op, a, b) {
136
+ const c = compare(a, b);
137
+ if (typeof c === "string") return err(c);
138
+ switch (op) {
139
+ case "=": return bool(c === 0);
140
+ case "<>": return bool(c !== 0);
141
+ case "<": return bool(c < 0);
142
+ case ">": return bool(c > 0);
143
+ case "<=": return bool(c <= 0);
144
+ case ">=": return bool(c >= 0);
145
+ default: return err("#VALUE!");
146
+ }
147
+ }
148
+ function compare(a, b) {
149
+ if (a.t === "err") return a.v;
150
+ if (b.t === "err") return b.v;
151
+ const na = normalizeBlank(a, b);
152
+ const nb = normalizeBlank(b, a);
153
+ const ca = category(na);
154
+ const cb = category(nb);
155
+ if (ca !== cb) return ca < cb ? -1 : 1;
156
+ if (na.t === "str" && nb.t === "str") {
157
+ const la = na.v.toLowerCase();
158
+ const lb = nb.v.toLowerCase();
159
+ return la < lb ? -1 : la > lb ? 1 : 0;
160
+ }
161
+ const va = scalarNumber(na);
162
+ const vb = scalarNumber(nb);
163
+ return va < vb ? -1 : va > vb ? 1 : 0;
164
+ }
165
+ function normalizeBlank(s, other) {
166
+ if (s.t !== "blank") return s;
167
+ switch (other.t) {
168
+ case "str": return {
169
+ t: "str",
170
+ v: ""
171
+ };
172
+ case "bool": return {
173
+ t: "bool",
174
+ v: false
175
+ };
176
+ default: return {
177
+ t: "num",
178
+ v: 0
179
+ };
180
+ }
181
+ }
182
+ function category(s) {
183
+ switch (s.t) {
184
+ case "num": return 0;
185
+ case "str": return 1;
186
+ case "bool": return 2;
187
+ default: return 0;
188
+ }
189
+ }
190
+ function scalarNumber(s) {
191
+ if (s.t === "num") return s.v;
192
+ if (s.t === "bool") return s.v ? 1 : 0;
193
+ return 0;
194
+ }
195
+ //#endregion
196
+ export { NO_SHIFT, evaluate };
@@ -0,0 +1,7 @@
1
+ import { Ast } from './parser.js';
2
+ import { EvalContext } from './context.js';
3
+ import { Shift } from './eval.js';
4
+ import { FValue } from './value.js';
5
+ type Ev = (a: Ast) => FValue;
6
+ export declare function callFn(name: string, args: ReadonlyArray<Ast>, ev: Ev, ctx: EvalContext, shift: Shift): FValue;
7
+ export {};