commit 5722b45633b0cea02be00f765a5633ddd79ea4cc
parent 0236cdc619f0f3cbd5059cb6588b18669a0ebf5b
Author: Emily Eisenberg <xymostech@gmail.com>
Date: Sat, 27 Sep 2014 01:32:30 -0700
Merge pull request #101 from kevinb7/over
added support for \over
Diffstat:
3 files changed, 159 insertions(+), 5 deletions(-)
diff --git a/src/Parser.js b/src/Parser.js
@@ -120,22 +120,78 @@ Parser.prototype.parseInput = function(pos, mode) {
/**
* Handles a body of an expression.
*/
-Parser.prototype.handleExpressionBody = function(pos, mode) {
+Parser.prototype.handleExpressionBody = function(pos, mode, breakOnInfix) {
var body = [];
var atom;
// Keep adding atoms to the body until we can't parse any more atoms (either
// we reached the end, a }, or a \right)
while ((atom = this.parseAtom(pos, mode))) {
- body.push(atom.result);
- pos = atom.position;
+ if (breakOnInfix && atom.result.type === "infix") {
+ break;
+ } else {
+ body.push(atom.result);
+ pos = atom.position;
+ }
}
return {
- body: body,
+ body: this.handleInfixNodes(body, mode),
position: pos
};
};
/**
+ * Rewrites infix operators such as \over with corresponding commands such
+ * as \frac.
+ *
+ * There can only be one infix operator per group. If there's more than one
+ * then the expression is ambiguous. This can be resolved by adding {}.
+ *
+ * @returns {Array}
+ */
+Parser.prototype.handleInfixNodes = function (body, mode) {
+ var overIndex = -1;
+ var func;
+ var funcName;
+
+ for (var i = 0; i < body.length; i++) {
+ var node = body[i];
+ if (node.type === "infix") {
+ if (overIndex !== -1) {
+ throw new ParseError("only one infix operator per group",
+ this.lexer, -1);
+ }
+ overIndex = i;
+ funcName = node.value.replaceWith;
+ func = functions.funcs[funcName];
+ }
+ }
+
+ if (overIndex !== -1) {
+ var numerNode, denomNode;
+
+ var numerBody = body.slice(0, overIndex);
+ var denomBody = body.slice(overIndex + 1);
+
+ if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
+ numerNode = numerBody[0];
+ } else {
+ numerNode = new ParseNode("ordgroup", numerBody, mode);
+ }
+
+ if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
+ denomNode = denomBody[0];
+ } else {
+ denomNode = new ParseNode("ordgroup", denomBody, mode);
+ }
+
+ var value = func.handler(funcName, numerNode, denomNode);
+ return [new ParseNode(value.type, value, mode)];
+ } else {
+ return body;
+ }
+};
+
+/**
* Parses an "expression", which is a list of atoms.
*
* @return {ParseResult}
@@ -332,7 +388,7 @@ Parser.prototype.parseImplicitGroup = function(pos, mode) {
body.position);
} else if (utils.contains(styleFuncs, func)) {
// If we see a styling function, parse out the implict body
- var body = this.handleExpressionBody(start.result.position, mode);
+ var body = this.handleExpressionBody(start.result.position, mode, true);
return new ParseResult(
new ParseNode("styling", {
// Figure out what style to use by pulling out the style from
diff --git a/src/functions.js b/src/functions.js
@@ -150,6 +150,16 @@ var functions = {
type: "katex"
};
}
+ },
+
+ "\\over": {
+ numArgs: 0,
+ handler: function (func) {
+ return {
+ type: "infix",
+ replaceWith: "\\frac"
+ }
+ }
}
};
diff --git a/test/katex-spec.js b/test/katex-spec.js
@@ -474,6 +474,94 @@ describe("A frac parser", function() {
});
});
+describe("An over parser", function() {
+ var simpleOver = "1 \\over x";
+ var complexOver = "1+2i \\over 3+4i";
+
+ it("should not fail", function () {
+ expect(simpleOver).toParse();
+ expect(complexOver).toParse();
+ });
+
+ it("should produce a frac", function() {
+ var parse;
+
+ parse = parseTree(simpleOver)[0];
+
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer).toBeDefined();
+ expect(parse.value.denom).toBeDefined();
+
+ parse = parseTree(complexOver)[0];
+
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer).toBeDefined();
+ expect(parse.value.denom).toBeDefined();
+ });
+
+ it("should create a numerator from the atoms before \\over", function () {
+ var parse = parseTree(complexOver)[0];
+
+ var numer = parse.value.numer;
+ expect(numer.value.length).toEqual(4);
+ });
+
+ it("should create a demonimator from the atoms after \\over", function () {
+ var parse = parseTree(complexOver)[0];
+
+ var denom = parse.value.numer;
+ expect(denom.value.length).toEqual(4);
+ });
+
+ it("should handle empty numerators", function () {
+ var emptyNumerator = "\\over x";
+ expect(emptyNumerator).toParse();
+
+ var parse = parseTree(emptyNumerator)[0];
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer).toBeDefined();
+ expect(parse.value.denom).toBeDefined();
+ });
+
+ it("should handle empty denominators", function () {
+ var emptyDenominator = "1 \\over";
+ expect(emptyDenominator).toParse();
+
+ var parse = parseTree(emptyDenominator)[0];
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer).toBeDefined();
+ expect(parse.value.denom).toBeDefined();
+ });
+
+ it("should handle \\displaystyle correctly", function () {
+ var displaystyleExpression = "\\displaystyle 1 \\over 2";
+ expect(displaystyleExpression).toParse();
+
+ var parse = parseTree(displaystyleExpression)[0];
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer.value[0].type).toMatch("styling");
+ expect(parse.value.denom).toBeDefined();
+ });
+
+ it("should handle nested factions", function () {
+ var nestedOverExpression = "{1 \\over 2} \\over 3";
+ expect(nestedOverExpression).toParse();
+
+ var parse = parseTree(nestedOverExpression)[0];
+ expect(parse.type).toMatch("frac");
+ expect(parse.value.numer.value[0].type).toMatch("frac");
+ expect(parse.value.numer.value[0].value.numer.value[0].value).toMatch(1);
+ expect(parse.value.numer.value[0].value.denom.value[0].value).toMatch(2);
+ expect(parse.value.denom).toBeDefined();
+ expect(parse.value.denom.value[0].value).toMatch(3);
+ });
+
+ it("should fail with multiple overs in the same group", function () {
+ var badMultipleOvers = "1 \\over 2 + 3 \\over 4";
+ expect(badMultipleOvers).toNotParse();
+ });
+});
+
describe("A sizing parser", function() {
var sizeExpression = "\\Huge{x}\\small{x}";
var nestedSizeExpression = "\\Huge{\\small{x}}";