end-of-semester-report/node_modules/@unified-latex/unified-latex-util-arguments/index.js
2025-05-14 16:14:35 +07:00

406 lines
11 KiB
JavaScript

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