Spaces:
Running
on
T4
Running
on
T4
| /* eslint no-constant-condition:0 */ | |
| var fontMetrics = require("./fontMetrics"); | |
| var parseData = require("./parseData"); | |
| var ParseError = require("./ParseError"); | |
| var ParseNode = parseData.ParseNode; | |
| /** | |
| * 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. | |
| */ | |
| var q = 0 ; | |
| function parseArray(parser, result) { | |
| var row = []; | |
| var body = [row]; | |
| var rowGaps = []; | |
| while (true) { | |
| // if (q == 1) console.error(parser.nextToken.text); | |
| try { | |
| var cell = parser.parseExpression(false, null); | |
| } catch (e) { | |
| // console.error(e); | |
| exit(); | |
| } | |
| // if (q == 1) exit(); | |
| row.push(new ParseNode("ordgroup", cell, parser.mode)); | |
| var next = parser.nextToken.text; | |
| if (next === "&") { | |
| parser.consume(); | |
| } else if (next === "\\end" || next == "}") { | |
| break; | |
| } else if (next === "\\\\" || next === "\\cr") { | |
| var cr = parser.parseFunction(); | |
| rowGaps.push(cr.value.size); | |
| row = []; | |
| body.push(row); | |
| } else { | |
| // TODO: Clean up the following hack once #385 got merged | |
| var pos = Math.min(parser.pos + 1, parser.lexer._input.length); | |
| throw new ParseError("Expected & or \\\\ or \\end", | |
| parser.lexer, pos); | |
| } | |
| } | |
| result.body = body; | |
| result.rowGaps = rowGaps; | |
| // if (q == 1) exit(); | |
| var node = new ParseNode(result.type, result, parser.mode); | |
| return node; | |
| } | |
| /* | |
| * An environment definition is very similar to a function definition: | |
| * it is declared with a name or a list of names, a set of properties | |
| * and a handler containing the actual implementation. | |
| * | |
| * The properties include: | |
| * - 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 | |
| * A bare number instead of that object indicates the numArgs value. | |
| * | |
| * The handler function will receive two arguments | |
| * - context: information and references provided by the parser | |
| * - args: an array of arguments passed to \begin{name} | |
| * The context contains the following properties: | |
| * - envName: the name of the environment, one of the listed names. | |
| * - parser: the parser object | |
| * - lexer: the lexer object | |
| * - positions: the positions associated with these arguments from args. | |
| * The handler must return a ParseResult. | |
| */ | |
| function defineEnvironment(names, props, handler) { | |
| if (typeof names === "string") { | |
| names = [names]; | |
| } | |
| if (typeof props === "number") { | |
| props = { numArgs: props }; | |
| } | |
| // Set default values of environments | |
| var data = { | |
| numArgs: props.numArgs || 0, | |
| argTypes: props.argTypes, | |
| greediness: 1, | |
| allowedInText: !!props.allowedInText, | |
| numOptionalArgs: props.numOptionalArgs || 0, | |
| handler: handler, | |
| }; | |
| for (var i = 0; i < names.length; ++i) { | |
| module.exports[names[i]] = data; | |
| } | |
| } | |
| // Arrays are part of LaTeX, defined in lttab.dtx so its documentation | |
| // is part of the source2e.pdf file of LaTeX2e source documentation. | |
| defineEnvironment("array", { | |
| numArgs: 1, | |
| }, function(context, args) { | |
| var colalign = args[0]; | |
| 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, | |
| // context.lexer, context.positions[1]); | |
| }); | |
| var res = { | |
| type: "array", | |
| style: "array", | |
| cols: cols, | |
| hskipBeforeAndAfter: true, // \@preamble in lttab.dtx | |
| }; | |
| res = parseArray(context.parser, res); | |
| return res; | |
| }); | |
| defineEnvironment("tabular", { | |
| numArgs: 1, | |
| }, function(context, args) { | |
| var colalign = args[0]; | |
| 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, | |
| // context.lexer, context.positions[1]); | |
| }); | |
| var res = { | |
| type: "array", | |
| style: "tabular", | |
| cols: cols, | |
| hskipBeforeAndAfter: true, // \@preamble in lttab.dtx | |
| }; | |
| res = parseArray(context.parser, res); | |
| return res; | |
| }); | |
| // The matrix environments of amsmath builds on the array environment | |
| // of LaTeX, which is discussed above. | |
| defineEnvironment([ | |
| "matrix", | |
| "pmatrix", | |
| "bmatrix", | |
| "Bmatrix", | |
| "vmatrix", | |
| "Vmatrix", | |
| ], { | |
| }, function(context) { | |
| var delimiters = { | |
| "matrix": null, | |
| "pmatrix": ["(", ")"], | |
| "bmatrix": ["[", "]"], | |
| "Bmatrix": ["\\{", "\\}"], | |
| "vmatrix": ["|", "|"], | |
| "Vmatrix": ["\\Vert", "\\Vert"], | |
| }[context.envName]; | |
| var res = { | |
| type: "array", | |
| style: "matrix", | |
| hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath | |
| }; | |
| q = 1; | |
| res = parseArray(context.parser, res); | |
| if (delimiters) { | |
| res = new ParseNode("leftright", { | |
| body: [res], | |
| left: delimiters[0], | |
| right: delimiters[1], | |
| }, context.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. | |
| defineEnvironment("picture", { | |
| }, function(context) { | |
| var res = { | |
| type: "array", | |
| style: "picture", | |
| arraystretch: 1.2, | |
| cols: [{ | |
| type: "align", | |
| align: "l", | |
| pregap: 0, | |
| postgap: fontMetrics.metrics.quad, | |
| }, { | |
| type: "align", | |
| align: "l", | |
| pregap: 0, | |
| postgap: 0, | |
| }], | |
| }; | |
| res = parseArray(context.parser, res); | |
| res = new ParseNode("leftright", { | |
| body: [res], | |
| left: "\\{", | |
| right: ".", | |
| }, context.mode); | |
| return res; | |
| }); | |
| defineEnvironment("cases", { | |
| }, function(context) { | |
| var res = { | |
| type: "array", | |
| style: "cases", | |
| arraystretch: 1.2, | |
| cols: [{ | |
| type: "align", | |
| align: "l", | |
| pregap: 0, | |
| postgap: fontMetrics.metrics.quad, | |
| }, { | |
| type: "align", | |
| align: "l", | |
| pregap: 0, | |
| postgap: 0, | |
| }], | |
| }; | |
| res = parseArray(context.parser, res); | |
| res = new ParseNode("leftright", { | |
| body: [res], | |
| left: "\\{", | |
| right: ".", | |
| }, context.mode); | |
| return res; | |
| }); | |
| // An aligned environment is like the align* environment | |
| // except it operates within math mode. | |
| // Note that we assume \nomallineskiplimit to be zero, | |
| // so that \strut@ is the same as \strut. | |
| defineEnvironment("aligned", { | |
| }, function(context) { | |
| var res = { | |
| type: "array", | |
| style: "aligned", | |
| cols: [], | |
| }; | |
| res = parseArray(context.parser, res); | |
| var emptyGroup = new ParseNode("ordgroup", [], context.mode); | |
| var numCols = 0; | |
| res.value.body.forEach(function(row) { | |
| var i; | |
| for (i = 1; i < row.length; i += 2) { | |
| row[i].value.unshift(emptyGroup); | |
| } | |
| if (numCols < row.length) { | |
| numCols = row.length; | |
| } | |
| }); | |
| for (var i = 0; i < numCols; ++i) { | |
| var align = "r"; | |
| var pregap = 0; | |
| if (i % 2 === 1) { | |
| align = "l"; | |
| } else if (i > 0) { | |
| pregap = 2; // one \qquad between columns | |
| } | |
| res.value.cols[i] = { | |
| type: "align", | |
| align: align, | |
| pregap: pregap, | |
| postgap: 0, | |
| }; | |
| } | |
| return res; | |
| }); | |