UNPKG

@borgar/fx

Version:

Utilities for working with Excel formulas

135 lines (131 loc) 3.47 kB
import { REF_RANGE, REF_BEAM, REF_TERNARY, MAX_COLS, MAX_ROWS } from '../constants.ts'; import type { Token } from '../types.ts'; import { advRangeOp } from './advRangeOp.ts'; import { canEndRange, canEndPartialRange } from './canEndRange.ts'; function advA1Col (str: string, pos: number): number { // [A-Z]{1,3} const start = pos; if (str.charCodeAt(pos) === 36) { // $ pos++; } const stop = pos + 3; let col = 0; do { const c = str.charCodeAt(pos); if (c >= 65 && c <= 90) { // A-Z col = 26 * col + c - 64; pos++; } else if (c >= 97 && c <= 122) { // a-z col = 26 * col + c - 96; pos++; } else { break; } } while (pos < stop && pos < str.length); return (col && col <= MAX_COLS + 1) ? pos - start : 0; } function advA1Row (str: string, pos: number): number { // [1-9][0-9]{0,6} const start = pos; if (str.charCodeAt(pos) === 36) { // $ pos++; } const stop = pos + 7; let row = 0; let c = str.charCodeAt(pos); if (c >= 49 && c <= 57) { // 1-9 row = row * 10 + c - 48; pos++; do { c = str.charCodeAt(pos); if (c >= 48 && c <= 57) { // 0-9 row = row * 10 + c - 48; pos++; } else { break; } } while (pos < stop && pos < str.length); } return (row && row <= MAX_ROWS + 1) ? pos - start : 0; } export function lexRangeA1 ( str: string, pos: number, options: { mergeRefs: boolean, allowTernary: boolean } ): Token | undefined { let p = pos; const left = advA1Col(str, p); let right = 0; let bottom = 0; if (left) { // TLBR: could be A1:A1 // TL R: could be A1:A (if allowTernary) // TLB : could be A1:1 (if allowTernary) // LBR: could be A:A1 (if allowTernary) // L R: could be A:A p += left; const top = advA1Row(str, p); p += top; const op = advRangeOp(str, p); const preOp = p; if (op) { p += op; right = advA1Col(str, p); p += right; bottom = advA1Row(str, p); p += bottom; if (top && bottom && right) { if (canEndRange(str, p) && options.mergeRefs) { return { type: REF_RANGE, value: str.slice(pos, p) }; } } else if (!top && !bottom) { if (canEndRange(str, p)) { return { type: REF_BEAM, value: str.slice(pos, p) }; } } else if (options.allowTernary && (bottom || right)) { if (canEndPartialRange(str, p)) { return { type: REF_TERNARY, value: str.slice(pos, p) }; } } } // LT : this is A1 if (top && canEndRange(str, preOp) && str.charCodeAt(preOp) !== 33) { // 33 = "!" return { type: REF_RANGE, value: str.slice(pos, preOp) }; } } else { // T B : could be 1:1 // T BR: could be 1:A1 (if allowTernary) const top = advA1Row(str, p); if (top) { p += top; const op = advRangeOp(str, p); if (op) { p += op; right = advA1Col(str, p); if (right) { p += right; } bottom = advA1Row(str, p); p += bottom; if (right && bottom && options.allowTernary) { if (canEndPartialRange(str, p)) { return { type: REF_TERNARY, value: str.slice(pos, p) }; } } if (!right && bottom) { if (canEndRange(str, p)) { return { type: REF_BEAM, value: str.slice(pos, p) }; } } } } } }