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.
*/
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))) {
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.
*
@ -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

View File

@ -150,6 +150,16 @@ var functions = {
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() {
var sizeExpression = "\\Huge{x}\\small{x}";
var nestedSizeExpression = "\\Huge{\\small{x}}";