Skip to content

Commit

Permalink
chore: minor change as per reviews
Browse files Browse the repository at this point in the history
  • Loading branch information
theseanl committed Jan 25, 2024
1 parent dcb22be commit 916e10f
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 56 deletions.
2 changes: 1 addition & 1 deletion packages/unified-latex-prettier/libs/printer/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function printLatexAst(
case "whitespace":
return line;
case "hash_number":
return "#" + node.number.toString(10);
return `#${node.number}`;
default:
console.warn("Printing unknown type", node);
return printRaw(node);
Expand Down
40 changes: 25 additions & 15 deletions packages/unified-latex-util-argspec/libs/argspec-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ export function printRaw(
function printRawInner(node: ArgSpec.Node) {
const decorators = getDecorators(node);
let spec = decorators;
function appendDefaultArg() {
if (node.defaultArg) {
spec = appendTokenOrGroup(spec, node.defaultArg);
}
}
function appendCollection(collection: string[]) {
spec += printTokenOrCollection(collection);
}
const type = node.type;
switch (type) {
case "body":
Expand All @@ -58,7 +50,9 @@ function printRawInner(node: ArgSpec.Node) {
spec += node.defaultArg ? "D" : "d";
spec += node.openBrace + node.closeBrace;
}
appendDefaultArg();
if (node.defaultArg) {
spec = appendTokenOrGroup(spec, node.defaultArg);
}
return spec;
case "mandatory":
// {...} is the default enclosure for mandatory arguments
Expand All @@ -68,20 +62,22 @@ function printRawInner(node: ArgSpec.Node) {
spec += node.defaultArg ? "R" : "r";
spec += node.openBrace + node.closeBrace;
}
appendDefaultArg();
if (node.defaultArg) {
spec = appendTokenOrGroup(spec, node.defaultArg);
}
return spec;
case "embellishment":
spec += node.defaultArgs ? "E" : "e";
appendCollection(node.tokens);
spec += printTokenOrCollection(node.tokens);
if (node.defaultArgs) {
appendCollection(node.defaultArgs);
spec += printTokenOrCollection(node.defaultArgs);
}
return spec;
case "verbatim":
return spec + "v" + node.openBrace;
case "until": {
spec += "u";
appendCollection(node.stopTokens);
spec += printTokenOrCollection(node.stopTokens);
return spec;
}
default:
Expand All @@ -95,8 +91,22 @@ function printRawInner(node: ArgSpec.Node) {
* This function will reconstruct a representative in an inverse image of token_or_group
* for a given array of strings, and append it to a given string.
* In order to avoid parsing ambiguity, we force enclose the representative with braces in some case.
* For instance, if the given string ends with a control word such as "\asdf", and if the representative is a
* whitespace where we are in a circumstance where no whitespaces are allowed.
*
* Examples)
* Appending a `token_or_group` representing an embellishment tokens, with existingString `e`.
* token of several chars "ab" --> {ab} will be appended, so the result will be `e{ab}`.
* token of single char "^" ---> ^ will be appended, so the result will be `e^`.
* token of single char " " ---> { } will be appended, so the result will be `e{ }`.
*
* Appending a `token_or_group` representing a bracespec of a delimited argument spec.
* Already constructed a string until the opening brace, existingString = "r\open".
* If the bracespec is a single alphabetric char "a", then we need to append it with braces, so the result will be "r\open{a}".
*
* Appending an embellishment token, with existingString = "e{a\token".
* If a next token is again a control world, say \anotherToken, we can append it as-is. "e{a\token\anotherToken".
* If a next token is a single char, say "b", we may either enclose it with braces or separate it with a space,
* because in this circumstance, space can be used to separate tokens (as conveyed by allowWhitespace = true).
* Then the result will be "e{a\token b".
*/
function appendTokenOrGroup(
existingString: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,6 @@ export function gobbleSingleArgument(
}
case "until": {
const stopTokens = argSpec.stopTokens.map(parseToken);
// TODO: in order to match xparse's behavior, multiple spaces at the start
// or in a middle should be collapsed to a single whitespace token,
// and spaces at the end should be ignored.
let nextStartPos = startPos;
let bracePos: [number, number] | undefined;
while (nextStartPos < nodes.length) {
Expand Down Expand Up @@ -236,7 +233,6 @@ function cloneStringNode(node: Ast.String, content: string): Ast.String {
return Object.assign({}, node, { content });
}

type Braces = string | Ast.Macro | Ast.Whitespace;
/**
* Find the position of the open brace and the closing brace.
* Returns undefined if the brace isn't found.
Expand All @@ -246,8 +242,8 @@ type Braces = string | Ast.Macro | Ast.Whitespace;
function findBracePositions(
nodes: Ast.Node[],
startPos: number,
openMark?: Braces,
closeMark?: Braces,
openMark?: string | Ast.Macro | Ast.Whitespace,
closeMark?: string | Ast.Macro | Ast.Whitespace,
endPos?: number
): [number, number] | undefined {
let openMarkPos: number | undefined = startPos;
Expand Down Expand Up @@ -285,9 +281,15 @@ function findBracePositions(
return [openMarkPos, closeMarkPos];
}

/**
* Find the position of the delimiter in `nodes`. A delimiter can either be a single character,
* or a single control word or a symbol (represented as Ast.Macro). Returns `undefined`
* if it cannot be found. If a search found a character delimiter in a middle of a string,
* this function may mutate `nodes` to split the string.
*/
function findDelimiter(
nodes: Ast.Node[],
token: Braces,
token: string | Ast.Macro | Ast.Whitespace,
startPos: number,
endPos?: number
): number | undefined {
Expand Down Expand Up @@ -338,7 +340,7 @@ function findDelimiter(

function parseToken(
str: string | undefined
): string | Ast.Whitespace | Ast.Macro {
): string | Ast.Macro | Ast.Whitespace {
if (!str) {
return "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,8 @@ describe("unified-latex-util-arguments", () => {
expect(nodes).toEqual([{ content: "yx", type: "string" }]);
});
it("can gobble an 'until' argument with multiple stop tokens", () => {
let argspec = parseArgspec("u{a \\bcd}")[0];
value = "asdf asydfxya{x}sa \\bcd2df";
let argspec = parseArgspec("u{| \\stop}")[0];
value = "|ThisBarIsNotAStop|{Token}This| \\stop Is.";
file = processLatexToAstViaUnified().processSync({ value });
let nodes = trimRenderInfo((file.result as any).content) as Ast.Node[];
expect(gobbleSingleArgument(nodes, argspec)).toEqual({
Expand All @@ -611,28 +611,24 @@ describe("unified-latex-util-arguments", () => {
content: [
// Due to a current implementation of gobbleSingleArgument,
// we may introduce extra string split during the search.
{ type: "string", content: "a" },
{ type: "string", content: "sdf" },
{ type: "whitespace" },
{ type: "string", content: "a" },
{ type: "string", content: "sydfxy" },
{ type: "string", content: "a" },
{ type: "string", content: "|" },
{ type: "string", content: "ThisBarIsNotAStop" },
{ type: "string", content: "|" },
{
type: "group",
content: [{ type: "string", content: "x" }],
content: [{ type: "string", content: "Token" }],
},
{ type: "string", content: "s" },
{ type: "string", content: "This" },
],
openMark: "",
closeMark: "a \\bcd",
closeMark: "| \\stop",
},
nodesRemoved: 11,
nodesRemoved: 8,
});
expect(nodes).toEqual([
{
type: "string",
content: "2df",
},
{ type: "whitespace" },
{ type: "string", content: "Is" },
{ type: "string", content: "." },
]);
});
it("gobbleSingleArgument gobbles non-punctuation delimited arguments", () => {
Expand Down Expand Up @@ -825,7 +821,7 @@ describe("unified-latex-util-arguments", () => {
nodesRemoved: 2,
});
});
it("can gobble optional argument with default argument", () => {
it("can skip optional argument with default argument", () => {
const expectNoMatch = (ast: Ast.Node[]) => {
expect(
gobbleSingleArgument(ast, parseArgspec("O{default}")[0])
Expand Down
12 changes: 6 additions & 6 deletions packages/unified-latex-util-macros/libs/newcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,24 @@ export function createMacroExpander(
// 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);
const sub = parse(defaultArg).content;
const hasSubstitutions = attachHashNumbers(sub);

if (!hasSubstitutions) {
return subst;
return sub;
}

stack.push(hashNum);
try {
expandArgs(subst);
expandArgs(sub);

if (lastSelfReference !== hashNum) {
return subst;
return sub;
}
// 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)) {
if (`#${hashNum}` === printRaw(sub)) {
// We are good, clear the last self-reference variable
lastSelfReference = null;
return emptyArg();
Expand Down
17 changes: 8 additions & 9 deletions packages/unified-latex-util-macros/tests/macro-expansion.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import util from "util";
import { m } from "@unified-latex/unified-latex-builder";
import * as Ast from "@unified-latex/unified-latex-types";
import util from "util";
import { attachMacroArgsInArray } from "../../unified-latex-util-arguments/libs/attach-arguments";

import * as latexParser from "@unified-latex/unified-latex-util-parse";
import { printRaw } from "@unified-latex/unified-latex-util-print-raw";
import { trimRenderInfo } from "@unified-latex/unified-latex-util-render-info";
import { attachMacroArgsInArray } from "../../unified-latex-util-arguments/libs/attach-arguments";
import { expandMacros } from "../libs/expand-macros";
import { createMacroExpander } from "../libs/newcommand";

Expand Down Expand Up @@ -169,29 +168,29 @@ describe("unified-latex-utils-macros", () => {
});

it("Can substitute default arguments that cross-references each other", () => {
let substitutionBody = latexParser.parse("#1/#2").content;
let substitutionBody = latexParser.parse("#1.#2").content;
// This macro defines the args that will be substituted
let macro = parseXxxMacro("\\xxx", "O{#2} O{#1}");

const expander = createMacroExpander(substitutionBody, "O{#2} O{#1}");
expect(printRaw(expander(macro))).toEqual("/");
expect(printRaw(expander(macro))).toEqual(".");
});

it("Can substitute default arguments referencing other default arguments that cross-references each other", () => {
let substitutionBody = latexParser.parse("#1/#2/#3/#4").content;
let substitutionBody = latexParser.parse("#1.#2.#3.#4").content;
// This macro defines the args that will be substituted
let macro = parseXxxMacro("\\xxx", "O{a#2#4} O{#3} O{#2} O{b#2}");

const expander = createMacroExpander(
substitutionBody,
"O{a#2#4} O{#3} O{#2} O{b#2}"
);
expect(printRaw(expander(macro))).toEqual("ab///b");
expect(printRaw(expander(macro))).toEqual("ab...b");
});

it("Can substitute default arguments with complex dependency graph", () => {
let substitutionBody = latexParser.parse(
"#1/#2/#3/#4/#5/#6/#7/#8/#9"
"#1.#2.#3.#4.#5.#6.#7.#8.#9"
).content;
// This macro defines the args that will be substituted
let macro = parseXxxMacro(
Expand All @@ -203,7 +202,7 @@ describe("unified-latex-utils-macros", () => {
substitutionBody,
"D<>{#2} E{^_}{{#1}{#4}} O{#5} E{<>,}{{#3}{a#3b#1c#7}{#9d#4}} O{#6e} D\\a\\b{f#8}"
);
expect(printRaw(expander(macro))).toEqual("/////abc9d/9d/abc9de/9");
expect(printRaw(expander(macro))).toEqual(".....abc9d.9d.abc9de.9");
});

it("Can expand macro", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ required
/ "r" braceSpec:brace_spec { return createNode("mandatory", braceSpec); }
// An "until" argument gobbles tokens until the specified stop token(s). Until token allows whitespace.
// TODO: in order to match xparse's behavior, multiple spaces at the start or in a middle
// should be collapsed to a single whitespace token, and spaces at the end should be ignored.
until
= "u" stopTokens:(x:token { return [x] } / '{' @(token_or_whitespace+) '}') {
return createNode("until", { stopTokens });
Expand Down

0 comments on commit 916e10f

Please sign in to comment.