From c08fadfaa93b22016c05cdb3905bd4973e91339b Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Tue, 16 Jul 2013 22:00:54 -0700 Subject: [PATCH] Parse single superscripts and subscripts Summary: Add the ability to parse lone `^x` and `_y`, etc. This basically just involves more checking of cases in the increasingly ugly `parseAtom` function. Also, now we manually check for the cases of double superscripts and subscripts. Test Plan: Make sure the tests pass. Make sure things like `^x` and `_y` parse. Reviewers: emily Reviewed By: emily Differential Revision: http://phabricator.khanacademy.org/D3095 --- Parser.js | 77 ++++++++++++++++++++++++++------------------- katex.js | 4 +++ test/katex-tests.js | 38 ++++++++++++++++++++-- 3 files changed, 84 insertions(+), 35 deletions(-) diff --git a/Parser.js b/Parser.js index a971f8ea2..6361e66aa 100644 --- a/Parser.js +++ b/Parser.js @@ -110,42 +110,55 @@ Parser.prototype.parseSubscript = function(pos) { Parser.prototype.parseAtom = function(pos) { // Parse the nucleus var nucleus = this.parseGroup(pos); + var nextPos = pos; + var nucleusNode; + if (nucleus) { - // Now, we try to parse a subscript or a superscript. If one of those - // succeeds, we then try to parse the opposite one, and depending on - // whether that succeeds, we return the correct type. - var sup, sub; - if (sup = this.parseSuperscript(nucleus.position)) { - if (sub = this.parseSubscript(sup.position)) { - return new ParseResult( - new ParseNode("supsub", - {base: nucleus.result, sup: sup.result, - sub: sub.result}), - sub.position); - } else { - return new ParseResult( - new ParseNode("sup", - {base: nucleus.result, sup: sup.result}), - sup.position); + nextPos = nucleus.position; + nucleusNode = nucleus.result; + } + + var sup; + var sub; + + // Now, we try to parse a subscript or a superscript (or both!), and + // depending on whether those succeed, we return the correct type. + while (true) { + var node; + if ((node = this.parseSuperscript(nextPos))) { + if (sup) { + throw "Parse error: Double superscript"; } - } else if (sub = this.parseSubscript(nucleus.position)) { - if (sup = this.parseSuperscript(sub.position)) { - return new ParseResult( - new ParseNode("supsub", - {base: nucleus.result, sup: sup.result, - sub: sub.result}), - sup.position); - } else { - return new ParseResult( - new ParseNode("sub", - {base: nucleus.result, sub: sub.result}), - sub.position); - } - } else { - return nucleus; + nextPos = node.position; + sup = node.result; + continue; } + if ((node = this.parseSubscript(nextPos))) { + if (sub) { + throw "Parse error: Double subscript"; + } + nextPos = node.position; + sub = node.result; + continue; + } + break; + } + + if (sup && sub) { + return new ParseResult( + new ParseNode("supsub", {base: nucleusNode, sup: sup, + sub: sub}), + nextPos); + } else if (sup) { + return new ParseResult( + new ParseNode("sup", {base: nucleusNode, sup: sup}), + nextPos); + } else if (sub) { + return new ParseResult( + new ParseNode("sub", {base: nucleusNode, sub: sub}), + nextPos); } else { - return null; + return nucleus; } } diff --git a/katex.js b/katex.js index 48f54c2fc..c42c50104 100644 --- a/katex.js +++ b/katex.js @@ -27,6 +27,10 @@ var makeSpan = function(className, children) { }; var buildGroup = function(style, color, group, prev) { + if (!group) { + return makeSpan(); + } + if (group.type === "mathord") { return makeSpan("mord" + color, [mathit(group.value)]); } else if (group.type === "textord") { diff --git a/test/katex-tests.js b/test/katex-tests.js index 213808745..f49691635 100644 --- a/test/katex-tests.js +++ b/test/katex-tests.js @@ -164,6 +164,24 @@ describe("A subscript and superscript parser", function() { }).not.toThrow(); }); + it("should not fail when there is no nucleus", function() { + expect(function() { + parseTree("^3"); + }).not.toThrow(); + + expect(function() { + parseTree("_2"); + }).not.toThrow(); + + expect(function() { + parseTree("^3_2"); + }).not.toThrow(); + + expect(function() { + parseTree("_2^3"); + }).not.toThrow(); + }); + it("should produce sups for superscript", function() { var parse = parseTree("x^2")[0]; @@ -207,16 +225,30 @@ describe("A subscript and superscript parser", function() { expect(parseA).toEqual(parseB); }); - it("should not parse x^x^x", function() { + it("should not parse double subscripts or superscripts", function() { expect(function() { parseTree("x^x^x"); }).toThrow(); - }); - it("should not parse x_x_x", function() { expect(function() { parseTree("x_x_x"); }).toThrow(); + + expect(function() { + parseTree("x_x^x_x"); + }).toThrow(); + + expect(function() { + parseTree("x_x^x^x"); + }).toThrow(); + + expect(function() { + parseTree("x^x_x_x"); + }).toThrow(); + + expect(function() { + parseTree("x^x_x^x"); + }).toThrow(); }); it("should work correctly with {}s", function() {