Skip to content

Commit

Permalink
chore: simplify codes
Browse files Browse the repository at this point in the history
  • Loading branch information
theseanl committed Jan 22, 2024
1 parent bd1f972 commit 7d12d13
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 102 deletions.
10 changes: 5 additions & 5 deletions packages/unified-latex-util-argspec/libs/argspec-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function printRawInner(node: ArgSpec.Node) {
const decorators = getDecorators(node);
let spec = decorators;
function appendDefaultArg() {
if ("defaultArg" in node && node.defaultArg) {
if (node.defaultArg) {
spec = appendTokenOrGroup(spec, node.defaultArg);
}
}
Expand Down Expand Up @@ -71,10 +71,10 @@ function printRawInner(node: ArgSpec.Node) {
appendDefaultArg();
return spec;
case "embellishment":
spec += node.embellishmentDefaultArg ? "E" : "e";
appendCollection(node.embellishmentTokens);
if (node.embellishmentDefaultArg) {
appendCollection(node.embellishmentDefaultArg);
spec += node.defaultArgs ? "E" : "e";
appendCollection(node.tokens);
if (node.defaultArgs) {
appendCollection(node.defaultArgs);
}
return spec;
case "verbatim":
Expand Down
4 changes: 2 additions & 2 deletions packages/unified-latex-util-argspec/libs/argspec-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ interface OptionalToken extends LeadingWhitespace, AstNode {
}
export interface Embellishment extends AstNode {
type: "embellishment";
embellishmentTokens: string[];
embellishmentDefaultArg?: string[]; // Embellishment default arguments are always a collection of arguments
tokens: string[];
defaultArgs?: string[]; // Embellishment default arguments are always a collection of arguments
}
interface Mandatory extends DefaultArgument, AstNode, Arg {
type: "mandatory";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
exports[`unified-latex-util-argspec > parses xparse argument specification string "E{\\token ^}{{D1}2}" 1`] = `
[
{
"embellishmentDefaultArg": [
"defaultArgs": [
"D1",
"2",
],
"embellishmentTokens": [
"tokens": [
"\\\\token",
"^",
],
Expand Down Expand Up @@ -128,7 +128,7 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
"type": "mandatory",
},
{
"embellishmentTokens": [
"tokens": [
"^",
],
"type": "embellishment",
Expand All @@ -144,7 +144,7 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
"type": "mandatory",
},
{
"embellishmentTokens": [
"tokens": [
"_",
"^",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ describe("unified-latex-util-argspec", () => {
it("Embellishments always return a string", () => {
let ast = argspecParser.parse("e{{x}y{z}}");
expect(ast).toEqual([
{ type: "embellishment", embellishmentTokens: ["x", "y", "z"] },
{ type: "embellishment", tokens: ["x", "y", "z"] },
]);
ast = argspecParser.parse("E{{x}y{z}}{{One}{Two}{Three}}");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["x", "y", "z"],
embellishmentDefaultArg: ["One", "Two", "Three"],
tokens: ["x", "y", "z"],
defaultArgs: ["One", "Two", "Three"],
},
]);
});
Expand Down
10 changes: 4 additions & 6 deletions packages/unified-latex-util-arguments/libs/gobble-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export function gobbleArguments(
// We need special behavior for embellishment argspecs.
// Because an embellishment argspec specifies more than one argument,
// we need to keep gobbling arguments until we've got them all.
const { embellishmentTokens } = spec;
const { tokens } = spec;

const remainingTokens = new Set<string>(embellishmentTokens);
const remainingTokens = new Set<string>(tokens);
const tokenToArgs = new Map<string, Ast.Argument>();

for (;;) {
Expand All @@ -57,9 +57,7 @@ export function gobbleArguments(
}

args.push(
...spec.embellishmentTokens.map(
(t) => tokenToArgs.get(t) || emptyArg()
)
...spec.tokens.map((t) => tokenToArgs.get(t) || emptyArg())
);
} else {
const { argument, nodesRemoved: removed } = gobbleSingleArgument(
Expand All @@ -81,6 +79,6 @@ export function gobbleArguments(
function embellishmentSpec(tokens: Set<string>): ArgSpec.Embellishment {
return {
type: "embellishment",
embellishmentTokens: [...tokens],
tokens: [...tokens],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export function gobbleSingleArgument(
break;
}
case "embellishment": {
for (const token of argSpec.embellishmentTokens) {
for (const token of argSpec.tokens) {
const bracePos = findBracePositions(nodes, currPos, token);
if (!bracePos) {
continue;
Expand Down
138 changes: 66 additions & 72 deletions packages/unified-latex-util-macros/libs/newcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ export function createMacroExpander(
signature?: string
): (macro: Ast.Macro) => Ast.Node[] {
const cachedSubstitutionTree = structuredClone(substitution);
const res = getMacroSubstitutionHashNumbers(cachedSubstitutionTree);
const hasSubstitutions = attachHashNumbers(cachedSubstitutionTree);

if (res.size === 0) {
if (!hasSubstitutions) {
return () => structuredClone(cachedSubstitutionTree);
}

Expand All @@ -203,10 +203,8 @@ export function createMacroExpander(
.map((node) => {
if (node.type === "embellishment") {
return (
node.embellishmentDefaultArg ||
(Array(node.embellishmentTokens.length).fill(
undefined
) as undefined[])
node.defaultArgs ||
(Array(node.tokens.length).fill(undefined) as undefined[])
);
}
return node.defaultArg;
Expand All @@ -215,82 +213,79 @@ export function createMacroExpander(

return (macro: Ast.Macro) => {
const retTree = structuredClone(cachedSubstitutionTree);
const args = macro.args;

const stack: number[] = [];
let lastSelfReference: number | null = null;

// Recursively expand macro arguments. If a self-reference is found, it returns
// the corresponding hash number, which is used to special-case `O{#2} O{#1}`.
// Recursively expand macro arguments. If a self-reference is found, sets a
// lastSelfReference variable, which is used to special-case `O{#2} O{#1}`.
function expandArgs(retTree: Ast.Node[]): void {
replaceNode(retTree, (node) => {
if (node.type !== "hash_number") {
return;
}

const hashNum = node.number;
const arg = args?.[hashNum - 1];
const arg = macro.args?.[hashNum - 1];

// Check if this argument is -NoValue-
if (!arg || match.blankArgument(arg)) {
// Check if there exists a default argument for this hash number
const defaultArg = defaultArgs[hashNum - 1];
if (!defaultArg) {
return s(`#${hashNum}`);
}

// Detect self-references
if (stack.includes(hashNum)) {
lastSelfReference = hashNum;
return s(`#${hashNum}`);
}

// `defaultArg` is a string expression. The same `defaultArg` may be parsed
// differently depending on the context of `macro`, so we cannot cache
// the parse result of `defaultArg`. Currently we just call `parse` without
// taking account of parsing contexts, so actually the result can be cached,
// but this is not the correct thing to do. FIXME: we should probably pass
// some options that is provided to whatever function that called this to
// the below parse call. Note that `parse` is done in several passes, and we
// may be able to cache result of a first few passes that aren't context-dependent.
const subst = parse(defaultArg).content;
const nextHashNums = getMacroSubstitutionHashNumbers(subst);
// If `arg` is provided, return it
if (arg && !match.blankArgument(arg)) {
return arg.content;
}

if (nextHashNums.size === 0) {
return subst;
}
// Check if there exists a default argument for this hash number
const defaultArg = defaultArgs[hashNum - 1];
if (!defaultArg) {
return s(`#${hashNum}`);
}

stack.push(hashNum);
try {
expandArgs(subst);
// Detect self-references
if (stack.includes(hashNum)) {
lastSelfReference = hashNum;
return s(`#${hashNum}`);
}
// `defaultArg` is a string expression. The same `defaultArg` may be parsed
// differently depending on the context of `macro`, so we cannot cache
// the parse result of `defaultArg`. Currently we just call `parse` without
// taking account of parsing contexts, so actually the result can be cached,
// but this is not the correct thing to do. FIXME: we should probably pass
// some options that is provided to whatever function that called this to
// the below parse call. Note that `parse` is done in several passes, and we
// may be able to cache result of a first few passes that aren't context-dependent.
const subst = parse(defaultArg).content;
const hasSubstitutions = attachHashNumbers(subst);

if (lastSelfReference !== hashNum) {
return subst;
}
if (!hasSubstitutions) {
return subst;
}

// At this point, we have encountered #n while expanding #n.
// Check if we got exactly #n by expanding #n,
// in which case we should return the -NoValue-.
if (`#${hashNum}` === printRaw(subst)) {
// We are good, clear the last self-reference variable
lastSelfReference = null;
return emptyArg();
}
stack.push(hashNum);
try {
expandArgs(subst);

console.warn(
`Detected unrecoverable self-reference while expanding macro: ${printRaw(
macro
)}`
);
// Return a placeholder string, so that we know that
// this code path is not taken in unit tests.
return s("-Circular-");
} finally {
stack.pop();
if (lastSelfReference !== hashNum) {
return subst;
}
// At this point, we have encountered #n while expanding #n.
// Check if we got exactly #n by expanding #n,
// in which case we should return the -NoValue-.
if (`#${hashNum}` === printRaw(subst)) {
// We are good, clear the last self-reference variable
lastSelfReference = null;
return emptyArg();
}
}

return arg.content;
console.warn(
`Detected unrecoverable self-reference while expanding macro: ${printRaw(
macro
)}`
);
// Return a placeholder string, so that we know that
// this code path is not taken in unit tests.
return s("-Circular-");
} finally {
stack.pop();
}
});
}

Expand All @@ -300,19 +295,18 @@ export function createMacroExpander(
}

/**
* Parses macro substitutions, mutates tree, and returns a list of hashnumbers that were encountered.
* Parses macro substitutions, mutates tree, and signal if there are any hash numbers.
*/
export function getMacroSubstitutionHashNumbers(tree: Ast.Node[]) {
const hashNumbers = new Set<number>();
export function attachHashNumbers(tree: Ast.Node[]) {
let hasSubstitutions = false;
visit(
tree,
(nodes) => {
const parsed = parseMacroSubstitutions(nodes);
parsed.forEach((node) => {
if (node.type === "hash_number") {
hashNumbers.add(node.number);
}
});
// Keep track of whether there are any substitutions so we can bail early if not.
hasSubstitutions =
hasSubstitutions ||
parsed.some((node) => node.type === "hash_number");
nodes.length = 0;
nodes.push(...(parsed as Ast.Node[]));
},
Expand All @@ -321,5 +315,5 @@ export function getMacroSubstitutionHashNumbers(tree: Ast.Node[]) {
test: Array.isArray,
}
);
return hashNumbers;
return hasSubstitutions;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
} catch (e) {
console.warn("Error when initializing parser", e);
}
}2
}
}

body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ optional_standard
optional_embellishment
= "e" args:token_or_collection {
return createNode("embellishment", {
embellishmentTokens:args
tokens:args
});
}
/ "E" args:token_or_collection g:token_or_collection {
return createNode("embellishment", {
embellishmentTokens: args,
embellishmentDefaultArg: g
tokens: args,
defaultArgs: g
});
}
Expand Down
8 changes: 3 additions & 5 deletions packages/unified-latex-util-scan/libs/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,16 @@ export function scan(
}
): number | null {
const {
startIndex,
endIndex,
startIndex = 0,
endIndex = nodes.length - 1,
onlySkipWhitespaceAndComments,
allowSubstringMatches,
} = options || {};
if (typeof token === "string") {
token = { type: "string", content: token } as Ast.String;
}
const start = typeof startIndex === "number" ? startIndex : 0;
const end = typeof endIndex === "number" ? endIndex : nodes.length - 1;

for (let i = start; i <= end; i++) {
for (let i = startIndex; i <= endIndex; i++) {
const node = nodes[i];
if (node.type === token.type) {
switch (node.type) {
Expand Down

0 comments on commit 7d12d13

Please sign in to comment.