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; | |
}); | |