reamkit 1.14.0 → 1.15.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 (67) 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/ir/features.d.ts +1 -0
  7. package/dist/esm/core/ir/features.js +1 -0
  8. package/dist/esm/core/ir/sheet.d.ts +7 -0
  9. package/dist/esm/core/ole/cfb.d.ts +19 -0
  10. package/dist/esm/core/ole/cfb.js +188 -0
  11. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  12. package/dist/esm/core/spreadsheet-model/types.d.ts +20 -1
  13. package/dist/esm/excel/activex-parser.d.ts +9 -0
  14. package/dist/esm/excel/activex-parser.js +188 -0
  15. package/dist/esm/excel/conditional-format.d.ts +4 -2
  16. package/dist/esm/excel/conditional-format.js +138 -4
  17. package/dist/esm/excel/formula/context.d.ts +11 -0
  18. package/dist/esm/excel/formula/dates.d.ts +11 -0
  19. package/dist/esm/excel/formula/dates.js +46 -0
  20. package/dist/esm/excel/formula/eval.d.ts +11 -0
  21. package/dist/esm/excel/formula/eval.js +196 -0
  22. package/dist/esm/excel/formula/functions.d.ts +7 -0
  23. package/dist/esm/excel/formula/functions.js +1359 -0
  24. package/dist/esm/excel/formula/index.d.ts +14 -0
  25. package/dist/esm/excel/formula/index.js +17 -0
  26. package/dist/esm/excel/formula/lexer.d.ts +8 -0
  27. package/dist/esm/excel/formula/lexer.js +163 -0
  28. package/dist/esm/excel/formula/parser.d.ts +57 -0
  29. package/dist/esm/excel/formula/parser.js +274 -0
  30. package/dist/esm/excel/formula/value.d.ts +52 -0
  31. package/dist/esm/excel/formula/value.js +102 -0
  32. package/dist/esm/excel/number-format.d.ts +2 -0
  33. package/dist/esm/excel/number-format.js +5 -1
  34. package/dist/esm/excel/print-model.d.ts +3 -0
  35. package/dist/esm/excel/print-model.js +1 -1
  36. package/dist/esm/excel/sheet-to-flow.d.ts +4 -1
  37. package/dist/esm/excel/sheet-to-flow.js +46 -2
  38. package/dist/esm/excel/worksheet-parser.js +66 -0
  39. package/dist/esm/excel/xls/biff-chart.d.ts +8 -0
  40. package/dist/esm/excel/xls/biff-chart.js +121 -0
  41. package/dist/esm/excel/xls/biff-reader.d.ts +4 -0
  42. package/dist/esm/excel/xls/biff-reader.js +1418 -0
  43. package/dist/esm/excel/xls/biff-styles.d.ts +8 -0
  44. package/dist/esm/excel/xls/biff-styles.js +299 -0
  45. package/dist/esm/excel/xls/escher.d.ts +24 -0
  46. package/dist/esm/excel/xls/escher.js +171 -0
  47. package/dist/esm/excel/xls/xls-reader.d.ts +3 -0
  48. package/dist/esm/excel/xls/xls-reader.js +30 -0
  49. package/dist/esm/excel/xlsx-reader.d.ts +2 -1
  50. package/dist/esm/excel/xlsx-reader.js +28 -3
  51. package/dist/esm/excel/xlsx-to-pdf.d.ts +1 -0
  52. package/dist/esm/excel/xlsx-to-pdf.js +2 -2
  53. package/dist/esm/excel/xlsx-writer.js +2 -0
  54. package/dist/esm/pdf-reader/flow-build.d.ts +4 -2
  55. package/dist/esm/pdf-reader/flow-build.js +25 -2
  56. package/dist/esm/pdf-reader/layout.js +2 -2
  57. package/dist/esm/pdf-reader/tagged.js +2 -2
  58. package/dist/esm/pptx/ppt/ppt-reader.d.ts +4 -0
  59. package/dist/esm/pptx/ppt/ppt-reader.js +337 -0
  60. package/dist/esm/pptx/ppt/ppt-text.d.ts +69 -0
  61. package/dist/esm/pptx/ppt/ppt-text.js +756 -0
  62. package/dist/esm/pptx/pptx-reader.js +16 -1
  63. package/dist/esm/word/doc/doc-reader.d.ts +4 -0
  64. package/dist/esm/word/doc/doc-reader.js +427 -0
  65. package/dist/esm/word/doc/doc-text.d.ts +83 -0
  66. package/dist/esm/word/doc/doc-text.js +731 -0
  67. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ import { XMLParser } from "fast-xml-parser";
