From 7723d3dcaf3604040b91ed3805de09cb96a9942c Mon Sep 17 00:00:00 2001 From: Emily Eisenberg Date: Wed, 26 Mar 2014 22:17:41 -0400 Subject: [PATCH] First attempt at `\text` function Summary: Make all of the parsing functions keep track of whether they are parsing in math mode or text mode. Then, add a separate lexing function to lex text mode, which is different than the normal mode because it does weird things with spacing and allows a different set of characters. Test Plan: - See that the normal tests work - See that the huxley screenshot looks reasonable - See that none of the other huxley screenshots changed Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D7578 --- Lexer.js | 34 +- Parser.js | 117 ++- buildTree.js | 82 +- symbols.js | 1123 ++++++++++++----------- test/huxley/Huxleyfile | 5 +- test/huxley/Text.huxley/record.json | 1 + test/huxley/Text.huxley/screenshot0.png | Bin 0 -> 12747 bytes test/katex-tests.js | 49 + 8 files changed, 785 insertions(+), 626 deletions(-) create mode 100644 test/huxley/Text.huxley/record.json create mode 100644 test/huxley/Text.huxley/screenshot0.png diff --git a/Lexer.js b/Lexer.js index e06171d9..37ec056c 100644 --- a/Lexer.js +++ b/Lexer.js @@ -13,7 +13,7 @@ function LexResult(type, text, position) { } // "normal" types of tokens -var normals = [ +var mathNormals = [ [/^[/|@."`0-9]/, "textord"], [/^[a-zA-Z]/, "mathord"], [/^[*+-]/, "bin"], @@ -28,17 +28,30 @@ var normals = [ [/^[)\]?!]/, "close"] ]; +var textNormals = [ + [/^[a-zA-Z0-9`!@*()-=+\[\]'";:?\/.,]/, "textord"], + [/^{/, "{"], + [/^}/, "}"] +]; + // Build a regex to easily parse the functions var anyFunc = /^\\(?:[a-zA-Z]+|.)/; -// Lex a single token -Lexer.prototype.lex = function(pos) { +Lexer.prototype._innerLex = function(pos, normals, ignoreWhitespace) { var input = this._input.slice(pos); // Get rid of whitespace - var whitespace = input.match(/^\s*/)[0]; - pos += whitespace.length; - input = input.slice(whitespace.length); + if (ignoreWhitespace) { + var whitespace = input.match(/^\s*/)[0]; + pos += whitespace.length; + input = input.slice(whitespace.length); + } else { + // Do the funky concatenation of whitespace + var whitespace = input.match(/^( +|\\ +)/); + if (whitespace !== null) { + return new LexResult(" ", " ", pos + whitespace[0].length); + } + } // If there's no more input to parse, return an EOF token if (input.length === 0) { @@ -66,6 +79,15 @@ Lexer.prototype.lex = function(pos) { // We didn't match any of the tokens, so throw an error. throw new ParseError("Unexpected character: '" + input[0] + "' at position " + pos); +} + +// Lex a single token +Lexer.prototype.lex = function(pos, mode) { + if (mode === "math") { + return this._innerLex(pos, mathNormals, true); + } else if (mode === "text") { + return this._innerLex(pos, textNormals, false); + } }; module.exports = Lexer; diff --git a/Parser.js b/Parser.js index 28eb3865..0db2e145 100644 --- a/Parser.js +++ b/Parser.js @@ -16,9 +16,10 @@ function ParseResult(result, newPosition) { } // The resulting parse tree nodes of the parse tree. -function ParseNode(type, value) { +function ParseNode(type, value, mode) { this.type = type; this.value = value; + this.mode = mode; } // Checks a result to make sure it has the right type, and throws an @@ -37,27 +38,27 @@ Parser.prototype.parse = function(input) { this.lexer = new Lexer(input); // Try to parse the input - var parse = this.parseInput(0); + var parse = this.parseInput(0, "math"); return parse.result; }; // Parses an entire input tree -Parser.prototype.parseInput = function(pos) { +Parser.prototype.parseInput = function(pos, mode) { // Parse an expression - var expression = this.parseExpression(pos); + var expression = this.parseExpression(pos, mode); // If we succeeded, make sure there's an EOF at the end - var EOF = this.lexer.lex(expression.position); + var EOF = this.lexer.lex(expression.position, mode); expect(EOF, "EOF"); return expression; }; // Parses an "expression", which is a list of atoms -Parser.prototype.parseExpression = function(pos) { +Parser.prototype.parseExpression = function(pos, mode) { // Start with a list of nodes var expression = []; while (true) { // Try to parse atoms - var parse = this.parseAtom(pos); + var parse = this.parseAtom(pos, mode); if (parse) { // Copy them into the list expression.push(parse.result); @@ -70,12 +71,16 @@ Parser.prototype.parseExpression = function(pos) { }; // Parses a superscript expression, like "^3" -Parser.prototype.parseSuperscript = function(pos) { +Parser.prototype.parseSuperscript = function(pos, mode) { + if (mode !== "math") { + throw new ParseError("Trying to parse superscript in non-math mode"); + } + // Try to parse a "^" character - var sup = this.lexer.lex(pos); + var sup = this.lexer.lex(pos, mode); if (sup.type === "^") { // If we got one, parse the corresponding group - var group = this.parseGroup(sup.position); + var group = this.parseGroup(sup.position, mode); if (group) { return group; } else { @@ -85,19 +90,23 @@ Parser.prototype.parseSuperscript = function(pos) { } else if (sup.type === "'") { var pos = sup.position; return new ParseResult( - new ParseNode("textord", "\\prime"), sup.position); + new ParseNode("textord", "\\prime"), sup.position, mode); } else { return null; } }; // Parses a subscript expression, like "_3" -Parser.prototype.parseSubscript = function(pos) { +Parser.prototype.parseSubscript = function(pos, mode) { + if (mode !== "math") { + throw new ParseError("Trying to parse subscript in non-math mode"); + } + // Try to parse a "_" character - var sub = this.lexer.lex(pos); + var sub = this.lexer.lex(pos, mode); if (sub.type === "_") { // If we got one, parse the corresponding group - var group = this.parseGroup(sub.position); + var group = this.parseGroup(sub.position, mode); if (group) { return group; } else { @@ -111,12 +120,18 @@ Parser.prototype.parseSubscript = function(pos) { // Parses an atom, which consists of a nucleus, and an optional superscript and // subscript -Parser.prototype.parseAtom = function(pos) { +Parser.prototype.parseAtom = function(pos, mode) { // Parse the nucleus - var nucleus = this.parseGroup(pos); + var nucleus = this.parseGroup(pos, mode); var nextPos = pos; var nucleusNode; + // Text mode doesn't have superscripts or subscripts, so we only parse the + // nucleus in this case + if (mode === "text") { + return nucleus; + } + if (nucleus) { nextPos = nucleus.position; nucleusNode = nucleus.result; @@ -129,7 +144,7 @@ Parser.prototype.parseAtom = function(pos) { // depending on whether those succeed, we return the correct type. while (true) { var node; - if ((node = this.parseSuperscript(nextPos))) { + if ((node = this.parseSuperscript(nextPos, mode))) { if (sup) { throw new ParseError("Parse error: Double superscript"); } @@ -137,7 +152,7 @@ Parser.prototype.parseAtom = function(pos) { sup = node.result; continue; } - if ((node = this.parseSubscript(nextPos))) { + if ((node = this.parseSubscript(nextPos, mode))) { if (sub) { throw new ParseError("Parse error: Double subscript"); } @@ -151,7 +166,7 @@ Parser.prototype.parseAtom = function(pos) { if (sup || sub) { return new ParseResult( new ParseNode("supsub", {base: nucleusNode, sup: sup, - sub: sub}), + sub: sub}, mode), nextPos); } else { return nucleus; @@ -160,25 +175,24 @@ Parser.prototype.parseAtom = function(pos) { // Parses a group, which is either a single nucleus (like "x") or an expression // in braces (like "{x+y}") -Parser.prototype.parseGroup = function(pos) { - var start = this.lexer.lex(pos); +Parser.prototype.parseGroup = function(pos, mode) { + var start = this.lexer.lex(pos, mode); // Try to parse an open brace if (start.type === "{") { // If we get a brace, parse an expression - var expression = this.parseExpression(start.position); + var expression = this.parseExpression(start.position, mode); // Make sure we get a close brace - var closeBrace = this.lexer.lex(expression.position); + var closeBrace = this.lexer.lex(expression.position, mode); expect(closeBrace, "}"); return new ParseResult( - new ParseNode("ordgroup", expression.result), + new ParseNode("ordgroup", expression.result, mode), closeBrace.position); } else { // Otherwise, just return a nucleus - return this.parseNucleus(pos); + return this.parseNucleus(pos, mode); } }; - // A list of 1-argument color functions var colorFuncs = [ "\\blue", "\\orange", "\\pink", "\\red", "\\green", "\\gray", "\\purple" @@ -200,12 +214,12 @@ var namedFns = [ // Parses a "nucleus", which is either a single token from the tokenizer or a // function and its arguments -Parser.prototype.parseNucleus = function(pos) { - var nucleus = this.lexer.lex(pos); +Parser.prototype.parseNucleus = function(pos, mode) { + var nucleus = this.lexer.lex(pos, mode); if (utils.contains(colorFuncs, nucleus.type)) { // If this is a color function, parse its argument and return - var group = this.parseGroup(nucleus.position); + var group = this.parseGroup(nucleus.position, mode); if (group) { var atoms; if (group.result.type === "ordgroup") { @@ -215,55 +229,66 @@ Parser.prototype.parseNucleus = function(pos) { } return new ParseResult( new ParseNode("color", - {color: nucleus.type.slice(1), value: atoms}), + {color: nucleus.type.slice(1), value: atoms}, mode), group.position); } else { throw new ParseError( "Expected group after '" + nucleus.text + "'"); } - } else if (utils.contains(sizeFuncs, nucleus.type)) { + } else if (mode === "math" && utils.contains(sizeFuncs, nucleus.type)) { // If this is a size function, parse its argument and return - var group = this.parseGroup(nucleus.position); + var group = this.parseGroup(nucleus.position, mode); if (group) { return new ParseResult( new ParseNode("sizing", { size: "size" + (utils.indexOf(sizeFuncs, nucleus.type) + 1), value: group.result - }), + }, mode), group.position); } else { throw new ParseError( "Expected group after '" + nucleus.text + "'"); } - } else if (utils.contains(namedFns, nucleus.type)) { + } else if (mode === "math" && utils.contains(namedFns, nucleus.type)) { // If this is a named function, just return it plain return new ParseResult( - new ParseNode("namedfn", nucleus.text), + new ParseNode("namedfn", nucleus.text, mode), nucleus.position); } else if (nucleus.type === "\\llap" || nucleus.type === "\\rlap") { // If this is an llap or rlap, parse its argument and return - var group = this.parseGroup(nucleus.position); + var group = this.parseGroup(nucleus.position, mode); if (group) { return new ParseResult( - new ParseNode(nucleus.type.slice(1), group.result), + new ParseNode(nucleus.type.slice(1), group.result, mode), group.position); } else { throw new ParseError( "Expected group after '" + nucleus.text + "'"); } - } else if (nucleus.type === "\\dfrac" || nucleus.type === "\\frac" || - nucleus.type === "\\tfrac") { + } else if (mode === "math" && nucleus.type === "\\text") { + var group = this.parseGroup(nucleus.position, "text"); + if (group) { + return new ParseResult( + new ParseNode(nucleus.type.slice(1), group.result, mode), + group.position); + } else { + throw new ParseError( + "Expected group after '" + nucleus.text + "'"); + } + } else if (mode === "math" && (nucleus.type === "\\dfrac" || + nucleus.type === "\\frac" || + nucleus.type === "\\tfrac")) { // If this is a frac, parse its two arguments and return - var numer = this.parseGroup(nucleus.position); + var numer = this.parseGroup(nucleus.position, mode); if (numer) { - var denom = this.parseGroup(numer.position); + var denom = this.parseGroup(numer.position, mode); if (denom) { return new ParseResult( new ParseNode("frac", { numer: numer.result, denom: denom.result, size: nucleus.type.slice(1) - }), + }, mode), denom.position); } else { throw new ParseError("Expected denominator after '" + @@ -273,17 +298,17 @@ Parser.prototype.parseNucleus = function(pos) { throw new ParseError("Parse error: Expected numerator after '" + nucleus.type + "'"); } - } else if (nucleus.type === "\\KaTeX") { + } else if (mode === "math" && nucleus.type === "\\KaTeX") { // If this is a KaTeX node, return the special katex result return new ParseResult( - new ParseNode("katex", null), + new ParseNode("katex", null, mode), nucleus.position ); - } else if (symbols[nucleus.text]) { + } else if (symbols[mode][nucleus.text]) { // Otherwise if this is a no-argument function, find the type it // corresponds to in the symbols map return new ParseResult( - new ParseNode(symbols[nucleus.text].group, nucleus.text), + new ParseNode(symbols[mode][nucleus.text].group, nucleus.text, mode), nucleus.position); } else { // Otherwise, we couldn't parse it diff --git a/buildTree.js b/buildTree.js index 4bb0608b..9c6e9ad3 100644 --- a/buildTree.js +++ b/buildTree.js @@ -50,6 +50,7 @@ var groupToType = { ordgroup: "mord", namedfn: "mop", katex: "mord", + text: "mord", }; var getTypeOfGroup = function(group) { @@ -69,11 +70,17 @@ var getTypeOfGroup = function(group) { var groupTypes = { mathord: function(group, options, prev) { - return makeSpan(["mord", options.color], [mathit(group.value)]); + return makeSpan( + ["mord", options.color], + [mathit(group.value, group.mode)] + ); }, textord: function(group, options, prev) { - return makeSpan(["mord", options.color], [mathrm(group.value)]); + return makeSpan( + ["mord", options.color], + [mathrm(group.value, group.mode)] + ); }, bin: function(group, options, prev) { @@ -88,15 +95,23 @@ var groupTypes = { group.type = "ord"; className = "mord"; } - return makeSpan([className, options.color], [mathrm(group.value)]); + return makeSpan( + [className, options.color], + [mathrm(group.value, group.mode)] + ); }, rel: function(group, options, prev) { - return makeSpan(["mrel", options.color], [mathrm(group.value)]); + return makeSpan( + ["mrel", options.color], + [mathrm(group.value, group.mode)] + ); }, - amsrel: function(group, options, prev) { - return makeSpan(["mrel", options.color], [amsrm(group.value)]); + text: function(group, options, prev) { + return makeSpan(["text mord", options.style.cls()], + [buildGroup(group.value, options.reset())] + ); }, supsub: function(group, options, prev) { @@ -185,11 +200,17 @@ var groupTypes = { }, open: function(group, options, prev) { - return makeSpan(["mopen", options.color], [mathrm(group.value)]); + return makeSpan( + ["mopen", options.color], + [mathrm(group.value, group.mode)] + ); }, close: function(group, options, prev) { - return makeSpan(["mclose", options.color], [mathrm(group.value)]); + return makeSpan( + ["mclose", options.color], + [mathrm(group.value, group.mode)] + ); }, frac: function(group, options, prev) { @@ -283,8 +304,14 @@ var groupTypes = { }, spacing: function(group, options, prev) { - if (group.value === "\\ " || group.value === "\\space") { - return makeSpan(["mord", "mspace"], [mathrm(group.value)]); + if (group.value === "\\ " || group.value === "\\space" || + group.value === " ") { + return makeSpan( + ["mord", "mspace"], + [mathrm(group.value, group.mode)] + ); + } else if(group.value === "~") { + return makeSpan(["mord", "mspace"], [mathrm(" ", group.mode)]); } else { var spacingClassMap = { "\\qquad": "qquad", @@ -311,7 +338,10 @@ var groupTypes = { }, punct: function(group, options, prev) { - return makeSpan(["mpunct", options.color], [mathrm(group.value)]); + return makeSpan( + ["mpunct", options.color], + [mathrm(group.value, group.mode)] + ); }, ordgroup: function(group, options, prev) { @@ -323,26 +353,26 @@ var groupTypes = { namedfn: function(group, options, prev) { var chars = []; for (var i = 1; i < group.value.length; i++) { - chars.push(mathrm(group.value[i])); + chars.push(mathrm(group.value[i], group.mode)); } return makeSpan(["mop", options.color], chars); }, katex: function(group, options, prev) { - var k = makeSpan(["k"], [mathrm("K")]); - var a = makeSpan(["a"], [mathrm("A")]); + var k = makeSpan(["k"], [mathrm("K", group.mode)]); + var a = makeSpan(["a"], [mathrm("A", group.mode)]); a.height = (a.height + 0.2) * 0.75; a.depth = (a.height - 0.2) * 0.75; - var t = makeSpan(["t"], [mathrm("T")]); - var e = makeSpan(["e"], [mathrm("E")]); + var t = makeSpan(["t"], [mathrm("T", group.mode)]); + var e = makeSpan(["e"], [mathrm("E", group.mode)]); e.height = (e.height - 0.2155); e.depth = (e.depth + 0.2155); - var x = makeSpan(["x"], [mathrm("X")]); + var x = makeSpan(["x"], [mathrm("X", group.mode)]); return makeSpan(["katex-logo", options.color], [k, a, t, e, x]); }, @@ -407,9 +437,9 @@ var buildGroup = function(group, options, prev) { } }; -var makeText = function(value, style) { - if (symbols[value].replace) { - value = symbols[value].replace; +var makeText = function(value, style, mode) { + if (symbols[mode][value].replace) { + value = symbols[mode][value].replace; } var metrics = fontMetrics.getCharacterMetrics(value, style); @@ -432,15 +462,15 @@ var makeText = function(value, style) { } }; -var mathit = function(value) { - return makeSpan(["mathit"], [makeText(value, "math-italic")]); +var mathit = function(value, mode) { + return makeSpan(["mathit"], [makeText(value, "math-italic", mode)]); }; -var mathrm = function(value) { - if (symbols[value].font === "main") { - return makeText(value, "main-regular"); +var mathrm = function(value, mode) { + if (symbols[mode][value].font === "main") { + return makeText(value, "main-regular", mode); } else { - return makeSpan(["amsrm"], [makeText(value, "ams-regular")]); + return makeSpan(["amsrm"], [makeText(value, "ams-regular", mode)]); } }; diff --git a/symbols.js b/symbols.js index 0f0d28d1..d4294f32 100644 --- a/symbols.js +++ b/symbols.js @@ -8,567 +8,596 @@ * - replace (optiona): the character that this symbol or function should be * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi * character in the main font) + * There outermost map in the table indicates what mode the symbols should be + * accepted in (e.g. "math" or "text") */ var symbols = { - "`": { - font: "main", - group: "textord", - replace: "\u2018" - }, - "\\$": { - font: "main", - group: "textord", - replace: "$" - }, - "\\%": { - font: "main", - group: "textord", - replace: "%" - }, - "\\angle": { - font: "main", - group: "textord", - replace: "\u2220" - }, - "\\infty": { - font: "main", - group: "textord", - replace: "\u221e" - }, - "\\prime": { - font: "main", - group: "textord", - replace: "\u2032" - }, - "\\triangle": { - font: "main", - group: "textord", - replace: "\u25b3" - }, - "\\Gamma": { - font: "main", - group: "textord", - replace: "\u0393" - }, - "\\Delta": { - font: "main", - group: "textord", - replace: "\u0394" - }, - "\\Theta": { - font: "main", - group: "textord", - replace: "\u0398" - }, - "\\Lambda": { - font: "main", - group: "textord", - replace: "\u039b" - }, - "\\Xi": { - font: "main", - group: "textord", - replace: "\u039e" - }, - "\\Pi": { - font: "main", - group: "textord", - replace: "\u03a0" - }, - "\\Sigma": { - font: "main", - group: "textord", - replace: "\u03a3" - }, - "\\Upsilon": { - font: "main", - group: "textord", - replace: "\u03a5" - }, - "\\Phi": { - font: "main", - group: "textord", - replace: "\u03a6" - }, - "\\Psi": { - font: "main", - group: "textord", - replace: "\u03a8" - }, - "\\Omega": { - font: "main", - group: "textord", - replace: "\u03a9" - }, - "\\alpha": { - font: "main", - group: "mathord", - replace: "\u03b1" - }, - "\\beta": { - font: "main", - group: "mathord", - replace: "\u03b2" - }, - "\\gamma": { - font: "main", - group: "mathord", - replace: "\u03b3" - }, - "\\delta": { - font: "main", - group: "mathord", - replace: "\u03b4" - }, - "\\epsilon": { - font: "main", - group: "mathord", - replace: "\u03f5" - }, - "\\zeta": { - font: "main", - group: "mathord", - replace: "\u03b6" - }, - "\\eta": { - font: "main", - group: "mathord", - replace: "\u03b7" - }, - "\\theta": { - font: "main", - group: "mathord", - replace: "\u03b8" - }, - "\\iota": { - font: "main", - group: "mathord", - replace: "\u03b9" - }, - "\\kappa": { - font: "main", - group: "mathord", - replace: "\u03ba" - }, - "\\lambda": { - font: "main", - group: "mathord", - replace: "\u03bb" - }, - "\\mu": { - font: "main", - group: "mathord", - replace: "\u03bc" - }, - "\\nu": { - font: "main", - group: "mathord", - replace: "\u03bd" - }, - "\\xi": { - font: "main", - group: "mathord", - replace: "\u03be" - }, - "\\omicron": { - font: "main", - group: "mathord", - replace: "o" - }, - "\\pi": { - font: "main", - group: "mathord", - replace: "\u03c0" - }, - "\\rho": { - font: "main", - group: "mathord", - replace: "\u03c1" - }, - "\\sigma": { - font: "main", - group: "mathord", - replace: "\u03c3" - }, - "\\tau": { - font: "main", - group: "mathord", - replace: "\u03c4" - }, - "\\upsilon": { - font: "main", - group: "mathord", - replace: "\u03c5" - }, - "\\phi": { - font: "main", - group: "mathord", - replace: "\u03d5" - }, - "\\chi": { - font: "main", - group: "mathord", - replace: "\u03c7" - }, - "\\psi": { - font: "main", - group: "mathord", - replace: "\u03c8" - }, - "\\omega": { - font: "main", - group: "mathord", - replace: "\u03c9" - }, - "\\varepsilon": { - font: "main", - group: "mathord", - replace: "\u03b5" - }, - "\\vartheta": { - font: "main", - group: "mathord", - replace: "\u03d1" - }, - "\\varpi": { - font: "main", - group: "mathord", - replace: "\u03d6" - }, - "\\varrho": { - font: "main", - group: "mathord", - replace: "\u03f1" - }, - "\\varsigma": { - font: "main", - group: "mathord", - replace: "\u03c2" - }, - "\\varphi": { - font: "main", - group: "mathord", - replace: "\u03c6" - }, - "*": { - font: "main", - group: "bin", - replace: "\u2217" - }, - "+": { - font: "main", - group: "bin" - }, - "-": { - font: "main", - group: "bin", - replace: "\u2212" - }, - "\\cdot": { - font: "main", - group: "bin", - replace: "\u22c5" - }, - "\\circ": { - font: "main", - group: "bin", - replace: "\u2218" - }, - "\\div": { - font: "main", - group: "bin", - replace: "\u00f7" - }, - "\\pm": { - font: "main", - group: "bin", - replace: "\u00b1" - }, - "\\times": { - font: "main", - group: "bin", - replace: "\u00d7" - }, - "(": { - font: "main", - group: "open" - }, - "[": { - font: "main", - group: "open" - }, - "\\langle": { - font: "main", - group: "open", - replace: "\u27e8" - }, - "\\lvert": { - font: "main", - group: "open", - replace: "|" - }, - ")": { - font: "main", - group: "close" - }, - "]": { - font: "main", - group: "close" - }, - "?": { - font: "main", - group: "close" - }, - "!": { - font: "main", - group: "close" - }, - "\\rangle": { - font: "main", - group: "close", - replace: "\u27e9" - }, - "\\rvert": { - font: "main", - group: "close", - replace: "|" - }, - "=": { - font: "main", - group: "rel" - }, - "<": { - font: "main", - group: "rel" - }, - ">": { - font: "main", - group: "rel" - }, - ":": { - font: "main", - group: "rel" - }, - "\\approx": { - font: "main", - group: "rel", - replace: "\u2248" - }, - "\\cong": { - font: "main", - group: "rel", - replace: "\u2245" - }, - "\\ge": { - font: "main", - group: "rel", - replace: "\u2265" - }, - "\\geq": { - font: "main", - group: "rel", - replace: "\u2265" - }, - "\\gets": { - font: "main", - group: "rel", - replace: "\u2190" - }, - "\\in": { - font: "main", - group: "rel", - replace: "\u2208" - }, - "\\leftarrow": { - font: "main", - group: "rel", - replace: "\u2190" - }, - "\\le": { - font: "main", - group: "rel", - replace: "\u2264" - }, - "\\leq": { - font: "main", - group: "rel", - replace: "\u2264" - }, - "\\ne": { - font: "main", - group: "rel", - replace: "\u2260" - }, - "\\neq": { - font: "main", - group: "rel", - replace: "\u2260" - }, - "\\rightarrow": { - font: "main", - group: "rel", - replace: "\u2192" - }, - "\\to": { - font: "main", - group: "rel", - replace: "\u2192" - }, - "\\ngeq": { - font: "ams", - group: "rel", - replace: "\u2271" - }, - "\\nleq": { - font: "ams", - group: "rel", - replace: "\u2270" - }, - "\\!": { - font: "main", - group: "spacing" - }, - "\\ ": { - font: "main", - group: "spacing", - replace: "\u00a0" - }, - "\\,": { - font: "main", - group: "spacing" - }, - "\\:": { - font: "main", - group: "spacing" - }, - "\\;": { - font: "main", - group: "spacing" - }, - "\\enspace": { - font: "main", - group: "spacing" - }, - "\\qquad": { - font: "main", - group: "spacing" - }, - "\\quad": { - font: "main", - group: "spacing" - }, - "\\space": { - font: "main", - group: "spacing", - replace: "\u00a0" - }, - ",": { - font: "main", - group: "punct" - }, - ";": { - font: "main", - group: "punct" - }, - "\\colon": { - font: "main", - group: "punct", - replace: ":" - }, - "\\barwedge": { - font: "ams", - group: "textord", - replace: "\u22bc" - }, - "\\veebar": { - font: "ams", - group: "textord", - replace: "\u22bb" - }, - "\\odot": { - font: "main", - group: "textord", - replace: "\u2299" - }, - "\\oplus": { - font: "main", - group: "textord", - replace: "\u2295" - }, - "\\otimes": { - font: "main", - group: "textord", - replace: "\u2297" - }, - "\\oslash": { - font: "main", - group: "textord", - replace: "\u2298" - }, - "\\circledcirc": { - font: "ams", - group: "textord", - replace: "\u229a" - }, - "\\boxdot": { - font: "ams", - group: "textord", - replace: "\u22a1" - }, - "\\bigtriangleup": { - font: "main", - group: "textord", - replace: "\u25b3" - }, - "\\bigtriangledown": { - font: "main", - group: "textord", - replace: "\u25bd" - }, - "\\dagger": { - font: "main", - group: "textord", - replace: "\u2020" - }, - "\\diamond": { - font: "main", - group: "textord", - replace: "\u22c4" - }, - "\\star": { - font: "main", - group: "textord", - replace: "\u22c6" - }, - "\\triangleleft": { - font: "main", - group: "textord", - replace: "\u25c3" - }, - "\\triangleright": { - font: "main", - group: "textord", - replace: "\u25b9" + "math": { + "`": { + font: "main", + group: "textord", + replace: "\u2018" + }, + "\\$": { + font: "main", + group: "textord", + replace: "$" + }, + "\\%": { + font: "main", + group: "textord", + replace: "%" + }, + "\\angle": { + font: "main", + group: "textord", + replace: "\u2220" + }, + "\\infty": { + font: "main", + group: "textord", + replace: "\u221e" + }, + "\\prime": { + font: "main", + group: "textord", + replace: "\u2032" + }, + "\\triangle": { + font: "main", + group: "textord", + replace: "\u25b3" + }, + "\\Gamma": { + font: "main", + group: "textord", + replace: "\u0393" + }, + "\\Delta": { + font: "main", + group: "textord", + replace: "\u0394" + }, + "\\Theta": { + font: "main", + group: "textord", + replace: "\u0398" + }, + "\\Lambda": { + font: "main", + group: "textord", + replace: "\u039b" + }, + "\\Xi": { + font: "main", + group: "textord", + replace: "\u039e" + }, + "\\Pi": { + font: "main", + group: "textord", + replace: "\u03a0" + }, + "\\Sigma": { + font: "main", + group: "textord", + replace: "\u03a3" + }, + "\\Upsilon": { + font: "main", + group: "textord", + replace: "\u03a5" + }, + "\\Phi": { + font: "main", + group: "textord", + replace: "\u03a6" + }, + "\\Psi": { + font: "main", + group: "textord", + replace: "\u03a8" + }, + "\\Omega": { + font: "main", + group: "textord", + replace: "\u03a9" + }, + "\\alpha": { + font: "main", + group: "mathord", + replace: "\u03b1" + }, + "\\beta": { + font: "main", + group: "mathord", + replace: "\u03b2" + }, + "\\gamma": { + font: "main", + group: "mathord", + replace: "\u03b3" + }, + "\\delta": { + font: "main", + group: "mathord", + replace: "\u03b4" + }, + "\\epsilon": { + font: "main", + group: "mathord", + replace: "\u03f5" + }, + "\\zeta": { + font: "main", + group: "mathord", + replace: "\u03b6" + }, + "\\eta": { + font: "main", + group: "mathord", + replace: "\u03b7" + }, + "\\theta": { + font: "main", + group: "mathord", + replace: "\u03b8" + }, + "\\iota": { + font: "main", + group: "mathord", + replace: "\u03b9" + }, + "\\kappa": { + font: "main", + group: "mathord", + replace: "\u03ba" + }, + "\\lambda": { + font: "main", + group: "mathord", + replace: "\u03bb" + }, + "\\mu": { + font: "main", + group: "mathord", + replace: "\u03bc" + }, + "\\nu": { + font: "main", + group: "mathord", + replace: "\u03bd" + }, + "\\xi": { + font: "main", + group: "mathord", + replace: "\u03be" + }, + "\\omicron": { + font: "main", + group: "mathord", + replace: "o" + }, + "\\pi": { + font: "main", + group: "mathord", + replace: "\u03c0" + }, + "\\rho": { + font: "main", + group: "mathord", + replace: "\u03c1" + }, + "\\sigma": { + font: "main", + group: "mathord", + replace: "\u03c3" + }, + "\\tau": { + font: "main", + group: "mathord", + replace: "\u03c4" + }, + "\\upsilon": { + font: "main", + group: "mathord", + replace: "\u03c5" + }, + "\\phi": { + font: "main", + group: "mathord", + replace: "\u03d5" + }, + "\\chi": { + font: "main", + group: "mathord", + replace: "\u03c7" + }, + "\\psi": { + font: "main", + group: "mathord", + replace: "\u03c8" + }, + "\\omega": { + font: "main", + group: "mathord", + replace: "\u03c9" + }, + "\\varepsilon": { + font: "main", + group: "mathord", + replace: "\u03b5" + }, + "\\vartheta": { + font: "main", + group: "mathord", + replace: "\u03d1" + }, + "\\varpi": { + font: "main", + group: "mathord", + replace: "\u03d6" + }, + "\\varrho": { + font: "main", + group: "mathord", + replace: "\u03f1" + }, + "\\varsigma": { + font: "main", + group: "mathord", + replace: "\u03c2" + }, + "\\varphi": { + font: "main", + group: "mathord", + replace: "\u03c6" + }, + "*": { + font: "main", + group: "bin", + replace: "\u2217" + }, + "+": { + font: "main", + group: "bin" + }, + "-": { + font: "main", + group: "bin", + replace: "\u2212" + }, + "\\cdot": { + font: "main", + group: "bin", + replace: "\u22c5" + }, + "\\circ": { + font: "main", + group: "bin", + replace: "\u2218" + }, + "\\div": { + font: "main", + group: "bin", + replace: "\u00f7" + }, + "\\pm": { + font: "main", + group: "bin", + replace: "\u00b1" + }, + "\\times": { + font: "main", + group: "bin", + replace: "\u00d7" + }, + "(": { + font: "main", + group: "open" + }, + "[": { + font: "main", + group: "open" + }, + "\\langle": { + font: "main", + group: "open", + replace: "\u27e8" + }, + "\\lvert": { + font: "main", + group: "open", + replace: "|" + }, + ")": { + font: "main", + group: "close" + }, + "]": { + font: "main", + group: "close" + }, + "?": { + font: "main", + group: "close" + }, + "!": { + font: "main", + group: "close" + }, + "\\rangle": { + font: "main", + group: "close", + replace: "\u27e9" + }, + "\\rvert": { + font: "main", + group: "close", + replace: "|" + }, + "=": { + font: "main", + group: "rel" + }, + "<": { + font: "main", + group: "rel" + }, + ">": { + font: "main", + group: "rel" + }, + ":": { + font: "main", + group: "rel" + }, + "\\approx": { + font: "main", + group: "rel", + replace: "\u2248" + }, + "\\cong": { + font: "main", + group: "rel", + replace: "\u2245" + }, + "\\ge": { + font: "main", + group: "rel", + replace: "\u2265" + }, + "\\geq": { + font: "main", + group: "rel", + replace: "\u2265" + }, + "\\gets": { + font: "main", + group: "rel", + replace: "\u2190" + }, + "\\in": { + font: "main", + group: "rel", + replace: "\u2208" + }, + "\\leftarrow": { + font: "main", + group: "rel", + replace: "\u2190" + }, + "\\le": { + font: "main", + group: "rel", + replace: "\u2264" + }, + "\\leq": { + font: "main", + group: "rel", + replace: "\u2264" + }, + "\\ne": { + font: "main", + group: "rel", + replace: "\u2260" + }, + "\\neq": { + font: "main", + group: "rel", + replace: "\u2260" + }, + "\\rightarrow": { + font: "main", + group: "rel", + replace: "\u2192" + }, + "\\to": { + font: "main", + group: "rel", + replace: "\u2192" + }, + "\\ngeq": { + font: "ams", + group: "rel", + replace: "\u2271" + }, + "\\nleq": { + font: "ams", + group: "rel", + replace: "\u2270" + }, + "\\!": { + font: "main", + group: "spacing" + }, + "\\ ": { + font: "main", + group: "spacing", + replace: "\u00a0" + }, + "\\,": { + font: "main", + group: "spacing" + }, + "\\:": { + font: "main", + group: "spacing" + }, + "\\;": { + font: "main", + group: "spacing" + }, + "\\enspace": { + font: "main", + group: "spacing" + }, + "\\qquad": { + font: "main", + group: "spacing" + }, + "\\quad": { + font: "main", + group: "spacing" + }, + "\\space": { + font: "main", + group: "spacing", + replace: "\u00a0" + }, + ",": { + font: "main", + group: "punct" + }, + ";": { + font: "main", + group: "punct" + }, + "\\colon": { + font: "main", + group: "punct", + replace: ":" + }, + "\\barwedge": { + font: "ams", + group: "textord", + replace: "\u22bc" + }, + "\\veebar": { + font: "ams", + group: "textord", + replace: "\u22bb" + }, + "\\odot": { + font: "main", + group: "textord", + replace: "\u2299" + }, + "\\oplus": { + font: "main", + group: "textord", + replace: "\u2295" + }, + "\\otimes": { + font: "main", + group: "textord", + replace: "\u2297" + }, + "\\oslash": { + font: "main", + group: "textord", + replace: "\u2298" + }, + "\\circledcirc": { + font: "ams", + group: "textord", + replace: "\u229a" + }, + "\\boxdot": { + font: "ams", + group: "textord", + replace: "\u22a1" + }, + "\\bigtriangleup": { + font: "main", + group: "textord", + replace: "\u25b3" + }, + "\\bigtriangledown": { + font: "main", + group: "textord", + replace: "\u25bd" + }, + "\\dagger": { + font: "main", + group: "textord", + replace: "\u2020" + }, + "\\diamond": { + font: "main", + group: "textord", + replace: "\u22c4" + }, + "\\star": { + font: "main", + group: "textord", + replace: "\u22c6" + }, + "\\triangleleft": { + font: "main", + group: "textord", + replace: "\u25c3" + }, + "\\triangleright": { + font: "main", + group: "textord", + replace: "\u25b9" + } + }, + "text": { + "\\ ": { + font: "main", + group: "spacing", + replace: "\u00a0" + }, + " ": { + font: "main", + group: "spacing", + replace: "\u00a0" + } } }; -var textSymbols = "0123456789/|@.\""; -for (var i = 0; i < textSymbols.length; i++) { - var ch = textSymbols.charAt(i); - symbols[ch] = { +var mathTextSymbols = "0123456789/|@.\""; +for (var i = 0; i < mathTextSymbols.length; i++) { + var ch = mathTextSymbols.charAt(i); + symbols["math"][ch] = { font: "main", group: "textord" }; } -var mathSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -for (var i = 0; i < mathSymbols.length; i++) { - var ch = mathSymbols.charAt(i); - symbols[ch] = { +var textSymbols = "0123456789`!@*()-=+[]'\";:?/.,"; +for (var i = 0; i < textSymbols.length; i++) { + var ch = textSymbols.charAt(i); + symbols["text"][ch] = { + font: "main", + group: "textord" + }; +} + +var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +for (var i = 0; i < letters.length; i++) { + var ch = letters.charAt(i); + symbols["math"][ch] = { font: "main", group: "mathord" }; + symbols["text"][ch] = { + font: "main", + group: "textord" + }; } module.exports = symbols; diff --git a/test/huxley/Huxleyfile b/test/huxley/Huxleyfile index 77c6ff6b..18116a13 100644 --- a/test/huxley/Huxleyfile +++ b/test/huxley/Huxleyfile @@ -34,5 +34,8 @@ url=http://localhost:7936/test/huxley/test.html?m=\Huge{x}\LARGE{y}\normalsize{z [SizingBaseline] url=http://localhost:7936/test/huxley/test.html?m=\tiny{a+b}a+b\Huge{a+b}&pre=x&post=M +[Text] +url=http://localhost:7936/test/huxley/test.html?m=\frac{a}{b}\text{c {ab} \ e}+fg + [KaTeX] -url=http://localhost:7936/test/huxley/test.html?m=\KaTeX \ No newline at end of file +url=http://localhost:7936/test/huxley/test.html?m=\KaTeX diff --git a/test/huxley/Text.huxley/record.json b/test/huxley/Text.huxley/record.json new file mode 100644 index 00000000..9369c51e --- /dev/null +++ b/test/huxley/Text.huxley/record.json @@ -0,0 +1 @@ +{"py/object": "huxley.run.Test", "screen_size": {"py/tuple": [1024, 768]}, "steps": [{"py/object": "huxley.steps.ScreenshotTestStep", "index": 0, "offset_time": 0}]} diff --git a/test/huxley/Text.huxley/screenshot0.png b/test/huxley/Text.huxley/screenshot0.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e477fa6b60710ed35e04fa775870a23c9906ff GIT binary patch literal 12747 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

