added support for \over

changed stopType (string) parameter to breakOnInfix (boolean)
renamed rewriteInfixNodes to handleInfixNodes

added a test for {1 \over 2} \over 3, fixed some grammar, and added code in the parser to squash superfluous ordgroups

removed squashOrdGroups and instead don't create an "ordgroup" if one already exists

removed unnecessary variable

moved variable declarations out of "if" statements
removed comment

Fixed style issue with where variables are declared and remove unnecessary comment from functions.js
This commit is contained in:
Kevin Barabash 2014-09-21 20:50:58 -06:00
parent d2f41a19a5
commit c566ae6888
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))) {
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.
*
@ -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}}";