2
+ //#region src/excel/activex-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 parseActiveX(data) {
11
+ const ocx = asObject(parser.parse(decoder.decode(data))["ocx"]);
12
+ if (!ocx) return {};
13
+ const props = /* @__PURE__ */ new Map();
14
+ for (const pr of toArray(ocx["ocxPr"])) {
15
+ const o = asObject(pr);
16
+ const name = o && strAttr(o, "name");
17
+ const value = o && strAttr(o, "value");
18
+ if (name) props.set(name.toLowerCase(), value ?? "");
19
+ }
20
+ const caption = props.get("caption");
21
+ const value = props.get("value");
22
+ const groupName = props.get("groupname");
23
+ return {
24
+ ...caption !== void 0 ? { caption } : {},
25
+ ...value !== void 0 ? { value } : {},
26
+ ...groupName !== void 0 ? { groupName } : {}
27
+ };
28
+ }
29
+ function activeXType(progId) {
30
+ switch ((/Forms\.(\w+)\.\d+/i.exec(progId ?? "")?.[1] ?? "").toLowerCase()) {
31
+ case "checkbox": return "checkbox";
32
+ case "optionbutton": return "option";
33
+ case "commandbutton": return "button";
34
+ case "togglebutton": return "toggle";
35
+ case "textbox": return "textbox";
36
+ case "combobox": return "combo";
37
+ case "listbox": return "list";
38
+ case "label": return "label";
39
+ case "spinbutton": return "spin";
40
+ case "scrollbar": return "scroll";
41
+ case "frame": return "frame";
42
+ default: return "control";
43
+ }
44
+ }
45
+ function asObject(v) {
46
+ return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
47
+ }
48
+ function toArray(v) {
49
+ return Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
50
+ }
51
+ function strAttr(obj, key) {
52
+ const v = obj[`@_${key}`];
53
+ return typeof v === "string" ? v : void 0;
54
+ }
55
+ function activeXBinRelId(xmlData) {
56
+ const ocx = asObject(parser.parse(decoder.decode(xmlData))["ocx"]);
57
+ if (!ocx) return void 0;
58
+ const persistence = strAttr(ocx, "persistence");
59
+ if (persistence !== "persistStreamInit" && persistence !== "persistStream" && persistence !== "persistStorage") return;
60
+ return strAttr(ocx, "id");
61
+ }
62
+ var CFB_MAGIC = [
63
+ 208,
64
+ 207,
65
+ 17,
66
+ 224,
67
+ 161,
68
+ 177,
69
+ 26,
70
+ 225
71
+ ];
72
+ var MORPH_DATA_BLOCK_1 = [
73
+ [0, 4],
74
+ [1, 4],
75
+ [2, 4],
76
+ [3, 4],
77
+ [4, 1],
78
+ [5, 1],
79
+ [6, 1],
80
+ [7, 1],
81
+ [9, 2],
82
+ [10, 4],
83
+ [11, 2],
84
+ [12, 2],
85
+ [13, 2],
86
+ [14, 2],
87
+ [15, 2],
88
+ [16, 1],
89
+ [17, 1],
90
+ [18, 1],
91
+ [20, 1],
92
+ [21, 1]
93
+ ];
94
+ var MORPH_DATA_BLOCK_2 = [
95
+ [24, 4],
96
+ [25, 4],
97
+ [26, 4],
98
+ [27, 2],
99
+ [28, 2],
100
+ [29, 2]
101
+ ];
102
+ var BIT_F_SIZE = 8;
103
+ var BIT_F_VALUE = 22;
104
+ var BIT_F_CAPTION = 23;
105
+ var BIT_F_GROUP_NAME = 32;
106
+ var MORPH_MAJOR_VERSION = 2;
107
+ var ONE_8 = new TextDecoder("windows-1252");
108
+ var UTF16 = new TextDecoder("utf-16le");
109
+ function u16le(d, i) {
110
+ return d[i] | d[i + 1] << 8;
111
+ }
112
+ function u32le(d, i) {
113
+ return (d[i] | d[i + 1] << 8 | d[i + 2] << 16 | d[i + 3] << 24) >>> 0;
114
+ }
115
+ function parseActiveXBin(data) {
116
+ if (data.length >= 8 && CFB_MAGIC.every((b, i) => data[i] === b)) return {};
117
+ let off;
118
+ if (data.length >= 18 && data[16] === 0 && data[17] === MORPH_MAJOR_VERSION) off = 16;
119
+ else if (data.length >= 2 && data[0] === 0 && data[1] === MORPH_MAJOR_VERSION) off = 0;
120
+ if (off === void 0) return {};
121
+ let p = off + 2;
122
+ if (p + 10 > data.length) return {};
123
+ const cb = u16le(data, p);
124
+ p += 2;
125
+ const blockEnd = Math.min(data.length, p + cb);
126
+ const propLo = u32le(data, p);
127
+ const propHi = u32le(data, p + 4);
128
+ p += 8;
129
+ const dataStart = p;
130
+ const bit = (b) => (b < 32 ? propLo >>> b & 1 : propHi >>> b - 32 & 1) === 1;
131
+ const align = (n) => {
132
+ p += (n - (p - dataStart) % n) % n;
133
+ };
134
+ for (const [b, size] of MORPH_DATA_BLOCK_1) if (bit(b)) {
135
+ align(size);
136
+ p += size;
137
+ }
138
+ const readCount = () => {
139
+ align(4);
140
+ if (p + 4 > blockEnd) return void 0;
141
+ const v = u32le(data, p);
142
+ p += 4;
143
+ return v;
144
+ };
145
+ let valueCount;
146
+ let captionCount;
147
+ let groupCount;
148
+ if (bit(BIT_F_VALUE)) {
149
+ valueCount = readCount();
150
+ if (valueCount === void 0) return {};
151
+ }
152
+ if (bit(BIT_F_CAPTION)) {
153
+ captionCount = readCount();
154
+ if (captionCount === void 0) return {};
155
+ }
156
+ for (const [b, size] of MORPH_DATA_BLOCK_2) if (bit(b)) {
157
+ align(size);
158
+ p += size;
159
+ }
160
+ if (bit(BIT_F_GROUP_NAME)) {
161
+ groupCount = readCount();
162
+ if (groupCount === void 0) return {};
163
+ }
164
+ if (bit(BIT_F_SIZE)) {
165
+ align(4);
166
+ p += 8;
167
+ }
168
+ const readString = (count) => {
169
+ if (count === void 0) return void 0;
170
+ align(4);
171
+ const len = count & 2147483647;
172
+ if (len === 0) return "";
173
+ if (p + len > blockEnd) return void 0;
174
+ const slice = data.subarray(p, p + len);
175
+ p += len;
176
+ return (count & 2147483648) !== 0 ? ONE_8.decode(slice) : UTF16.decode(slice);
177
+ };
178
+ const value = readString(valueCount);
179
+ const caption = readString(captionCount);
180
+ const groupName = readString(groupCount);
181
+ return {
182
+ ...caption !== void 0 && caption !== "" ? { caption } : {},
183
+ ...value !== void 0 && value !== "" ? { value } : {},
184
+ ...groupName !== void 0 && groupName !== "" ? { groupName } : {}
185
+ };
186
+ }
187
+ //#endregion
188
+ export { activeXBinRelId, activeXType, parseActiveX, parseActiveXBin };
@@ -1,5 +1,5 @@
1
1
  import { CellIcon } from '../core/document-model/index.js';
