
Summary: Make our own parser that doesn't use jison, so that we can handle funny TeX syntax, and to make it smaller. Test Plan: Make sure the tests pass with the new parser. Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D3029
149 lines
4.7 KiB
JavaScript
149 lines
4.7 KiB
JavaScript
var parseTree = require("./parseTree");
|
|
|
|
var buildExpression = function(expression) {
|
|
return _.map(expression, function(ex, i) {
|
|
var prev = i > 0 ? expression[i-1] : null;
|
|
|
|
return buildGroup(ex, prev);
|
|
});
|
|
};
|
|
|
|
var makeSpan = function(className, children) {
|
|
var span = document.createElement("span");
|
|
span.className = className || "";
|
|
|
|
if (children) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
span.appendChild(children[i]);
|
|
}
|
|
}
|
|
|
|
return span;
|
|
};
|
|
|
|
var buildGroup = function(group, prev) {
|
|
if (group.type === "mathord") {
|
|
return makeSpan("mord", [mathit(group.value)]);
|
|
} else if (group.type === "textord") {
|
|
return makeSpan("mord", [textit(group.value)]);
|
|
} else if (group.type === "bin") {
|
|
var className = "mbin";
|
|
if (prev == null || _.contains(["bin", "open", "rel"], prev.type)) {
|
|
group.type = "ord";
|
|
className = "mord";
|
|
}
|
|
return makeSpan(className, [textit(group.value)]);
|
|
} else if (group.type === "rel") {
|
|
return makeSpan("mrel", [textit(group.value)]);
|
|
} else if (group.type === "sup") {
|
|
var sup = makeSpan("msup", [buildGroup(group.value.sup)]);
|
|
return makeSpan("mord", [buildGroup(group.value.base), sup]);
|
|
} else if (group.type === "sub") {
|
|
var sub = makeSpan("msub", [buildGroup(group.value.sub)]);
|
|
return makeSpan("mord", [buildGroup(group.value.base), sub]);
|
|
} else if (group.type === "supsub") {
|
|
var sup = makeSpan("msup", [buildGroup(group.value.sup)]);
|
|
var sub = makeSpan("msub", [buildGroup(group.value.sub)]);
|
|
|
|
var supsub = makeSpan("msupsub", [sup, sub]);
|
|
|
|
return makeSpan("mord", [buildGroup(group.value.base), supsub]);
|
|
} else if (group.type === "open") {
|
|
return makeSpan("mopen", [textit(group.value)]);
|
|
} else if (group.type === "close") {
|
|
return makeSpan("mclose", [textit(group.value)]);
|
|
} else if (group.type === "dfrac") {
|
|
var numer = makeSpan("mfracnum", [makeSpan("", [buildGroup(group.value.numer)])]);
|
|
var mid = makeSpan("mfracmid", [makeSpan()]);
|
|
var denom = makeSpan("mfracden", [buildGroup(group.value.denom)]);
|
|
|
|
return makeSpan("minner mfrac", [numer, mid, denom]);
|
|
} else if (group.type === "color") {
|
|
return makeSpan("mord " + group.value.color, [buildGroup(group.value.value)]);
|
|
} else if (group.type === "spacing") {
|
|
if (group.value === "\\ " || group.value === "\\space") {
|
|
return makeSpan("mord mspace", [textit(group.value)]);
|
|
} else {
|
|
var spacingClassMap = {
|
|
"\\qquad": "qquad",
|
|
"\\quad": "quad",
|
|
"\\;": "thickspace",
|
|
"\\:": "mediumspace",
|
|
"\\,": "thinspace"
|
|
};
|
|
|
|
return makeSpan("mord mspace " + spacingClassMap[group.value]);
|
|
}
|
|
} else if (group.type === "llap") {
|
|
var inner = makeSpan("", buildExpression(group.value));
|
|
return makeSpan("llap", [inner]);
|
|
} else if (group.type === "rlap") {
|
|
var inner = makeSpan("", buildExpression(group.value));
|
|
return makeSpan("rlap", [inner]);
|
|
} else if (group.type === "punct") {
|
|
return makeSpan("mpunct", [textit(group.value)]);
|
|
} else if (group.type === "ordgroup") {
|
|
return makeSpan("mord", buildExpression(group.value));
|
|
} else if (group.type === "namedfn") {
|
|
return makeSpan("mop", [textit(group.value.slice(1))]);
|
|
} else {
|
|
throw "Lex error: Got group of unknown type: '" + group.type + "'";
|
|
}
|
|
};
|
|
|
|
var charLookup = {
|
|
"*": "\u2217",
|
|
"-": "\u2212",
|
|
"`": "\u2018",
|
|
"\\cdot": "\u22C5",
|
|
"\\lvert": "|",
|
|
"\\rvert": "|",
|
|
"\\pm": "\u00b1",
|
|
"\\div": "\u00f7",
|
|
"\\leq": "\u2264",
|
|
"\\geq": "\u2265",
|
|
"\\neq": "\u2260",
|
|
"\\nleq": "\u2270",
|
|
"\\ngeq": "\u2271",
|
|
"\\ ": "\u00a0",
|
|
"\\space": "\u00a0",
|
|
"\\colon": ":"
|
|
};
|
|
|
|
var textit = function(value) {
|
|
if (value in charLookup) {
|
|
value = charLookup[value];
|
|
}
|
|
return document.createTextNode(value);
|
|
};
|
|
|
|
var mathit = function(value) {
|
|
return makeSpan("mathit", [textit(value)]);
|
|
};
|
|
|
|
var clearNode = function(node) {
|
|
if ("textContent" in node) {
|
|
node.textContent = "";
|
|
} else {
|
|
node.innerText = "";
|
|
}
|
|
};
|
|
|
|
var process = function(toParse, baseElem) {
|
|
var tree = parseTree(toParse);
|
|
if (tree) {
|
|
clearNode(baseElem);
|
|
var expression = buildExpression(tree);
|
|
for (var i = 0; i < expression.length; i++) {
|
|
baseElem.appendChild(expression[i]);
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
process: process
|
|
};
|