You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
525 lines
13 KiB
JavaScript
525 lines
13 KiB
JavaScript
// @flow
|
|
import {NON_ATOMS} from "./symbols";
|
|
import type SourceLocation from "./SourceLocation";
|
|
import type {AlignSpec, ColSeparationType} from "./environments/array";
|
|
import type {Atom} from "./symbols";
|
|
import type {Mode, StyleStr} from "./types";
|
|
import type {Token} from "./Token";
|
|
import type {Measurement} from "./units";
|
|
|
|
export type NodeType = $Keys<ParseNodeTypes>;
|
|
export type ParseNode<TYPE: NodeType> = $ElementType<ParseNodeTypes, TYPE>;
|
|
|
|
// ParseNode's corresponding to Symbol `Group`s in symbols.js.
|
|
export type SymbolParseNode =
|
|
ParseNode<"atom"> |
|
|
ParseNode<"accent-token"> |
|
|
ParseNode<"mathord"> |
|
|
ParseNode<"op-token"> |
|
|
ParseNode<"spacing"> |
|
|
ParseNode<"textord">;
|
|
|
|
// ParseNode from `Parser.formatUnsupportedCmd`
|
|
export type UnsupportedCmdParseNode = ParseNode<"color">;
|
|
|
|
// Union of all possible `ParseNode<>` types.
|
|
export type AnyParseNode = $Values<ParseNodeTypes>;
|
|
|
|
// Map from `NodeType` to the corresponding `ParseNode`.
|
|
type ParseNodeTypes = {
|
|
"array": {|
|
|
type: "array",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
colSeparationType?: ColSeparationType,
|
|
hskipBeforeAndAfter?: boolean,
|
|
addJot?: boolean,
|
|
cols?: AlignSpec[],
|
|
arraystretch: number,
|
|
body: AnyParseNode[][], // List of rows in the (2D) array.
|
|
rowGaps: (?Measurement)[],
|
|
hLinesBeforeRow: Array<boolean[]>,
|
|
// Whether each row should be automatically numbered, or an explicit tag
|
|
tags?: (boolean | AnyParseNode[])[],
|
|
leqno?: boolean,
|
|
isCD?: boolean,
|
|
|},
|
|
"cdlabel": {|
|
|
type: "cdlabel",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
side: string,
|
|
label: AnyParseNode,
|
|
|},
|
|
"cdlabelparent": {|
|
|
type: "cdlabelparent",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
fragment: AnyParseNode,
|
|
|},
|
|
"color": {|
|
|
type: "color",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
color: string,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"color-token": {|
|
|
type: "color-token",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
color: string,
|
|
|},
|
|
// To avoid requiring run-time type assertions, this more carefully captures
|
|
// the requirements on the fields per the op.js htmlBuilder logic:
|
|
// - `body` and `value` are NEVER set simultaneously.
|
|
// - When `symbol` is true, `body` is set.
|
|
"op": {|
|
|
type: "op",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
limits: boolean,
|
|
alwaysHandleSupSub?: boolean,
|
|
suppressBaseShift?: boolean,
|
|
parentIsSupSub: boolean,
|
|
symbol: boolean,
|
|
name: string,
|
|
body?: void,
|
|
|} | {|
|
|
type: "op",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
limits: boolean,
|
|
alwaysHandleSupSub?: boolean,
|
|
suppressBaseShift?: boolean,
|
|
parentIsSupSub: boolean,
|
|
symbol: false, // If 'symbol' is true, `body` *must* be set.
|
|
name?: void,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"ordgroup": {|
|
|
type: "ordgroup",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
semisimple?: boolean,
|
|
|},
|
|
"raw": {|
|
|
type: "raw",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
string: string,
|
|
|},
|
|
"size": {|
|
|
type: "size",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
value: Measurement,
|
|
isBlank: boolean,
|
|
|},
|
|
"styling": {|
|
|
type: "styling",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
style: StyleStr,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"supsub": {|
|
|
type: "supsub",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
base: ?AnyParseNode,
|
|
sup?: ?AnyParseNode,
|
|
sub?: ?AnyParseNode,
|
|
|},
|
|
"tag": {|
|
|
type: "tag",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
tag: AnyParseNode[],
|
|
|},
|
|
"text": {|
|
|
type: "text",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
font?: string,
|
|
|},
|
|
"url": {|
|
|
type: "url",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
url: string,
|
|
|},
|
|
"verb": {|
|
|
type: "verb",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: string,
|
|
star: boolean,
|
|
|},
|
|
// From symbol groups, constructed in Parser.js via `symbols` lookup.
|
|
// (Some of these have "-token" suffix to distinguish them from existing
|
|
// `ParseNode` types.)
|
|
"atom": {|
|
|
type: "atom",
|
|
family: Atom,
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
"mathord": {|
|
|
type: "mathord",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
"spacing": {|
|
|
type: "spacing",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
"textord": {|
|
|
type: "textord",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
// These "-token" types don't have corresponding HTML/MathML builders.
|
|
"accent-token": {|
|
|
type: "accent-token",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
"op-token": {|
|
|
type: "op-token",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
text: string,
|
|
|},
|
|
// From functions.js and functions/*.js. See also "color", "op", "styling",
|
|
// and "text" above.
|
|
"accent": {|
|
|
type: "accent",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
label: string,
|
|
isStretchy?: boolean,
|
|
isShifty?: boolean,
|
|
base: AnyParseNode,
|
|
|},
|
|
"accentUnder": {|
|
|
type: "accentUnder",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
label: string,
|
|
isStretchy?: boolean,
|
|
isShifty?: boolean,
|
|
base: AnyParseNode,
|
|
|},
|
|
"cr": {|
|
|
type: "cr",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
newLine: boolean,
|
|
size: ?Measurement,
|
|
|},
|
|
"delimsizing": {|
|
|
type: "delimsizing",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
size: 1 | 2 | 3 | 4,
|
|
mclass: "mopen" | "mclose" | "mrel" | "mord",
|
|
delim: string,
|
|
|},
|
|
"enclose": {|
|
|
type: "enclose",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
label: string,
|
|
backgroundColor?: string,
|
|
borderColor?: string,
|
|
body: AnyParseNode,
|
|
|},
|
|
"environment": {|
|
|
type: "environment",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
name: string,
|
|
nameGroup: AnyParseNode,
|
|
|},
|
|
"font": {|
|
|
type: "font",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
font: string,
|
|
body: AnyParseNode,
|
|
|},
|
|
"genfrac": {|
|
|
type: "genfrac",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
continued: boolean,
|
|
numer: AnyParseNode,
|
|
denom: AnyParseNode,
|
|
hasBarLine: boolean,
|
|
leftDelim: ?string,
|
|
rightDelim: ?string,
|
|
size: StyleStr | "auto",
|
|
barSize: Measurement | null,
|
|
|},
|
|
"hbox": {|
|
|
type: "hbox",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"horizBrace": {|
|
|
type: "horizBrace",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
label: string,
|
|
isOver: boolean,
|
|
base: AnyParseNode,
|
|
|},
|
|
"href": {|
|
|
type: "href",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
href: string,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"html": {|
|
|
type: "html",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
attributes: {[string]: string},
|
|
body: AnyParseNode[],
|
|
|},
|
|
"htmlmathml": {|
|
|
type: "htmlmathml",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
html: AnyParseNode[],
|
|
mathml: AnyParseNode[],
|
|
|},
|
|
"includegraphics": {|
|
|
type: "includegraphics",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
alt: string,
|
|
width: Measurement,
|
|
height: Measurement,
|
|
totalheight: Measurement,
|
|
src: string,
|
|
|},
|
|
"infix": {|
|
|
type: "infix",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
replaceWith: string,
|
|
size?: Measurement,
|
|
token: ?Token,
|
|
|},
|
|
"internal": {|
|
|
type: "internal",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
|},
|
|
"kern": {|
|
|
type: "kern",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
dimension: Measurement,
|
|
|},
|
|
"lap": {|
|
|
type: "lap",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
alignment: string,
|
|
body: AnyParseNode,
|
|
|},
|
|
"leftright": {|
|
|
type: "leftright",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
left: string,
|
|
right: string,
|
|
rightColor: ?string, // undefined means "inherit"
|
|
|},
|
|
"leftright-right": {|
|
|
type: "leftright-right",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
delim: string,
|
|
color: ?string, // undefined means "inherit"
|
|
|},
|
|
"mathchoice": {|
|
|
type: "mathchoice",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
display: AnyParseNode[],
|
|
text: AnyParseNode[],
|
|
script: AnyParseNode[],
|
|
scriptscript: AnyParseNode[],
|
|
|},
|
|
"middle": {|
|
|
type: "middle",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
delim: string,
|
|
|},
|
|
"mclass": {|
|
|
type: "mclass",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
mclass: string,
|
|
body: AnyParseNode[],
|
|
isCharacterBox: boolean,
|
|
|},
|
|
"operatorname": {|
|
|
type: "operatorname",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
alwaysHandleSupSub: boolean,
|
|
limits: boolean,
|
|
parentIsSupSub: boolean,
|
|
|},
|
|
"overline": {|
|
|
type: "overline",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
|},
|
|
"phantom": {|
|
|
type: "phantom",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"hphantom": {|
|
|
type: "hphantom",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
|},
|
|
"vphantom": {|
|
|
type: "vphantom",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
|},
|
|
"pmb": {|
|
|
type: "pmb",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
mclass: string,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"raisebox": {|
|
|
type: "raisebox",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
dy: Measurement,
|
|
body: AnyParseNode,
|
|
|},
|
|
"rule": {|
|
|
type: "rule",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
shift: ?Measurement,
|
|
width: Measurement,
|
|
height: Measurement,
|
|
|},
|
|
"sizing": {|
|
|
type: "sizing",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
size: number,
|
|
body: AnyParseNode[],
|
|
|},
|
|
"smash": {|
|
|
type: "smash",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
smashHeight: boolean,
|
|
smashDepth: boolean,
|
|
|},
|
|
"sqrt": {|
|
|
type: "sqrt",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
index: ?AnyParseNode,
|
|
|},
|
|
"underline": {|
|
|
type: "underline",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
|},
|
|
"vcenter": {|
|
|
type: "vcenter",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
body: AnyParseNode,
|
|
|},
|
|
"xArrow": {|
|
|
type: "xArrow",
|
|
mode: Mode,
|
|
loc?: ?SourceLocation,
|
|
label: string,
|
|
body: AnyParseNode,
|
|
below: ?AnyParseNode,
|
|
|},
|
|
};
|
|
|
|
/**
|
|
* Asserts that the node is of the given type and returns it with stricter
|
|
* typing. Throws if the node's type does not match.
|
|
*/
|
|
export function assertNodeType<NODETYPE: NodeType>(
|
|
node: ?AnyParseNode,
|
|
type: NODETYPE,
|
|
): ParseNode<NODETYPE> {
|
|
if (!node || node.type !== type) {
|
|
throw new Error(
|
|
`Expected node of type ${type}, but got ` +
|
|
(node ? `node of type ${node.type}` : String(node)));
|
|
}
|
|
// $FlowFixMe, >=0.125
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Returns the node more strictly typed iff it is of the given type. Otherwise,
|
|
* returns null.
|
|
*/
|
|
export function assertSymbolNodeType(node: ?AnyParseNode): SymbolParseNode {
|
|
const typedNode = checkSymbolNodeType(node);
|
|
if (!typedNode) {
|
|
throw new Error(
|
|
`Expected node of symbol group type, but got ` +
|
|
(node ? `node of type ${node.type}` : String(node)));
|
|
}
|
|
return typedNode;
|
|
}
|
|
|
|
/**
|
|
* Returns the node more strictly typed iff it is of the given type. Otherwise,
|
|
* returns null.
|
|
*/
|
|
export function checkSymbolNodeType(node: ?AnyParseNode): ?SymbolParseNode {
|
|
if (node && (node.type === "atom" || NON_ATOMS.hasOwnProperty(node.type))) {
|
|
// $FlowFixMe
|
|
return node;
|
|
}
|
|
return null;
|
|
}
|