UNPKG

@andatoshiki/toshiki-cdn

Version:
179 lines (170 loc) 6.32 kB
var fontMetrics = require("./fontMetrics"); var parseData = require("./parseData"); var ParseError = require("./ParseError"); var ParseNode = parseData.ParseNode; var ParseResult = parseData.ParseResult; /** * Parse the body of the environment, with rows delimited by \\ and * columns delimited by &, and create a nested list in row-major order * with one group per cell. */ function parseArray(parser, pos, mode, result) { var row = [], body = [row], rowGaps = []; while (true) { var cell = parser.parseExpression(pos, mode, false, null); row.push(new ParseNode("ordgroup", cell.result, mode)); pos = cell.position; var next = cell.peek.text; if (next === "&") { pos = cell.peek.position; } else if (next === "\\end") { break; } else if (next === "\\\\" || next === "\\cr") { var cr = parser.parseFunction(pos, mode); rowGaps.push(cr.result.value.size); pos = cr.position; row = []; body.push(row); } else { throw new ParseError("Expected & or \\\\ or \\end", parser.lexer, cell.peek.position); } } result.body = body; result.rowGaps = rowGaps; return new ParseResult(new ParseNode(result.type, result, mode), pos); } /* * An environment definition is very similar to a function definition. * Each element of the following array may contain * - names: The names associated with a function. This can be used to * share one implementation between several similar environments. * - numArgs: The number of arguments after the \begin{name} function. * - argTypes: (optional) Just like for a function * - allowedInText: (optional) Whether or not the environment is allowed inside * text mode (default false) (not enforced yet) * - numOptionalArgs: (optional) Just like for a function * - handler: The function that is called to handle this environment. * It will receive the following arguments: * - pos: the current position of the parser. * - mode: the current parsing mode. * - envName: the name of the environment, one of the listed names. * - [args]: the arguments passed to \begin. * - positions: the positions associated with these arguments. */ var environmentDefinitions = [ // Arrays are part of LaTeX, defined in lttab.dtx so its documentation // is part of the source2e.pdf file of LaTeX2e source documentation. { names: ["array"], numArgs: 1, handler: function(pos, mode, envName, colalign, positions) { var parser = this; colalign = colalign.value.map ? colalign.value : [colalign]; var cols = colalign.map(function(node) { var ca = node.value; if ("lcr".indexOf(ca) !== -1) { return { type: "align", align: ca }; } else if (ca === "|") { return { type: "separator", separator: "|" }; } throw new ParseError( "Unknown column alignment: " + node.value, parser.lexer, positions[1]); }); var res = { type: "array", cols: cols, hskipBeforeAndAfter: true // \@preamble in lttab.dtx }; res = parseArray(parser, pos, mode, res); return res; } }, // The matrix environments of amsmath builds on the array environment // of LaTeX, which is discussed above. { names: [ "matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix" ], handler: function(pos, mode, envName) { var delimiters = { "matrix": null, "pmatrix": ["(", ")"], "bmatrix": ["[", "]"], "Bmatrix": ["\\{", "\\}"], "vmatrix": ["|", "|"], "Vmatrix": ["\\Vert", "\\Vert"] }[envName]; var res = { type: "array", hskipBeforeAndAfter: false // \hskip -\arraycolsep in amsmath }; res = parseArray(this, pos, mode, res); if (delimiters) { res.result = new ParseNode("leftright", { body: [res.result], left: delimiters[0], right: delimiters[1] }, mode); } return res; } }, // A cases environment (in amsmath.sty) is almost equivalent to // \def\arraystretch{1.2}% // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. { names: ["cases"], handler: function(pos, mode, envName) { var res = { type: "array", arraystretch: 1.2, cols: [{ type: "align", align: "l", pregap: 0, postgap: fontMetrics.metrics.quad }, { type: "align", align: "l", pregap: 0, postgap: 0 }] }; res = parseArray(this, pos, mode, res); res.result = new ParseNode("leftright", { body: [res.result], left: "\\{", right: "." }, mode); return res; } } ]; module.exports = (function() { // nested function so we don't leak i and j into the module scope var exports = {}; for (var i = 0; i < environmentDefinitions.length; ++i) { var def = environmentDefinitions[i]; def.greediness = 1; def.allowedInText = !!def.allowedInText; def.numArgs = def.numArgs || 0; def.numOptionalArgs = def.numOptionalArgs || 0; for (var j = 0; j < def.names.length; ++j) { exports[def.names[j]] = def; } } return exports; })();