2
- import { ConditionalFormat, WorksheetCell, XlsxStyles } from '../core/spreadsheet-model/index.js';
2
+ import { ConditionalFormat, DefinedName, WorksheetCell, XlsxStyles } from '../core/spreadsheet-model/index.js';
3
3
  export interface CfOverride {
4
4
  readonly fillHex?: string;
5
5
  readonly fontColorHex?: string;
@@ -13,4 +13,6 @@ export interface CfOverride {
13
13
  readonly icon?: CellIcon;
14
14
  }
15
15
  export type CellConditionalFormatter = (row: number, col: number, numericValue: number | undefined, text: string | undefined) => CfOverride | undefined;
16
- export declare function buildConditionalFormatter(conditionalFormats: ReadonlyArray<ConditionalFormat> | undefined, styles: XlsxStyles, cells: ReadonlyArray<WorksheetCell>, resolveText?: (cell: WorksheetCell) => string): CellConditionalFormatter | undefined;
16
+ export declare function buildConditionalFormatter(conditionalFormats: ReadonlyArray<ConditionalFormat> | undefined, styles: XlsxStyles, cells: ReadonlyArray<WorksheetCell>, resolveText?: (cell: WorksheetCell) => string, date1904?: boolean, now?: Date, sheetGrids?: ReadonlyMap<string, {
17
+ readonly cells: ReadonlyArray<WorksheetCell>;
18
+ }>, currentSheet?: string, definedNames?: ReadonlyArray<DefinedName>): CellConditionalFormatter | undefined;
@@ -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, resolveText) {
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 = [];
@@ -33,13 +37,35 @@ function buildConditionalFormatter(conditionalFormats, styles, cells, resolveTex
33
37
  rule,
34
38
  dupKeys: resolveDupUnique(rule, cf.ranges, cells, resolveText)
35
39
  });
