-
Notifications
You must be signed in to change notification settings - Fork 24
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
Changes from 13 commits
3a85c98
0c1971a
a140ea3
575141d
2711765
a450ac1
bd2f4d5
76352c5
6f6221a
b55e08c
49e25bc
577d1be
7648bc7
80fce28
34214da
8383767
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
There was a problem hiding this comment.
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?