latex2im / katex /src /environments.js
da03
.
b498cbf
/* 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;
});