Convert ParseNode to struct (#1534)

* Define the nested version of ParseNodes structs explicitly.

Passes test:jest, but fails test:flow.

* Fix additional type errors reported by flow.

* Migrate rebased code from master.

* Rename ParseNode.js to parseNode.js.

* Update defineEnvironment output type to fix the flow errors in environment/array.js.
This commit is contained in:
Ashish Myles
2018-08-01 02:41:27 -04:00
committed by ylemkimon
parent 2202aa774f
commit 0ac4b6e89d
50 changed files with 1386 additions and 866 deletions

View File

@@ -18,7 +18,7 @@ import domTree from "./src/domTree";
import utils from "./src/utils";
import type {SettingsOptions} from "./src/Settings";
import type {AnyParseNode} from "./src/ParseNode";
import type {AnyParseNode} from "./src/parseNode";
import {defineSymbol} from './src/symbols';
import {defineMacro} from './src/macros';

View File

@@ -1,7 +1,7 @@
// @flow
import {Token} from "./Token";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
/**
* This is the ParseError class, which is the main error thrown by KaTeX

View File

@@ -1,426 +0,0 @@
// @flow
import SourceLocation from "./SourceLocation";
import {GROUPS} from "./symbols";
import type {ArrayEnvNodeData} from "./environments/array";
import type {Mode, StyleStr} from "./types";
import type {Token} from "./Token";
import type {Measurement} from "./units";
/**
* The resulting parse tree nodes of the parse tree.
*
* It is possible to provide position information, so that a `ParseNode` can
* fulfill a role similar to a `Token` in error reporting.
* For details on the corresponding properties see `Token` constructor.
* Providing such information can lead to better error reporting.
*/
export default class ParseNode<TYPE: NodeType> {
type: TYPE;
value: NodeValue<TYPE>;
mode: Mode;
loc: ?SourceLocation;
constructor(
type: TYPE, // type of node, like e.g. "ordgroup"
value: NodeValue<TYPE>, // type-specific representation of the node
mode: Mode, // parse mode in action for this node, "math" or "text"
first?: {loc: ?SourceLocation}, // first token or node of the input for
// this node, will omit position information if unset
last?: {loc: ?SourceLocation}, // last token or node of the input for this
// node, will default to firstToken if unset
) {
this.type = type;
this.value = value;
this.mode = mode;
this.loc = SourceLocation.range(first, last);
}
}
export type NodeType = $Keys<ParseNodeTypes>;
export type NodeValue<TYPE: NodeType> = $ElementType<ParseNodeTypes, TYPE>;
export type LeftRightDelimType = {|
type: "leftright",
body: AnyParseNode[],
left: string,
right: string,
|};
// ParseNode's corresponding to Symbol `Group`s in symbols.js.
export type SymbolParseNode =
ParseNode<"accent-token"> |
ParseNode<"bin"> |
ParseNode<"close"> |
ParseNode<"inner"> |
ParseNode<"mathord"> |
ParseNode<"op-token"> |
ParseNode<"open"> |
ParseNode<"punct"> |
ParseNode<"rel"> |
ParseNode<"spacing"> |
ParseNode<"textord">;
// Union of all possible `ParseNode<>` types.
// Unable to derive this directly from `ParseNodeTypes` due to
// https://github.com/facebook/flow/issues/6369.
// Cannot use `ParseNode<NodeType>` since `ParseNode` is not strictly co-variant
// w.r.t. its type parameter due to the way the value type is computed.
export type AnyParseNode =
SymbolParseNode |
ParseNode<"array"> |
ParseNode<"color"> |
ParseNode<"color-token"> |
ParseNode<"op"> |
ParseNode<"ordgroup"> |
ParseNode<"size"> |
ParseNode<"styling"> |
ParseNode<"supsub"> |
ParseNode<"tag"> |
ParseNode<"text"> |
ParseNode<"url"> |
ParseNode<"verb"> |
ParseNode<"accent"> |
ParseNode<"accentUnder"> |
ParseNode<"cr"> |
ParseNode<"delimsizing"> |
ParseNode<"enclose"> |
ParseNode<"environment"> |
ParseNode<"font"> |
ParseNode<"genfrac"> |
ParseNode<"horizBrace"> |
ParseNode<"href"> |
ParseNode<"htmlmathml"> |
ParseNode<"infix"> |
ParseNode<"kern"> |
ParseNode<"lap"> |
ParseNode<"leftright"> |
ParseNode<"leftright-right"> |
ParseNode<"mathchoice"> |
ParseNode<"middle"> |
ParseNode<"mclass"> |
ParseNode<"mod"> |
ParseNode<"operatorname"> |
ParseNode<"overline"> |
ParseNode<"phantom"> |
ParseNode<"hphantom"> |
ParseNode<"vphantom"> |
ParseNode<"raisebox"> |
ParseNode<"rule"> |
ParseNode<"sizing"> |
ParseNode<"smash"> |
ParseNode<"sqrt"> |
ParseNode<"underline"> |
ParseNode<"xArrow">;
// Map from `type` field value to corresponding `value` type.
export type ParseNodeTypes = {
"array": ArrayEnvNodeData,
"color": {|
type: "color",
color: string,
value: AnyParseNode[],
|},
"color-token": 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 simultanouesly.
// - When `symbol` is true, `body` is set.
"op": {|
type: "op",
limits: boolean,
alwaysHandleSupSub?: boolean,
suppressBaseShift?: boolean,
symbol: boolean,
body: string,
value?: void,
|} | {|
type: "op",
limits: boolean,
alwaysHandleSupSub?: boolean,
suppressBaseShift?: boolean,
symbol: false, // If 'symbol' is true, `body` *must* be set.
body?: void,
value: AnyParseNode[],
|},
"ordgroup": AnyParseNode[],
"size": {|
type: "size",
value: Measurement,
isBlank: boolean,
|},
"styling": {|
type: "styling",
style: StyleStr,
value: AnyParseNode[],
|},
"supsub": {|
type: "supsub",
base: ?AnyParseNode,
sup?: ?AnyParseNode,
sub?: ?AnyParseNode,
|},
"tag": {|
type: "tag",
body: AnyParseNode[],
tag: AnyParseNode[],
|},
"text": {|
type: "text",
body: AnyParseNode[],
font?: string,
|},
"url": {|
type: "url",
value: string,
|},
"verb": {|
type: "verb",
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.)
"accent-token": string,
"bin": string,
"close": string,
"inner": string,
"mathord": string,
"op-token": string,
"open": string,
"punct": string,
"rel": string,
"spacing": string,
"textord": string,
// From functions.js and functions/*.js. See also "color", "op", "styling",
// and "text" above.
"accent": {|
type: "accent",
label: string,
isStretchy?: boolean,
isShifty?: boolean,
base: AnyParseNode,
|},
"accentUnder": {|
type: "accentUnder",
label: string,
isStretchy?: boolean,
isShifty?: boolean,
base: AnyParseNode,
|},
"cr": {|
type: "cr",
newRow: boolean,
newLine: boolean,
size: ?ParseNode<"size">,
|},
"delimsizing": {|
type: "delimsizing",
size: 1 | 2 | 3 | 4,
mclass: "mopen" | "mclose" | "mrel" | "mord",
value: string,
|},
"enclose": {|
type: "enclose",
label: string,
backgroundColor?: ParseNode<"color-token">,
borderColor?: ParseNode<"color-token">,
body: AnyParseNode,
|},
"environment": {|
type: "environment",
name: string,
nameGroup: AnyParseNode,
|},
"font": {|
type: "font",
font: string,
body: AnyParseNode,
|},
"genfrac": {|
type: "genfrac",
continued: boolean,
numer: AnyParseNode,
denom: AnyParseNode,
hasBarLine: boolean,
leftDelim: ?string,
rightDelim: ?string,
size: StyleStr | "auto",
barSize: Measurement | null,
|},
"horizBrace": {|
type: "horizBrace",
label: string,
isOver: boolean,
base: AnyParseNode,
|},
"href": {|
type: "href",
href: string,
body: AnyParseNode[],
|},
"htmlmathml": {|
type: "htmlmathml",
html: AnyParseNode[],
mathml: AnyParseNode[],
|},
"infix": {|
type: "infix",
replaceWith: string,
sizeNode?: ParseNode<"size">,
token: ?Token,
|},
"kern": {|
type: "kern",
dimension: Measurement,
|},
"lap": {|
type: "lap",
alignment: string,
body: AnyParseNode,
|},
"leftright": LeftRightDelimType,
"leftright-right": {|
type: "leftright-right",
value: string,
|},
"mathchoice": {|
type: "mathchoice",
display: AnyParseNode[],
text: AnyParseNode[],
script: AnyParseNode[],
scriptscript: AnyParseNode[],
|},
"middle": {|
type: "middle",
value: string,
|},
"mclass": {|
type: "mclass",
mclass: string,
value: AnyParseNode[],
|},
"mod": {|
type: "mod",
modType: string,
value: ?AnyParseNode[],
|},
"operatorname": {|
type: "operatorname",
value: AnyParseNode[],
|},
"overline": {|
type: "overline",
body: AnyParseNode,
|},
"phantom": {|
type: "phantom",
value: AnyParseNode[],
|},
"hphantom": {|
type: "hphantom",
body: AnyParseNode,
value: AnyParseNode[],
|},
"vphantom": {|
type: "vphantom",
body: AnyParseNode,
value: AnyParseNode[],
|},
"raisebox": {|
type: "raisebox",
dy: ParseNode<"size">,
body: AnyParseNode,
value: AnyParseNode[],
|},
"rule": {|
type: "rule",
shift: ?Measurement,
width: Measurement,
height: Measurement,
|},
"sizing": {|
type: "sizing",
size: number,
value: AnyParseNode[],
|},
"smash": {|
type: "smash",
body: AnyParseNode,
smashHeight: boolean,
smashDepth: boolean,
|},
"sqrt": {|
type: "sqrt",
body: AnyParseNode,
index: ?AnyParseNode,
|},
"underline": {|
type: "underline",
body: AnyParseNode,
|},
"xArrow": {|
type: "xArrow",
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> {
const typedNode = checkNodeType(node, type);
if (!typedNode) {
throw new Error(
`Expected node of type ${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 checkNodeType<NODETYPE: NodeType>(
node: ?AnyParseNode,
type: NODETYPE,
): ?ParseNode<NODETYPE> {
if (node && node.type === type) {
// $FlowFixMe: Inference not sophisticated enough to figure this out.
return node;
}
return null;
}
/**
* 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 && GROUPS.hasOwnProperty(node.type)) {
// $FlowFixMe
return node;
}
return null;
}

View File

@@ -9,12 +9,13 @@ import {supportedCodepoint} from "./unicodeScripts";
import unicodeAccents from "./unicodeAccents";
import unicodeSymbols from "./unicodeSymbols";
import utils from "./utils";
import ParseNode, {assertNodeType, checkNodeType} from "./ParseNode";
import {assertNodeType, checkNodeType} from "./parseNode";
import ParseError from "./ParseError";
import {combiningDiacriticalMarksEndRegex, urlFunctionRegex} from "./Lexer.js";
import Settings from "./Settings";
import SourceLocation from "./SourceLocation";
import {Token} from "./Token";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode, SymbolParseNode} from "./parseNode";
import type {Mode, ArgType, BreakToken} from "./types";
import type {FunctionContext, FunctionSpec} from "./defineFunction";
import type {EnvSpec} from "./defineEnvironment";
@@ -238,13 +239,13 @@ export default class Parser {
if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
numerNode = numerBody[0];
} else {
numerNode = new ParseNode("ordgroup", numerBody, this.mode);
numerNode = {type: "ordgroup", mode: this.mode, value: numerBody};
}
if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
denomNode = denomBody[0];
} else {
denomNode = new ParseNode("ordgroup", denomBody, this.mode);
denomNode = {type: "ordgroup", mode: this.mode, value: denomBody};
}
let node;
@@ -311,25 +312,27 @@ export default class Parser {
const textordArray = [];
for (let i = 0; i < text.length; i++) {
textordArray.push(new ParseNode("textord", text[i], "text"));
textordArray.push({type: "textord", mode: "text", value: text[i]});
}
const textNode = new ParseNode(
"text",
{
body: textordArray,
const textNode = {
type: "text",
mode: this.mode,
value: {
type: "text",
body: textordArray,
},
this.mode);
};
const colorNode = new ParseNode(
"color",
{
const colorNode = {
type: "color",
value: {
type: "color",
color: this.settings.errorColor,
value: [textNode],
type: "color",
},
this.mode);
mode: this.mode,
};
this.consume();
return colorNode;
@@ -389,7 +392,7 @@ export default class Parser {
if (superscript) {
throw new ParseError("Double superscript", lex);
}
const prime = new ParseNode("textord", "\\prime", this.mode);
const prime = {type: "textord", mode: this.mode, value: "\\prime"};
// Many primes can be grouped together, so we handle this here
const primes = [prime];
@@ -406,7 +409,7 @@ export default class Parser {
primes.push(this.handleSupSubscript("superscript"));
}
// Put everything into an ordgroup as the superscript
superscript = new ParseNode("ordgroup", primes, this.mode);
superscript = {type: "ordgroup", mode: this.mode, value: primes};
} else {
// If it wasn't ^, _, or ', stop parsing super/subscripts
break;
@@ -417,12 +420,16 @@ export default class Parser {
// but need to check here for type check to pass.
if (superscript || subscript) {
// If we got either a superscript or subscript, create a supsub
return new ParseNode("supsub", {
return {
type: "supsub",
base: base,
sup: superscript,
sub: subscript,
}, this.mode);
mode: this.mode,
value: {
type: "supsub",
base: base,
sup: superscript,
sub: subscript,
},
};
} else {
// Otherwise return the original body
return base;
@@ -749,7 +756,11 @@ export default class Parser {
if (!match) {
throw new ParseError("Invalid color: '" + res.text + "'", res);
}
return newArgument(new ParseNode("color-token", match[0], this.mode), res);
return newArgument({
type: "color-token",
mode: this.mode,
value: match[0],
}, res);
}
/**
@@ -785,11 +796,15 @@ export default class Parser {
if (!validUnit(data)) {
throw new ParseError("Invalid unit: '" + data.unit + "'", res);
}
return newArgument(new ParseNode("size", {
return newArgument({
type: "size",
value: data,
isBlank: isBlank,
}, this.mode), res);
mode: this.mode,
value: {
type: "size",
value: data,
isBlank: isBlank,
},
}, res);
}
/**
@@ -825,10 +840,12 @@ export default class Parser {
this.gullet.endGroup();
// Make sure we get a close brace
this.expect(optional ? "]" : "}");
return newArgument(
new ParseNode(
"ordgroup", expression, this.mode, firstToken, lastToken),
firstToken.range(lastToken, firstToken.text));
return newArgument({
type: "ordgroup",
mode: this.mode,
loc: SourceLocation.range(firstToken, lastToken),
value: expression,
}, firstToken.range(lastToken, firstToken.text));
} else {
// Otherwise, just return a nucleus, or nothing for an optional group
if (mode) {
@@ -857,18 +874,30 @@ export default class Parser {
const v = a.value;
if (v === "-" && group[i + 1].value === "-") {
if (i + 1 < n && group[i + 2].value === "-") {
group.splice(i, 3, new ParseNode(
"textord", "---", "text", a, group[i + 2]));
group.splice(i, 3, {
type: "textord",
mode: "text",
loc: SourceLocation.range(a, group[i + 2]),
value: "---",
});
n -= 2;
} else {
group.splice(i, 2, new ParseNode(
"textord", "--", "text", a, group[i + 1]));
group.splice(i, 2, {
type: "textord",
mode: "text",
loc: SourceLocation.range(a, group[i + 1]),
value: "--",
});
n -= 1;
}
}
if ((v === "'" || v === "`") && group[i + 1].value === v) {
group.splice(i, 2, new ParseNode(
"textord", v + v, "text", a, group[i + 1]));
group.splice(i, 2, {
type: "textord",
mode: "text",
loc: SourceLocation.range(a, group[i + 1]),
value: v + v,
});
n -= 1;
}
}
@@ -911,10 +940,14 @@ export default class Parser {
throw new ParseError(
`Forbidden protocol '${protocol}' in ${funcName}`, nucleus);
}
const urlArg = new ParseNode("url", {
const urlArg = {
type: "url",
value: url,
}, this.mode);
mode: this.mode,
value: {
type: "url",
value: url,
},
};
this.consume();
if (funcName === "\\href") { // two arguments
this.consumeSpaces(); // ignore spaces between arguments
@@ -948,12 +981,15 @@ export default class Parser {
please report what input caused this bug`);
}
arg = arg.slice(1, -1); // remove first and last char
return newArgument(
new ParseNode("verb", {
return newArgument({
type: "verb",
mode: "text",
value: {
type: "verb",
body: arg,
star: star,
}, "text"), nucleus);
},
}, nucleus);
}
// At this point, we should have a symbol, possibly with accents.
// First expand any accented base symbol according to unicodeSymbols.
@@ -978,7 +1014,7 @@ export default class Parser {
}
}
// Recognize base symbol
let symbol = null;
let symbol: AnyParseNode;
if (symbols[this.mode][text]) {
if (this.settings.strict && this.mode === 'math' &&
extraLatin.indexOf(text) >= 0) {
@@ -986,8 +1022,15 @@ export default class Parser {
`Latin-1/Unicode text character "${text[0]}" used in ` +
`math mode`, nucleus);
}
symbol = new ParseNode(symbols[this.mode][text].group,
text, this.mode, nucleus);
// TODO(#1492): Remove this override once this becomes an "atom" type.
// $FlowFixMe
const s: SymbolParseNode = {
type: symbols[this.mode][text].group,
mode: this.mode,
loc: SourceLocation.range(nucleus),
value: text,
};
symbol = s;
} else if (text.charCodeAt(0) >= 0x80) { // no symbol for e.g. ^
if (this.settings.strict) {
if (!supportedCodepoint(text.charCodeAt(0))) {
@@ -1000,7 +1043,12 @@ export default class Parser {
nucleus);
}
}
symbol = new ParseNode("textord", text, this.mode, nucleus);
symbol = {
type: "textord",
mode: this.mode,
loc: SourceLocation.range(nucleus),
value: text,
};
} else {
return null; // EOF, ^, _, {, }, etc.
}
@@ -1018,13 +1066,18 @@ export default class Parser {
`Accent ${accent} unsupported in ${this.mode} mode`,
nucleus);
}
symbol = new ParseNode("accent", {
symbol = {
type: "accent",
label: command,
isStretchy: false,
isShifty: true,
base: symbol,
}, this.mode, nucleus);
mode: this.mode,
loc: SourceLocation.range(nucleus),
value: {
type: "accent",
label: command,
isStretchy: false,
isShifty: true,
base: symbol,
},
};
}
}
return newArgument(symbol, nucleus);

View File

@@ -9,7 +9,7 @@ import utils from "./utils";
import ParseError from "./ParseError.js";
import {Token} from "./Token";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
import type {MacroMap} from "./macros";
export type StrictFunction =

View File

@@ -14,8 +14,8 @@ import {calculateSize} from "./units";
import * as tree from "./tree";
import type Options from "./Options";
import type ParseNode from "./ParseNode";
import type {NodeType} from "./ParseNode";
import type {ParseNode} from "./parseNode";
import type {NodeType} from "./parseNode";
import type {CharacterMetrics} from "./fontMetrics";
import type {FontVariant, Mode} from "./types";
import type {documentFragment as HtmlDocumentFragment} from "./domTree";

View File

@@ -11,13 +11,13 @@ import Style from "./Style";
import buildCommon from "./buildCommon";
import domTree from "./domTree";
import utils, {assert} from "./utils";
import {checkNodeType} from "./ParseNode";
import {checkNodeType} from "./parseNode";
import {spacings, tightSpacings} from "./spacingData";
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
import * as tree from "./tree";
import type Options from "./Options";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
import type {HtmlDomNode, DomSpan} from "./domTree";
const makeSpan = buildCommon.makeSpan;

View File

@@ -15,7 +15,7 @@ import {_mathmlGroupBuilders as groupBuilders} from "./defineFunction";
import {MathNode, TextNode} from "./mathMLTree";
import type Options from "./Options";
import type {AnyParseNode, SymbolParseNode} from "./ParseNode";
import type {AnyParseNode, SymbolParseNode} from "./parseNode";
import type {DomSpan} from "./domTree";
import type {MathDomNode} from "./mathMLTree";
import type {FontVariant, Mode} from "./types";

View File

@@ -6,7 +6,7 @@ import Options from "./Options";
import Settings from "./Settings";
import Style from "./Style";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
import type {DomSpan} from "./domTree";
const optionsFromSettings = function(settings: Settings) {

View File

@@ -1,12 +1,10 @@
// @flow
import {_htmlGroupBuilders, _mathmlGroupBuilders} from "./defineFunction";
import ParseNode from "./ParseNode";
import type Parser from "./Parser";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
import type {ArgType, Mode} from "./types";
import type {NodeType} from "./ParseNode";
import type {NodeType} from "./parseNode";
import type {HtmlBuilder, MathMLBuilder} from "./defineFunction";
/**
@@ -26,11 +24,11 @@ type EnvContext = {|
* - args: an array of arguments passed to \begin{name}
* - optArgs: an array of optional arguments passed to \begin{name}
*/
type EnvHandler<NODETYPE: NodeType> = (
type EnvHandler = (
context: EnvContext,
args: AnyParseNode[],
optArgs: (?AnyParseNode)[],
) => ParseNode<NODETYPE>;
) => AnyParseNode;
/**
* - numArgs: (default 0) The number of arguments after the \begin{name} function.
@@ -57,9 +55,7 @@ export type EnvSpec<NODETYPE: NodeType> = {|
greediness: number,
allowedInText: boolean,
numOptionalArgs: number,
// FLOW TYPE NOTES: Same issue as the notes on the handler of FunctionSpec
// in defineFunction.
handler: EnvHandler<*>,
handler: EnvHandler,
|};
/**
@@ -80,7 +76,7 @@ type EnvDefSpec<NODETYPE: NodeType> = {|
// Properties that control how the environments are parsed.
props: EnvProps,
handler: EnvHandler<NODETYPE>,
handler: EnvHandler,
// This function returns an object representing the DOM structure to be
// created when rendering the defined LaTeX function.

View File

@@ -1,8 +1,8 @@
// @flow
import {checkNodeType} from "./ParseNode";
import {checkNodeType} from "./parseNode";
import type Parser from "./Parser";
import type ParseNode, {AnyParseNode, NodeType} from "./ParseNode";
import type {ParseNode, AnyParseNode, NodeType} from "./parseNode";
import type Options from "./Options";
import type {ArgType, BreakToken, Mode} from "./types";
import type {HtmlDomNode} from "./domTree";

View File

@@ -4,9 +4,8 @@ import defineEnvironment from "../defineEnvironment";
import defineFunction from "../defineFunction";
import mathMLTree from "../mathMLTree";
import ParseError from "../ParseError";
import ParseNode from "../ParseNode";
import {assertNodeType, assertSymbolNodeType} from "../ParseNode";
import {checkNodeType, checkSymbolNodeType} from "../ParseNode";
import {assertNodeType, assertSymbolNodeType} from "../parseNode";
import {checkNodeType, checkSymbolNodeType} from "../parseNode";
import {calculateSize} from "../units";
import utils from "../utils";
@@ -14,7 +13,7 @@ import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Parser from "../Parser";
import type {AnyParseNode} from "../ParseNode";
import type {ParseNode, AnyParseNode} from "../parseNode";
import type {StyleStr} from "../types";
import type {HtmlBuilder, MathMLBuilder} from "../defineFunction";
@@ -29,9 +28,9 @@ type AlignSpec = { type: "separator", separator: string } | {
export type ArrayEnvNodeData = {|
type: "array",
hskipBeforeAndAfter?: boolean,
arraystretch: number,
addJot?: boolean,
cols?: AlignSpec[],
arraystretch: number,
body: AnyParseNode[][], // List of rows in the (2D) array.
rowGaps: (?ParseNode<"size">)[],
hLinesBeforeRow: Array<boolean[]>,
@@ -40,10 +39,10 @@ export type ArrayEnvNodeData = {|
type ArrayEnvNodeDataIncomplete = {|
type: "array",
hskipBeforeAndAfter?: boolean,
arraystretch?: number,
addJot?: boolean,
cols?: AlignSpec[],
// Before these fields are filled.
arraystretch?: number,
body?: AnyParseNode[][],
rowGaps?: (?ParseNode<"size">)[],
hLinesBeforeRow?: Array<boolean[]>,
@@ -103,13 +102,21 @@ function parseArray(
while (true) { // eslint-disable-line no-constant-condition
let cell = parser.parseExpression(false, "\\cr");
cell = new ParseNode("ordgroup", cell, parser.mode);
cell = {
type: "ordgroup",
mode: parser.mode,
value: cell,
};
if (style) {
cell = new ParseNode("styling", {
cell = {
type: "styling",
style: style,
value: [cell],
}, parser.mode);
mode: parser.mode,
value: {
type: "styling",
style: style,
value: [cell],
},
};
}
row.push(cell);
const next = parser.nextToken.text;
@@ -119,7 +126,8 @@ function parseArray(
// Arrays terminate newlines with `\crcr` which consumes a `\cr` if
// the last line is empty.
// NOTE: Currently, `cell` is the last item added into `row`.
if (row.length === 1 && cell.value.value[0].value.length === 0) {
if (row.length === 1 && cell.type === "styling" &&
cell.value.value[0].value.length === 0) {
body.pop();
}
if (hLinesBeforeRow.length < body.length + 1) {
@@ -149,7 +157,11 @@ function parseArray(
// $FlowFixMe: The required fields were added immediately above.
const res: ArrayEnvNodeData = result;
parser.gullet.endGroup();
return new ParseNode("array", res, parser.mode);
return {
type: "array",
mode: parser.mode,
value: res,
};
}
@@ -410,7 +422,11 @@ const alignedHandler = function(context, args) {
// binary. This behavior is implemented in amsmath's \start@aligned.
let numMaths;
let numCols = 0;
const emptyGroup = new ParseNode("ordgroup", [], context.mode);
const emptyGroup = {
type: "ordgroup",
mode: context.mode,
value: [],
};
const ordgroup = checkNodeType(args[0], "ordgroup");
if (ordgroup) {
let arg0 = "";
@@ -529,7 +545,7 @@ defineEnvironment({
props: {
numArgs: 0,
},
handler: function(context) {
handler(context) {
const delimiters = {
"matrix": null,
"pmatrix": ["(", ")"],
@@ -538,20 +554,22 @@ defineEnvironment({
"vmatrix": ["|", "|"],
"Vmatrix": ["\\Vert", "\\Vert"],
}[context.envName];
let res = {
const payload = {
type: "array",
hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
};
res = parseArray(context.parser, res, dCellStyle(context.envName));
if (delimiters) {
res = new ParseNode("leftright", {
const res: ParseNode<"array"> =
parseArray(context.parser, payload, dCellStyle(context.envName));
return delimiters ? {
type: "leftright",
mode: context.mode,
value: {
type: "leftright",
body: [res],
left: delimiters[0],
right: delimiters[1],
}, context.mode);
}
return res;
},
} : res;
},
htmlBuilder,
mathmlBuilder,
@@ -571,8 +589,8 @@ defineEnvironment({
props: {
numArgs: 0,
},
handler: function(context) {
let res = {
handler(context) {
const payload = {
type: "array",
arraystretch: 1.2,
cols: [{
@@ -591,14 +609,18 @@ defineEnvironment({
postgap: 0,
}],
};
res = parseArray(context.parser, res, dCellStyle(context.envName));
res = new ParseNode("leftright", {
const res: ParseNode<"array"> =
parseArray(context.parser, payload, dCellStyle(context.envName));
return {
type: "leftright",
body: [res],
left: "\\{",
right: ".",
}, context.mode);
return res;
mode: context.mode,
value: {
type: "leftright",
body: [res],
left: "\\{",
right: ".",
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -4,13 +4,13 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import stretchy from "../stretchy";
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
import {assertNodeType, checkNodeType} from "../parseNode";
import {assertSpan, assertSymbolDomNode} from "../domTree";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {AnyParseNode} from "../ParseNode";
import type {ParseNode, AnyParseNode} from "../parseNode";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "accent", but
@@ -230,13 +230,17 @@ defineFunction({
context.funcName === "\\widetilde" ||
context.funcName === "\\widecheck";
return new ParseNode("accent", {
return {
type: "accent",
label: context.funcName,
isStretchy: isStretchy,
isShifty: isShifty,
base: base,
}, context.parser.mode);
mode: context.parser.mode,
value: {
type: "accent",
label: context.funcName,
isStretchy: isStretchy,
isShifty: isShifty,
base: base,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -257,13 +261,17 @@ defineFunction({
handler: (context, args) => {
const base = args[0];
return new ParseNode("accent", {
return {
type: "accent",
label: context.funcName,
isStretchy: false,
isShifty: true,
base: base,
}, context.parser.mode);
mode: context.parser.mode,
value: {
type: "accent",
label: context.funcName,
isStretchy: false,
isShifty: true,
base: base,
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -4,11 +4,12 @@ import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {ParseNode} from "../parseNode";
defineFunction({
type: "accentUnder",
names: [
@@ -20,11 +21,15 @@ defineFunction({
},
handler: ({parser, funcName}, args) => {
const base = args[0];
return new ParseNode("accentUnder", {
return {
type: "accentUnder",
label: funcName,
base: base,
}, parser.mode);
mode: parser.mode,
value: {
type: "accentUnder",
label: funcName,
base: base,
},
};
},
htmlBuilder: (group: ParseNode<"accentUnder">, options) => {
// Treat under accents much like underlines.

View File

@@ -3,11 +3,12 @@ import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import ParseNode from "../ParseNode.js";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {ParseNode} from "../parseNode";
// Stretchy arrows with an optional argument
defineFunction({
type: "xArrow",
@@ -27,12 +28,16 @@ defineFunction({
numOptionalArgs: 1,
},
handler({parser, funcName}, args, optArgs) {
return new ParseNode("xArrow", {
type: "xArrow", // x for extensible
label: funcName,
body: args[0],
below: optArgs[0],
}, parser.mode);
return {
type: "xArrow",
mode: parser.mode,
value: {
type: "xArrow", // x for extensible
label: funcName,
body: args[0],
below: optArgs[0],
},
};
},
// Flow is unable to correctly infer the type of `group`, even though it's
// unamibiguously determined from the passed-in `type` above.

View File

@@ -1,7 +1,7 @@
// @flow
import defineFunction from "../defineFunction";
import ParseError from "../ParseError";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
// \@char is an internal function that takes a grouped decimal argument like
// {123} and converts into symbol with code 123. It is used by the *macro*
@@ -25,6 +25,10 @@ defineFunction({
if (isNaN(code)) {
throw new ParseError(`\\@char has non-numeric argument ${number}`);
}
return new ParseNode("textord", String.fromCharCode(code), parser.mode);
return {
type: "textord",
mode: parser.mode,
value: String.fromCharCode(code),
};
},
});

View File

@@ -2,7 +2,7 @@
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -43,11 +43,15 @@ defineFunction({
handler({parser}, args) {
const color = assertNodeType(args[0], "color-token");
const body = args[1];
return new ParseNode("color", {
return {
type: "color",
color: color.value,
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "color",
color: color.value,
value: ordargument(body),
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -78,11 +82,15 @@ defineFunction({
},
handler({parser, funcName}, args) {
const body = args[0];
return new ParseNode("color", {
return {
type: "color",
color: "katex-" + funcName.slice(1),
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "color",
color: "katex-" + funcName.slice(1),
value: ordargument(body),
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -103,11 +111,15 @@ defineFunction({
// If we see a styling function, parse out the implicit body
const body = parser.parseExpression(true, breakOnTokenText);
return new ParseNode("color", {
return {
type: "color",
color: color.value,
value: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "color",
color: color.value,
value: body,
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -6,7 +6,7 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import {calculateSize} from "../units";
import ParseError from "../ParseError";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
// \\ is a macro mapping to either \cr or \newline. Because they have the
// same signature, we implement them as one megafunction, with newRow
@@ -37,12 +37,16 @@ defineFunction({
newLine = true;
}
}
return new ParseNode("cr", {
return {
type: "cr",
newLine,
newRow,
size: size && assertNodeType(size, "size"),
}, parser.mode);
mode: parser.mode,
value: {
type: "cr",
newLine,
newRow,
size: size && assertNodeType(size, "size"),
},
};
},
// The following builders are called only at the top level,

View File

@@ -5,14 +5,14 @@ import delimiter from "../delimiter";
import mathMLTree from "../mathMLTree";
import ParseError from "../ParseError";
import utils from "../utils";
import ParseNode, {assertNodeType, checkSymbolNodeType} from "../ParseNode";
import {assertNodeType, checkSymbolNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type {AnyParseNode, SymbolParseNode} from "../ParseNode";
import type {LeftRightDelimType} from "../ParseNode";
import type {AnyParseNode, ParseNode, SymbolParseNode} from "../parseNode";
import type {LeftRightDelimType} from "../parseNode";
import type {FunctionContext} from "../defineFunction";
// Extra data needed for the delimiter handler down below
@@ -83,12 +83,16 @@ defineFunction({
handler: (context, args) => {
const delim = checkDelimiter(args[0], context);
return new ParseNode("delimsizing", {
return {
type: "delimsizing",
size: delimiterSizes[context.funcName].size,
mclass: delimiterSizes[context.funcName].mclass,
value: delim.value,
}, context.parser.mode);
mode: context.parser.mode,
value: {
type: "delimsizing",
size: delimiterSizes[context.funcName].size,
mclass: delimiterSizes[context.funcName].mclass,
value: delim.value,
},
};
},
htmlBuilder: (group, options) => {
const delim = group.value.value;
@@ -147,10 +151,14 @@ defineFunction({
// \left case below triggers parsing of \right in
// `const right = parser.parseFunction();`
// uses this return value.
return new ParseNode("leftright-right", {
return {
type: "leftright-right",
value: checkDelimiter(args[0], context).value,
}, context.parser.mode);
mode: context.parser.mode,
value: {
type: "leftright-right",
value: checkDelimiter(args[0], context).value,
},
};
},
});
@@ -176,12 +184,16 @@ defineFunction({
if (!right) {
throw new ParseError('failed to parse function after \\right');
}
return new ParseNode("leftright", {
return {
type: "leftright",
body: body,
left: delim.value,
right: assertNodeType(right, "leftright-right").value.value,
}, parser.mode);
mode: parser.mode,
value: {
type: "leftright",
body: body,
left: delim.value,
right: assertNodeType(right, "leftright-right").value.value,
},
};
},
htmlBuilder: (group, options) => {
const groupValue = leftRightGroupValue(group);
@@ -295,10 +307,14 @@ defineFunction({
throw new ParseError("\\middle without preceding \\left", delim);
}
return new ParseNode("middle", {
return {
type: "middle",
value: delim.value,
}, context.parser.mode);
mode: context.parser.mode,
value: {
type: "middle",
value: delim.value,
},
};
},
htmlBuilder: (group, options) => {
let middleDelim;

View File

@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import stretchy from "../stretchy";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -149,12 +149,16 @@ defineFunction({
handler({parser, funcName}, args, optArgs) {
const color = assertNodeType(args[0], "color-token");
const body = args[1];
return new ParseNode("enclose", {
return {
type: "enclose",
label: funcName,
backgroundColor: color,
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "enclose",
label: funcName,
backgroundColor: color,
body: body,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -173,13 +177,17 @@ defineFunction({
const borderColor = assertNodeType(args[0], "color-token");
const backgroundColor = assertNodeType(args[1], "color-token");
const body = args[2];
return new ParseNode("enclose", {
return {
type: "enclose",
label: funcName,
backgroundColor: backgroundColor,
borderColor: borderColor,
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "enclose",
label: funcName,
backgroundColor: backgroundColor,
borderColor: borderColor,
body: body,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -193,11 +201,15 @@ defineFunction({
},
handler({parser, funcName}, args, optArgs) {
const body = args[0];
return new ParseNode("enclose", {
return {
type: "enclose",
label: funcName,
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "enclose",
label: funcName,
body: body,
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -1,7 +1,7 @@
// @flow
import defineFunction from "../defineFunction";
import ParseError from "../ParseError";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
// Environment delimiters. HTML/MathML rendering is defined in the corresponding
// defineEnvironment definitions.
@@ -21,10 +21,14 @@ defineFunction({
for (let i = 0; i < nameGroup.value.length; ++i) {
name += assertNodeType(nameGroup.value[i], "textord").value;
}
return new ParseNode("environment", {
return {
type: "environment",
name: name,
nameGroup: nameGroup,
}, parser.mode);
mode: parser.mode,
value: {
type: "environment",
name: name,
nameGroup: nameGroup,
},
};
},
});

View File

@@ -3,7 +3,6 @@
import {binrelClass} from "./mclass";
import defineFunction from "../defineFunction";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -51,11 +50,15 @@ defineFunction({
if (func in fontAliases) {
func = fontAliases[func];
}
return new ParseNode("font", {
return {
type: "font",
font: func.slice(1),
body,
}, parser.mode);
mode: parser.mode,
value: {
type: "font",
font: func.slice(1),
body,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -72,17 +75,25 @@ defineFunction({
const body = args[0];
// amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the
// argument's bin|rel|ord status
return new ParseNode("mclass", {
return {
type: "mclass",
mclass: binrelClass(body),
value: [
new ParseNode("font", {
type: "font",
font: "boldsymbol",
body,
}, parser.mode),
],
}, parser.mode);
mode: parser.mode,
value: {
type: "mclass",
mclass: binrelClass(body),
value: [
{
type: "font",
mode: parser.mode,
value: {
type: "font",
font: "boldsymbol",
body,
},
},
],
},
};
},
});
@@ -100,11 +111,19 @@ defineFunction({
const body = parser.parseExpression(true, breakOnTokenText);
const style = `math${funcName.slice(1)}`;
return new ParseNode("font", {
return {
type: "font",
font: style,
body: new ParseNode("ordgroup", body, parser.mode),
}, mode);
mode: mode,
value: {
type: "font",
font: style,
body: {
type: "ordgroup",
mode: parser.mode,
value: body,
},
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
import delimiter from "../delimiter";
import mathMLTree from "../mathMLTree";
import Style from "../Style";
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
import {assertNodeType, checkNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -281,17 +281,21 @@ defineFunction({
break;
}
return new ParseNode("genfrac", {
return {
type: "genfrac",
continued: funcName === "\\cfrac",
numer: numer,
denom: denom,
hasBarLine: hasBarLine,
leftDelim: leftDelim,
rightDelim: rightDelim,
size: size,
barSize: null,
}, parser.mode);
mode: parser.mode,
value: {
type: "genfrac",
continued: funcName === "\\cfrac",
numer: numer,
denom: denom,
hasBarLine: hasBarLine,
leftDelim: leftDelim,
rightDelim: rightDelim,
size: size,
barSize: null,
},
};
},
htmlBuilder,
@@ -328,11 +332,15 @@ defineFunction({
default:
throw new Error("Unrecognized infix genfrac command");
}
return new ParseNode("infix", {
return {
type: "infix",
replaceWith: replaceWith,
token: token,
}, parser.mode);
mode: parser.mode,
value: {
type: "infix",
replaceWith: replaceWith,
token: token,
},
};
},
});
@@ -401,17 +409,21 @@ defineFunction({
size = stylArray[Number(styl.value)];
}
return new ParseNode("genfrac", {
return {
type: "genfrac",
numer: numer,
denom: denom,
continued: false,
hasBarLine: hasBarLine,
barSize: barSize,
leftDelim: leftDelim,
rightDelim: rightDelim,
size: size,
}, parser.mode);
mode: parser.mode,
value: {
type: "genfrac",
numer: numer,
denom: denom,
continued: false,
hasBarLine: hasBarLine,
barSize: barSize,
leftDelim: leftDelim,
rightDelim: rightDelim,
size: size,
},
};
},
htmlBuilder,
@@ -429,12 +441,16 @@ defineFunction({
},
handler({parser, funcName, token}, args) {
const sizeNode = assertNodeType(args[0], "size");
return new ParseNode("infix", {
return {
type: "infix",
replaceWith: "\\\\abovefrac",
sizeNode: sizeNode,
token: token,
}, parser.mode);
mode: parser.mode,
value: {
type: "infix",
replaceWith: "\\\\abovefrac",
sizeNode: sizeNode,
token: token,
},
};
},
});
@@ -453,17 +469,21 @@ defineFunction({
const barSize = sizeNode.value.value;
const hasBarLine = barSize.number > 0;
return new ParseNode("genfrac", {
return {
type: "genfrac",
numer: numer,
denom: denom,
continued: false,
hasBarLine: hasBarLine,
barSize: barSize,
leftDelim: null,
rightDelim: null,
size: "auto",
}, parser.mode);
mode: parser.mode,
value: {
type: "genfrac",
numer: numer,
denom: denom,
continued: false,
hasBarLine: hasBarLine,
barSize: barSize,
leftDelim: null,
rightDelim: null,
size: "auto",
},
};
},
htmlBuilder,

View File

@@ -4,12 +4,13 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import Style from "../Style";
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
import {assertNodeType, checkNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {ParseNode} from "../parseNode";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but
// also "supsub" since an over/underbrace can affect super/subscripting.
@@ -126,12 +127,16 @@ defineFunction({
numArgs: 1,
},
handler({parser, funcName}, args) {
return new ParseNode("horizBrace", {
return {
type: "horizBrace",
label: funcName,
isOver: /^\\over/.test(funcName),
base: args[0],
}, parser.mode);
mode: parser.mode,
value: {
type: "horizBrace",
label: funcName,
isOver: /^\\over/.test(funcName),
base: args[0],
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -1,7 +1,7 @@
// @flow
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import {assertType} from "../utils";
import {MathNode} from "../mathMLTree";
@@ -19,11 +19,15 @@ defineFunction({
handler: ({parser}, args) => {
const body = args[1];
const href = assertNodeType(args[0], "url").value.value;
return new ParseNode("href", {
return {
type: "href",
href: href,
body: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "href",
href: href,
body: ordargument(body),
},
};
},
htmlBuilder: (group, options) => {
const elements = html.buildExpression(
@@ -59,17 +63,29 @@ defineFunction({
if (c === "~") {
c = "\\textasciitilde";
}
chars.push(new ParseNode("textord", c, "text"));
chars.push({
type: "textord",
mode: "text",
value: c,
});
}
const body = new ParseNode("text", {
const body = {
type: "text",
font: "\\texttt",
body: chars,
}, parser.mode);
return new ParseNode("href", {
mode: parser.mode,
value: {
type: "text",
font: "\\texttt",
body: chars,
},
};
return {
type: "href",
href: href,
body: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "href",
href: href,
body: ordargument(body),
},
};
},
});

View File

@@ -1,7 +1,6 @@
// @flow
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -14,11 +13,15 @@ defineFunction({
allowedInText: true,
},
handler: ({parser}, args) => {
return new ParseNode("htmlmathml", {
return {
type: "htmlmathml",
html: ordargument(args[0]),
mathml: ordargument(args[1]),
}, parser.mode);
mode: parser.mode,
value: {
type: "htmlmathml",
html: ordargument(args[0]),
mathml: ordargument(args[1]),
},
};
},
htmlBuilder: (group, options) => {
const elements = html.buildExpression(

View File

@@ -5,7 +5,7 @@ import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import {calculateSize} from "../units";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
// TODO: \hskip and \mskip should support plus and minus in lengths
@@ -39,10 +39,14 @@ defineFunction({
}
}
}
return new ParseNode("kern", {
return {
type: "kern",
dimension: size.value.value,
}, parser.mode);
mode: parser.mode,
value: {
type: "kern",
dimension: size.value.value,
},
};
},
htmlBuilder: (group, options) => {
return buildCommon.makeGlue(group.value.dimension, options);

View File

@@ -3,7 +3,6 @@
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -17,11 +16,15 @@ defineFunction({
},
handler: ({parser, funcName}, args) => {
const body = args[0];
return new ParseNode("lap", {
return {
type: "lap",
alignment: funcName.slice(5),
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "lap",
alignment: funcName.slice(5),
body: body,
},
};
},
htmlBuilder: (group, options) => {
// mathllap, mathrlap, mathclap

View File

@@ -1,7 +1,6 @@
// @flow
import defineFunction from "../defineFunction";
import ParseError from "../ParseError";
import ParseNode from "../ParseNode";
// Switching from text mode back to math mode
defineFunction({
@@ -23,11 +22,15 @@ defineFunction({
parser.expect(close, false);
parser.switchMode(outerMode);
parser.consume();
return new ParseNode("styling", {
return {
type: "styling",
style: "text",
value: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "styling",
style: "text",
value: body,
},
};
},
});

View File

@@ -2,7 +2,6 @@
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import Style from "../Style";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -29,13 +28,17 @@ defineFunction({
numArgs: 4,
},
handler: ({parser}, args) => {
return new ParseNode("mathchoice", {
return {
type: "mathchoice",
display: ordargument(args[0]),
text: ordargument(args[1]),
script: ordargument(args[2]),
scriptscript: ordargument(args[3]),
}, parser.mode);
mode: parser.mode,
value: {
type: "mathchoice",
display: ordargument(args[0]),
text: ordargument(args[1]),
script: ordargument(args[2]),
scriptscript: ordargument(args[3]),
},
};
},
htmlBuilder: (group, options) => {
const body = chooseMathStyle(group, options);

View File

@@ -2,8 +2,7 @@
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode from "../ParseNode";
import type {AnyParseNode} from "../ParseNode";
import type {AnyParseNode} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -32,11 +31,15 @@ defineFunction({
},
handler({parser, funcName}, args) {
const body = args[0];
return new ParseNode("mclass", {
return {
type: "mclass",
mclass: "m" + funcName.substr(5),
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "mclass",
mclass: "m" + funcName.substr(5),
value: ordargument(body),
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -65,11 +68,15 @@ defineFunction({
numArgs: 2,
},
handler({parser}, args) {
return new ParseNode("mclass", {
return {
type: "mclass",
mclass: binrelClass(args[0]),
value: [args[1]],
}, parser.mode);
mode: parser.mode,
value: {
type: "mclass",
mclass: binrelClass(args[0]),
value: [args[1]],
},
};
},
});
@@ -92,27 +99,39 @@ defineFunction({
mclass = "mrel"; // for \stackrel
}
const baseOp = new ParseNode("op", {
const baseOp = {
type: "op",
limits: true,
alwaysHandleSupSub: true,
symbol: false,
suppressBaseShift: funcName !== "\\stackrel",
value: ordargument(baseArg),
}, baseArg.mode);
mode: baseArg.mode,
value: {
type: "op",
limits: true,
alwaysHandleSupSub: true,
symbol: false,
suppressBaseShift: funcName !== "\\stackrel",
value: ordargument(baseArg),
},
};
const supsub = new ParseNode("supsub", {
const supsub = {
type: "supsub",
base: baseOp,
sup: funcName === "\\underset" ? null : shiftedArg,
sub: funcName === "\\underset" ? shiftedArg : null,
}, shiftedArg.mode);
mode: shiftedArg.mode,
value: {
type: "supsub",
base: baseOp,
sup: funcName === "\\underset" ? null : shiftedArg,
sub: funcName === "\\underset" ? shiftedArg : null,
},
};
return new ParseNode("mclass", {
return {
type: "mclass",
mclass: mclass,
value: [supsub],
}, parser.mode);
mode: parser.mode,
value: {
type: "mclass",
mclass: mclass,
value: [supsub],
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -6,12 +6,13 @@ import domTree from "../domTree";
import * as mathMLTree from "../mathMLTree";
import utils from "../utils";
import Style from "../Style";
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
import {assertNodeType, checkNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {ParseNode} from "../parseNode";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also
// "supsub" since some of them (like \int) can affect super/subscripting.
@@ -296,12 +297,16 @@ defineFunction({
if (fName.length === 1) {
fName = singleCharBigOps[fName];
}
return new ParseNode("op", {
return {
type: "op",
limits: true,
symbol: true,
body: fName,
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: true,
symbol: true,
body: fName,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -317,12 +322,16 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("op", {
return {
type: "op",
limits: false,
symbol: false,
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: false,
symbol: false,
value: ordargument(body),
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -349,12 +358,16 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("op", {
return {
type: "op",
limits: false,
symbol: false,
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: false,
symbol: false,
value: ordargument(body),
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -374,12 +387,16 @@ defineFunction({
numArgs: 0,
},
handler({parser, funcName}) {
return new ParseNode("op", {
return {
type: "op",
limits: false,
symbol: false,
body: funcName,
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: false,
symbol: false,
body: funcName,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -395,12 +412,16 @@ defineFunction({
numArgs: 0,
},
handler({parser, funcName}) {
return new ParseNode("op", {
return {
type: "op",
limits: true,
symbol: false,
body: funcName,
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: true,
symbol: false,
body: funcName,
},
};
},
htmlBuilder,
mathmlBuilder,
@@ -421,12 +442,16 @@ defineFunction({
if (fName.length === 1) {
fName = singleCharIntegrals[fName];
}
return new ParseNode("op", {
return {
type: "op",
limits: false,
symbol: true,
body: fName,
}, parser.mode);
mode: parser.mode,
value: {
type: "op",
limits: false,
symbol: true,
body: fName,
},
};
},
htmlBuilder,
mathmlBuilder,

View File

@@ -1,5 +1,4 @@
// @flow
import ParseNode from "../ParseNode";
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
@@ -18,10 +17,14 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("operatorname", {
return {
type: "operatorname",
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "operatorname",
value: ordargument(body),
},
};
},
htmlBuilder: (group, options) => {
@@ -29,7 +32,11 @@ defineFunction({
const groupValue = group.value.value.map(child => {
const childValue = child.value;
if (typeof childValue === "string") {
return new ParseNode("textord", childValue, child.mode);
return {
type: "textord",
mode: child.mode,
value: childValue,
};
} else {
return child;
}

View File

@@ -2,7 +2,6 @@
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -15,10 +14,14 @@ defineFunction({
},
handler({parser}, args) {
const body = args[0];
return new ParseNode("overline", {
return {
type: "overline",
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "overline",
body: body,
},
};
},
htmlBuilder(group, options) {
// Overlines are handled in the TeXbook pg 443, Rule 9.

View File

@@ -2,7 +2,6 @@
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -16,10 +15,14 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("phantom", {
return {
type: "phantom",
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "phantom",
value: ordargument(body),
},
};
},
htmlBuilder: (group, options) => {
const elements = html.buildExpression(
@@ -47,11 +50,15 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("hphantom", {
return {
type: "hphantom",
value: ordargument(body),
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "hphantom",
value: ordargument(body),
body: body,
},
};
},
htmlBuilder: (group, options) => {
let node = buildCommon.makeSpan(
@@ -90,11 +97,15 @@ defineFunction({
},
handler: ({parser}, args) => {
const body = args[0];
return new ParseNode("vphantom", {
return {
type: "vphantom",
value: ordargument(body),
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "vphantom",
value: ordargument(body),
body: body,
},
};
},
htmlBuilder: (group, options) => {
const inner = buildCommon.makeSpan(

View File

@@ -2,7 +2,7 @@
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import {calculateSize} from "../units";
import * as mml from "../buildMathML";
@@ -20,24 +20,36 @@ defineFunction({
handler({parser}, args) {
const amount = assertNodeType(args[0], "size");
const body = args[1];
return new ParseNode("raisebox", {
return {
type: "raisebox",
dy: amount,
body: body,
value: ordargument(body),
}, parser.mode);
mode: parser.mode,
value: {
type: "raisebox",
dy: amount,
body: body,
value: ordargument(body),
},
};
},
htmlBuilder(group, options) {
const text = new ParseNode("text", {
const text = {
type: "text",
body: group.value.value,
font: "mathrm", // simulate \textrm
}, group.mode);
const sizedText = new ParseNode("sizing", {
mode: group.mode,
value: {
type: "text",
body: group.value.value,
font: "mathrm", // simulate \textrm
},
};
const sizedText = {
type: "sizing",
value: [text],
size: 6, // simulate \normalsize
}, group.mode);
mode: group.mode,
value: {
type: "sizing",
value: [text],
size: 6, // simulate \normalsize
},
};
const body = sizing.htmlBuilder(sizedText, options);
const dy = calculateSize(group.value.dy.value.value, options);
return buildCommon.makeVList({

View File

@@ -2,7 +2,7 @@
import buildCommon from "../buildCommon";
import defineFunction from "../defineFunction";
import mathMLTree from "../mathMLTree";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import {calculateSize} from "../units";
defineFunction({
@@ -17,12 +17,16 @@ defineFunction({
const shift = optArgs[0];
const width = assertNodeType(args[0], "size");
const height = assertNodeType(args[1], "size");
return new ParseNode("rule", {
return {
type: "rule",
shift: shift && assertNodeType(shift, "size").value.value,
width: width.value.value,
height: height.value.value,
}, parser.mode);
mode: parser.mode,
value: {
type: "rule",
shift: shift && assertNodeType(shift, "size").value.value,
width: width.value.value,
height: height.value.value,
},
};
},
htmlBuilder(group, options) {
// Make an empty span for the rule

View File

@@ -3,7 +3,6 @@ import buildCommon from "../buildCommon";
import defineFunction from "../defineFunction";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -60,12 +59,16 @@ defineFunction({
parser.consumeSpaces();
const body = parser.parseExpression(false, breakOnTokenText);
return new ParseNode("sizing", {
return {
type: "sizing",
// Figure out what size to use based on the list of functions above
size: utils.indexOf(sizeFuncs, funcName) + 1,
value: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "sizing",
// Figure out what size to use based on the list of functions above
size: utils.indexOf(sizeFuncs, funcName) + 1,
value: body,
},
};
},
htmlBuilder,
mathmlBuilder: (group, options) => {

View File

@@ -3,7 +3,7 @@
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode, {assertNodeType} from "../ParseNode";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -43,12 +43,16 @@ defineFunction({
}
const body = args[0];
return new ParseNode("smash", {
return {
type: "smash",
body: body,
smashHeight: smashHeight,
smashDepth: smashDepth,
}, parser.mode);
mode: parser.mode,
value: {
type: "smash",
body: body,
smashHeight: smashHeight,
smashDepth: smashDepth,
},
};
},
htmlBuilder: (group, options) => {
const node = buildCommon.makeSpan(

View File

@@ -4,7 +4,6 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import delimiter from "../delimiter";
import Style from "../Style";
import ParseNode from "../ParseNode";
import * as tree from "../tree";
import * as html from "../buildHTML";
@@ -20,11 +19,15 @@ defineFunction({
handler({parser}, args, optArgs) {
const index = optArgs[0];
const body = args[0];
return new ParseNode("sqrt", {
return {
type: "sqrt",
body: body,
index: index,
}, parser.mode);
mode: parser.mode,
value: {
type: "sqrt",
body: body,
index: index,
},
};
},
htmlBuilder(group, options) {
// Square roots are handled in the TeXbook pg. 443, Rule 11.

View File

@@ -3,7 +3,6 @@ import defineFunction from "../defineFunction";
import mathMLTree from "../mathMLTree";
import Style from "../Style";
import {sizingGroup} from "./sizing";
import ParseNode from "../ParseNode";
import * as mml from "../buildMathML";
@@ -33,13 +32,17 @@ defineFunction({
// here and in buildHTML and de-dupe the enumeration of all the styles).
// $FlowFixMe: The names above exactly match the styles.
const style: StyleStr = funcName.slice(1, funcName.length - 5);
return new ParseNode("styling", {
return {
type: "styling",
// Figure out what style to use by pulling out the style from
// the function name
style,
value: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "styling",
// Figure out what style to use by pulling out the style from
// the function name
style,
value: body,
},
};
},
htmlBuilder: (group, options) => {
// Style changes are handled in the TeXbook on pg. 442, Rule 3.

View File

@@ -5,7 +5,7 @@ import domTree from "../domTree";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import Style from "../Style";
import {checkNodeType} from "../ParseNode";
import {checkNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -14,7 +14,7 @@ import * as horizBrace from "./horizBrace";
import * as op from "./op";
import type Options from "../Options";
import type ParseNode from "../ParseNode";
import type {ParseNode} from "../parseNode";
import type {HtmlBuilder} from "../defineFunction";
import type {MathNodeType} from "../mathMLTree";

View File

@@ -6,7 +6,7 @@ import mathMLTree from "../mathMLTree";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type ParseNode from "../ParseNode";
import type {ParseNode} from "../parseNode";
import type {Group} from "../symbols";
// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.

View File

@@ -5,7 +5,7 @@ import mathMLTree from "../mathMLTree";
import * as mml from "../buildMathML";
import type ParseNode from "../ParseNode";
import type {ParseNode} from "../parseNode";
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
// src/symbols.js.

View File

@@ -1,7 +1,6 @@
// @flow
import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -53,11 +52,15 @@ defineFunction({
},
handler({parser, funcName}, args) {
const body = args[0];
return new ParseNode("text", {
return {
type: "text",
body: ordargument(body),
font: funcName,
}, parser.mode);
mode: parser.mode,
value: {
type: "text",
body: ordargument(body),
font: funcName,
},
};
},
htmlBuilder(group, options) {
const newOptions = optionsWithFont(group, options);

View File

@@ -2,7 +2,6 @@
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseNode from "../ParseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -16,10 +15,14 @@ defineFunction({
},
handler({parser}, args) {
const body = args[0];
return new ParseNode("underline", {
return {
type: "underline",
body: body,
}, parser.mode);
mode: parser.mode,
value: {
type: "underline",
body: body,
},
};
},
htmlBuilder(group, options) {
// Underlines are handled in the TeXbook pg 443, Rule 10.

626
src/parseNode.js Normal file
View File

@@ -0,0 +1,626 @@
// @flow
import {GROUPS} from "./symbols";
import type SourceLocation from "./SourceLocation";
import type {ArrayEnvNodeData} from "./environments/array";
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>;
export type LeftRightDelimType = {|
type: "leftright",
body: AnyParseNode[],
left: string,
right: string,
|};
// ParseNode's corresponding to Symbol `Group`s in symbols.js.
export type SymbolParseNode =
ParseNode<"accent-token"> |
ParseNode<"bin"> |
ParseNode<"close"> |
ParseNode<"inner"> |
ParseNode<"mathord"> |
ParseNode<"op-token"> |
ParseNode<"open"> |
ParseNode<"punct"> |
ParseNode<"rel"> |
ParseNode<"spacing"> |
ParseNode<"textord">;
// 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,
value: ArrayEnvNodeData,
|},
"color": {|
type: "color",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "color",
color: string,
value: AnyParseNode[],
|},
|},
"color-token": {|
type: "color-token",
mode: Mode,
loc?: ?SourceLocation,
value: 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 simultanouesly.
// - When `symbol` is true, `body` is set.
"op": {|
type: "op",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "op",
limits: boolean,
alwaysHandleSupSub?: boolean,
suppressBaseShift?: boolean,
symbol: boolean,
body: string,
value?: void,
|} | {|
type: "op",
limits: boolean,
alwaysHandleSupSub?: boolean,
suppressBaseShift?: boolean,
symbol: false, // If 'symbol' is true, `body` *must* be set.
body?: void,
value: AnyParseNode[],
|},
|},
"ordgroup": {|
type: "ordgroup",
mode: Mode,
loc?: ?SourceLocation,
value: AnyParseNode[],
|},
"size": {|
type: "size",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "size",
value: Measurement,
isBlank: boolean,
|},
|},
"styling": {|
type: "styling",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "styling",
style: StyleStr,
value: AnyParseNode[],
|},
|},
"supsub": {|
type: "supsub",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "supsub",
base: ?AnyParseNode,
sup?: ?AnyParseNode,
sub?: ?AnyParseNode,
|},
|},
"tag": {|
type: "tag",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "tag",
body: AnyParseNode[],
tag: AnyParseNode[],
|},
|},
"text": {|
type: "text",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "text",
body: AnyParseNode[],
font?: string,
|},
|},
"url": {|
type: "url",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "url",
value: string,
|},
|},
"verb": {|
type: "verb",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "verb",
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.)
"accent-token": {|
type: "accent-token",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"bin": {|
type: "bin",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"close": {|
type: "close",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"inner": {|
type: "inner",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"mathord": {|
type: "mathord",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"op-token": {|
type: "op-token",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"open": {|
type: "open",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"punct": {|
type: "punct",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"rel": {|
type: "rel",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"spacing": {|
type: "spacing",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
"textord": {|
type: "textord",
mode: Mode,
loc?: ?SourceLocation,
value: string,
|},
// From functions.js and functions/*.js. See also "color", "op", "styling",
// and "text" above.
"accent": {|
type: "accent",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "accent",
label: string,
isStretchy?: boolean,
isShifty?: boolean,
base: AnyParseNode,
|},
|},
"accentUnder": {|
type: "accentUnder",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "accentUnder",
label: string,
isStretchy?: boolean,
isShifty?: boolean,
base: AnyParseNode,
|},
|},
"cr": {|
type: "cr",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "cr",
newRow: boolean,
newLine: boolean,
size: ?ParseNode<"size">,
|},
|},
"delimsizing": {|
type: "delimsizing",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "delimsizing",
size: 1 | 2 | 3 | 4,
mclass: "mopen" | "mclose" | "mrel" | "mord",
value: string,
|},
|},
"enclose": {|
type: "enclose",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "enclose",
label: string,
backgroundColor?: ParseNode<"color-token">,
borderColor?: ParseNode<"color-token">,
body: AnyParseNode,
|},
|},
"environment": {|
type: "environment",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "environment",
name: string,
nameGroup: AnyParseNode,
|},
|},
"font": {|
type: "font",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "font",
font: string,
body: AnyParseNode,
|},
|},
"genfrac": {|
type: "genfrac",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "genfrac",
continued: boolean,
numer: AnyParseNode,
denom: AnyParseNode,
hasBarLine: boolean,
leftDelim: ?string,
rightDelim: ?string,
size: StyleStr | "auto",
barSize: Measurement | null,
|},
|},
"horizBrace": {|
type: "horizBrace",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "horizBrace",
label: string,
isOver: boolean,
base: AnyParseNode,
|},
|},
"href": {|
type: "href",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "href",
href: string,
body: AnyParseNode[],
|},
|},
"htmlmathml": {|
type: "htmlmathml",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "htmlmathml",
html: AnyParseNode[],
mathml: AnyParseNode[],
|},
|},
"infix": {|
type: "infix",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "infix",
replaceWith: string,
sizeNode?: ParseNode<"size">,
token: ?Token,
|},
|},
"kern": {|
type: "kern",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "kern",
dimension: Measurement,
|},
|},
"lap": {|
type: "lap",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "lap",
alignment: string,
body: AnyParseNode,
|},
|},
"leftright": {|
type: "leftright",
mode: Mode,
loc?: ?SourceLocation,
value: LeftRightDelimType,
|},
"leftright-right": {|
type: "leftright-right",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "leftright-right",
value: string,
|},
|},
"mathchoice": {|
type: "mathchoice",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "mathchoice",
display: AnyParseNode[],
text: AnyParseNode[],
script: AnyParseNode[],
scriptscript: AnyParseNode[],
|},
|},
"middle": {|
type: "middle",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "middle",
value: string,
|},
|},
"mclass": {|
type: "mclass",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "mclass",
mclass: string,
value: AnyParseNode[],
|},
|},
"mod": {|
type: "mod",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "mod",
modType: string,
value: ?AnyParseNode[],
|},
|},
"operatorname": {|
type: "operatorname",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "operatorname",
value: AnyParseNode[],
|},
|},
"overline": {|
type: "overline",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "overline",
body: AnyParseNode,
|},
|},
"phantom": {|
type: "phantom",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "phantom",
value: AnyParseNode[],
|},
|},
"hphantom": {|
type: "hphantom",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "hphantom",
body: AnyParseNode,
value: AnyParseNode[],
|},
|},
"vphantom": {|
type: "vphantom",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "vphantom",
body: AnyParseNode,
value: AnyParseNode[],
|},
|},
"raisebox": {|
type: "raisebox",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "raisebox",
dy: ParseNode<"size">,
body: AnyParseNode,
value: AnyParseNode[],
|},
|},
"rule": {|
type: "rule",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "rule",
shift: ?Measurement,
width: Measurement,
height: Measurement,
|},
|},
"sizing": {|
type: "sizing",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "sizing",
size: number,
value: AnyParseNode[],
|},
|},
"smash": {|
type: "smash",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "smash",
body: AnyParseNode,
smashHeight: boolean,
smashDepth: boolean,
|},
|},
"sqrt": {|
type: "sqrt",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "sqrt",
body: AnyParseNode,
index: ?AnyParseNode,
|},
|},
"underline": {|
type: "underline",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "underline",
body: AnyParseNode,
|},
|},
"xArrow": {|
type: "xArrow",
mode: Mode,
loc?: ?SourceLocation,
value: {|
type: "xArrow",
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> {
const typedNode = checkNodeType(node, type);
if (!typedNode) {
throw new Error(
`Expected node of type ${type}, but got ` +
(node ? `node of type ${node.type}` : String(node)));
}
// $FlowFixMe: Unsure why.
return typedNode;
}
/**
* Returns the node more strictly typed iff it is of the given type. Otherwise,
* returns null.
*/
export function checkNodeType<NODETYPE: NodeType>(
node: ?AnyParseNode,
type: NODETYPE,
): ?ParseNode<NODETYPE> {
if (node && node.type === type) {
// The definition of ParseNode<TYPE> doesn't communicate to flow that
// `type: TYPE` (as that's not explicitly mentioned anywhere), though that
// happens to be true for all our value types.
// $FlowFixMe
return node;
}
return null;
}
/**
* 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 && GROUPS.hasOwnProperty(node.type)) {
// $FlowFixMe
return node;
}
return null;
}

View File

@@ -6,10 +6,9 @@
import Parser from "./Parser";
import ParseError from "./ParseError";
import ParseNode from "./ParseNode";
import type Settings from "./Settings";
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
/**
* Parses an expression using a Parser, then returns the parsed result.
@@ -30,11 +29,15 @@ const parseTree = function(toParse: string, settings: Settings): AnyParseNode[]
throw new ParseError("\\tag works only in display equations");
}
parser.gullet.feed("\\df@tag");
tree = [new ParseNode("tag", {
tree = [{
type: "tag",
body: tree,
tag: parser.parse(),
}, "text")];
mode: "text",
value: {
type: "tag",
body: tree,
tag: parser.parse(),
},
}];
}
return tree;

View File

@@ -11,7 +11,7 @@ import mathMLTree from "./mathMLTree";
import utils from "./utils";
import type Options from "./Options";
import type ParseNode, {AnyParseNode} from "./ParseNode";
import type {ParseNode, AnyParseNode} from "./parseNode";
import type {DomSpan, HtmlDomNode, SvgSpan} from "./domTree";
const stretchyCodePoint: {[string]: string} = {

View File

@@ -4,7 +4,7 @@
* files.
*/
import type {AnyParseNode} from "./ParseNode";
import type {AnyParseNode} from "./parseNode";
/**
* Provide an `indexOf` function which works in IE8, but defers to native if