Flatten "styling", "text", and "array" ParseNodes. (#1559)

* Flatten "styling" ParseNode.

* Flatten "text" ParseNode.

* Flatten "array" ParseNode.
This commit is contained in:
Ashish Myles
2018-08-07 01:26:40 -04:00
committed by ylemkimon
parent f0b9a344ed
commit f3a9bd4b3d
11 changed files with 215 additions and 287 deletions

View File

@@ -319,10 +319,7 @@ export default class Parser {
const textNode = {
type: "text",
mode: this.mode,
value: {
type: "text",
body: textordArray,
},
body: textordArray,
};
const colorNode = {

View File

@@ -171,8 +171,7 @@ export const buildExpression = function(
} else if (node.type === "sizing") {
glueOptions = options.havingSize(node.size);
} else if (node.type === "styling") {
glueOptions = options.havingStyle(
styleMap[node.value.style]);
glueOptions = options.havingStyle(styleMap[node.style]);
}
}

View File

@@ -16,39 +16,15 @@ import type Parser from "../Parser";
import type {ParseNode, AnyParseNode} from "../parseNode";
import type {StyleStr} from "../types";
import type {HtmlBuilder, MathMLBuilder} from "../defineFunction";
import type {Measurement} from "../units";
// Data stored in the ParseNode associated with the environment.
type AlignSpec = { type: "separator", separator: string } | {
export type AlignSpec = { type: "separator", separator: string } | {
type: "align",
align: string,
pregap?: number,
postgap?: number,
};
export type ArrayEnvNodeData = {|
type: "array",
hskipBeforeAndAfter?: boolean,
addJot?: boolean,
cols?: AlignSpec[],
arraystretch: number,
body: AnyParseNode[][], // List of rows in the (2D) array.
rowGaps: (?Measurement)[],
hLinesBeforeRow: Array<boolean[]>,
|};
// Same as above but with some fields not yet filled.
type ArrayEnvNodeDataIncomplete = {|
type: "array",
hskipBeforeAndAfter?: boolean,
addJot?: boolean,
cols?: AlignSpec[],
// Before these fields are filled.
arraystretch?: number,
body?: AnyParseNode[][],
rowGaps?: (?Measurement)[],
hLinesBeforeRow?: Array<boolean[]>,
|};
function getHLines(parser: Parser): boolean[] {
// Return an array. The array length = number of hlines.
// Each element in the array tells if the line is dashed.
@@ -72,7 +48,12 @@ function getHLines(parser: Parser): boolean[] {
*/
function parseArray(
parser: Parser,
result: ArrayEnvNodeDataIncomplete,
{hskipBeforeAndAfter, addJot, cols, arraystretch}: {|
hskipBeforeAndAfter?: boolean,
addJot?: boolean,
cols?: AlignSpec[],
arraystretch?: number,
|},
style: StyleStr,
): ParseNode<"array"> {
// Parse body of array with \\ temporarily mapped to \cr
@@ -80,15 +61,15 @@ function parseArray(
parser.gullet.macros.set("\\\\", "\\cr");
// Get current arraystretch if it's not set by the environment
if (!result.arraystretch) {
const arraystretch = parser.gullet.expandMacroAsText("\\arraystretch");
if (arraystretch == null) {
if (!arraystretch) {
const stretch = parser.gullet.expandMacroAsText("\\arraystretch");
if (stretch == null) {
// Default \arraystretch from lttab.dtx
result.arraystretch = 1;
arraystretch = 1;
} else {
result.arraystretch = parseFloat(arraystretch);
if (!result.arraystretch || result.arraystretch < 0) {
throw new ParseError(`Invalid \\arraystretch: ${arraystretch}`);
arraystretch = parseFloat(stretch);
if (!arraystretch || arraystretch < 0) {
throw new ParseError(`Invalid \\arraystretch: ${stretch}`);
}
}
}
@@ -112,11 +93,8 @@ function parseArray(
cell = {
type: "styling",
mode: parser.mode,
value: {
type: "styling",
style: style,
value: [cell],
},
style,
body: [cell],
};
}
row.push(cell);
@@ -128,7 +106,7 @@ function parseArray(
// the last line is empty.
// NOTE: Currently, `cell` is the last item added into `row`.
if (row.length === 1 && cell.type === "styling" &&
cell.value.value[0].value.length === 0) {
cell.body[0].value.length === 0) {
body.pop();
}
if (hLinesBeforeRow.length < body.length + 1) {
@@ -152,16 +130,17 @@ function parseArray(
parser.nextToken);
}
}
result.body = body;
result.rowGaps = rowGaps;
result.hLinesBeforeRow = hLinesBeforeRow;
// $FlowFixMe: The required fields were added immediately above.
const res: ArrayEnvNodeData = result;
parser.gullet.endGroup();
return {
type: "array",
mode: parser.mode,
value: res,
addJot,
arraystretch,
body,
cols,
rowGaps,
hskipBeforeAndAfter,
hLinesBeforeRow,
};
}
@@ -186,8 +165,8 @@ type Outrow = {
const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
let r;
let c;
const nr = group.value.body.length;
const hLinesBeforeRow = group.value.hLinesBeforeRow;
const nr = group.body.length;
const hLinesBeforeRow = group.hLinesBeforeRow;
let nc = 0;
let body = new Array(nr);
const hlines = [];
@@ -201,7 +180,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
// Default \jot from ltmath.dtx
// TODO(edemaine): allow overriding \jot via \setlength (#687)
const jot = 3 * pt;
const arrayskip = group.value.arraystretch * baselineskip;
const arrayskip = group.arraystretch * baselineskip;
const arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
const arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
@@ -218,8 +197,8 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
}
setHLinePos(hLinesBeforeRow[0]);
for (r = 0; r < group.value.body.length; ++r) {
const inrow = group.value.body[r];
for (r = 0; r < group.body.length; ++r) {
const inrow = group.body[r];
let height = arstrutHeight; // \@array adds an \@arstrut
let depth = arstrutDepth; // to each tow (via the template)
@@ -239,7 +218,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
outrow[c] = elt;
}
const rowGap = group.value.rowGaps[r];
const rowGap = group.rowGaps[r];
let gap = 0;
if (rowGap) {
gap = calculateSize(rowGap, options);
@@ -254,7 +233,7 @@ const htmlBuilder: HtmlBuilder<"array"> = 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 (group.value.addJot) {
if (group.addJot) {
depth += jot;
}
@@ -270,7 +249,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
}
const offset = totalHeight / 2 + options.fontMetrics().axisHeight;
const colDescriptions = group.value.cols || [];
const colDescriptions = group.cols || [];
const cols = [];
let colSep;
let colDescrNum;
@@ -326,7 +305,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
}
let sepwidth;
if (c > 0 || group.value.hskipBeforeAndAfter) {
if (c > 0 || group.hskipBeforeAndAfter) {
sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
if (sepwidth !== 0) {
colSep = buildCommon.makeSpan(["arraycolsep"], []);
@@ -357,7 +336,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
[col]);
cols.push(col);
if (c < nc - 1 || group.value.hskipBeforeAndAfter) {
if (c < nc - 1 || group.hskipBeforeAndAfter) {
sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
if (sepwidth !== 0) {
colSep = buildCommon.makeSpan(["arraycolsep"], []);
@@ -393,7 +372,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
return new mathMLTree.MathNode(
"mtable", group.value.body.map(function(row) {
"mtable", group.body.map(function(row) {
return new mathMLTree.MathNode(
"mtr", row.map(function(cell) {
return new mathMLTree.MathNode(
@@ -405,12 +384,7 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
// Convenience function for aligned and alignedat environments.
const alignedHandler = function(context, args) {
const cols = [];
let res = {
type: "array",
cols,
addJot: true,
};
res = parseArray(context.parser, res, "display");
const res = parseArray(context.parser, {cols, addJot: true}, "display");
// Determining number of columns.
// 1. If the first argument is given, we use it as a number of columns,
@@ -439,11 +413,11 @@ const alignedHandler = function(context, args) {
numCols = numMaths * 2;
}
const isAligned = !numCols;
res.value.body.forEach(function(row) {
res.body.forEach(function(row) {
for (let i = 1; i < row.length; i += 2) {
// Modify ordgroup node within styling node
const styling = assertNodeType(row[i], "styling");
const ordgroup = assertNodeType(styling.value.value[0], "ordgroup");
const ordgroup = assertNodeType(styling.body[0], "ordgroup");
ordgroup.value.unshift(emptyGroup);
}
if (!isAligned) { // Case 1
@@ -519,13 +493,11 @@ defineEnvironment({
}
throw new ParseError("Unknown column alignment: " + ca, nde);
});
let res = {
type: "array",
cols: cols,
const res = {
cols,
hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
};
res = parseArray(context.parser, res, dCellStyle(context.envName));
return res;
return parseArray(context.parser, res, dCellStyle(context.envName));
},
htmlBuilder,
mathmlBuilder,
@@ -555,10 +527,8 @@ defineEnvironment({
"vmatrix": ["|", "|"],
"Vmatrix": ["\\Vert", "\\Vert"],
}[context.envName];
const payload = {
type: "array",
hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
};
// \hskip -\arraycolsep in amsmath
const payload = {hskipBeforeAndAfter: false};
const res: ParseNode<"array"> =
parseArray(context.parser, payload, dCellStyle(context.envName));
return delimiters ? {
@@ -589,7 +559,6 @@ defineEnvironment({
},
handler(context) {
const payload = {
type: "array",
arraystretch: 1.2,
cols: [{
type: "align",
@@ -645,17 +614,15 @@ defineEnvironment({
props: {
numArgs: 0,
},
handler: function(context) {
let res = {
type: "array",
handler(context) {
const res = {
cols: [{
type: "align",
align: "c",
}],
addJot: true,
};
res = parseArray(context.parser, res, "display");
return res;
return parseArray(context.parser, res, "display");
},
htmlBuilder,
mathmlBuilder,

View File

@@ -62,11 +62,8 @@ defineFunction({
const body = {
type: "text",
mode: parser.mode,
value: {
type: "text",
font: "\\texttt",
body: chars,
},
font: "\\texttt",
body: chars,
};
return {
type: "href",

View File

@@ -25,11 +25,8 @@ defineFunction({
return {
type: "styling",
mode: parser.mode,
value: {
type: "styling",
style: "text",
value: body,
},
style: "text",
body,
};
},
});

View File

@@ -31,11 +31,8 @@ defineFunction({
const text = {
type: "text",
mode: group.mode,
value: {
type: "text",
body: ordargument(group.body),
font: "mathrm", // simulate \textrm
},
body: ordargument(group.body),
font: "mathrm", // simulate \textrm
};
const sizedText = {
type: "sizing",

View File

@@ -23,7 +23,7 @@ defineFunction({
numArgs: 0,
allowedInText: true,
},
handler: ({breakOnTokenText, funcName, parser}, args) => {
handler({breakOnTokenText, funcName, parser}, args) {
// parse out the implicit body
parser.consumeSpaces();
const body = parser.parseExpression(true, breakOnTokenText);
@@ -35,22 +35,19 @@ defineFunction({
return {
type: "styling",
mode: parser.mode,
value: {
type: "styling",
// Figure out what style to use by pulling out the style from
// the function name
style,
value: body,
},
// Figure out what style to use by pulling out the style from
// the function name
style,
body,
};
},
htmlBuilder: (group, options) => {
htmlBuilder(group, options) {
// Style changes are handled in the TeXbook on pg. 442, Rule 3.
const newStyle = styleMap[group.value.style];
const newStyle = styleMap[group.style];
const newOptions = options.havingStyle(newStyle).withFont('');
return sizingGroup(group.value.value, newOptions, options);
return sizingGroup(group.body, newOptions, options);
},
mathmlBuilder: (group, options) => {
mathmlBuilder(group, options) {
// Figure out what style we're changing to.
// TODO(kevinb): dedupe this with buildHTML.js
// This will be easier of handling of styling nodes is in the same file.
@@ -61,10 +58,10 @@ defineFunction({
"scriptscript": Style.SCRIPTSCRIPT,
};
const newStyle = styleMap[group.value.style];
const newStyle = styleMap[group.style];
const newOptions = options.havingStyle(newStyle);
const inner = mml.buildExpression(group.value.value, newOptions);
const inner = mml.buildExpression(group.body, newOptions);
const node = new mathMLTree.MathNode("mstyle", inner);
@@ -75,7 +72,7 @@ defineFunction({
"scriptscript": ["2", "false"],
};
const attr = styleAttributes[group.value.style];
const attr = styleAttributes[group.style];
node.setAttribute("scriptlevel", attr[0]);
node.setAttribute("displaystyle", attr[1]);

View File

@@ -20,7 +20,7 @@ const textFontShapes = {
};
const optionsWithFont = (group, options) => {
const font = group.value.font;
const font = group.font;
// Checks if the argument is a font family or a font style.
if (!font) {
return options;
@@ -55,21 +55,18 @@ defineFunction({
return {
type: "text",
mode: parser.mode,
value: {
type: "text",
body: ordargument(body),
font: funcName,
},
body: ordargument(body),
font: funcName,
};
},
htmlBuilder(group, options) {
const newOptions = optionsWithFont(group, options);
const inner = html.buildExpression(group.value.body, newOptions, true);
const inner = html.buildExpression(group.body, newOptions, true);
buildCommon.tryCombineChars(inner);
return buildCommon.makeSpan(["mord", "text"], inner, newOptions);
},
mathmlBuilder(group, options) {
const newOptions = optionsWithFont(group, options);
return mml.buildExpressionRow(group.value.body, newOptions);
return mml.buildExpressionRow(group.body, newOptions);
},
});

View File

@@ -1,7 +1,7 @@
// @flow
import {NON_ATOMS} from "./symbols";
import type SourceLocation from "./SourceLocation";
import type {ArrayEnvNodeData} from "./environments/array";
import type {AlignSpec} from "./environments/array";
import type {Atom} from "./symbols";
import type {Mode, StyleStr} from "./types";
import type {Token} from "./Token";
@@ -28,7 +28,13 @@ type ParseNodeTypes = {
type: "array",
mode: Mode,
loc?: ?SourceLocation,
value: ArrayEnvNodeData,
hskipBeforeAndAfter?: boolean,
addJot?: boolean,
cols?: AlignSpec[],
arraystretch: number,
body: AnyParseNode[][], // List of rows in the (2D) array.
rowGaps: (?Measurement)[],
hLinesBeforeRow: Array<boolean[]>,
|},
"color": {|
type: "color",
@@ -85,11 +91,8 @@ type ParseNodeTypes = {
type: "styling",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "styling",
style: StyleStr,
value: AnyParseNode[],
|},
style: StyleStr,
body: AnyParseNode[],
|},
"supsub": {|
type: "supsub",
@@ -110,11 +113,8 @@ type ParseNodeTypes = {
type: "text",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "text",
body: AnyParseNode[],
font?: string,
|},
body: AnyParseNode[],
font?: string,
|},
"url": {|
type: "url",