-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 KpUXO@geCwYnmB6! literal 0 HcmV?d00001 diff --git a/test/katex-tests.js b/test/katex-tests.js index f75ad72c..95b366e7 100644 --- a/test/katex-tests.js +++ b/test/katex-tests.js @@ -447,3 +447,52 @@ describe("A sizing parser", function() { }).toThrow(); }); }); + +describe("A text parser", function() { + var textExpression = "\\text{a b}"; + var badTextExpression = "\\text{a b%}"; + var nestedTextExpression = "\\text{a {b} \\blue{c}}"; + var spaceTextExpression = "\\text{ a \\ }"; + + it("should not fail", function() { + expect(function() { + parseTree(textExpression); + }).not.toThrow(); + }); + + it("should produce a text", function() { + var parse = parseTree(textExpression)[0]; + + expect(parse.type).toMatch("text"); + expect(parse.value).toBeDefined(); + }); + + it("should produce textords instead of mathords", function() { + var parse = parseTree(textExpression)[0]; + var group = parse.value.value; + + expect(group[0].type).toMatch("textord"); + }); + + it("should not parse bad text", function() { + expect(function() { + parseTree(badTextExpression); + }).toThrow(); + }); + + it("should parse nested expressions", function() { + expect(function() { + parseTree(nestedTextExpression); + }).not.toThrow(); + }); + + it("should contract spaces", function() { + var parse = parseTree(spaceTextExpression)[0]; + var group = parse.value.value; + + expect(group[0].type).toMatch("spacing"); + expect(group[1].type).toMatch("textord"); + expect(group[2].type).toMatch("spacing"); + expect(group[3].type).toMatch("spacing"); + }); +});