36
- else flat.push({
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({
37
52
  ranges: cf.ranges,
38
53
  rule
39
54
  });
40
55
  flat.sort((a, b) => a.rule.priority - b.rule.priority);
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;
41
67
  return (row, col, value, text) => {
42
- if (value === void 0 && (text === void 0 || text.length === 0)) return void 0;
68
+ if (!hasExpr && value === void 0 && (text === void 0 || text.length === 0)) return;
43
69
  const dupKey = dupKeyOf(value, text);
44
70
  let textFmt;
45
71
  let textClaimed = false;
@@ -49,7 +75,7 @@ function buildConditionalFormatter(conditionalFormats, styles, cells, resolveTex
49
75
  textFmt = dxfToOverride(dxfs[dxfId]);
50
76
  textClaimed = true;
51
77
  };
52
- for (const { ranges, rule, scale, bar: resolved, iconThresholds, threshold, dupKeys } of flat) {
78
+ for (const { ranges, rule, scale, bar: resolved, iconThresholds, threshold, dupKeys, compiled, origin } of flat) {
53
79
  if (!coversCell(ranges, row, col)) continue;
54
80
  switch (rule.type) {
55
81
  case "cellIs":
@@ -84,6 +110,19 @@ function buildConditionalFormatter(conditionalFormats, styles, cells, resolveTex
84
110
  case "endsWith":
85
111
  if (!textClaimed && text !== void 0 && textRuleMatches(rule, text)) claim(rule.dxfId);
86
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;
87
126
  }
88
127
  }
89
128
  if (!bar && !icon) return textFmt;
@@ -94,6 +133,101 @@ function buildConditionalFormatter(conditionalFormats, styles, cells, resolveTex
94
133
  };
95
134
  };
96
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
+ }
97
231
  function dupKeyOf(value, text) {
98
232
  if (value !== void 0) return `n:${value}`;
99
233
  if (text === void 0) return void 0;
@@ -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 {};