Support \tag, \tag*, and \gdef (#1309)

* Tag sketch

* Drop objectAssign; already using Object.assign elsewhere

* Basic \gdef support

* Bug fix

* Finish \tag

* MathML numbers equations with <mlabeledtr>

* Fix flow bugs

* \gdef tests

* Add basic \tag tests

* Screenshot test for \tag

* \tag* test

* Add missing file

* Bug fix screenshot

* Major refactor

* Represent tag at top level of parse tree, requiring less hackery
  * No more \@tag function; it was essentially just doing \text
* Wrap tag in group so e.g. ( and ) are formatted the same
* Add `feed` method to MacroExpander for multiple inputs (for tag)
* Bug fixes in buildHTML, makeTextRow, _getBuilt (for display mode)
* Remove excess <mrow> wrapper when unnecessary

* Update screenshot from tag being wrapped in group

* Add maxExpand limit
This commit is contained in:
Erik Demaine
2018-05-19 16:19:21 -04:00
committed by Kevin Barabash
parent 99b2afa935
commit a0ddad338e
19 changed files with 370 additions and 107 deletions

View File

@@ -2663,6 +2663,24 @@ describe("A macro expander", function() {
// {"\\mode": "\\TextOrMath{text}{math}"});
//});
it("\\gdef defines macros", function() {
compareParseTree("\\gdef\\foo{x^2}\\foo+\\foo", "x^2+x^2");
compareParseTree("\\gdef{\\foo}{x^2}\\foo+\\foo", "x^2+x^2");
compareParseTree("\\gdef\\foo{hi}\\foo+\\text{\\foo}", "hi+\\text{hi}");
compareParseTree("\\gdef\\foo#1{hi #1}\\text{\\foo{Alice}, \\foo{Bob}}",
"\\text{hi Alice, hi Bob}");
compareParseTree("\\gdef\\foo#1#2{(#1,#2)}\\foo 1 2+\\foo 3 4",
"(1,2)+(3,4)");
expect("\\gdef\\foo#2{}").toNotParse();
expect("\\gdef\\foo#1#3{}").toNotParse();
expect("\\gdef\\foo#1#2#3#4#5#6#7#8#9{}").toParse();
expect("\\gdef\\foo#1#2#3#4#5#6#7#8#9#10{}").toNotParse();
expect("\\gdef\\foo#{}").toNotParse();
expect("\\gdef\\foo\\bar").toParse();
expect("\\gdef{\\foo\\bar}{}").toNotParse();
expect("\\gdef{}{}").toNotParse();
});
// This may change in the future, if we support the extra features of
// \hspace.
it("should treat \\hspace, \\hskip like \\kern", function() {
@@ -2681,6 +2699,30 @@ describe("A macro expander", function() {
});
});
describe("\\tag support", function() {
const displayMode = new Settings({displayMode: true});
it("should fail outside display mode", () => {
expect("\\tag{hi}x+y").toNotParse();
});
it("should fail with multiple tags", () => {
expect("\\tag{1}\\tag{2}x+y").toNotParse(displayMode);
});
it("should build", () => {
expect("\\tag{hi}x+y").toBuild(displayMode);
});
it("should ignore location of \\tag", () => {
expect("\\tag{hi}x+y").toParseLike("x+y\\tag{hi}", displayMode);
});
it("should handle \\tag* like \\tag", () => {
expect("\\tag{hi}x+y").toParseLike("\\tag*{({hi})}x+y", displayMode);
});
});
describe("A parser taking String objects", function() {
it("should not fail on an empty String object", function() {
expect(new String("")).toParse();
@@ -2862,6 +2904,20 @@ describe("The maxSize setting", function() {
});
});
describe("The maxExpand setting", () => {
it("should prevent expansion", () => {
expect("\\gdef\\foo{1}\\foo").toParse();
expect("\\gdef\\foo{1}\\foo").toParse(new Settings({maxExpand: 2}));
expect("\\gdef\\foo{1}\\foo").toNotParse(new Settings({maxExpand: 1}));
expect("\\gdef\\foo{1}\\foo").toNotParse(new Settings({maxExpand: 0}));
});
it("should prevent infinite loops", () => {
expect("\\gdef\\foo{\\foo}\\foo").toNotParse(
new Settings({maxExpand: 10}));
});
});
describe("The \\mathchoice function", function() {
const cmd = "\\sum_{k = 0}^{\\infty} x^k";