diff --git a/katex.js b/katex.js index 9f9bb794..93a80b79 100644 --- a/katex.js +++ b/katex.js @@ -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'; diff --git a/src/ParseError.js b/src/ParseError.js index 8591665e..49118be1 100644 --- a/src/ParseError.js +++ b/src/ParseError.js @@ -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 diff --git a/src/ParseNode.js b/src/ParseNode.js deleted file mode 100644 index 4778c1c1..00000000 --- a/src/ParseNode.js +++ /dev/null @@ -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: TYPE; - value: NodeValue; - mode: Mode; - loc: ?SourceLocation; - - constructor( - type: TYPE, // type of node, like e.g. "ordgroup" - value: NodeValue, // 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; -export type NodeValue = $ElementType; - -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` 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( - node: ?AnyParseNode, - type: NODETYPE, -): ParseNode { - 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( - node: ?AnyParseNode, - type: NODETYPE, -): ?ParseNode { - 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; -} diff --git a/src/Parser.js b/src/Parser.js index 4e6552d6..c258b67c 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -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); diff --git a/src/Settings.js b/src/Settings.js index 0770bc1f..0b0fffa4 100644 --- a/src/Settings.js +++ b/src/Settings.js @@ -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 = diff --git a/src/buildCommon.js b/src/buildCommon.js index 1ebc2252..f2b3e98a 100644 --- a/src/buildCommon.js +++ b/src/buildCommon.js @@ -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"; diff --git a/src/buildHTML.js b/src/buildHTML.js index 6a7e6ce0..7757b110 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -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; diff --git a/src/buildMathML.js b/src/buildMathML.js index 93650e20..dddb05bd 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -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"; diff --git a/src/buildTree.js b/src/buildTree.js index b9416421..7bd6ba8c 100644 --- a/src/buildTree.js +++ b/src/buildTree.js @@ -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) { diff --git a/src/defineEnvironment.js b/src/defineEnvironment.js index 54a7f6f9..bfd35822 100644 --- a/src/defineEnvironment.js +++ b/src/defineEnvironment.js @@ -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 = ( +type EnvHandler = ( context: EnvContext, args: AnyParseNode[], optArgs: (?AnyParseNode)[], -) => ParseNode; +) => AnyParseNode; /** * - numArgs: (default 0) The number of arguments after the \begin{name} function. @@ -57,9 +55,7 @@ export type EnvSpec = {| 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 = {| // Properties that control how the environments are parsed. props: EnvProps, - handler: EnvHandler, + handler: EnvHandler, // This function returns an object representing the DOM structure to be // created when rendering the defined LaTeX function. diff --git a/src/defineFunction.js b/src/defineFunction.js index 2aca6c1e..a07efdc0 100644 --- a/src/defineFunction.js +++ b/src/defineFunction.js @@ -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"; diff --git a/src/environments/array.js b/src/environments/array.js index b0c959de..3bfa98e9 100644 --- a/src/environments/array.js +++ b/src/environments/array.js @@ -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, @@ -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, @@ -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, diff --git a/src/functions/accent.js b/src/functions/accent.js index f811b51d..a022de3d 100644 --- a/src/functions/accent.js +++ b/src/functions/accent.js @@ -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, diff --git a/src/functions/accentunder.js b/src/functions/accentunder.js index 01e53825..8a6457a3 100644 --- a/src/functions/accentunder.js +++ b/src/functions/accentunder.js @@ -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. diff --git a/src/functions/arrow.js b/src/functions/arrow.js index f6b927e0..c4a3fcab 100644 --- a/src/functions/arrow.js +++ b/src/functions/arrow.js @@ -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. diff --git a/src/functions/char.js b/src/functions/char.js index b91f0ee8..da1197d2 100644 --- a/src/functions/char.js +++ b/src/functions/char.js @@ -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), + }; }, }); diff --git a/src/functions/color.js b/src/functions/color.js index 431f086a..df40a505 100644 --- a/src/functions/color.js +++ b/src/functions/color.js @@ -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, diff --git a/src/functions/cr.js b/src/functions/cr.js index ee06d8bd..7fa4d2d9 100644 --- a/src/functions/cr.js +++ b/src/functions/cr.js @@ -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, diff --git a/src/functions/delimsizing.js b/src/functions/delimsizing.js index 8bd77250..76d5221c 100644 --- a/src/functions/delimsizing.js +++ b/src/functions/delimsizing.js @@ -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; diff --git a/src/functions/enclose.js b/src/functions/enclose.js index ce9387e8..5e1b5641 100644 --- a/src/functions/enclose.js +++ b/src/functions/enclose.js @@ -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, diff --git a/src/functions/environment.js b/src/functions/environment.js index e7000276..2e45dc5b 100644 --- a/src/functions/environment.js +++ b/src/functions/environment.js @@ -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, + }, + }; }, }); diff --git a/src/functions/font.js b/src/functions/font.js index 91b810b8..68d83245 100644 --- a/src/functions/font.js +++ b/src/functions/font.js @@ -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, diff --git a/src/functions/genfrac.js b/src/functions/genfrac.js index d58f4e3b..5fb9a740 100644 --- a/src/functions/genfrac.js +++ b/src/functions/genfrac.js @@ -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, diff --git a/src/functions/horizBrace.js b/src/functions/horizBrace.js index 348ee76b..2b0e9f0c 100644 --- a/src/functions/horizBrace.js +++ b/src/functions/horizBrace.js @@ -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, diff --git a/src/functions/href.js b/src/functions/href.js index 23c096e6..4fc74108 100644 --- a/src/functions/href.js +++ b/src/functions/href.js @@ -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), + }, + }; }, }); diff --git a/src/functions/htmlmathml.js b/src/functions/htmlmathml.js index cd65b134..c9540477 100644 --- a/src/functions/htmlmathml.js +++ b/src/functions/htmlmathml.js @@ -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( diff --git a/src/functions/kern.js b/src/functions/kern.js index fd044947..0c074336 100644 --- a/src/functions/kern.js +++ b/src/functions/kern.js @@ -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); diff --git a/src/functions/lap.js b/src/functions/lap.js index 2a39025f..3b30eedc 100644 --- a/src/functions/lap.js +++ b/src/functions/lap.js @@ -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 diff --git a/src/functions/math.js b/src/functions/math.js index a05b83be..c7b231bf 100644 --- a/src/functions/math.js +++ b/src/functions/math.js @@ -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, + }, + }; }, }); diff --git a/src/functions/mathchoice.js b/src/functions/mathchoice.js index 19316b96..663325e8 100644 --- a/src/functions/mathchoice.js +++ b/src/functions/mathchoice.js @@ -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); diff --git a/src/functions/mclass.js b/src/functions/mclass.js index 7d9b06f2..7ada92d1 100644 --- a/src/functions/mclass.js +++ b/src/functions/mclass.js @@ -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, diff --git a/src/functions/op.js b/src/functions/op.js index 63fe1ae2..75511cd7 100644 --- a/src/functions/op.js +++ b/src/functions/op.js @@ -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, diff --git a/src/functions/operatorname.js b/src/functions/operatorname.js index 4d872b53..e2ae1d0b 100644 --- a/src/functions/operatorname.js +++ b/src/functions/operatorname.js @@ -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; } diff --git a/src/functions/overline.js b/src/functions/overline.js index 97159d2e..ba04caa8 100644 --- a/src/functions/overline.js +++ b/src/functions/overline.js @@ -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. diff --git a/src/functions/phantom.js b/src/functions/phantom.js index ec8de12b..e43e469c 100644 --- a/src/functions/phantom.js +++ b/src/functions/phantom.js @@ -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( diff --git a/src/functions/raisebox.js b/src/functions/raisebox.js index 41782e39..43d30adf 100644 --- a/src/functions/raisebox.js +++ b/src/functions/raisebox.js @@ -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({ diff --git a/src/functions/rule.js b/src/functions/rule.js index 1ff7324e..c6a4c8b5 100644 --- a/src/functions/rule.js +++ b/src/functions/rule.js @@ -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 diff --git a/src/functions/sizing.js b/src/functions/sizing.js index 960d2f11..45cdceee 100644 --- a/src/functions/sizing.js +++ b/src/functions/sizing.js @@ -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) => { diff --git a/src/functions/smash.js b/src/functions/smash.js index 4b7b1cdd..fd9d4c75 100644 --- a/src/functions/smash.js +++ b/src/functions/smash.js @@ -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( diff --git a/src/functions/sqrt.js b/src/functions/sqrt.js index 189fc2f0..4106d43a 100644 --- a/src/functions/sqrt.js +++ b/src/functions/sqrt.js @@ -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. diff --git a/src/functions/styling.js b/src/functions/styling.js index 42499509..e3350912 100644 --- a/src/functions/styling.js +++ b/src/functions/styling.js @@ -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. diff --git a/src/functions/supsub.js b/src/functions/supsub.js index f240e994..386ba8f8 100644 --- a/src/functions/supsub.js +++ b/src/functions/supsub.js @@ -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"; diff --git a/src/functions/symbolsOp.js b/src/functions/symbolsOp.js index 3c781065..a8244381 100644 --- a/src/functions/symbolsOp.js +++ b/src/functions/symbolsOp.js @@ -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. diff --git a/src/functions/symbolsOrd.js b/src/functions/symbolsOrd.js index aa5a4360..1f3e78cf 100644 --- a/src/functions/symbolsOrd.js +++ b/src/functions/symbolsOrd.js @@ -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. diff --git a/src/functions/text.js b/src/functions/text.js index 5f257062..86890fc6 100644 --- a/src/functions/text.js +++ b/src/functions/text.js @@ -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); diff --git a/src/functions/underline.js b/src/functions/underline.js index 91ff9d54..41c1430d 100644 --- a/src/functions/underline.js +++ b/src/functions/underline.js @@ -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. diff --git a/src/parseNode.js b/src/parseNode.js new file mode 100644 index 00000000..26b2bbe4 --- /dev/null +++ b/src/parseNode.js @@ -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; +export type ParseNode = $ElementType; + +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; + +// 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( + node: ?AnyParseNode, + type: NODETYPE, +): ParseNode { + 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( + node: ?AnyParseNode, + type: NODETYPE, +): ?ParseNode { + if (node && node.type === type) { + // The definition of ParseNode 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; +} diff --git a/src/parseTree.js b/src/parseTree.js index 7e5d641d..625515ea 100644 --- a/src/parseTree.js +++ b/src/parseTree.js @@ -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; diff --git a/src/stretchy.js b/src/stretchy.js index cffd72da..f63db8c3 100644 --- a/src/stretchy.js +++ b/src/stretchy.js @@ -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} = { diff --git a/src/utils.js b/src/utils.js index 9c7071cd..d357b643 100644 --- a/src/utils.js +++ b/src/utils.js @@ -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