Merge pull request #101 from kevinb7/over

added support for \over
This commit is contained in:
Emily Eisenberg 2014-09-27 01:32:30 -07:00
commit 5722b45633
3 changed files with 159 additions and 5 deletions

View File

@ -120,21 +120,77 @@ Parser.prototype.parseInput = function(pos, mode) {
/** /**
* Handles a body of an expression. * Handles a body of an expression.
*/ */
Parser.prototype.handleExpressionBody = function(pos, mode) { Parser.prototype.handleExpressionBody = function(pos, mode, breakOnInfix) {
var body = []; var body = [];
var atom; var atom;
// Keep adding atoms to the body until we can't parse any more atoms (either // Keep adding atoms to the body until we can't parse any more atoms (either
// we reached the end, a }, or a \right) // we reached the end, a }, or a \right)
while ((atom = this.parseAtom(pos, mode))) { while ((atom = this.parseAtom(pos, mode))) {
if (breakOnInfix && atom.result.type === "infix") {
break;
} else {
body.push(atom.result); body.push(atom.result);
pos = atom.position; pos = atom.position;
} }
}
return { return {
body: body, body: this.handleInfixNodes(body, mode),
position: pos 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. * Parses an "expression", which is a list of atoms.
* *
@ -332,7 +388,7 @@ Parser.prototype.parseImplicitGroup = function(pos, mode) {
body.position); body.position);
} else if (utils.contains(styleFuncs, func)) { } else if (utils.contains(styleFuncs, func)) {
// If we see a styling function, parse out the implict body // 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( return new ParseResult(
new ParseNode("styling", { new ParseNode("styling", {
// Figure out what style to use by pulling out the style from // Figure out what style to use by pulling out the style from

View File

@ -150,6 +150,16 @@ var functions = {
type: "katex" type: "katex"
}; };
} }
},
"\\over": {
numArgs: 0,
handler: function (func) {
return {
type: "infix",
replaceWith: "\\frac"
}
}
} }
}; };

View File

@ -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() { describe("A sizing parser", function() {
var sizeExpression = "\\Huge{x}\\small{x}"; var sizeExpression = "\\Huge{x}\\small{x}";
var nestedSizeExpression = "\\Huge{\\small{x}}"; var nestedSizeExpression = "\\Huge{\\small{x}}";