UNPKG

@borgar/fx

Version:

Utilities for working with Excel formulas

1,925 lines (1,895 loc) 83.4 kB
// lib/constants.ts var OPERATOR = "operator"; var OPERATOR_TRIM = "operator-trim"; var BOOLEAN = "bool"; var ERROR = "error"; var NUMBER = "number"; var FUNCTION = "func"; var NEWLINE = "newline"; var WHITESPACE = "whitespace"; var STRING = "string"; var CONTEXT_QUOTE = "context_quote"; var CONTEXT = "context"; var REF_RANGE = "range"; var REF_BEAM = "range_beam"; var REF_TERNARY = "range_ternary"; var REF_NAMED = "range_named"; var REF_STRUCT = "structured"; var REF_CELL = "cell"; var FX_PREFIX = "fx_prefix"; var UNKNOWN = "unknown"; var UNARY = "UnaryExpression"; var BINARY = "BinaryExpression"; var REFERENCE = "ReferenceIdentifier"; var LITERAL = "Literal"; var ERROR_LITERAL = "ErrorLiteral"; var CALL = "CallExpression"; var LAMBDA = "LambdaExpression"; var LET = "LetExpression"; var ARRAY = "ArrayExpression"; var IDENTIFIER = "Identifier"; var LET_DECL = "LetDeclarator"; var MAX_COLS = 2 ** 14 - 1; var MAX_ROWS = 2 ** 20 - 1; // lib/mergeRefTokens.ts var END = "$"; var validRunsMerge = [ [REF_CELL, ":", REF_CELL], [REF_CELL, ".:", REF_CELL], [REF_CELL, ":.", REF_CELL], [REF_CELL, ".:.", REF_CELL], [REF_RANGE], [REF_BEAM], [REF_TERNARY], [CONTEXT, "!", REF_CELL, ":", REF_CELL], [CONTEXT, "!", REF_CELL, ".:", REF_CELL], [CONTEXT, "!", REF_CELL, ":.", REF_CELL], [CONTEXT, "!", REF_CELL, ".:.", REF_CELL], [CONTEXT, "!", REF_CELL], [CONTEXT, "!", REF_RANGE], [CONTEXT, "!", REF_BEAM], [CONTEXT, "!", REF_TERNARY], [CONTEXT_QUOTE, "!", REF_CELL, ":", REF_CELL], [CONTEXT_QUOTE, "!", REF_CELL, ".:", REF_CELL], [CONTEXT_QUOTE, "!", REF_CELL, ":.", REF_CELL], [CONTEXT_QUOTE, "!", REF_CELL, ".:.", REF_CELL], [CONTEXT_QUOTE, "!", REF_CELL], [CONTEXT_QUOTE, "!", REF_RANGE], [CONTEXT_QUOTE, "!", REF_BEAM], [CONTEXT_QUOTE, "!", REF_TERNARY], [REF_NAMED], [CONTEXT, "!", REF_NAMED], [CONTEXT_QUOTE, "!", REF_NAMED], [REF_STRUCT], [REF_NAMED, REF_STRUCT], [CONTEXT, "!", REF_NAMED, REF_STRUCT], [CONTEXT_QUOTE, "!", REF_NAMED, REF_STRUCT] ]; var refPartsTree = {}; function packList(f, node) { if (f.length) { const key = f[0]; if (!node[key]) { node[key] = {}; } packList(f.slice(1), node[key]); } else { node[END] = true; } } validRunsMerge.forEach((run) => packList(run.concat().reverse(), refPartsTree)); var matcher = (tokens2, currNode, anchorIndex, index = 0) => { let i = index; let node = currNode; const max = tokens2.length - index; while (i <= max) { const token = tokens2[anchorIndex - i]; if (token) { const value = token.value; let key = token.type === OPERATOR ? value : token.type; if (key === REF_RANGE && !value.includes(":")) { key = REF_CELL; } if (key in node) { node = node[key]; i += 1; continue; } } return node[END] ? i : 0; } }; function mergeRefTokens(tokenlist) { const finalTokens = []; for (let i = tokenlist.length - 1; i >= 0; i--) { let token = tokenlist[i]; const type = token.type; if (type === REF_RANGE || type === REF_BEAM || type === REF_TERNARY || type === REF_NAMED || type === REF_STRUCT) { const valid = matcher(tokenlist, refPartsTree, i); if (valid > 1) { token = { ...token, value: "" }; const start = i - valid + 1; for (let j = start; j <= i; j++) { token.value += tokenlist[j].value; } if (token.loc && tokenlist[start].loc) { token.loc[0] = tokenlist[start].loc[0]; } i -= valid - 1; } } finalTokens[finalTokens.length] = token; } return finalTokens.reverse(); } // lib/lexers/lexError.ts var re_ERROR = /#(?:NAME\?|FIELD!|CALC!|VALUE!|REF!|DIV\/0!|NULL!|NUM!|N\/A|GETTING_DATA\b|SPILL!|UNKNOWN!|SYNTAX\?|ERROR!|CONNECT!|BLOCKED!|EXTERNAL!)/iy; var HASH = 35; function lexError(str, pos) { if (str.charCodeAt(pos) === HASH) { re_ERROR.lastIndex = pos; const m = re_ERROR.exec(str); if (m) { return { type: ERROR, value: m[0] }; } } } // lib/lexers/lexRangeTrim.ts var PERIOD = 46; var COLON = 58; function lexRangeTrim(str, pos) { const c0 = str.charCodeAt(pos); if (c0 === PERIOD || c0 === COLON) { const c1 = str.charCodeAt(pos + 1); if (c0 !== c1) { if (c1 === COLON) { return { type: OPERATOR_TRIM, value: str.slice(pos, pos + (str.charCodeAt(pos + 2) === PERIOD ? 3 : 2)) }; } else if (c1 === PERIOD) { return { type: OPERATOR_TRIM, value: str.slice(pos, pos + 2) }; } } } } // lib/lexers/lexOperator.ts function lexOperator(str, pos) { const c0 = str.charCodeAt(pos); const c1 = str.charCodeAt(pos + 1); if (c0 === 60 && c1 === 61 || // <= c0 === 62 && c1 === 61 || // >= c0 === 60 && c1 === 62) { return { type: OPERATOR, value: str.slice(pos, pos + 2) }; } if ( // { } ! # % & c0 === 123 || c0 === 125 || c0 === 33 || c0 === 35 || c0 === 37 || c0 === 38 || // ( ) * + , - c0 >= 40 && c0 <= 45 || // / : ; < = > c0 === 47 || c0 >= 58 && c0 <= 62 || // @ ^ c0 === 64 || c0 === 94 ) { return { type: OPERATOR, value: str[pos] }; } } // lib/lexers/lexBoolean.ts function preventMatch(c) { return c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c >= 48 && c <= 57 || // 0-9 c === 95 || // _ c === 92 || // \ c === 40 || // ( c === 46 || // . c === 63 || // ? c > 160; } function lexBoolean(str, pos) { const c0 = str.charCodeAt(pos); if (c0 === 84 || c0 === 116) { const c1 = str.charCodeAt(pos + 1); if (c1 === 82 || c1 === 114) { const c2 = str.charCodeAt(pos + 2); if (c2 === 85 || c2 === 117) { const c3 = str.charCodeAt(pos + 3); if (c3 === 69 || c3 === 101) { const c4 = str.charCodeAt(pos + 4); if (!preventMatch(c4)) { return { type: BOOLEAN, value: str.slice(pos, pos + 4) }; } } } } } if (c0 === 70 || c0 === 102) { const c1 = str.charCodeAt(pos + 1); if (c1 === 65 || c1 === 97) { const c2 = str.charCodeAt(pos + 2); if (c2 === 76 || c2 === 108) { const c3 = str.charCodeAt(pos + 3); if (c3 === 83 || c3 === 115) { const c4 = str.charCodeAt(pos + 4); if (c4 === 69 || c4 === 101) { const c5 = str.charCodeAt(pos + 5); if (!preventMatch(c5)) { return { type: BOOLEAN, value: str.slice(pos, pos + 5) }; } } } } } } } // lib/lexers/lexNewLine.ts function lexNewLine(str, pos) { const start = pos; while (str.charCodeAt(pos) === 10) { pos++; } if (pos !== start) { return { type: NEWLINE, value: str.slice(start, pos) }; } } // lib/lexers/lexWhitespace.ts function isWS(c) { return c === 9 || c === 11 || c === 12 || c === 13 || c === 32 || c === 160 || c === 5760 || c === 8232 || c === 8233 || c === 8239 || c === 8287 || c === 12288 || c === 65279 || c >= 8192 && c <= 8202; } function lexWhitespace(str, pos) { const start = pos; while (isWS(str.charCodeAt(pos))) { pos++; } if (pos !== start) { return { type: WHITESPACE, value: str.slice(start, pos) }; } } // lib/lexers/lexString.ts var QUOT = 34; function lexString(str, pos) { const start = pos; if (str.charCodeAt(pos) === QUOT) { pos++; while (pos < str.length) { const c = str.charCodeAt(pos); if (c === QUOT) { pos++; if (str.charCodeAt(pos) !== QUOT) { return { type: STRING, value: str.slice(start, pos) }; } } pos++; } return { type: STRING, value: str.slice(start, pos), unterminated: true }; } } // lib/lexers/lexContext.ts var QUOT_SINGLE = 39; var BR_OPEN = 91; var BR_CLOSE = 93; var EXCL = 33; function lexContextQuoted(str, pos, options) { const c0 = str.charCodeAt(pos); let br1; let br2; if (c0 === QUOT_SINGLE) { const start = pos; pos++; while (pos < str.length) { const c = str.charCodeAt(pos); if (c === BR_OPEN) { if (br1) { return; } br1 = pos; } else if (c === BR_CLOSE) { if (br2) { return; } br2 = pos; } else if (c === QUOT_SINGLE) { pos++; if (str.charCodeAt(pos) !== QUOT_SINGLE) { let valid = br1 == null && br2 == null; if (options.xlsx && br1 === start + 1 && br2 === pos - 2) { valid = true; } if (br1 >= start + 1 && br2 < pos - 2 && br2 > br1 + 1) { valid = true; } if (valid && str.charCodeAt(pos) === EXCL) { return { type: CONTEXT_QUOTE, value: str.slice(start, pos) }; } return; } } pos++; } } } function lexContextUnquoted(str, pos, options) { const c0 = str.charCodeAt(pos); let br1; let br2; if (c0 !== QUOT_SINGLE && c0 !== EXCL) { const start = pos; while (pos < str.length) { const c = str.charCodeAt(pos); if (c === BR_OPEN) { if (br1) { return; } br1 = pos; } else if (c === BR_CLOSE) { if (br2) { return; } br2 = pos; } else if (c === EXCL) { let valid = br1 == null && br2 == null; if (options.xlsx && br1 === start && br2 === pos - 1) { valid = true; } if (br1 >= start && br2 < pos - 1 && br2 > br1 + 1) { valid = true; } if (valid) { return { type: CONTEXT, value: str.slice(start, pos) }; } } else if ((br1 == null || br2 != null) && // [0-9A-Za-z._¡¤§¨ª\u00ad¯-\uffff] !(c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c >= 48 && c <= 57 || // 0-9 c === 46 || // . c === 95 || // _ c === 161 || // ¡ c === 164 || // ¤ c === 167 || // § c === 168 || // ¨ c === 170 || // ª c === 173 || // \u00ad c >= 175)) { return; } pos++; } } } // lib/lexers/advRangeOp.ts var PERIOD2 = 46; var COLON2 = 58; function advRangeOp(str, pos) { const c0 = str.charCodeAt(pos); if (c0 === PERIOD2) { const c1 = str.charCodeAt(pos + 1); if (c1 === COLON2) { return str.charCodeAt(pos + 2) === PERIOD2 ? 3 : 2; } } else if (c0 === COLON2) { const c1 = str.charCodeAt(pos + 1); return c1 === PERIOD2 ? 2 : 1; } return 0; } // lib/lexers/canEndRange.ts function canEndRange(str, pos) { const c = str.charCodeAt(pos); return !(c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c >= 48 && c <= 57 || // 0-9 c === 95 || // _ c === 40 || // ( c > 160); } function canEndPartialRange(str, pos) { const c = str.charCodeAt(pos); return !(c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c >= 48 && c <= 57 || // 0-9 c === 95 || // _ c === 40 || // ( c === 36 || // $ c === 46 || // . c === 33); } // lib/lexers/lexRangeA1.ts function advA1Col(str, pos) { 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) { col = 26 * col + c - 64; pos++; } else if (c >= 97 && c <= 122) { 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, pos) { 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) { row = row * 10 + c - 48; pos++; do { c = str.charCodeAt(pos); if (c >= 48 && c <= 57) { row = row * 10 + c - 48; pos++; } else { break; } } while (pos < stop && pos < str.length); } return row && row <= MAX_ROWS + 1 ? pos - start : 0; } function lexRangeA1(str, pos, options) { let p = pos; const left = advA1Col(str, p); let right = 0; let bottom = 0; if (left) { 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) }; } } } if (top && canEndRange(str, preOp) && str.charCodeAt(preOp) !== 33) { return { type: REF_RANGE, value: str.slice(pos, preOp) }; } } else { 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) }; } } } } } } // lib/lexers/lexRangeR1C1.ts var BR_OPEN2 = 91; var BR_CLOSE2 = 93; var UC_R = 82; var LC_R = 114; var UC_C = 67; var LC_C = 99; var PLUS = 43; var MINUS = 45; var EXCL2 = 33; function lexR1C1Part(str, pos, isRow = false) { const start = pos; const c0 = str.charCodeAt(pos); if (isRow ? c0 === UC_R || c0 === LC_R : c0 === UC_C || c0 === LC_C) { pos++; let digits = 0; let value = 0; let stop = str.length; const c1 = str.charCodeAt(pos); let c; let sign = 1; const relative = c1 === BR_OPEN2; if (relative) { stop = Math.min(stop, pos + (isRow ? 8 : 6)); pos++; c = str.charCodeAt(pos); if (c === PLUS || c === MINUS) { pos++; stop++; sign = c === MINUS ? -1 : 1; } } else if (c1 < 49 || c1 > 57 || isNaN(c1)) { return 1; } do { const c2 = str.charCodeAt(pos); if (c2 >= 48 && c2 <= 57) { value = value * 10 + c2 - 48; digits++; pos++; } else { break; } } while (pos < stop); const MAX = isRow ? MAX_ROWS : MAX_COLS; if (relative) { const c2 = str.charCodeAt(pos); if (c2 !== BR_CLOSE2) { return 0; } pos++; value *= sign; return digits && -MAX <= value && value <= MAX ? pos - start : 0; } return digits && value <= MAX + 1 ? pos - start : 0; } return 0; } function lexRangeR1C1(str, pos, options) { let p = pos; const r1 = lexR1C1Part(str, p, true); p += r1; const c1 = lexR1C1Part(str, p); p += c1; if ((c1 || r1) && str.charCodeAt(p) !== EXCL2) { const op = advRangeOp(str, p); const preOp = p; if (op) { p += op; const r2 = lexR1C1Part(str, p, true); p += r2; const c2 = lexR1C1Part(str, p); p += c2; if (r1 && !c1 && r2 && c2 || !r1 && c1 && r2 && c2 || r1 && c1 && r2 && !c2 || r1 && c1 && !r2 && c2) { if (options.allowTernary && canEndRange(str, p)) { return { type: REF_TERNARY, value: str.slice(pos, p) }; } } else if (c1 && c2 && !r1 && !r2 || !c1 && !c2 && r1 && r2) { if (canEndRange(str, p)) { return { type: REF_BEAM, value: str.slice(pos, p) }; } } } if (canEndRange(str, preOp)) { return { type: r1 && c1 ? REF_RANGE : REF_BEAM, value: str.slice(pos, preOp) }; } } } // lib/lexers/lexRange.ts function lexRange(str, pos, options) { return options.r1c1 ? lexRangeR1C1(str, pos, options) : lexRangeA1(str, pos, options); } // lib/parseSRange.ts var AT = 64; var BR_CLOSE3 = 93; var BR_OPEN3 = 91; var COLON3 = 58; var COMMA = 44; var HASH2 = 35; var QUOT_SINGLE2 = 39; var keyTerms = { "headers": 1, "data": 2, "totals": 4, "all": 8, "this row": 16, "@": 16 }; var fz = (...a) => Object.freeze(a); var sectionMap = { // no terms 0: fz(), // single term 1: fz("headers"), 2: fz("data"), 4: fz("totals"), 8: fz("all"), 16: fz("this row"), // headers+data 3: fz("headers", "data"), // totals+data 6: fz("data", "totals") }; function matchKeyword(str, pos) { let p = pos; if (str.charCodeAt(p++) !== BR_OPEN3) { return; } if (str.charCodeAt(p++) !== HASH2) { return; } do { const c = str.charCodeAt(p); if (c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c === 32) { p++; } else { break; } } while (p < pos + 11); if (str.charCodeAt(p++) !== BR_CLOSE3) { return; } return p - pos; } function skipWhitespace(str, pos) { let p = pos; while (isWS(str.charCodeAt(p))) { p++; } return p - pos; } function matchColumn(str, pos, allowUnbraced = true) { let p = pos; let column = ""; if (str.charCodeAt(p) === BR_OPEN3) { p++; let c; do { c = str.charCodeAt(p); if (c === QUOT_SINGLE2) { p++; c = str.charCodeAt(p); if (c === QUOT_SINGLE2 || c === HASH2 || c === AT || c === BR_OPEN3 || c === BR_CLOSE3) { column += String.fromCharCode(c); p++; } else { return; } } else if (c === QUOT_SINGLE2 || c === HASH2 || c === AT || c === BR_OPEN3) { return; } else if (c === BR_CLOSE3) { p++; return [str.slice(pos, p), column]; } else { column += String.fromCharCode(c); p++; } } while (p < str.length); } else if (allowUnbraced) { let c; do { c = str.charCodeAt(p); if (c === QUOT_SINGLE2 || c === HASH2 || c === AT || c === BR_OPEN3 || c === BR_CLOSE3 || c === COLON3) { break; } else { column += String.fromCharCode(c); p++; } } while (p < str.length); if (p !== pos) { return [column, column]; } } } function parseSRange(str, pos = 0) { const columns = []; const start = pos; let m; let terms = 0; if (str.charCodeAt(pos) !== BR_OPEN3) { return; } if (m = matchKeyword(str, pos)) { const k = str.slice(pos + 2, pos + m - 1); pos += m; const term = keyTerms[k.toLowerCase()]; if (!term) { return; } terms |= term; } else if (m = matchColumn(str, pos, false)) { pos += m[0].length; if (m[1]) { columns.push(m[1]); } } else { let expect_more = true; pos++; pos += skipWhitespace(str, pos); while (expect_more && (m = matchKeyword(str, pos))) { const k = str.slice(pos + 2, pos + m - 1); const term = keyTerms[k.toLowerCase()]; if (!term) { return; } terms |= term; pos += m; pos += skipWhitespace(str, pos); expect_more = str.charCodeAt(pos) === COMMA; if (expect_more) { pos++; pos += skipWhitespace(str, pos); } } if (expect_more && str.charCodeAt(pos) === AT) { terms |= keyTerms["@"]; pos += 1; expect_more = str.charCodeAt(pos) !== BR_CLOSE3; } if (!sectionMap[terms]) { return; } const leftCol = expect_more && matchColumn(str, pos, true); if (leftCol) { pos += leftCol[0].length; columns.push(leftCol[1]); if (str.charCodeAt(pos) === COLON3) { pos++; const rightCol = matchColumn(str, pos, true); if (rightCol) { pos += rightCol[0].length; columns.push(rightCol[1]); } else { return; } } expect_more = false; } pos += skipWhitespace(str, pos); if (expect_more || str.charCodeAt(pos) !== BR_CLOSE3) { return; } pos++; } const sections = sectionMap[terms]; return { columns, sections: sections ? sections.concat() : sections, length: pos - start, token: str.slice(start, pos) }; } // lib/lexers/lexStructured.ts var EXCL3 = 33; function lexStructured(str, pos) { const structData = parseSRange(str, pos); if (structData && structData.length) { let i = structData.length; while (isWS(str.charCodeAt(pos + i))) { i++; } if (str.charCodeAt(pos + i) !== EXCL3) { return { type: REF_STRUCT, value: structData.token }; } } } // lib/lexers/lexNumber.ts function advDigits(str, pos) { const start = pos; do { const c = str.charCodeAt(pos); if (c < 48 || c > 57) { break; } pos++; } while (pos < str.length); return pos - start; } function lexNumber(str, pos) { const start = pos; const lead = advDigits(str, pos); if (!lead) { return; } pos += lead; const c0 = str.charCodeAt(pos); if (c0 === 46) { pos++; const frac = advDigits(str, pos); if (!frac) { return; } pos += frac; } const c1 = str.charCodeAt(pos); if (c1 === 69 || c1 === 101) { pos++; const sign = str.charCodeAt(pos); if (sign === 43 || sign === 45) { pos++; } const exp = advDigits(str, pos); if (!exp) { return; } pos += exp; } return { type: NUMBER, value: str.slice(start, pos) }; } // lib/lexers/lexNamed.ts function lexNamed(str, pos) { const start = pos; const s = str.charCodeAt(pos); if (s >= 65 && s <= 90 || // A-Z s >= 97 && s <= 122 || // a-z s === 95 || // _ s === 92 || // \ s > 160) { pos++; } else { return; } let c; do { c = str.charCodeAt(pos); if (c >= 65 && c <= 90 || // A-Z c >= 97 && c <= 122 || // a-z c >= 48 && c <= 57 || // 0-9 c === 95 || // _ c === 92 || // \ c === 46 || // . c === 63 || // ? c > 160) { pos++; } else { break; } } while (isFinite(c)); const len = pos - start; if (len && len < 255) { if (s === 92 && len < 3) { return; } if (len === 1 && (s === 114 || s === 82 || s === 99 || s === 67)) { return; } return { type: REF_NAMED, value: str.slice(start, pos) }; } } // lib/lexers/lexRefOp.ts var EXCL4 = 33; function lexRefOp(str, pos, opts) { if (str.charCodeAt(pos) === EXCL4) { return { type: OPERATOR, value: str[pos] }; } if (!opts.r1c1) { const opLen = advRangeOp(str, pos); if (opLen) { return { type: OPERATOR, value: str.slice(pos, pos + opLen) }; } } } // lib/lexers/lexNameFuncCntx.ts var BR_OPEN4 = 91; var PAREN_OPEN = 40; var EXCL5 = 33; var OFFS = 32; var ALLOWED = new Uint8Array(180 - OFFS); var OK_NAME_0 = 1; var OK_FUNC_0 = 2; var OK_CNTX_0 = 4; var OK_NAME_N = 8; var OK_FUNC_N = 16; var OK_CNTX_N = 32; var OK_0 = OK_NAME_0 | OK_FUNC_0 | OK_CNTX_0; var OK_N = OK_NAME_N | OK_FUNC_N | OK_CNTX_N; var OK_HIGHCHAR = OK_NAME_0 | OK_NAME_N | OK_CNTX_0 | OK_CNTX_N; for (let c = OFFS; c < 180; c++) { const char = String.fromCharCode(c); const n0 = /^[a-zA-Z_\\\u00a1-\uffff]$/.test(char); const f0 = /^[a-zA-Z_]$/.test(char); const nN = /^[a-zA-Z0-9_.\\?\u00a1-\uffff]$/.test(char); const fN = /^[a-zA-Z0-9_.]$/.test(char); const cX = /^[a-zA-Z0-9_.¡¤§¨ª\u00ad¯-\uffff]$/.test(char); ALLOWED[c - OFFS] = (n0 ? OK_NAME_0 : 0) | (nN ? OK_NAME_N : 0) | (f0 ? OK_FUNC_0 : 0) | (fN ? OK_FUNC_N : 0) | (cX ? OK_CNTX_0 : 0) | (cX ? OK_CNTX_N : 0); } function nameOrUnknown(str, s, start, pos, name) { const len = pos - start; if (name && len && len < 255) { if (s === 92 && len < 3) { return; } if (len === 1 && (s === 114 || s === 82 || s === 99 || s === 67)) { return; } return { type: REF_NAMED, value: str.slice(start, pos) }; } return { type: UNKNOWN, value: str.slice(start, pos) }; } function lexNameFuncCntx(str, pos, opts) { const start = pos; const s = str.charCodeAt(pos); const a = s > 180 ? OK_HIGHCHAR : ALLOWED[s - OFFS]; if (a & OK_CNTX_0 && !(a & OK_NAME_0) && !(a & OK_FUNC_0) || s === BR_OPEN4) { return lexContextUnquoted(str, pos, opts); } if (!(a & OK_0)) { return; } let name = a & OK_NAME_0 ? 1 : 0; let func = a & OK_FUNC_0 ? 1 : 0; let cntx = a & OK_CNTX_0 ? 1 : 0; pos++; let c; do { c = str.charCodeAt(pos); const a2 = s > 180 ? OK_HIGHCHAR : ALLOWED[c - OFFS] ?? 0; if (a2 & OK_N) { if (name && !(a2 & OK_NAME_N)) { name = 0; } if (func && !(a2 & OK_FUNC_N)) { func = 0; } if (cntx && !(a2 & OK_CNTX_N)) { cntx = 0; } } else { if (c === PAREN_OPEN && func) { return { type: FUNCTION, value: str.slice(start, pos) }; } else if (c === EXCL5 && cntx) { return { type: CONTEXT, value: str.slice(start, pos) }; } return nameOrUnknown(str, s, start, pos, name); } pos++; } while ((name || func || cntx) && pos < str.length); if (start !== pos) { return nameOrUnknown(str, s, start, pos, name); } } // lib/lexers/sets.ts var lexers = [ lexError, lexRangeTrim, lexOperator, lexNewLine, lexWhitespace, lexString, lexRange, lexNumber, lexBoolean, lexContextQuoted, lexNameFuncCntx, lexStructured ]; var lexersRefs = [ lexRefOp, lexContextQuoted, lexContextUnquoted, lexRange, lexStructured, lexNamed ]; // lib/isRCTokenValue.ts function isRCTokenValue(value) { return value === "r" || value === "R" || value === "c" || value === "C"; } // lib/tokenize.ts var reLetLambda = /^l(?:ambda|et)$/i; var isType = (t, type) => t && t.type === type; var isTextTokenType = (tokenType) => tokenType === REF_NAMED || tokenType === FUNCTION; var causesBinaryMinus = (token) => { return !isType(token, OPERATOR) || (token.value === "%" || token.value === "}" || token.value === ")" || token.value === "#"); }; function fixRCNames(tokens2, r1c1Mode) { let withinCall = 0; let parenDepth = 0; let lastToken; for (const token of tokens2) { const tokenType = token.type; if (tokenType === OPERATOR) { if (token.value === "(") { parenDepth++; if (lastToken.type === FUNCTION) { if (reLetLambda.test(lastToken.value)) { withinCall = parenDepth; } } } else if (token.value === ")") { parenDepth--; if (parenDepth < withinCall) { withinCall = 0; } } } else if (withinCall && tokenType === UNKNOWN && isRCTokenValue(token.value)) { token.type = REF_NAMED; } else if (withinCall && r1c1Mode && tokenType === REF_BEAM && isRCTokenValue(token.value)) { token.type = REF_NAMED; } lastToken = token; } return tokens2; } function getTokens(fx, tokenHandlers, options = {}) { const { withLocation = false, mergeRefs = true, negativeNumbers = true } = options; const opts = { withLocation, mergeRefs, allowTernary: options.allowTernary ?? false, negativeNumbers, r1c1: options.r1c1 ?? false, xlsx: options.xlsx ?? false }; const tokens2 = []; let pos = 0; let letOrLambda = 0; let unknownRC = 0; const trimOps = []; let tail0; let tail1; let lastToken; const pushToken = (token) => { let tokenType = token.type; const isCurrUnknown = tokenType === UNKNOWN; const isLastUnknown = lastToken && lastToken.type === UNKNOWN; if (lastToken && (isCurrUnknown && isLastUnknown || isCurrUnknown && isTextTokenType(lastToken.type) || isLastUnknown && isTextTokenType(tokenType))) { lastToken.value += token.value; lastToken.type = UNKNOWN; if (withLocation) { lastToken.loc[1] = token.loc[1]; } } else { if (tokenType === OPERATOR_TRIM) { trimOps.push(tokens2.length); tokenType = UNKNOWN; token.type = UNKNOWN; } tokens2[tokens2.length] = token; lastToken = token; if (tokenType !== WHITESPACE && tokenType !== NEWLINE) { tail1 = tail0; tail0 = token; } } }; if (fx.startsWith("=")) { const token = { type: FX_PREFIX, value: "=" }; if (withLocation) { token.loc = [0, 1]; } pos++; pushToken(token); } const numHandlers = tokenHandlers.length; while (pos < fx.length) { const startPos = pos; let token; for (let i = 0; i < numHandlers; i++) { token = tokenHandlers[i](fx, pos, opts); if (token) { pos += token.value.length; break; } } if (!token) { token = { type: UNKNOWN, value: fx[pos] }; pos++; } if (withLocation) { token.loc = [startPos, pos]; } if (lastToken && token.value === "(" && lastToken.type === FUNCTION) { if (reLetLambda.test(lastToken.value)) { letOrLambda++; } } if (token.value.length === 1 && (token.type === UNKNOWN || opts.r1c1 && token.type === REF_BEAM)) { unknownRC += isRCTokenValue(token.value) ? 1 : 0; } if (negativeNumbers && token.type === NUMBER) { const last1 = lastToken; if (last1?.type === OPERATOR && last1.value === "-") { if (!tail1 || tail1.type === FX_PREFIX || !causesBinaryMinus(tail1)) { const minus = tokens2.pop(); token.value = "-" + token.value; if (token.loc) { token.loc[0] = minus.loc[0]; } tail0 = tail1; lastToken = tokens2[tokens2.length - 1]; } } } pushToken(token); } if (unknownRC && letOrLambda) { fixRCNames(tokens2, opts.r1c1); } for (const index of trimOps) { const before = tokens2[index - 1]; const after = tokens2[index + 1]; tokens2[index].type = before?.type === REF_RANGE && after?.type === REF_RANGE ? OPERATOR : UNKNOWN; } if (mergeRefs) { return mergeRefTokens(tokens2); } return tokens2; } function tokenizeXlsx(formula, options = {}) { const opts = { withLocation: options.withLocation ?? false, mergeRefs: options.mergeRefs ?? true, allowTernary: options.allowTernary ?? false, negativeNumbers: options.negativeNumbers ?? true, r1c1: options.r1c1 ?? false, xlsx: true }; return getTokens(formula, lexers, opts); } // lib/isType.ts function isRange(token) { return !!token && (token.type === REF_RANGE || token.type === REF_BEAM || token.type === REF_TERNARY); } function isReference(token) { return !!token && (token.type === REF_RANGE || token.type === REF_BEAM || token.type === REF_TERNARY || token.type === REF_STRUCT || token.type === REF_NAMED); } function isLiteral(token) { return !!token && (token.type === BOOLEAN || token.type === ERROR || token.type === NUMBER || token.type === STRING); } function isError(token) { return !!token && token.type === ERROR; } function isWhitespace(token) { return !!token && (token.type === WHITESPACE || token.type === NEWLINE); } function isFunction(token) { return !!token && token.type === FUNCTION; } function isFxPrefix(token) { return !!token && token.type === FX_PREFIX; } function isOperator(token) { return !!token && token.type === OPERATOR; } // lib/parse.ts var END2 = "(END)"; var FUNCTION2 = "(FUNCTION)"; var WHITESPACE2 = "(WHITESPACE)"; var refFunctions = [ "ANCHORARRAY", "CHOOSE", "DROP", "IF", "IFS", "INDEX", "INDIRECT", "LAMBDA", "LET", "OFFSET", "REDUCE", "SINGLE", "SWITCH", "TAKE", "TRIMRANGE", "XLOOKUP" ]; var symbolTable = {}; var currentNode; var tokens; var tokenIndex; var permitArrayRanges = false; var permitArrayCalls = false; var looseRefCalls = false; var isReferenceFunctionName = (fnName) => { return looseRefCalls || refFunctions.includes(fnName.toUpperCase()); }; var isReferenceToken = (token, allowOperators = false) => { const value = (token && token.value) + ""; if (isReference(token)) { return true; } if (allowOperators && isOperator(token) && (value === ":" || value === "," || !value.trim())) { return true; } if (isFunction(token) && isReferenceFunctionName(value)) { return true; } if (isError(token) && value === "#REF!") { return true; } return false; }; var isReferenceNode = (node) => { return !!node && (node.type === REFERENCE || (node.type === ERROR_LITERAL || node.type === ERROR) && node.value === "#REF!" || node.type === BINARY && (node.operator === ":" || node.operator === " " || node.operator === ",") || isReference(node) || node.type === CALL && isReferenceFunctionName(node.callee.name)); }; function halt(message, atIndex = null) { const err = new Error(message); err.source = tokens.map((d) => d.value).join(""); err.sourceOffset = tokens.slice(0, atIndex ?? tokenIndex).reduce((a, d) => a + d.value.length, 0); throw err; } function refIsUpcoming(allowOperators = false) { let i = tokenIndex; let next; do { next = tokens[++i]; } while (next && (isWhitespace(next) || isOperator(next) && next.value === "(")); return isReferenceToken(next, allowOperators); } function advance(expectNext = null, leftNode = null) { if (expectNext && expectNext !== currentNode.id) { halt(`Expected ${expectNext} but got ${currentNode.id}`); } if (isWhitespace(tokens[tokenIndex])) { const haveRef = isReferenceNode(leftNode); const possibleWSOp = haveRef && refIsUpcoming(false); const nextIsCall = haveRef && tokens[tokenIndex + 1] && tokens[tokenIndex + 1].value === "("; if (!possibleWSOp && !nextIsCall) { while (isWhitespace(tokens[tokenIndex])) { tokenIndex++; } } } if (tokenIndex >= tokens.length) { currentNode = symbolTable[END2]; return; } const token = tokens[tokenIndex]; tokenIndex += 1; if (token.unterminated) { halt("Encountered an unterminated token"); } let node; if (isOperator(token)) { node = symbolTable[token.value]; if (!node) { halt(`Unknown operator ${token.value}`); } } else if (isWhitespace(token)) { node = symbolTable[WHITESPACE2]; } else if (isLiteral(token)) { node = symbolTable[LITERAL]; } else if (isReference(token)) { node = symbolTable[REFERENCE]; } else if (isFunction(token)) { node = symbolTable[FUNCTION2]; } else { halt(`Unexpected ${token.type} token: ${token.value}`); } currentNode = Object.create(node); currentNode.type = token.type; currentNode.value = token.value; if (token.loc) { currentNode.loc = [...token.loc]; } return currentNode; } function expression(rbp) { let t = currentNode; advance(null, t); let left = t.nud(); while (rbp < currentNode.lbp) { t = currentNode; advance(null, t); left = t.led(left); } return left; } var original_symbol = { // null denotation nud: () => halt("Invalid syntax"), // Undefined // left denotation led: () => halt("Missing operator") }; function symbol(id, bp = 0) { let s = symbolTable[id]; if (s) { if (bp >= s.lbp) { s.lbp = bp; } } else { s = { ...original_symbol }; s.id = id; s.value = id; s.lbp = bp; symbolTable[id] = s; } return s; } function infix(id, bp, led) { const s = symbol(id, bp); s.led = led || function(left) { this.type = BINARY; this.operator = this.value; delete this.value; const right = expression(bp); this.arguments = [left, right]; if (this.loc) { this.loc = [left.loc[0], right.loc[1]]; } return this; }; return s; } function postfix(id, led) { const s = symbol(id, 0); s.lbp = 70; s.led = led || function(left) { this.type = UNARY; this.operator = this.value; delete this.value; this.arguments = [left]; if (this.loc) { this.loc[0] = left.loc[0]; } return this; }; return s; } function prefix(id, nud) { const s = symbol(id); s.nud = nud || function() { this.type = UNARY; this.operator = this.value; delete this.value; const subexpr = expression(70); this.arguments = [subexpr]; if (this.loc) { this.loc[1] = subexpr.loc[1]; } return this; }; return s; } function rangeInfix(id, bp) { return infix(id, bp, function(left) { if (!isReferenceNode(left)) { halt(`Unexpected ${id} operator`); } const right = expression(bp); if (!isReferenceNode(right)) { halt(`Unexpected ${currentNode.type} following ${this.id}`); } this.type = BINARY; this.operator = this.value.trim() ? this.value : " "; delete this.value; this.arguments = [left, right]; if (this.loc) { this.loc = [left.loc[0], right.loc[1]]; } return this; }); } symbol(END2); rangeInfix(":", 80); var comma = rangeInfix(",", 80); rangeInfix(WHITESPACE2, 80); var unionRefs = (enable) => { const currState = comma.lbp > 0; if (enable != null) { comma.lbp = enable ? 80 : 0; } return currState; }; postfix("%"); postfix("#", function(left) { if (!isReferenceNode(left)) { halt("# expects a reference"); } this.type = UNARY; this.operator = this.value; delete this.value; this.arguments = [left]; return this; }); prefix("+"); prefix("-"); prefix("@"); infix("^", 50); infix("*", 40); infix("/", 40); infix("+", 30); infix("-", 30); infix("&", 20); infix("=", 10); infix("<", 10); infix(">", 10); infix("<=", 10); infix(">=", 10); infix("<>", 10); symbol(LITERAL).nud = function() { const { type, value } = this; this.type = LITERAL; this.raw = value; if (type === NUMBER) { this.value = +value; } else if (type === BOOLEAN) { this.value = value.toUpperCase() === "TRUE"; } else if (type === ERROR) { this.type = ERROR_LITERAL; this.value = value.toUpperCase(); } else if (type === STRING) { this.value = value.slice(1, -1).replace(/""/g, '"'); } else { throw new Error("Unsupported literal type: " + type); } return this; }; symbol(REFERENCE).nud = function() { if (this.type === REF_NAMED) { this.kind = "name"; } else if (this.type === REF_STRUCT) { this.kind = "table"; } else if (this.type === REF_BEAM) { this.kind = "beam"; } else { this.kind = "range"; } this.type = REFERENCE; return this; }; symbol(")"); prefix("(", function() { const prevState = unionRefs(true); const e = expression(0); advance(")", e); unionRefs(prevState); return e; }); symbol(FUNCTION2).nud = function() { return this; }; infix("(", 90, function(left) { let callee = { type: IDENTIFIER, name: left.value }; if (left.id !== FUNCTION2) { if (left.type === LAMBDA || // Excel only allows calls to "names" and ref functions. Since we don't // differentiate between the two (this requires a table of function names) // we're overly permissive here: left.type === CALL || left.type === LET || left.type === REFERENCE || left.type === UNARY && left.value === "#" || // Because it's really SINGLE(...)() left.type === ERROR_LITERAL && left.value === "#REF!") { callee = left; } else { halt("Unexpected call", tokenIndex - 1); } } const lcFn = left.value.toLowerCase(); if (lcFn === "lambda") { return parseLambda.call(this, left); } if (lcFn === "let") { return parseLet.call(this, left); } const args = []; let lastWasComma = false; if (currentNode.id !== ")") { const prevState = unionRefs(false); while (currentNode.id !== ")") { if (isWhitespace(currentNode)) { advance(); } if (currentNode.id === ",") { args.push(null); lastWasComma = true; advance(); } else { const arg = expression(0); args.push(arg); lastWasComma = false; if (currentNode.id === ",") { advance(","); lastWasComma = true; } } } unionRefs(prevState); } if (lastWasComma) { args.push(null); } const closeParen = currentNode; delete this.value; this.type = CALL; this.callee = callee; if (left.loc) { this.callee.loc = [...left.loc]; } this.arguments = args; if (left.loc) { this.loc = [left.loc[0], closeParen.loc[1]]; } advance(")", this); return this; }); function parseLambda(left) { const args = []; const argNames = {}; let body; let done = false; const prevState = unionRefs(false); if (currentNode.id !== ")") { while (!done) { if (isWhitespace(currentNode)) { advance(); } const argTokenIndex = tokenIndex; const arg = expression(0); if (currentNode.id === ",") { if (arg.type === REFERENCE && arg.kind === "name") { const currName = arg.value.toLowerCase(); if (currName in argNames) { halt("Duplicate name: " + arg.value); } argNames[currName] = 1; const a = { type: IDENTIFIER, name: arg.value }; if (arg.loc) { a.loc = arg.loc; } args.push(a); } else { tokenIndex = argTokenIndex; halt("LAMBDA argument is not a name"); } advance(","); } else { body = arg; done = true; } } } unionRefs(prevState); delete this.value; this.type = LAMBDA; this.params = args; this.body = body || null; if (left.loc) { this.loc = [left.loc[0], currentNode.loc[1]]; } advance(")", this); return this; } function parseLet(left) { const args = []; const vals = []; const argNames = {}; let body; let argCounter = 0; const addArgument = (arg, lastArg) => { if (body) { halt("Unexpected argument following calculation"); } if (lastArg && argCounter >= 2) { body = arg; } else { const wantName = !(argCounter % 2); if (wantName) { if (arg && (arg.type === REFERENCE && arg.kind === "name")) { const currName = arg.value.toLowerCase(); if (currName in argNames) { halt("Duplicate name: " + arg.value); } argNames[currName] = 1; args.push({ type: IDENTIFIER, name: arg.value, loc: arg.loc }); } else if (argCounter >= 2) { body = arg; } else { halt("Argument is not a name"); } } else { vals.push(arg); } } argCounter++; }; const prevState = unionRefs(false); let lastWasComma = false; if (currentNode.id !== ")") { while (currentNode.id !== ")") { if (isWhitespace(currentNode)) { advance(); } if (currentNode.id === ",") { addArgument(null); lastWasComma = true; advance(); } else { const arg = expression(0); addArgument(arg, currentNode.id !== ","); lastWasComma = false; if (currentNode.id === ",") { advance(","); lastWasComma = true; } } } unionRefs(prevState); } if (lastWasComma) { addArgument(null, true); } if (body === void 0) { halt("Unexpected end of arguments"); } unionRefs(prevState); delete this.value; this.type = LET; this.declarations = []; if (!args.length) { halt("Unexpected end of arguments"); } for (let i = 0; i < args.length; i++) { const s = { type: LET_DECL, id: args[i], init: vals[i], loc: args[i].loc && [args[i].loc[0], vals[i].loc[1]] }; this.declarations.push(s); } this.body = body; if (left.loc) { this.loc = [left.loc[0], currentNode.loc[1]]; } advance(")", this); return this; } symbol("}"); symbol(";"); prefix("{", function() { if (currentNode.id === "}") { halt("Unexpected empty array"); } let row = []; let done = false; const rows = [row]; const prevState = unionRefs(false); while (!done) { if (isWhitespace(currentNode)) { advance(); } if (isLiteral(currentNode)) { row.push(symbolTable[LITERAL].nud.call(currentNode)); advance(); } else if (permitArrayRanges && isReferenceNode(currentNode)) { row.push(symbolTable[REFERENCE].nud.call(currentNode)); advance(); } else if (permitArrayCalls && isFunction(currentNode)) { const arg = expression(0); row.push(arg); } else { halt(`Unexpected ${currentNode.type} in array: ${currentNode.value}`); } if (currentNode.id === ",") { advance(","); } else if (currentNode.id === ";") { advance(";"); row = []; rows.push(row); } else { done = true; } } const closingBrace = currentNode; advance("}"); unionRefs(prevState); this.type = ARRAY; this.elements = rows; if (this.loc) { this.loc[1] = closingBrace.loc[1]; } delete this.value; return this; }); function parse(tokenlist, options = {}) { if (!Array.isArray(tokenlist)) { throw new Error("Parse requires an array of tokens."); } permitArrayRanges = options?.permitArrayRanges; permitArrayCalls = options?.permitArrayCalls; looseRefCalls = options?.looseRefCalls; tokens = tokenlist; tokenIndex = 0; while (isWhitespace(tokens[tokenIndex]) || isFxPrefix(tokens[tokenIndex])) { tokenIndex++; } advance(); unionRefs(true); const root = expression(0); advance(END2); return root; } // lib/stringifyPrefix.ts var reBannedChars = /[^0-9A-Za-z._¡¤§¨ª\u00ad¯-\uffff]/; var reIsRangelike = /^(R|C|RC|[A-Z]{1,3}\d{1,7})$/i; function needQuotes(scope, yesItDoes = 0) { if (yesItDoes) { return 1; } if (scope) { if (reBannedChars.test(scope)) { return 1; } if (reIsRangelike.test(scope)) { return 1; } if (/^\d/.test(scope)) { return 1; } } return 0; } function quotePrefix(prefix2) { return "'" + prefix2.replace(/'/g, "''") + "'"; } function stringifyPrefixXlsx(ref) { let pre = ""; let quote = 0; const { workbookName, sheetName } = ref; if (workbookName) { pre += "[" + workbookName + "]"; quote += needQuotes(workbookName); } if (sheetName) { pre += sheetName; quote += needQuotes(sheetName); } if (quote) { pre = quotePrefix(pre); } return pre ? pre + "!" : pre; } // lib/a1.ts function rangeOperator(trim) { if (trim === "both") { return ".:."; } else if (trim === "head") { return ".:"; } else if (trim === "tail") { return ":."; } return ":"; } // lib/stringifyR1C1Range.ts var clamp = (min, val, max) => Math.min(Math.max(val, min), max); function toCoord(value, isAbs) { if (isAbs) { return String(value + 1); } return value ? "[" + value + "]" : ""; } function stringifyR1C1Range(range) { let { r0, c0, r1, c1 } = range; const { $c0, $c1, $r0, $r1 } = range; const nullR0 = r0 == null; const nullC0 = c0 == null; let nullR1 = r1 == null; let nullC1 = c1 == null; const op = rangeOperator(range.trim); const hasTrim = !!range.trim; r0 = clamp($r0 ? 0 : -MAX_ROWS, r0 | 0, MAX_ROWS); c0 = clamp($c0 ? 0 : -MAX_COLS, c0 | 0, MAX_COLS); if (!nullR0 && nullR1 && !nullC0 && nullC1) { r1 = r0; nullR1 = false; c1 = c0; nullC1 = false; } else { r1 = clamp($r1 ? 0 : -MAX_ROWS, r1 | 0, MAX_ROWS); c1 = clamp($c1 ? 0 : -MAX_COLS, c1 | 0, MAX_COLS); } const allRows = r0 === 0 && r1 >= MAX_ROWS; if (allRows && !nullC0 && !nullC1 || nullR0 && nullR1) { const a = toCoord(c0, $c0); const b = toCoord(c1, $c1); return "C" + (a === b && !hasTrim ? a : a + op + "C" + b); } const allCols = c0 === 0 && c1 >= MAX_COLS; if (allCols && !nullR0 && !nullR1 || nullC0 && nullC1) { const a = toCoord(r0, $r0); const b = toCoord(r1, $r1); return "R" + (a === b && !hasTrim ? a : a + op + "R" + b); } const s_r0 = toCoord(r0, $r0); const s_r1 = toCoord(r1, $r1); const s_c0 = toCoord(c0, $c0); const s_c1 = toCoord(c1, $c1); if (nullR0 || nullR1 || nullC0 || nullC1) { return (nullR0 ? "" : "R" + s_r0) + (nullC0 ? "" : "C" + s_c0) + op + (nullR1 ? "" : "R" + s_r1) + (nullC1 ? "" : "C" + s_c1); } if (s_r0 !== s_r1 || s_c0 !== s_c1) { return "R" + s_r0 + "C" + s_c0 + op + "R" + s_r1 + "C" + s_c1; } return "R" + s_r0 + "C" + s_c0; } // lib/stringifyR1C1Ref.ts function stringifyR1C1RefXlsx(refObject) { const prefix2 = stringifyPrefixXlsx(refObject); return prefix2 + ("name" in refObject ? refObject.name : stringifyR1C1Range(refObject.range)); } // lib/parseA1Range.ts var CHAR_DOLLAR = 36; var CHAR_PERIOD = 46; var CHAR_COLON = 58; var CHAR_A_LC = 97; var CHAR_A_UC = 65; var CHAR_Z_LC = 122; var CHAR_Z_UC = 90; var CHAR_0 = 48; var CHAR_1 = 49; var CHAR_9 = 57; function advRangeOp2(str, pos) { const c0 = str.charCodeAt(pos); if (c0 === CHAR_PERIOD) { const c1 = str.charCodeAt(pos + 1); if (c1 === CHAR_COLON) { return str.charCodeAt(pos + 2) === CHAR_PERIOD ? [3, "both"] : [2, "head"]; } } else if (c0 === CHAR_COLON) { const c1 = str.charCodeAt(pos + 1); return c1 === CHAR_PERIOD ? [2, "tail"] : [1, ""]; } return [0, ""]; } function advA1Col2(str, pos) { const start = pos; const lock = str.charCodeAt(pos) === CHAR_DOLLAR; if (lock) { pos++; } const stop = pos + 3; let col = 0; do { const c = str.charCodeAt(pos); if (c >= CHAR_A_UC && c <= CHAR_Z_UC) { col = 26 * col + c - (CHAR_A_UC - 1); pos++; } else if (c >= CHAR_A_LC && c <= CHAR_Z_LC) { col = 26 * col + c - (CHAR_A_LC - 1); pos++; } else { break; } } while (pos < stop && pos < str.length); return col && col <= MAX_COLS + 1 ? [pos - start, col - 1, lock] : [0, 0, false]; } function advA1Row2(str, pos) { const start = pos; const lock = str.charCodeAt(pos) === CHAR_DOLLAR; if (lock) { pos++; } const stop = pos + 7; let row = 0; let c = str.charCodeAt(pos)