import { arg } from "@unified-latex/unified-latex-builder"; import { parse } from "@unified-latex/unified-latex-util-argspec"; import { match } from "@unified-latex/unified-latex-util-match"; import { scan } from "@unified-latex/unified-latex-util-scan"; import { updateRenderInfo } from "@unified-latex/unified-latex-util-render-info"; import { visit } from "@unified-latex/unified-latex-util-visit"; function gobbleSingleArgument(nodes, argSpec, startPos = 0) { if (typeof argSpec === "string" || !argSpec.type) { throw new Error( `argSpec must be an already-parsed argument specification, not "${JSON.stringify( argSpec )}"` ); } let argument = null; let currPos = startPos; const gobbleWhitespace = argSpec.noLeadingWhitespace ? () => { } : () => { while (currPos < nodes.length) { if (!match.whitespace(nodes[currPos])) { break; } currPos++; } }; const openMark = argSpec.openBrace || ""; const closeMark = argSpec.closeBrace || ""; const acceptGroup = (argSpec.type === "mandatory" || argSpec.type === "optional") && openMark === "{" && closeMark === "}"; gobbleWhitespace(); const currNode = nodes[currPos]; if (currNode == null || match.comment(currNode) || match.parbreak(currNode)) { const ret = { argument, nodesRemoved: 0 }; return ret; } switch (argSpec.type) { case "mandatory": if (acceptGroup) { let content = [currNode]; if (match.group(currNode)) { content = currNode.content; } argument = arg(content, { openMark, closeMark }); currPos++; break; } else { const bracePos2 = findBracePositions( nodes, currPos, openMark, closeMark ); if (bracePos2) { argument = arg(nodes.slice(bracePos2[0] + 1, bracePos2[1]), { openMark, closeMark }); currPos = bracePos2[1] + 1; break; } } // NOTE: Fallthrough is on purpose. // Matching a mandatory argument and an optional argument is the same for our purposes // because we're not going to fail to parse because of a missing argument. case "optional": if (acceptGroup && match.group(currNode)) { argument = arg(currNode.content, { openMark, closeMark }); currPos++; break; } const bracePos = findBracePositions( nodes, currPos, openMark, closeMark ); if (bracePos) { argument = arg(nodes.slice(bracePos[0] + 1, bracePos[1]), { openMark, closeMark }); currPos = bracePos[1] + 1; break; } break; case "optionalStar": case "optionalToken": { const bracePos2 = findBracePositions( nodes, currPos, argSpec.type === "optionalStar" ? "*" : argSpec.token ); if (bracePos2) { argument = arg(currNode, { openMark: "", closeMark: "" }); currPos = bracePos2[0] + 1; } break; } case "until": { if (argSpec.stopTokens.length > 1) { console.warn( `"until" matches with multi-token stop conditions are not yet implemented` ); break; } const rawToken = argSpec.stopTokens[0]; const stopToken = rawToken === " " ? { type: "whitespace" } : rawToken; let bracePos2 = findBracePositions( nodes, startPos, void 0, stopToken ); if (!bracePos2) { break; } argument = arg(nodes.slice(startPos, bracePos2[1]), { openMark: "", closeMark: rawToken }); currPos = bracePos2[1]; if (currPos < nodes.length) { currPos++; } break; } case "embellishment": { for (const token of argSpec.embellishmentTokens) { const bracePos2 = findBracePositions(nodes, currPos, token); if (!bracePos2) { continue; } let argNode = nodes[bracePos2[0] + 1]; argument = arg( match.group(argNode) ? argNode.content : argNode, { openMark: token, closeMark: "" } ); currPos = bracePos2[1] + 1; break; } break; } default: console.warn( `Don't know how to find an argument of argspec type "${argSpec.type}"` ); } const nodesRemoved = argument ? currPos - startPos : 0; nodes.splice(startPos, nodesRemoved); return { argument, nodesRemoved }; } function cloneStringNode(node, content) { return Object.assign({}, node, { content }); } function findBracePositions(nodes, startPos, openMark, closeMark) { const currNode = nodes[startPos]; let openMarkPos = startPos; let closeMarkPos = startPos; if (openMark) { if (!match.anyString(currNode)) { return; } const nodeContent = currNode.content; if (!nodeContent.startsWith(openMark)) { return; } openMarkPos = startPos; if (currNode.content.length > openMark.length) { const nodeContent2 = currNode.content; currNode.content = openMark; nodes.splice( openMarkPos + 1, 0, cloneStringNode(currNode, nodeContent2.slice(openMark.length)) ); } closeMarkPos = openMarkPos + 1; } if (!closeMark) { const argNode = nodes[closeMarkPos]; if (!argNode) { return; } if (match.anyString(argNode) && argNode.content.length > 1) { const argContent = argNode.content; argNode.content = argContent[0]; nodes.splice( closeMarkPos + 1, 0, cloneStringNode(argNode, argContent.slice(1)) ); } return [openMarkPos, closeMarkPos]; } closeMarkPos = scan(nodes, closeMark, { startIndex: closeMarkPos, allowSubstringMatches: true }); if (closeMarkPos === null) { return; } const closingNode = nodes[closeMarkPos]; if (match.anyString(closingNode) && typeof closeMark === "string") { const closingNodeContent = closingNode.content; let closeMarkIndex = closingNodeContent.indexOf(closeMark); if (closingNodeContent.length > closeMark.length) { closingNode.content = closeMark; const prev = closingNodeContent.slice(0, closeMarkIndex); const next = closingNodeContent.slice( closeMarkIndex + closeMark.length ); if (prev) { nodes.splice( closeMarkPos, 0, cloneStringNode(closingNode, prev) ); closeMarkPos++; } if (next) { nodes.splice( closeMarkPos + 1, 0, cloneStringNode(closingNode, next) ); } } } return [openMarkPos, closeMarkPos]; } function gobbleArguments(nodes, argSpec, startPos = 0) { if (typeof argSpec === "function") { return argSpec(nodes, startPos); } if (typeof argSpec === "string") { argSpec = parse(argSpec); } const args = []; let nodesRemoved = 0; for (const spec of argSpec) { if (spec.type === "embellishment") { const remainingTokens = new Set(spec.embellishmentTokens); const argForToken = Object.fromEntries( spec.embellishmentTokens.map((t, i) => { var _a; const defaultArg = "defaultArg" in spec ? (_a = spec.defaultArg) == null ? void 0 : _a[i] : void 0; return [t, emptyArg(defaultArg)]; }) ); let { argument, nodesRemoved: removed } = gobbleSingleArgument( nodes, embellishmentSpec(remainingTokens), startPos ); while (argument) { const token = argument.openMark; remainingTokens.delete(token); argForToken[token] = argument; nodesRemoved += removed; const newSpec = embellishmentSpec(remainingTokens); ({ argument, nodesRemoved: removed } = gobbleSingleArgument( nodes, newSpec, startPos )); } args.push(...spec.embellishmentTokens.map((t) => argForToken[t])); } else { const { argument, nodesRemoved: removed } = gobbleSingleArgument( nodes, spec, startPos ); const defaultArg = "defaultArg" in spec ? spec.defaultArg : void 0; args.push(argument || emptyArg(defaultArg)); nodesRemoved += removed; } } return { args, nodesRemoved }; } function embellishmentSpec(tokens) { return { type: "embellishment", embellishmentTokens: [...tokens] }; } function emptyArg(defaultArg) { const ret = arg([], { openMark: "", closeMark: "" }); if (defaultArg != null) { updateRenderInfo(ret, { defaultArg }); } return ret; } function attachMacroArgsInArray(nodes, macros) { let currIndex; const isRelevantMacro = match.createMacroMatcher(macros); function gobbleUntilMacro() { while (currIndex >= 0 && !isRelevantMacro(nodes[currIndex])) { currIndex--; } } currIndex = nodes.length - 1; while (currIndex >= 0) { gobbleUntilMacro(); if (currIndex < 0) { return; } const macroIndex = currIndex; const macro = nodes[macroIndex]; const macroName = macro.content; const macroInfo = macros[macroName]; updateRenderInfo(macro, macroInfo.renderInfo); const signatureOrParser = macroInfo.argumentParser || macroInfo.signature; if (signatureOrParser == null) { currIndex--; continue; } if (macro.args != null) { currIndex = macroIndex - 1; continue; } currIndex++; const { args } = gobbleArguments(nodes, signatureOrParser, currIndex); macro.args = args; currIndex = macroIndex - 1; } } function attachMacroArgs(tree, macros) { visit( tree, (nodes) => { attachMacroArgsInArray(nodes, macros); }, { includeArrays: true, test: Array.isArray } ); } const unifiedLatexAttachMacroArguments = function unifiedLatexAttachMacroArguments2(options) { return (tree) => { const { macros = {} } = options || {}; if (Object.keys(macros).length === 0) { console.warn( "Attempting to attach macro arguments but no macros are specified." ); } visit( tree, (nodes) => { attachMacroArgsInArray(nodes, macros); }, { includeArrays: true, test: Array.isArray } ); }; }; function getArgsContent(node) { if (!Array.isArray(node.args)) { return []; } return node.args.map((arg2) => { if (arg2.openMark === "" && arg2.content.length === 0) { return null; } return arg2.content; }); } function getNamedArgsContent(node, namedArgumentsFallback = []) { var _a; const names = ((_a = node._renderInfo) == null ? void 0 : _a.namedArguments) || namedArgumentsFallback; if (!Array.isArray(node.args) || !Array.isArray(names) || names.length === 0) { return {}; } const ret = {}; node.args.forEach((arg2, i) => { const name = names[i]; if (name == null) { return; } let val = arg2.content; if (arg2.openMark === "" && arg2.content.length === 0) { val = null; } ret[name] = val; }); return ret; } export { attachMacroArgs, attachMacroArgsInArray, getArgsContent, getNamedArgsContent, gobbleArguments, gobbleSingleArgument, unifiedLatexAttachMacroArguments }; //# sourceMappingURL=index.js.map