From 530ec97e74769422b313a872920df7ec438db845 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Thu, 8 Dec 2016 14:47:20 -0500 Subject: [PATCH] Allow unbraced kerns, such as \kern1em. This is actually the *only* syntax TeX allows; braced kern units are invalid. --- src/Parser.js | 39 +++++++++++++++++++++++++++++++++++++-- test/errors-spec.js | 2 +- test/katex-spec.js | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/Parser.js b/src/Parser.js index 97bbbf3..18a4c60 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -661,6 +661,35 @@ Parser.prototype.parseStringGroup = function(modeName, optional) { return firstToken.range(lastToken, str); }; +/** + * Parses a regex-delimited group: the largest sequence of tokens + * whose concatenated strings match `regex`. Returns the string + * formed by the tokens plus some position information. + * + * @param {RegExp} regex + * @param {string} modeName Used to describe the mode in error messages + */ +Parser.prototype.parseRegexGroup = function(regex, modeName) { + var outerMode = this.mode; + this.mode = "text"; + var firstToken = this.nextToken; + var lastToken = firstToken; + var str = ""; + while (this.nextToken.text !== "EOF" + && regex.test(str + this.nextToken.text)) { + lastToken = this.nextToken; + str += lastToken.text; + this.consume(); + } + if (str === "") { + throw new ParseError( + "Invalid " + modeName + ": '" + firstToken.text + "'", + firstToken); + } + this.mode = outerMode; + return firstToken.range(lastToken, str); +}; + /** * Parses a color description. */ @@ -682,11 +711,17 @@ Parser.prototype.parseColorGroup = function(optional) { * Parses a size specification, consisting of magnitude and unit. */ Parser.prototype.parseSizeGroup = function(optional) { - var res = this.parseStringGroup("size", optional); + var res; + if (!optional && this.nextToken.text !== "{") { + res = this.parseRegexGroup( + /^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2}$/, "size"); + } else { + res = this.parseStringGroup("size", optional); + } if (!res) { return null; } - var match = (/(-?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(res.text); + var match = (/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(res.text); if (!match) { throw new ParseError("Invalid size: '" + res.text + "'", res); } diff --git a/test/errors-spec.js b/test/errors-spec.js index e20f370..02f6b24 100644 --- a/test/errors-spec.js +++ b/test/errors-spec.js @@ -220,7 +220,7 @@ describe("Parser.expect calls:", function() { }); it("complains about missing { for size", function() { expect("\\rule{1em}[2em]").toFailWithParseError( - "Expected '{', got '[' at position 11: \\rule{1em}[̲2em]"); + "Invalid size: '[' at position 11: \\rule{1em}[̲2em]"); }); // Can't test for the [ of an optional group since it's optional it("complains about missing } for color", function() { diff --git a/test/katex-spec.js b/test/katex-spec.js index 2018b91..b4b5eee 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -997,6 +997,41 @@ describe("A kern parser", function() { var parse = getParsed("\\kern{-1em}")[0]; expect(parse.value.dimension.number).toBeCloseTo(-1); }); + + it("should parse positive sizes", function() { + var parse = getParsed("\\kern{+1em}")[0]; + expect(parse.value.dimension.number).toBeCloseTo(1); + }); +}); + +describe("A non-braced kern parser", function() { + var emKern = "\\kern1em"; + var exKern = "\\kern 1 ex"; + var badUnitRule = "\\kern1px"; + var noNumberRule = "\\kern em"; + + it("should list the correct units", function() { + var emParse = getParsed(emKern)[0]; + var exParse = getParsed(exKern)[0]; + + expect(emParse.value.dimension.unit).toEqual("em"); + expect(exParse.value.dimension.unit).toEqual("ex"); + }); + + it("should not parse invalid units", function() { + expect(badUnitRule).toNotParse(); + expect(noNumberRule).toNotParse(); + }); + + it("should parse negative sizes", function() { + var parse = getParsed("\\kern-1em")[0]; + expect(parse.value.dimension.number).toBeCloseTo(-1); + }); + + it("should parse positive sizes", function() { + var parse = getParsed("\\kern+1em")[0]; + expect(parse.value.dimension.number).toBeCloseTo(1); + }); }); describe("A left/right parser", function() {