@cj-tech-master/excelts 9.2.1 → 9.3.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.
- package/README.md +25 -2
- package/README_zh.md +29 -6
- package/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.browser.js +4 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +4 -0
- package/dist/browser/modules/excel/cell.d.ts +17 -3
- package/dist/browser/modules/excel/cell.js +170 -22
- package/dist/browser/modules/excel/defined-names.d.ts +96 -1
- package/dist/browser/modules/excel/defined-names.js +411 -21
- package/dist/browser/modules/excel/image.d.ts +11 -0
- package/dist/browser/modules/excel/image.js +24 -1
- package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/browser/modules/excel/table.js +15 -2
- package/dist/browser/modules/excel/types.d.ts +133 -2
- package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/browser/modules/excel/utils/col-cache.js +15 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
- package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/browser/modules/excel/workbook.browser.js +318 -34
- package/dist/browser/modules/excel/workbook.d.ts +1 -1
- package/dist/browser/modules/excel/worksheet.d.ts +3 -1
- package/dist/browser/modules/excel/worksheet.js +21 -2
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/browser/modules/formula/compile/address-utils.js +83 -0
- package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
- package/dist/browser/modules/formula/compile/binder.js +487 -0
- package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
- package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
- package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/browser/modules/formula/functions/_date-context.js +29 -0
- package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/browser/modules/formula/functions/_shared.js +381 -0
- package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/browser/modules/formula/functions/conditional.js +343 -0
- package/dist/browser/modules/formula/functions/database.d.ts +37 -0
- package/dist/browser/modules/formula/functions/database.js +274 -0
- package/dist/browser/modules/formula/functions/date.d.ts +61 -0
- package/dist/browser/modules/formula/functions/date.js +855 -0
- package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/browser/modules/formula/functions/engineering.js +1128 -0
- package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
- package/dist/browser/modules/formula/functions/financial.js +2296 -0
- package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/browser/modules/formula/functions/lookup.js +886 -0
- package/dist/browser/modules/formula/functions/math.d.ts +114 -0
- package/dist/browser/modules/formula/functions/math.js +1406 -0
- package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/browser/modules/formula/functions/statistical.js +3390 -0
- package/dist/browser/modules/formula/functions/text.d.ts +86 -0
- package/dist/browser/modules/formula/functions/text.js +1845 -0
- package/dist/browser/modules/formula/host-registry.d.ts +53 -0
- package/dist/browser/modules/formula/host-registry.js +69 -0
- package/dist/browser/modules/formula/index.d.ts +39 -0
- package/dist/browser/modules/formula/index.js +49 -0
- package/dist/browser/modules/formula/install.d.ts +62 -0
- package/dist/browser/modules/formula/install.js +88 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
- package/dist/browser/modules/formula/materialize/types.js +29 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
- package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
- package/dist/browser/modules/formula/runtime/values.js +385 -0
- package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/browser/modules/formula/syntax/ast.js +28 -0
- package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/browser/modules/formula/syntax/parser.js +439 -0
- package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/browser/modules/formula/syntax/token-types.js +59 -0
- package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/browser/modules/pdf/excel-bridge.js +9 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/modules/excel/cell.js +170 -22
- package/dist/cjs/modules/excel/defined-names.js +411 -21
- package/dist/cjs/modules/excel/image.js +24 -1
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/cjs/modules/excel/table.js +15 -2
- package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
- package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
- package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
- package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
- package/dist/cjs/modules/excel/workbook.browser.js +318 -34
- package/dist/cjs/modules/excel/worksheet.js +20 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
- package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
- package/dist/cjs/modules/formula/compile/binder.js +489 -0
- package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
- package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
- package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
- package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
- package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
- package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
- package/dist/cjs/modules/formula/functions/_shared.js +396 -0
- package/dist/cjs/modules/formula/functions/conditional.js +354 -0
- package/dist/cjs/modules/formula/functions/database.js +288 -0
- package/dist/cjs/modules/formula/functions/date.js +883 -0
- package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
- package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
- package/dist/cjs/modules/formula/functions/financial.js +2348 -0
- package/dist/cjs/modules/formula/functions/lookup.js +902 -0
- package/dist/cjs/modules/formula/functions/math.js +1487 -0
- package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
- package/dist/cjs/modules/formula/functions/text.js +1889 -0
- package/dist/cjs/modules/formula/host-registry.js +75 -0
- package/dist/cjs/modules/formula/index.js +58 -0
- package/dist/cjs/modules/formula/install.js +93 -0
- package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
- package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
- package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
- package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
- package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
- package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
- package/dist/cjs/modules/formula/materialize/types.js +32 -0
- package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
- package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
- package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
- package/dist/cjs/modules/formula/runtime/values.js +385 -0
- package/dist/cjs/modules/formula/syntax/ast.js +8 -0
- package/dist/cjs/modules/formula/syntax/parser.js +440 -0
- package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
- package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
- package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
- package/dist/esm/index.browser.js +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/modules/excel/cell.js +170 -22
- package/dist/esm/modules/excel/defined-names.js +411 -21
- package/dist/esm/modules/excel/image.js +24 -1
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/esm/modules/excel/table.js +15 -2
- package/dist/esm/modules/excel/utils/col-cache.js +15 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
- package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/esm/modules/excel/workbook.browser.js +318 -34
- package/dist/esm/modules/excel/worksheet.js +21 -2
- package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/esm/modules/formula/compile/address-utils.js +83 -0
- package/dist/esm/modules/formula/compile/binder.js +487 -0
- package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
- package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
- package/dist/esm/modules/formula/functions/_date-context.js +29 -0
- package/dist/esm/modules/formula/functions/_shared.js +381 -0
- package/dist/esm/modules/formula/functions/conditional.js +343 -0
- package/dist/esm/modules/formula/functions/database.js +274 -0
- package/dist/esm/modules/formula/functions/date.js +855 -0
- package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/esm/modules/formula/functions/engineering.js +1128 -0
- package/dist/esm/modules/formula/functions/financial.js +2296 -0
- package/dist/esm/modules/formula/functions/lookup.js +886 -0
- package/dist/esm/modules/formula/functions/math.js +1406 -0
- package/dist/esm/modules/formula/functions/statistical.js +3390 -0
- package/dist/esm/modules/formula/functions/text.js +1845 -0
- package/dist/esm/modules/formula/host-registry.js +69 -0
- package/dist/esm/modules/formula/index.js +49 -0
- package/dist/esm/modules/formula/install.js +88 -0
- package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
- package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/esm/modules/formula/materialize/types.js +29 -0
- package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
- package/dist/esm/modules/formula/runtime/values.js +385 -0
- package/dist/esm/modules/formula/syntax/ast.js +28 -0
- package/dist/esm/modules/formula/syntax/parser.js +439 -0
- package/dist/esm/modules/formula/syntax/token-types.js +59 -0
- package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/esm/modules/pdf/excel-bridge.js +9 -0
- package/dist/iife/excelts.iife.js +2302 -373
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +34 -34
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/modules/excel/cell.d.ts +17 -3
- package/dist/types/modules/excel/defined-names.d.ts +96 -1
- package/dist/types/modules/excel/image.d.ts +11 -0
- package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/types/modules/excel/types.d.ts +133 -2
- package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/types/modules/excel/workbook.d.ts +1 -1
- package/dist/types/modules/excel/worksheet.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/types/modules/formula/compile/binder.d.ts +42 -0
- package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/types/modules/formula/functions/database.d.ts +37 -0
- package/dist/types/modules/formula/functions/date.d.ts +61 -0
- package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/types/modules/formula/functions/financial.d.ts +202 -0
- package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/types/modules/formula/functions/math.d.ts +114 -0
- package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/types/modules/formula/functions/text.d.ts +86 -0
- package/dist/types/modules/formula/host-registry.d.ts +53 -0
- package/dist/types/modules/formula/index.d.ts +39 -0
- package/dist/types/modules/formula/install.d.ts +62 -0
- package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/types/modules/formula/materialize/types.d.ts +179 -0
- package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/types/modules/formula/runtime/values.d.ts +211 -0
- package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/package.json +28 -28
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Formula Tokenizer
|
|
4
|
+
*
|
|
5
|
+
* Converts an Excel formula string into a stream of tokens.
|
|
6
|
+
* Supports cell references (A1, $A$1), ranges (A1:B10),
|
|
7
|
+
* cross-sheet references (Sheet1!A1), operators, function calls,
|
|
8
|
+
* numbers, strings, booleans, and error literals.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.tokenize = tokenize;
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Character Helpers
|
|
14
|
+
// ============================================================================
|
|
15
|
+
function isDigit(ch) {
|
|
16
|
+
return ch >= "0" && ch <= "9";
|
|
17
|
+
}
|
|
18
|
+
// Matches a single Unicode letter (any script). Used by `isAlpha` for the
|
|
19
|
+
// non-ASCII path so that defined names in Chinese, Japanese, Korean, Cyrillic,
|
|
20
|
+
// Greek, etc. tokenise correctly (e.g. `=销售额+10`).
|
|
21
|
+
//
|
|
22
|
+
// Note: JavaScript strings are UTF-16, so `formula[i]` yields a single code
|
|
23
|
+
// unit. This regex correctly identifies any BMP letter. Astral-plane letters
|
|
24
|
+
// (U+10000+) require surrogate-pair handling which would be more invasive;
|
|
25
|
+
// BMP coverage is sufficient for virtually all real-world named ranges.
|
|
26
|
+
const UNICODE_LETTER = /\p{L}/u;
|
|
27
|
+
function isAlpha(ch) {
|
|
28
|
+
// ASCII fast path — the overwhelming majority of Excel formulas.
|
|
29
|
+
if ((ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z")) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Any other ASCII character (digits, punctuation, control) is not a letter.
|
|
33
|
+
if (ch.charCodeAt(0) < 128) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Non-ASCII: defer to Unicode letter classification.
|
|
37
|
+
return UNICODE_LETTER.test(ch);
|
|
38
|
+
}
|
|
39
|
+
function isAlphaNumOrUnderscore(ch) {
|
|
40
|
+
return isAlpha(ch) || isDigit(ch) || ch === "_" || ch === ".";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ASCII-only alpha check. Cell column letters are A–Z only — a Unicode
|
|
44
|
+
* identifier like `销售额` must never be misread as a column. Use this inside
|
|
45
|
+
* `parseCellRef` and other strictly-A1-notation parsers; use `isAlpha` for
|
|
46
|
+
* general identifier lexing where Unicode letters are valid.
|
|
47
|
+
*/
|
|
48
|
+
function isAsciiAlpha(ch) {
|
|
49
|
+
return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z");
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Cheap lookahead: does position `i` in `s` start a plausible cell or
|
|
53
|
+
* range reference? We only need to distinguish "this is a ref" from
|
|
54
|
+
* "this is anything else" — good enough to disambiguate 3D sheet
|
|
55
|
+
* references (`Q1:Q4!A1`) from name references.
|
|
56
|
+
*
|
|
57
|
+
* Accepts: `$?[A-Z]+$?\d+`-shaped prefixes, whole-column `$?[A-Z]+`,
|
|
58
|
+
* and whole-row `$?\d+`. Anything else (identifiers, punctuation,
|
|
59
|
+
* quotes) returns false.
|
|
60
|
+
*/
|
|
61
|
+
function looksLikeCellRefStart(s, i) {
|
|
62
|
+
const len = s.length;
|
|
63
|
+
let j = i;
|
|
64
|
+
if (j < len && s[j] === "$") {
|
|
65
|
+
j++;
|
|
66
|
+
}
|
|
67
|
+
// Either letters first (cell / col) or digits first (whole row)
|
|
68
|
+
if (j < len && isAsciiAlpha(s[j])) {
|
|
69
|
+
// Consume up to 3 letters (Excel column max "XFD"). More than 3
|
|
70
|
+
// consecutive letters means it's not a column ref.
|
|
71
|
+
let letters = 0;
|
|
72
|
+
while (j < len && isAsciiAlpha(s[j]) && letters < 4) {
|
|
73
|
+
j++;
|
|
74
|
+
letters++;
|
|
75
|
+
}
|
|
76
|
+
if (letters === 0 || letters > 3) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// Optional $digits for an A1-style cell ref; pure letters are a
|
|
80
|
+
// whole-column ref, also valid.
|
|
81
|
+
if (j < len && s[j] === "$") {
|
|
82
|
+
j++;
|
|
83
|
+
}
|
|
84
|
+
// At minimum the following char should be a digit or a delimiter
|
|
85
|
+
// that closes a whole-column ref (`:` / `,` / `)` / operators).
|
|
86
|
+
if (j < len && s[j] >= "0" && s[j] <= "9") {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return true; // whole-column ref, e.g. `A:A` or bare `A`
|
|
90
|
+
}
|
|
91
|
+
if (j < len && s[j] >= "0" && s[j] <= "9") {
|
|
92
|
+
// Whole-row ref (e.g. `1:5`)
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Advance past a run of spaces and tabs. Used inside bracketed structured
|
|
99
|
+
* reference parsing where only horizontal whitespace is significant (newlines
|
|
100
|
+
* and carriage returns are not expected inside `[...]` tokens).
|
|
101
|
+
*/
|
|
102
|
+
function skipHorizontalWhitespace(s, i, len) {
|
|
103
|
+
while (i < len && (s[i] === " " || s[i] === "\t")) {
|
|
104
|
+
i++;
|
|
105
|
+
}
|
|
106
|
+
return i;
|
|
107
|
+
}
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Error Literal Detection
|
|
110
|
+
// ============================================================================
|
|
111
|
+
/**
|
|
112
|
+
* Known Excel error literals. The constant below is sorted longest-first at
|
|
113
|
+
* module load so greedy longest-match in `matchErrorLiteral` is always
|
|
114
|
+
* correct — future entries can be added in any order.
|
|
115
|
+
*/
|
|
116
|
+
const ERROR_LITERALS = [
|
|
117
|
+
"#GETTING_DATA",
|
|
118
|
+
"#BLOCKED!",
|
|
119
|
+
"#CONNECT!",
|
|
120
|
+
"#UNKNOWN!",
|
|
121
|
+
"#SPILL!",
|
|
122
|
+
"#VALUE!",
|
|
123
|
+
"#FIELD!",
|
|
124
|
+
"#DIV/0!",
|
|
125
|
+
"#NAME?",
|
|
126
|
+
"#NULL!",
|
|
127
|
+
"#CALC!",
|
|
128
|
+
"#BUSY!",
|
|
129
|
+
"#REF!",
|
|
130
|
+
"#NUM!",
|
|
131
|
+
"#N/A"
|
|
132
|
+
].sort((a, b) => b.length - a.length);
|
|
133
|
+
/**
|
|
134
|
+
* Try to match a known Excel error literal at `pos` (which must point at `#`).
|
|
135
|
+
* Matches case-insensitively and greedily (longest list entry wins).
|
|
136
|
+
* Returns the canonical (upper-cased) value and end index, or null on no match.
|
|
137
|
+
*/
|
|
138
|
+
function matchErrorLiteral(formula, pos) {
|
|
139
|
+
for (const lit of ERROR_LITERALS) {
|
|
140
|
+
const end = pos + lit.length;
|
|
141
|
+
if (end > formula.length) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const slice = formula.slice(pos, end);
|
|
145
|
+
if (slice.toUpperCase() === lit) {
|
|
146
|
+
return { value: lit, end };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Cell Reference Detection
|
|
153
|
+
// ============================================================================
|
|
154
|
+
/**
|
|
155
|
+
* Check if a string is a valid cell reference (like A1, $B$2, XFD1048576).
|
|
156
|
+
* Returns null if not a cell ref, otherwise returns parsed info.
|
|
157
|
+
*/
|
|
158
|
+
function parseCellRef(s) {
|
|
159
|
+
let i = 0;
|
|
160
|
+
let colAbsolute = false;
|
|
161
|
+
let rowAbsolute = false;
|
|
162
|
+
if (i < s.length && s[i] === "$") {
|
|
163
|
+
colAbsolute = true;
|
|
164
|
+
i++;
|
|
165
|
+
}
|
|
166
|
+
const colStart = i;
|
|
167
|
+
while (i < s.length && isAsciiAlpha(s[i])) {
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
const colPart = s.slice(colStart, i).toUpperCase();
|
|
171
|
+
if (colPart.length === 0 || colPart.length > 3) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
// Validate column is <= XFD
|
|
175
|
+
if (colPart.length === 3 && colPart > "XFD") {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
if (i < s.length && s[i] === "$") {
|
|
179
|
+
rowAbsolute = true;
|
|
180
|
+
i++;
|
|
181
|
+
}
|
|
182
|
+
const rowStart = i;
|
|
183
|
+
while (i < s.length && isDigit(s[i])) {
|
|
184
|
+
i++;
|
|
185
|
+
}
|
|
186
|
+
const rowPart = s.slice(rowStart, i);
|
|
187
|
+
if (rowPart.length === 0 || i !== s.length) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const rowNum = parseInt(rowPart, 10);
|
|
191
|
+
if (rowNum < 1 || rowNum > 1048576) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return { col: colPart, row: rowPart, colAbsolute, rowAbsolute };
|
|
195
|
+
}
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Structured Reference Bracket Parser
|
|
198
|
+
// ============================================================================
|
|
199
|
+
/** Valid special items in structured references */
|
|
200
|
+
const STRUCTURED_REF_SPECIALS = new Set(["#All", "#Data", "#Headers", "#Totals", "#This Row"]);
|
|
201
|
+
/**
|
|
202
|
+
* Parse the bracketed portion of a structured reference starting at position `pos`.
|
|
203
|
+
* `pos` must point to the opening `[`.
|
|
204
|
+
*
|
|
205
|
+
* Handles:
|
|
206
|
+
* [Column] → simple column
|
|
207
|
+
* [#Headers] → special item
|
|
208
|
+
* [[#Headers],[Column]] → nested: special + column
|
|
209
|
+
* [[#This Row],[Column]] → nested: special + column
|
|
210
|
+
* [[Col1]:[Col2]] → column range
|
|
211
|
+
* [@Column] → shorthand for [#This Row],[Column]
|
|
212
|
+
* [@[Column]] → alternative shorthand
|
|
213
|
+
*
|
|
214
|
+
* Returns parsed specials, columns, and the position after the closing `]`.
|
|
215
|
+
*/
|
|
216
|
+
function parseStructuredRefBrackets(formula, pos) {
|
|
217
|
+
const len = formula.length;
|
|
218
|
+
if (pos >= len || formula[pos] !== "[") {
|
|
219
|
+
throw new Error("Expected '[' at position " + pos);
|
|
220
|
+
}
|
|
221
|
+
const specials = [];
|
|
222
|
+
const columns = [];
|
|
223
|
+
let i = pos + 1; // skip opening [
|
|
224
|
+
// Skip whitespace inside brackets
|
|
225
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
226
|
+
// Check for @ shorthand: [@Column] or [@[Column]]
|
|
227
|
+
if (i < len && formula[i] === "@") {
|
|
228
|
+
specials.push("#This Row");
|
|
229
|
+
i++; // skip @
|
|
230
|
+
// Skip whitespace
|
|
231
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
232
|
+
if (i < len && formula[i] === "[") {
|
|
233
|
+
// [@[Column]] form — inner bracketed column name
|
|
234
|
+
i++; // skip [
|
|
235
|
+
const colName = readBracketedItem(formula, i);
|
|
236
|
+
columns.push(colName.value);
|
|
237
|
+
i = colName.end; // after inner ]
|
|
238
|
+
}
|
|
239
|
+
else if (i < len && formula[i] !== "]") {
|
|
240
|
+
// [@Column] form — unbracketed column name until ]
|
|
241
|
+
const start = i;
|
|
242
|
+
while (i < len && formula[i] !== "]") {
|
|
243
|
+
i++;
|
|
244
|
+
}
|
|
245
|
+
const name = formula.slice(start, i).trim();
|
|
246
|
+
if (name.length > 0) {
|
|
247
|
+
columns.push(name);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Skip whitespace before closing ]
|
|
251
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
252
|
+
if (i < len && formula[i] === "]") {
|
|
253
|
+
i++; // skip closing ]
|
|
254
|
+
}
|
|
255
|
+
return { specials, columns, end: i };
|
|
256
|
+
}
|
|
257
|
+
// Check for nested brackets: [[...],[...]] or [[Col1]:[Col2]]
|
|
258
|
+
if (i < len && formula[i] === "[") {
|
|
259
|
+
// Parse comma-separated inner bracket items
|
|
260
|
+
while (i < len && formula[i] === "[") {
|
|
261
|
+
const item = readBracketedItem(formula, i + 1);
|
|
262
|
+
const value = item.value;
|
|
263
|
+
i = item.end; // after the inner ]
|
|
264
|
+
if (STRUCTURED_REF_SPECIALS.has(value)) {
|
|
265
|
+
specials.push(value);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
columns.push(value);
|
|
269
|
+
}
|
|
270
|
+
// Skip whitespace
|
|
271
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
272
|
+
// Check for : (column range) or , (next item)
|
|
273
|
+
if (i < len && formula[i] === ":") {
|
|
274
|
+
i++; // skip :
|
|
275
|
+
// Skip whitespace
|
|
276
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
277
|
+
// Next must be a [Column]
|
|
278
|
+
if (i < len && formula[i] === "[") {
|
|
279
|
+
const item2 = readBracketedItem(formula, i + 1);
|
|
280
|
+
columns.push(item2.value);
|
|
281
|
+
i = item2.end;
|
|
282
|
+
}
|
|
283
|
+
// Skip whitespace
|
|
284
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
285
|
+
}
|
|
286
|
+
else if (i < len && formula[i] === ",") {
|
|
287
|
+
i++; // skip ,
|
|
288
|
+
// Skip whitespace
|
|
289
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Skip whitespace before closing ]
|
|
293
|
+
i = skipHorizontalWhitespace(formula, i, len);
|
|
294
|
+
if (i < len && formula[i] === "]") {
|
|
295
|
+
i++; // skip outer closing ]
|
|
296
|
+
}
|
|
297
|
+
return { specials, columns, end: i };
|
|
298
|
+
}
|
|
299
|
+
// Check for special item: [#Headers], [#Data], etc.
|
|
300
|
+
if (i < len && formula[i] === "#") {
|
|
301
|
+
const start = i;
|
|
302
|
+
while (i < len && formula[i] !== "]") {
|
|
303
|
+
i++;
|
|
304
|
+
}
|
|
305
|
+
const value = formula.slice(start, i).trim();
|
|
306
|
+
if (STRUCTURED_REF_SPECIALS.has(value)) {
|
|
307
|
+
specials.push(value);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// Unknown `[#Something]` is an error, not a silent no-op. Stash
|
|
311
|
+
// the invalid string so the binder can route it to #NAME?; doing
|
|
312
|
+
// this the other way (returning an empty specials+columns) would
|
|
313
|
+
// alias to the default data range and silently mis-evaluate. We
|
|
314
|
+
// use a sentinel prefix that can never match a real special so
|
|
315
|
+
// downstream code distinguishes it unambiguously.
|
|
316
|
+
specials.push(`#__INVALID__:${value}`);
|
|
317
|
+
}
|
|
318
|
+
if (i < len && formula[i] === "]") {
|
|
319
|
+
i++; // skip closing ]
|
|
320
|
+
}
|
|
321
|
+
return { specials, columns, end: i };
|
|
322
|
+
}
|
|
323
|
+
// Simple column reference: [Column]
|
|
324
|
+
const start = i;
|
|
325
|
+
while (i < len && formula[i] !== "]") {
|
|
326
|
+
i++;
|
|
327
|
+
}
|
|
328
|
+
const name = formula.slice(start, i).trim();
|
|
329
|
+
if (name.length > 0) {
|
|
330
|
+
columns.push(name);
|
|
331
|
+
}
|
|
332
|
+
if (i < len && formula[i] === "]") {
|
|
333
|
+
i++; // skip closing ]
|
|
334
|
+
}
|
|
335
|
+
return { specials, columns, end: i };
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Read a single bracketed item starting after the opening `[`.
|
|
339
|
+
* Returns the text content and position after the closing `]`.
|
|
340
|
+
* Handles nested `'` escaping within bracket content (Excel uses `'` to escape
|
|
341
|
+
* special chars like `]`, `[`, `#` inside column names — but this is rare).
|
|
342
|
+
*/
|
|
343
|
+
function readBracketedItem(formula, pos) {
|
|
344
|
+
const len = formula.length;
|
|
345
|
+
let i = pos;
|
|
346
|
+
let result = "";
|
|
347
|
+
while (i < len && formula[i] !== "]") {
|
|
348
|
+
if (formula[i] === "'" && i + 1 < len) {
|
|
349
|
+
// Escape: the next character is literal
|
|
350
|
+
result += formula[i + 1];
|
|
351
|
+
i += 2;
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
result += formula[i];
|
|
355
|
+
i++;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (i < len && formula[i] === "]") {
|
|
359
|
+
i++; // skip ]
|
|
360
|
+
}
|
|
361
|
+
return { value: result.trim(), end: i };
|
|
362
|
+
}
|
|
363
|
+
// ============================================================================
|
|
364
|
+
// Tokenizer
|
|
365
|
+
// ============================================================================
|
|
366
|
+
function tokenize(formula) {
|
|
367
|
+
const tokens = [];
|
|
368
|
+
let i = 0;
|
|
369
|
+
const len = formula.length;
|
|
370
|
+
// Track whether previous token could produce a value
|
|
371
|
+
// (used to distinguish unary vs binary +/-)
|
|
372
|
+
function lastTokenIsValue() {
|
|
373
|
+
if (tokens.length === 0) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const last = tokens[tokens.length - 1];
|
|
377
|
+
return (last.type === 1 /* TokenType.Number */ ||
|
|
378
|
+
last.type === 2 /* TokenType.String */ ||
|
|
379
|
+
last.type === 3 /* TokenType.Boolean */ ||
|
|
380
|
+
last.type === 5 /* TokenType.CellRef */ ||
|
|
381
|
+
last.type === 6 /* TokenType.Range */ ||
|
|
382
|
+
last.type === 11 /* TokenType.CloseParen */ ||
|
|
383
|
+
last.type === 16 /* TokenType.CloseBrace */ ||
|
|
384
|
+
last.type === 14 /* TokenType.Percent */ ||
|
|
385
|
+
last.type === 19 /* TokenType.Name */ ||
|
|
386
|
+
last.type === 4 /* TokenType.Error */ ||
|
|
387
|
+
last.type === 20 /* TokenType.ColRange */ ||
|
|
388
|
+
last.type === 21 /* TokenType.RowRange */ ||
|
|
389
|
+
last.type === 22 /* TokenType.StructuredRef */);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* True if the previous token produces a reference (cell / range / area).
|
|
393
|
+
* Used to detect Excel's intersection operator — a whitespace character
|
|
394
|
+
* that separates two refs (e.g. `A1:A10 B1:B10`).
|
|
395
|
+
*
|
|
396
|
+
* Intentionally narrower than {@link lastTokenIsValue}: scalars (numbers,
|
|
397
|
+
* strings, booleans, errors) are not references, so whitespace that
|
|
398
|
+
* follows them must not be treated as an intersection operator.
|
|
399
|
+
*/
|
|
400
|
+
function lastTokenIsRef() {
|
|
401
|
+
if (tokens.length === 0) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const last = tokens[tokens.length - 1];
|
|
405
|
+
return (last.type === 5 /* TokenType.CellRef */ ||
|
|
406
|
+
last.type === 6 /* TokenType.Range */ ||
|
|
407
|
+
last.type === 20 /* TokenType.ColRange */ ||
|
|
408
|
+
last.type === 21 /* TokenType.RowRange */ ||
|
|
409
|
+
last.type === 22 /* TokenType.StructuredRef */ ||
|
|
410
|
+
last.type === 11 /* TokenType.CloseParen */ ||
|
|
411
|
+
last.type === 19 /* TokenType.Name */);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* True if `ch` can start a reference-producing token: a cell ref, range,
|
|
415
|
+
* sheet-qualified ref (`Sheet!`, `'Sheet'!`), named range, external ref,
|
|
416
|
+
* or a parenthesised ref expression.
|
|
417
|
+
*/
|
|
418
|
+
function couldStartRef(ch) {
|
|
419
|
+
return isAlpha(ch) || ch === "$" || ch === "_" || ch === "'" || ch === "[" || ch === "(";
|
|
420
|
+
}
|
|
421
|
+
while (i < len) {
|
|
422
|
+
const ch = formula[i];
|
|
423
|
+
// Whitespace — normally ignored, but between two refs it is Excel's
|
|
424
|
+
// intersection operator (e.g. `A1:A10 B1:B10` → intersection of areas).
|
|
425
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
|
|
426
|
+
// Consume every consecutive whitespace character in one go.
|
|
427
|
+
while (i < len) {
|
|
428
|
+
const c = formula[i];
|
|
429
|
+
if (c === " " || c === "\t" || c === "\n" || c === "\r") {
|
|
430
|
+
i++;
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Emit an intersection token only when the previous token is a ref
|
|
437
|
+
// AND the next character can start another ref. Emits at most one
|
|
438
|
+
// token per whitespace run.
|
|
439
|
+
if (i < len && lastTokenIsRef() && couldStartRef(formula[i])) {
|
|
440
|
+
tokens.push({ type: 24 /* TokenType.Intersect */ });
|
|
441
|
+
}
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
// String literals
|
|
445
|
+
if (ch === '"') {
|
|
446
|
+
i++; // skip opening quote
|
|
447
|
+
let str = "";
|
|
448
|
+
let closed = false;
|
|
449
|
+
while (i < len) {
|
|
450
|
+
if (formula[i] === '"') {
|
|
451
|
+
if (i + 1 < len && formula[i + 1] === '"') {
|
|
452
|
+
// Escaped quote
|
|
453
|
+
str += '"';
|
|
454
|
+
i += 2;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
i++; // skip closing quote
|
|
458
|
+
closed = true;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
str += formula[i];
|
|
464
|
+
i++;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (!closed) {
|
|
468
|
+
// Unterminated string literal — reject at tokenize time so we
|
|
469
|
+
// never hand the parser a truncated value that could alias to a
|
|
470
|
+
// different formula. Excel rejects this outright.
|
|
471
|
+
throw new Error(`Unterminated string literal at position ${i}`);
|
|
472
|
+
}
|
|
473
|
+
tokens.push({ type: 2 /* TokenType.String */, value: str });
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
// Error literals (#N/A, #REF!, #DIV/0!, etc.)
|
|
477
|
+
// Match greedily against the known Excel error literals (case-insensitive),
|
|
478
|
+
// preferring the longest match so that "#N/A" isn't parsed as "#N" + "/A".
|
|
479
|
+
if (ch === "#") {
|
|
480
|
+
const matched = matchErrorLiteral(formula, i);
|
|
481
|
+
if (matched) {
|
|
482
|
+
tokens.push({ type: 4 /* TokenType.Error */, value: matched.value });
|
|
483
|
+
i = matched.end;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
// Fallback: consume through any unknown `#...` token and emit it
|
|
487
|
+
// as a #NAME? error. Unknown error literals (`#FOOBAR!`) are what
|
|
488
|
+
// Excel itself surfaces as #NAME?, and consumers downstream
|
|
489
|
+
// whitelist a fixed set of error codes — letting arbitrary strings
|
|
490
|
+
// like `#FOOBAR!` or bare `#` leak through breaks their enum
|
|
491
|
+
// checks. Normalising to #NAME? here keeps error propagation safe.
|
|
492
|
+
i++;
|
|
493
|
+
while (i < len &&
|
|
494
|
+
formula[i] !== " " &&
|
|
495
|
+
formula[i] !== "," &&
|
|
496
|
+
formula[i] !== ")" &&
|
|
497
|
+
formula[i] !== "+" &&
|
|
498
|
+
formula[i] !== "-" &&
|
|
499
|
+
formula[i] !== "*" &&
|
|
500
|
+
formula[i] !== "/" &&
|
|
501
|
+
formula[i] !== "^" &&
|
|
502
|
+
formula[i] !== "&" &&
|
|
503
|
+
formula[i] !== "=" &&
|
|
504
|
+
formula[i] !== "<" &&
|
|
505
|
+
formula[i] !== ">" &&
|
|
506
|
+
formula[i] !== "}") {
|
|
507
|
+
i++;
|
|
508
|
+
}
|
|
509
|
+
tokens.push({ type: 4 /* TokenType.Error */, value: "#NAME?" });
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
// External workbook reference: [Book.xlsx]Sheet!A1
|
|
513
|
+
// When [ appears and is NOT preceded by a value token (not array literal context),
|
|
514
|
+
// consume the bracketed workbook name and subsequent sheet!ref as a Name token.
|
|
515
|
+
// This allows the engine to fall back to cached results gracefully.
|
|
516
|
+
//
|
|
517
|
+
// But: a bare [...] can also be a standalone structured reference
|
|
518
|
+
// ([@Col], [#Headers], [[#This Row],[Col]], [Column Name]). Only treat it
|
|
519
|
+
// as an external ref when it really is followed by a `Sheet!` suffix;
|
|
520
|
+
// otherwise fall through to the structured-reference branch below.
|
|
521
|
+
if (ch === "[" && !lastTokenIsValue()) {
|
|
522
|
+
// Peek at first non-whitespace char after [ — @ or # always means a
|
|
523
|
+
// structured reference, never an external workbook reference.
|
|
524
|
+
const peek = skipHorizontalWhitespace(formula, i + 1, len);
|
|
525
|
+
const firstCh = peek < len ? formula[peek] : "";
|
|
526
|
+
const looksStructured = firstCh === "@" || firstCh === "#" || firstCh === "[";
|
|
527
|
+
if (!looksStructured) {
|
|
528
|
+
// Try to parse as an external ref: [WorkbookName]Sheet!...
|
|
529
|
+
let scan = i + 1;
|
|
530
|
+
let hasClose = false;
|
|
531
|
+
while (scan < len) {
|
|
532
|
+
if (formula[scan] === "]") {
|
|
533
|
+
hasClose = true;
|
|
534
|
+
scan++;
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
scan++;
|
|
538
|
+
}
|
|
539
|
+
// Validate: after the closing ], we must see a sheet name (or quoted
|
|
540
|
+
// sheet name) followed by `!`. Otherwise this isn't an external ref.
|
|
541
|
+
let isExternal = false;
|
|
542
|
+
if (hasClose && scan < len) {
|
|
543
|
+
let p = scan;
|
|
544
|
+
if (formula[p] === "'") {
|
|
545
|
+
// Quoted sheet name — scan to matching '
|
|
546
|
+
p++;
|
|
547
|
+
while (p < len) {
|
|
548
|
+
if (formula[p] === "'") {
|
|
549
|
+
if (p + 1 < len && formula[p + 1] === "'") {
|
|
550
|
+
p += 2;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
p++;
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
p++;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (p < len && formula[p] === "!") {
|
|
562
|
+
isExternal = true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
// Unquoted sheet name — identifier chars, then !
|
|
567
|
+
const sheetStart = p;
|
|
568
|
+
while (p < len && (isAlphaNumOrUnderscore(formula[p]) || formula[p] === "$")) {
|
|
569
|
+
p++;
|
|
570
|
+
}
|
|
571
|
+
if (p > sheetStart && p < len && formula[p] === "!") {
|
|
572
|
+
isExternal = true;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (isExternal) {
|
|
577
|
+
// External workbook references are unsupported — consume the full
|
|
578
|
+
// ref syntax (including any `:CELL` range suffix) so downstream
|
|
579
|
+
// tokens aren't polluted, then emit a single `#REF!` error token.
|
|
580
|
+
// The boundary logic preserved here matches the pre-simplification
|
|
581
|
+
// tokenizer exactly so the range of characters consumed is stable.
|
|
582
|
+
i = scan;
|
|
583
|
+
while (i < len &&
|
|
584
|
+
formula[i] !== "+" &&
|
|
585
|
+
formula[i] !== "-" &&
|
|
586
|
+
formula[i] !== "*" &&
|
|
587
|
+
formula[i] !== "/" &&
|
|
588
|
+
formula[i] !== "^" &&
|
|
589
|
+
formula[i] !== "&" &&
|
|
590
|
+
formula[i] !== "=" &&
|
|
591
|
+
formula[i] !== "<" &&
|
|
592
|
+
formula[i] !== ">" &&
|
|
593
|
+
formula[i] !== ")" &&
|
|
594
|
+
formula[i] !== "," &&
|
|
595
|
+
formula[i] !== " " &&
|
|
596
|
+
formula[i] !== "}" &&
|
|
597
|
+
formula[i] !== ":" &&
|
|
598
|
+
formula[i] !== "%" &&
|
|
599
|
+
formula[i] !== ";" &&
|
|
600
|
+
formula[i] !== "{") {
|
|
601
|
+
i++;
|
|
602
|
+
}
|
|
603
|
+
// Extend consumption through `:CELL` if this is a range form
|
|
604
|
+
// like [Book]Sheet!A1:A5. Without this, `:` would split the
|
|
605
|
+
// external ref and pollute downstream tokens.
|
|
606
|
+
if (i < len && formula[i] === ":") {
|
|
607
|
+
let q = i + 1;
|
|
608
|
+
// Optional $ absolute markers, column letters, optional $, row digits
|
|
609
|
+
if (q < len && formula[q] === "$") {
|
|
610
|
+
q++;
|
|
611
|
+
}
|
|
612
|
+
const colStart = q;
|
|
613
|
+
while (q < len && isAsciiAlpha(formula[q])) {
|
|
614
|
+
q++;
|
|
615
|
+
}
|
|
616
|
+
if (q > colStart) {
|
|
617
|
+
if (q < len && formula[q] === "$") {
|
|
618
|
+
q++;
|
|
619
|
+
}
|
|
620
|
+
const rowStart = q;
|
|
621
|
+
while (q < len && isDigit(formula[q])) {
|
|
622
|
+
q++;
|
|
623
|
+
}
|
|
624
|
+
if (q > rowStart) {
|
|
625
|
+
// Valid `:CELL` suffix — consume it as part of the ref
|
|
626
|
+
i = q;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
tokens.push({ type: 4 /* TokenType.Error */, value: "#REF!" });
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
// Not an external ref — fall through to the structured-reference
|
|
634
|
+
// branch below, which handles bare [Column Name] etc.
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// Array constant braces
|
|
638
|
+
if (ch === "{") {
|
|
639
|
+
tokens.push({ type: 15 /* TokenType.OpenBrace */ });
|
|
640
|
+
i++;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (ch === "}") {
|
|
644
|
+
tokens.push({ type: 16 /* TokenType.CloseBrace */ });
|
|
645
|
+
i++;
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
// Semicolons (array row separator)
|
|
649
|
+
if (ch === ";") {
|
|
650
|
+
tokens.push({ type: 17 /* TokenType.Semicolon */ });
|
|
651
|
+
i++;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
// Numbers (and potential whole-row ranges like 1:5)
|
|
655
|
+
if (isDigit(ch) || (ch === "." && i + 1 < len && isDigit(formula[i + 1]))) {
|
|
656
|
+
const start = i;
|
|
657
|
+
while (i < len && isDigit(formula[i])) {
|
|
658
|
+
i++;
|
|
659
|
+
}
|
|
660
|
+
// Check for whole-row range: pure integer followed by : and another integer
|
|
661
|
+
const isInteger = i > start && formula[start] !== "." && i < len && formula[i] === ":";
|
|
662
|
+
if (isInteger) {
|
|
663
|
+
const row1Str = formula.slice(start, i);
|
|
664
|
+
const row1 = parseInt(row1Str, 10);
|
|
665
|
+
if (row1 >= 1 && row1 <= 1048576) {
|
|
666
|
+
const colonPos = i;
|
|
667
|
+
i++; // skip :
|
|
668
|
+
// Optional $ before second row number
|
|
669
|
+
const start2 = i;
|
|
670
|
+
if (i < len && formula[i] === "$") {
|
|
671
|
+
i++;
|
|
672
|
+
}
|
|
673
|
+
const rowStart2 = i;
|
|
674
|
+
while (i < len && isDigit(formula[i])) {
|
|
675
|
+
i++;
|
|
676
|
+
}
|
|
677
|
+
if (i > rowStart2) {
|
|
678
|
+
const row2Str = formula.slice(rowStart2, i);
|
|
679
|
+
const row2 = parseInt(row2Str, 10);
|
|
680
|
+
if (row2 >= 1 && row2 <= 1048576) {
|
|
681
|
+
tokens.push({
|
|
682
|
+
type: 21 /* TokenType.RowRange */,
|
|
683
|
+
value: row1Str + ":" + formula.slice(start2, i)
|
|
684
|
+
});
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// Not a valid row range, backtrack
|
|
689
|
+
i = colonPos;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
// Fractional part — only consume the `.` when at least one digit
|
|
693
|
+
// follows. This prevents "1..2" from being tokenised as "1." + ".2"
|
|
694
|
+
// with the first `.` silently absorbed into the integer number.
|
|
695
|
+
if (i < len && formula[i] === "." && i + 1 < len && isDigit(formula[i + 1])) {
|
|
696
|
+
i++;
|
|
697
|
+
while (i < len && isDigit(formula[i])) {
|
|
698
|
+
i++;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// Scientific notation. Only commit to consuming `e[+-]?` when at least
|
|
702
|
+
// one digit actually follows — otherwise restore position and stop the
|
|
703
|
+
// number here, so `1e` or `1e+` don't parse as the number `1`.
|
|
704
|
+
if (i < len && (formula[i] === "E" || formula[i] === "e")) {
|
|
705
|
+
const expStart = i;
|
|
706
|
+
let j = i + 1;
|
|
707
|
+
if (j < len && (formula[j] === "+" || formula[j] === "-")) {
|
|
708
|
+
j++;
|
|
709
|
+
}
|
|
710
|
+
if (j < len && isDigit(formula[j])) {
|
|
711
|
+
// Commit: consume e, optional sign, and the digit run.
|
|
712
|
+
i = j;
|
|
713
|
+
while (i < len && isDigit(formula[i])) {
|
|
714
|
+
i++;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
// Not a valid exponent — leave the `e`/`E` for the identifier lexer.
|
|
719
|
+
i = expStart;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
tokens.push({ type: 1 /* TokenType.Number */, value: formula.slice(start, i) });
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
// Quoted sheet name: 'Sheet Name'! or 3D ref 'Sheet1:Sheet3'!
|
|
726
|
+
if (ch === "'") {
|
|
727
|
+
i++; // skip opening quote
|
|
728
|
+
let sheetName = "";
|
|
729
|
+
while (i < len) {
|
|
730
|
+
if (formula[i] === "'") {
|
|
731
|
+
if (i + 1 < len && formula[i + 1] === "'") {
|
|
732
|
+
sheetName += "'";
|
|
733
|
+
i += 2;
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
i++; // skip closing quote
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
sheetName += formula[i];
|
|
742
|
+
i++;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// Expect ! after
|
|
746
|
+
if (i < len && formula[i] === "!") {
|
|
747
|
+
i++; // skip !
|
|
748
|
+
}
|
|
749
|
+
// Check for 3D reference: sheetName contains an unquoted colon separating two sheet names
|
|
750
|
+
// e.g. 'Sheet1:Sheet3' → startSheet="Sheet1", endSheet="Sheet3"
|
|
751
|
+
const colonIdx = sheetName.indexOf(":");
|
|
752
|
+
if (colonIdx > 0 && colonIdx < sheetName.length - 1) {
|
|
753
|
+
const startSheet = sheetName.slice(0, colonIdx);
|
|
754
|
+
const endSheet = sheetName.slice(colonIdx + 1);
|
|
755
|
+
tokens.push({
|
|
756
|
+
type: 7 /* TokenType.SheetRef */,
|
|
757
|
+
sheetName: startSheet,
|
|
758
|
+
endSheetName: endSheet
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
tokens.push({
|
|
763
|
+
type: 7 /* TokenType.SheetRef */,
|
|
764
|
+
sheetName
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
// Standalone structured reference: [@Column] or [[#This Row],[Column]]
|
|
770
|
+
if (ch === "[") {
|
|
771
|
+
const sr = parseStructuredRefBrackets(formula, i);
|
|
772
|
+
tokens.push({
|
|
773
|
+
type: 22 /* TokenType.StructuredRef */,
|
|
774
|
+
tableName: "",
|
|
775
|
+
columns: sr.columns,
|
|
776
|
+
specials: sr.specials
|
|
777
|
+
});
|
|
778
|
+
i = sr.end;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
// Identifiers: cell refs, function names, booleans, named ranges, sheet refs
|
|
782
|
+
if (isAlpha(ch) || ch === "$" || ch === "_") {
|
|
783
|
+
const start = i;
|
|
784
|
+
// Collect $-prefixed or alpha-numeric identifier
|
|
785
|
+
while (i < len && (isAlphaNumOrUnderscore(formula[i]) || formula[i] === "$")) {
|
|
786
|
+
i++;
|
|
787
|
+
}
|
|
788
|
+
const word = formula.slice(start, i);
|
|
789
|
+
// Check for sheet reference: WORD!
|
|
790
|
+
if (i < len && formula[i] === "!") {
|
|
791
|
+
i++; // skip !
|
|
792
|
+
tokens.push({
|
|
793
|
+
type: 7 /* TokenType.SheetRef */,
|
|
794
|
+
sheetName: word
|
|
795
|
+
});
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
// Check for 3D sheet reference: WORD:WORD!<ref>
|
|
799
|
+
// (e.g. Sheet1:Sheet3!A1, Q1:Q4!A1).
|
|
800
|
+
//
|
|
801
|
+
// Priority rule: when we see `WORD:WORD!` followed by an actual
|
|
802
|
+
// cell/range reference, that whole prefix is a 3D SheetRef —
|
|
803
|
+
// even when the first WORD looks like a cell reference on its
|
|
804
|
+
// own (sheet names like "Q1" are common). Previously we bailed
|
|
805
|
+
// out of this branch whenever `parseCellRef(word) !== null`,
|
|
806
|
+
// which left workbooks with sheets named Q1..Q4 unable to use
|
|
807
|
+
// 3D formulas.
|
|
808
|
+
//
|
|
809
|
+
// The fallback path below (RangeRef for `A1:B2`-shaped inputs)
|
|
810
|
+
// still fires when there is NO `!` — the lookahead disambiguates
|
|
811
|
+
// the two cases cleanly.
|
|
812
|
+
if (i < len && formula[i] === ":") {
|
|
813
|
+
const colonPos3D = i;
|
|
814
|
+
let j = i + 1;
|
|
815
|
+
// Collect the second sheet name (alphanumeric + underscore + .)
|
|
816
|
+
while (j < len && (isAlphaNumOrUnderscore(formula[j]) || formula[j] === "$")) {
|
|
817
|
+
j++;
|
|
818
|
+
}
|
|
819
|
+
if (j > colonPos3D + 1 && j < len && formula[j] === "!") {
|
|
820
|
+
const endSheet = formula.slice(colonPos3D + 1, j);
|
|
821
|
+
// Peek past `!` — accept the 3D reading only if a recognisable
|
|
822
|
+
// cell or range reference follows. Otherwise fall through
|
|
823
|
+
// so `A1:A3` (no trailing `!`) keeps its RangeRef reading.
|
|
824
|
+
const afterBang = j + 1;
|
|
825
|
+
if (afterBang < len && looksLikeCellRefStart(formula, afterBang)) {
|
|
826
|
+
i = afterBang; // skip past !
|
|
827
|
+
tokens.push({
|
|
828
|
+
type: 7 /* TokenType.SheetRef */,
|
|
829
|
+
sheetName: word,
|
|
830
|
+
endSheetName: endSheet
|
|
831
|
+
});
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
// Check for structured reference: WORD[...]
|
|
837
|
+
if (i < len && formula[i] === "[") {
|
|
838
|
+
const sr = parseStructuredRefBrackets(formula, i);
|
|
839
|
+
tokens.push({
|
|
840
|
+
type: 22 /* TokenType.StructuredRef */,
|
|
841
|
+
tableName: word,
|
|
842
|
+
columns: sr.columns,
|
|
843
|
+
specials: sr.specials
|
|
844
|
+
});
|
|
845
|
+
i = sr.end;
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
// Check for function call: WORD(
|
|
849
|
+
if (i < len && formula[i] === "(") {
|
|
850
|
+
tokens.push({ type: 8 /* TokenType.Function */, name: word.toUpperCase() });
|
|
851
|
+
// Don't consume the '(' — it will be consumed in the next iteration
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
// Check for boolean literals
|
|
855
|
+
const upper = word.toUpperCase();
|
|
856
|
+
if (upper === "TRUE" || upper === "FALSE") {
|
|
857
|
+
tokens.push({ type: 3 /* TokenType.Boolean */, value: upper });
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
// Check for cell reference
|
|
861
|
+
const ref = parseCellRef(word);
|
|
862
|
+
if (ref) {
|
|
863
|
+
const cellToken = {
|
|
864
|
+
type: 5 /* TokenType.CellRef */,
|
|
865
|
+
col: ref.col,
|
|
866
|
+
row: ref.row,
|
|
867
|
+
colAbsolute: ref.colAbsolute,
|
|
868
|
+
rowAbsolute: ref.rowAbsolute
|
|
869
|
+
};
|
|
870
|
+
tokens.push(cellToken);
|
|
871
|
+
// Check if this is part of a range (A1:B2)
|
|
872
|
+
if (i < len && formula[i] === ":") {
|
|
873
|
+
// Peek ahead for another cell ref
|
|
874
|
+
const colonPos = i;
|
|
875
|
+
i++; // skip :
|
|
876
|
+
const rangeStart = i;
|
|
877
|
+
while (i < len && (isAlphaNumOrUnderscore(formula[i]) || formula[i] === "$")) {
|
|
878
|
+
i++;
|
|
879
|
+
}
|
|
880
|
+
const secondWord = formula.slice(rangeStart, i);
|
|
881
|
+
const ref2 = parseCellRef(secondWord);
|
|
882
|
+
if (ref2) {
|
|
883
|
+
// It's a range — replace the last CellRef token with a Range token
|
|
884
|
+
tokens[tokens.length - 1] = {
|
|
885
|
+
type: 6 /* TokenType.Range */,
|
|
886
|
+
value: (ref.colAbsolute ? "$" : "") +
|
|
887
|
+
ref.col +
|
|
888
|
+
(ref.rowAbsolute ? "$" : "") +
|
|
889
|
+
ref.row +
|
|
890
|
+
":" +
|
|
891
|
+
(ref2.colAbsolute ? "$" : "") +
|
|
892
|
+
ref2.col +
|
|
893
|
+
(ref2.rowAbsolute ? "$" : "") +
|
|
894
|
+
ref2.row
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
// Not a valid range, push : as operator and backtrack
|
|
899
|
+
i = colonPos; // backtrack — let : be handled normally
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
// Otherwise it's a named range / defined name — but check for whole-column range first (A:B, $A:$B)
|
|
905
|
+
// A whole-column ref looks like: [pure-alpha or $+alpha] followed by ':'
|
|
906
|
+
if (i < len && formula[i] === ":") {
|
|
907
|
+
// Check if 'word' is a pure column reference (optional $ + letters only, no digits)
|
|
908
|
+
const colRefMatch = /^(\$?)([A-Za-z]{1,3})$/.exec(word);
|
|
909
|
+
if (colRefMatch) {
|
|
910
|
+
const col1Abs = colRefMatch[1] === "$";
|
|
911
|
+
const col1 = colRefMatch[2].toUpperCase();
|
|
912
|
+
// Validate column <= XFD
|
|
913
|
+
if (col1.length <= 3 && (col1.length < 3 || col1 <= "XFD")) {
|
|
914
|
+
const colonPos = i;
|
|
915
|
+
i++; // skip :
|
|
916
|
+
// Read the second column ref
|
|
917
|
+
const start2 = i;
|
|
918
|
+
if (i < len && formula[i] === "$") {
|
|
919
|
+
i++;
|
|
920
|
+
}
|
|
921
|
+
const colStart2 = i;
|
|
922
|
+
while (i < len && isAsciiAlpha(formula[i])) {
|
|
923
|
+
i++;
|
|
924
|
+
}
|
|
925
|
+
// Ensure it's ONLY letters (no digits follow = not a cell ref)
|
|
926
|
+
const afterLetters = i;
|
|
927
|
+
const isFollowedByDigit = i < len && isDigit(formula[i]);
|
|
928
|
+
if (!isFollowedByDigit && afterLetters > colStart2) {
|
|
929
|
+
const part2 = formula.slice(start2, i);
|
|
930
|
+
const col2Match = /^(\$?)([A-Za-z]{1,3})$/.exec(part2);
|
|
931
|
+
if (col2Match) {
|
|
932
|
+
const col2 = col2Match[2].toUpperCase();
|
|
933
|
+
if (col2.length <= 3 && (col2.length < 3 || col2 <= "XFD")) {
|
|
934
|
+
tokens.push({
|
|
935
|
+
type: 20 /* TokenType.ColRange */,
|
|
936
|
+
value: (col1Abs ? "$" : "") + col1 + ":" + part2.toUpperCase()
|
|
937
|
+
});
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// Not a valid column range, backtrack
|
|
943
|
+
i = colonPos;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
// Check for absolute whole-row range: $1:$5
|
|
947
|
+
const absRowMatch = /^(\$)(\d+)$/.exec(word);
|
|
948
|
+
if (absRowMatch) {
|
|
949
|
+
const row1 = parseInt(absRowMatch[2], 10);
|
|
950
|
+
if (row1 >= 1 && row1 <= 1048576) {
|
|
951
|
+
const colonPos = i;
|
|
952
|
+
i++; // skip :
|
|
953
|
+
const start2 = i;
|
|
954
|
+
if (i < len && formula[i] === "$") {
|
|
955
|
+
i++;
|
|
956
|
+
}
|
|
957
|
+
const rowStart2 = i;
|
|
958
|
+
while (i < len && isDigit(formula[i])) {
|
|
959
|
+
i++;
|
|
960
|
+
}
|
|
961
|
+
if (i > rowStart2) {
|
|
962
|
+
const row2 = parseInt(formula.slice(rowStart2, i), 10);
|
|
963
|
+
if (row2 >= 1 && row2 <= 1048576) {
|
|
964
|
+
tokens.push({
|
|
965
|
+
type: 21 /* TokenType.RowRange */,
|
|
966
|
+
value: word + ":" + formula.slice(start2, i)
|
|
967
|
+
});
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
i = colonPos;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
tokens.push({ type: 19 /* TokenType.Name */, value: word });
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
// Operators and punctuation
|
|
979
|
+
if (ch === "(") {
|
|
980
|
+
tokens.push({ type: 10 /* TokenType.OpenParen */ });
|
|
981
|
+
i++;
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
if (ch === ")") {
|
|
985
|
+
tokens.push({ type: 11 /* TokenType.CloseParen */ });
|
|
986
|
+
i++;
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (ch === ",") {
|
|
990
|
+
tokens.push({ type: 12 /* TokenType.Comma */ });
|
|
991
|
+
i++;
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (ch === ":") {
|
|
995
|
+
tokens.push({ type: 13 /* TokenType.Colon */ });
|
|
996
|
+
i++;
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
if (ch === "%") {
|
|
1000
|
+
tokens.push({ type: 14 /* TokenType.Percent */ });
|
|
1001
|
+
i++;
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
// Multi-character operators
|
|
1005
|
+
if (ch === "<") {
|
|
1006
|
+
if (i + 1 < len && formula[i + 1] === "=") {
|
|
1007
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "<=" });
|
|
1008
|
+
i += 2;
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (i + 1 < len && formula[i + 1] === ">") {
|
|
1012
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "<>" });
|
|
1013
|
+
i += 2;
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "<" });
|
|
1017
|
+
i++;
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (ch === ">") {
|
|
1021
|
+
if (i + 1 < len && formula[i + 1] === "=") {
|
|
1022
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: ">=" });
|
|
1023
|
+
i += 2;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: ">" });
|
|
1027
|
+
i++;
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
if (ch === "=") {
|
|
1031
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "=" });
|
|
1032
|
+
i++;
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
if (ch === "&") {
|
|
1036
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "&" });
|
|
1037
|
+
i++;
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (ch === "^") {
|
|
1041
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "^" });
|
|
1042
|
+
i++;
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
if (ch === "*") {
|
|
1046
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "*" });
|
|
1047
|
+
i++;
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
if (ch === "/") {
|
|
1051
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: "/" });
|
|
1052
|
+
i++;
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
// + and - : unary or binary
|
|
1056
|
+
if (ch === "+" || ch === "-") {
|
|
1057
|
+
if (!lastTokenIsValue()) {
|
|
1058
|
+
tokens.push({ type: 18 /* TokenType.UnaryPrefix */, value: ch });
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
tokens.push({ type: 9 /* TokenType.Operator */, value: ch });
|
|
1062
|
+
}
|
|
1063
|
+
i++;
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
// @ implicit intersection prefix (Excel 365)
|
|
1067
|
+
if (ch === "@") {
|
|
1068
|
+
tokens.push({ type: 23 /* TokenType.AtSign */ });
|
|
1069
|
+
i++;
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
// Unknown character — skip
|
|
1073
|
+
i++;
|
|
1074
|
+
}
|
|
1075
|
+
return tokens;
|
|
1076
|
+
}
|