Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

report unsupported macros by katex and expand user-define commands #99

Merged
merged 16 commits into from
Jun 12, 2024
Merged
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code returns macros that is newly defined by the user and expands them. macroInfo collects the information of the anything that is of new commends, which is implmented as part of the "../uified-latex-util-macros". is Object.fromEntries used to integrate everything into a string? The needToExpand function visits all the nodes and checks if it has nodes and if the content match, why is that?

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as Ast from "@unified-latex/unified-latex-types";
import {
expandMacrosExcludingDefinitions,
listNewcommands,
} from "@unified-latex/unified-latex-util-macros";
import { attachMacroArgs } from "@unified-latex/unified-latex-util-arguments";
import { anyMacro } from "@unified-latex/unified-latex-util-match";
import { EXIT, visit } from "@unified-latex/unified-latex-util-visit";

type NewCommandSpec = ReturnType<typeof listNewcommands>[number];

/**
* Expands user-defined macros
*/
export function expandUserDefinedMacros(ast: Ast.Ast): void {
const newcommands = listNewcommands(ast);

// get a set of all macros to be expanded
const macrosToExpand = new Set(newcommands.map((command) => command.name));

const macroInfo = Object.fromEntries(
newcommands.map((m) => [m.name, { signature: m.signature }])
);

// recursively expand at most 100 times
for (let i = 0; i < 100; i++) {
// check if any macros still need expanding
if (!needToExpand(ast, macrosToExpand)) {
break;
}

// attach the arguments to each macro before processing it
attachMacroArgs(ast, macroInfo);
expandMacrosExcludingDefinitions(ast, newcommands);
}
}

function needToExpand(ast: Ast.Ast, macros: Set<string>): boolean {
let needExpand = false;

visit(ast, (node) => {
if (anyMacro(node) && macros.has(node.content)) {
needExpand = true;
EXIT;
}
});

return needExpand;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code visits all the nodes and returns a list of macros' content which are not supported by katex in math mode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the unsupported macro which is being pushed to the list, is it just the name of the macro or as well as its parameters?

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as Ast from "@unified-latex/unified-latex-types";
import { anyMacro, match } from "@unified-latex/unified-latex-util-match";
import { visit } from "@unified-latex/unified-latex-util-visit";
import { KATEX_SUPPORT } from "./katex-subs";

/**
* Return list of macros unsupported by Katex
renee-k marked this conversation as resolved.
Show resolved Hide resolved
*/
export function reportMacrosUnsupportedByKatex(ast: Ast.Ast): string[] {
const unsupported: string[] = [];

// match a macro supported by Katex
const isSupported = match.createMacroMatcher(KATEX_SUPPORT.macros);

// visit all nodes
visit(ast, (node, info) => {
// macro in math mode
if (anyMacro(node) && info.context.hasMathModeAncestor) {
// check if not supported by katex
if (!isSupported(node)) {
unsupported.push((node as Ast.Macro).content);
}
}
});

return unsupported;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { describe, it, expect } from "vitest";
import util from "util";
import { getParser } from "@unified-latex/unified-latex-util-parse";
import { printRaw } from "@unified-latex/unified-latex-util-print-raw";
import { expandUserDefinedMacros } from "@unified-latex/unified-latex-to-pretext/libs/pre-conversion-subs/expand-user-defined-macros";

// Make console.log pretty-print by default
const origLog = console.log;
console.log = (...args) => {
origLog(...args.map((x) => util.inspect(x, false, 10, true)));
};

describe("unified-latex-to-pretext:expand-user-deifned-macros", () => {
let value: string;

it("can expand newcommand", () => {
value = String.raw`\newcommand{\foo}{\bar} \foo`;

const parser = getParser();
const ast = parser.parse(value);

expandUserDefinedMacros(ast);

expect(printRaw(ast)).toEqual(String.raw`\newcommand{\foo}{\bar} \bar`);
});

it("can expand renewcommand", () => {
value = String.raw`\renewcommand{\O}{\mathcal{O}} \O`;

const parser = getParser();
const ast = parser.parse(value);

expandUserDefinedMacros(ast);

expect(printRaw(ast)).toEqual(
String.raw`\renewcommand{\O}{\mathcal{O}} \mathcal{O}`
);
});

it("can recursively expand multiple user-defined commands", () => {
value =
String.raw`\newcommand{\join}{\vee}` +
String.raw`\join` +
String.raw`\renewcommand{\vee}{\foo}` +
String.raw`\vee` +
String.raw`\renewcommand{\foo}{\bar}` +
String.raw`\foo`;

const parser = getParser();
const ast = parser.parse(value);

expandUserDefinedMacros(ast);

expect(printRaw(ast)).toEqual(
String.raw`\newcommand{\join}{\vee}` +
String.raw`\bar` +
String.raw`\renewcommand{\vee}{\foo}` +
String.raw`\bar` +
String.raw`\renewcommand{\foo}{\bar}` +
String.raw`\bar`
);
});

it("can expand providecommand", () => {
value = String.raw`\providecommand{\bar}{\b} \bar`;

const parser = getParser();
const ast = parser.parse(value);

expandUserDefinedMacros(ast);

expect(printRaw(ast)).toEqual(String.raw`\providecommand{\bar}{\b} \b`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { describe, it, expect } from "vitest";
import util from "util";
import { getParser } from "@unified-latex/unified-latex-util-parse";
import { reportMacrosUnsupportedByKatex } from "@unified-latex/unified-latex-to-pretext/libs/pre-conversion-subs/report-unsupported-macro-katex";

// Make console.log pretty-print by default
const origLog = console.log;
console.log = (...args) => {
origLog(...args.map((x) => util.inspect(x, false, 10, true)));
};

describe("unified-latex-to-pretext:report-unsupported-macro-katex", () => {
let value: string;

it("can report unsupported macros in inline mathmode", () => {
value = String.raw`$\mathbb{R} \fakemacro{X}$`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual(["fakemacro"]);
});

it("can report no unsupported macros in mathmode", () => {
value = String.raw`$\mathbb{R} \frac{1}{2} \cup$`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual([]);
});

it("doesn't report unsupported macros outside of math mode", () => {
value = String.raw`\fakemacro`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual([]);
});

it("reports unsupported macros in text mode with a math anscestor", () => {
value = String.raw`$\frac{1}{\text{ hi \unsupported}}$`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual(["unsupported"]);
});

it("can report unsupported macros in display mathmode", () => {
value = String.raw`\[ \frac{a}{b} \fake \text{bar \baz}\] \bang`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual(["fake", "baz"]);
});

it("can report unsupported macros in equation environment", () => {
value = String.raw`\unsupported \begin{equation} \mathbb{N} \unsupported \text{\baz}\end{equation}`;

const parser = getParser();
const ast = parser.parse(value);

expect(reportMacrosUnsupportedByKatex(ast)).toEqual([
"unsupported",
"baz",
]);
});
});
renee-k marked this conversation as resolved.
Show resolved Hide resolved