@borgar/fx
Version:
Utilities for working with Excel formulas
1,923 lines (1,893 loc) • 81.5 kB
JavaScript
// 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 tokenize(formula, options = {}) {
return getTokens(formula, lexers, options);
}
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 stringifyPrefix(ref) {
let pre = "";
let quote = 0;
let nth = 0;
const context = ref.context || [];
for (let i = context.length; i > -1; i--) {
const scope = context[i];
if (scope) {
const part = nth % 2 ? "[" + scope + "]" : scope;
pre = part + pre;
quote += needQuotes(scope, quote);
nth++;
}
}
if (quote) {
pre = quotePrefix(pre);
}
return pre ? pre + "!" : pre;
}
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 stringifyR1C1Ref(refObject) {
const prefix2 = stringifyPrefix(refObject);
return prefix2 + ("name" in refObject ? refObject.name : stringifyR1C1Range(refObject.range));
}
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.charC