Replace ParseNode<*> with a more accurate AnyParseNode and fix flow errors. (#1387)

* Replace ParseNode<*> with a more accurate AnyParseNode and fix flow errors.

* Allow "array" environment type spec to use any all symbol type.

Before this commit, it was constrained to use "mathord" and "textord", but a
recent change in HEAD resulted in a "rel" node being used in the spec for, e.g.
\begin{array}{|l||c:r::}\end{array}

* Address reviewer comments: rename `lastRow` to `row` in array.js.
This commit is contained in:
Ashish Myles
2018-06-04 10:56:51 -04:00
committed by Kevin Barabash
parent 4492eedb68
commit 19d2aa63c3
20 changed files with 284 additions and 171 deletions

View File

@@ -4,7 +4,8 @@ import defineEnvironment from "../defineEnvironment";
import mathMLTree from "../mathMLTree";
import ParseError from "../ParseError";
import ParseNode from "../ParseNode";
import {assertNodeType} from "../ParseNode";
import {assertNodeType, assertSymbolNodeType} from "../ParseNode";
import {checkNodeType, checkSymbolNodeType} from "../ParseNode";
import {calculateSize} from "../units";
import utils from "../utils";
@@ -12,7 +13,9 @@ import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Parser from "../Parser";
import type {AnyParseNode} from "../ParseNode";
import type {StyleStr} from "../types";
import type {HtmlBuilder, MathMLBuilder} from "../defineFunction";
// Data stored in the ParseNode associated with the environment.
type AlignSpec = { type: "separator", separator: string } | {
@@ -28,8 +31,8 @@ export type ArrayEnvNodeData = {|
arraystretch: number,
addJot?: boolean,
cols?: AlignSpec[],
body: ParseNode<*>[][], // List of rows in the (2D) array.
rowGaps: (?ParseNode<*>)[],
body: AnyParseNode[][], // List of rows in the (2D) array.
rowGaps: (?ParseNode<"size">)[],
numHLinesBeforeRow: number[],
|};
// Same as above but with some fields not yet filled.
@@ -40,8 +43,8 @@ type ArrayEnvNodeDataIncomplete = {|
addJot?: boolean,
cols?: AlignSpec[],
// Before these fields are filled.
body?: ParseNode<*>[][],
rowGaps?: (?ParseNode<*>)[],
body?: AnyParseNode[][],
rowGaps?: (?ParseNode<"size">)[],
numHLinesBeforeRow?: number[],
|};
@@ -110,10 +113,8 @@ function parseArray(
} else if (next === "\\end") {
// Arrays terminate newlines with `\crcr` which consumes a `\cr` if
// the last line is empty.
const lastRow = body[body.length - 1];
if (body.length > 1
&& lastRow.length === 1
&& lastRow[0].value.value[0].value.length === 0) {
// NOTE: Currently, `cell` is the last item added into `row`.
if (row.length === 1 && cell.value.value[0].value.length === 0) {
body.pop();
}
break;
@@ -161,8 +162,7 @@ type Outrow = {
pos: number,
};
const htmlBuilder = function(group, options) {
const groupValue = assertNodeType(group, "array").value;
const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
let r;
let c;
const nr = group.value.body.length;
@@ -180,7 +180,7 @@ const htmlBuilder = function(group, options) {
// Default \jot from ltmath.dtx
// TODO(edemaine): allow overriding \jot via \setlength (#687)
const jot = 3 * pt;
const arrayskip = groupValue.arraystretch * baselineskip;
const arrayskip = group.value.arraystretch * baselineskip;
const arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
const arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
@@ -194,8 +194,8 @@ const htmlBuilder = function(group, options) {
hlinePos.push(totalHeight);
}
for (r = 0; r < groupValue.body.length; ++r) {
const inrow = groupValue.body[r];
for (r = 0; r < group.value.body.length; ++r) {
const inrow = group.value.body[r];
let height = arstrutHeight; // \@array adds an \@arstrut
let depth = arstrutDepth; // to each tow (via the template)
@@ -215,7 +215,7 @@ const htmlBuilder = function(group, options) {
outrow[c] = elt;
}
const rowGap = groupValue.rowGaps[r];
const rowGap = group.value.rowGaps[r];
let gap = 0;
if (rowGap) {
gap = calculateSize(rowGap.value.value, options);
@@ -230,7 +230,7 @@ const htmlBuilder = function(group, options) {
// In AMS multiline environments such as aligned and gathered, rows
// correspond to lines that have additional \jot added to the
// \baselineskip via \openup.
if (groupValue.addJot) {
if (group.value.addJot) {
depth += jot;
}
@@ -251,7 +251,7 @@ const htmlBuilder = function(group, options) {
}
const offset = totalHeight / 2 + options.fontMetrics().axisHeight;
const colDescriptions = groupValue.cols || [];
const colDescriptions = group.value.cols || [];
const cols = [];
let colSep;
let colDescrNum;
@@ -307,7 +307,7 @@ const htmlBuilder = function(group, options) {
}
let sepwidth;
if (c > 0 || groupValue.hskipBeforeAndAfter) {
if (c > 0 || group.value.hskipBeforeAndAfter) {
sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
if (sepwidth !== 0) {
colSep = buildCommon.makeSpan(["arraycolsep"], []);
@@ -338,7 +338,7 @@ const htmlBuilder = function(group, options) {
[col]);
cols.push(col);
if (c < nc - 1 || groupValue.hskipBeforeAndAfter) {
if (c < nc - 1 || group.value.hskipBeforeAndAfter) {
sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
if (sepwidth !== 0) {
colSep = buildCommon.makeSpan(["arraycolsep"], []);
@@ -366,10 +366,9 @@ const htmlBuilder = function(group, options) {
return buildCommon.makeSpan(["mord"], [body], options);
};
const mathmlBuilder = function(group, options) {
const groupValue = assertNodeType(group, "array").value;
const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
return new mathMLTree.MathNode(
"mtable", groupValue.body.map(function(row) {
"mtable", group.value.body.map(function(row) {
return new mathMLTree.MathNode(
"mtr", row.map(function(cell) {
return new mathMLTree.MathNode(
@@ -400,10 +399,12 @@ const alignedHandler = function(context, args) {
let numMaths;
let numCols = 0;
const emptyGroup = new ParseNode("ordgroup", [], context.mode);
if (args[0] && args[0].value) {
const ordgroup = checkNodeType(args[0], "ordgroup");
if (ordgroup) {
let arg0 = "";
for (let i = 0; i < args[0].value.length; i++) {
arg0 += args[0].value[i].value;
for (let i = 0; i < ordgroup.value.length; i++) {
const textord = assertNodeType(ordgroup.value[i], "textord");
arg0 += textord.value;
}
numMaths = Number(arg0);
numCols = numMaths * 2;
@@ -412,7 +413,8 @@ const alignedHandler = function(context, args) {
res.value.body.forEach(function(row) {
for (let i = 1; i < row.length; i += 2) {
// Modify ordgroup node within styling node
const ordgroup = row[i].value.value[0];
const styling = assertNodeType(row[i], "styling");
const ordgroup = assertNodeType(styling.value.value[0], "ordgroup");
ordgroup.value.unshift(emptyGroup);
}
if (!isAligned) { // Case 1
@@ -459,10 +461,16 @@ defineEnvironment({
props: {
numArgs: 1,
},
handler: function(context, args) {
let colalign = args[0];
colalign = colalign.value.map ? colalign.value : [colalign];
const cols = colalign.map(function(node) {
handler(context, args) {
// Since no types are specified above, the two possibilities are
// - The argument is wrapped in {} or [], in which case Parser's
// parseGroup() returns an "ordgroup" wrapping some symbol node.
// - The argument is a bare symbol node.
const symNode = checkSymbolNodeType(args[0]);
const colalign: AnyParseNode[] =
symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").value;
const cols = colalign.map(function(nde) {
const node = assertSymbolNodeType(nde);
const ca = node.value;
if ("lcr".indexOf(ca) !== -1) {
return {
@@ -480,9 +488,7 @@ defineEnvironment({
separator: ":",
};
}
throw new ParseError(
"Unknown column alignment: " + node.value,
node);
throw new ParseError("Unknown column alignment: " + ca, nde);
});
let res = {
type: "array",