From 9cde0336d3a30fa69351aebc46e83d6691f89ea6 Mon Sep 17 00:00:00 2001 From: Ashish Myles Date: Sun, 27 May 2018 03:24:30 -0400 Subject: [PATCH] Correct (type-wise) raisebox's usage of sizing's buildHtml. (#1361) * Correct (type-wise) raisebox's usage of sizing's buildHtml. * Move HTML and MathML groupTypes into defineFunction. Currently, functions defined in functions/* import all exports from buildHtml and buildMathML, but they should never use `groupTypes` directly as it loses type-safety. They should instead use more type-safe `htmlBuilder`s and `mathmlBuilder`s exported directly from other definitions `functions/*` to allow flow to catch errors. * Rename groupTypes to groupBuilders. --- src/buildHTML.js | 14 ++++++-------- src/buildMathML.js | 15 +++++---------- src/defineEnvironment.js | 7 +++---- src/defineFunction.js | 18 ++++++++++++++---- src/functions/raisebox.js | 23 ++++++++++++----------- src/functions/sizing.js | 17 ++++++++++------- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/buildHTML.js b/src/buildHTML.js index 446a8db6..c62e939c 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -1,8 +1,8 @@ /** * This file does the main work of building a domTree structure from a parse * tree. The entry point is the `buildHTML` function, which takes a parse tree. - * Then, the buildExpression, buildGroup, and various groupTypes functions are - * called, to produce a final HTML tree. + * Then, the buildExpression, buildGroup, and various groupBuilders functions + * are called, to produce a final HTML tree. */ import ParseError from "./ParseError"; @@ -12,6 +12,7 @@ import buildCommon from "./buildCommon"; import domTree from "./domTree"; import utils from "./utils"; import {spacings, tightSpacings} from "./spacingData"; +import {_htmlGroupBuilders as groupBuilders} from "./defineFunction"; const makeSpan = buildCommon.makeSpan; @@ -209,9 +210,6 @@ export const makeNullDelimiter = function(options, classes) { return makeSpan(classes.concat(moreClasses)); }; -/** This is a map of group types to the function used to handle that type. */ -export const groupTypes = {}; - /** * buildGroup is the function that takes a group and calls the correct groupType * function for it. It also handles the interaction of size and style changes @@ -222,9 +220,9 @@ export const buildGroup = function(group, options, baseOptions) { return makeSpan(); } - if (groupTypes[group.type]) { - // Call the groupTypes function - let groupNode = groupTypes[group.type](group, options); + if (groupBuilders[group.type]) { + // Call the groupBuilders function + let groupNode = groupBuilders[group.type](group, options); // If the size changed between the parent and the current group, account // for that size difference. diff --git a/src/buildMathML.js b/src/buildMathML.js index ea04745e..79e22a1f 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -10,6 +10,7 @@ import mathMLTree from "./mathMLTree"; import ParseError from "./ParseError"; import symbols from "./symbols"; import utils from "./utils"; +import {_mathmlGroupBuilders as groupBuilders} from "./defineFunction"; /** * Takes a symbol and converts it into a MathML text node after performing @@ -70,12 +71,6 @@ export const getVariant = function(group, options) { return null; }; -/** - * Functions for handling the different types of groups found in the parse - * tree. Each function should take a parse group and return a MathML node. - */ -export const groupTypes = {}; - /** * Takes a list of nodes, builds them, and returns a list of the generated * MathML nodes. Also combine consecutive outputs into a single @@ -118,7 +113,7 @@ export const buildExpressionRow = function(expression, options) { }; /** - * Takes a group from the parser and calls the appropriate groupTypes function + * Takes a group from the parser and calls the appropriate groupBuilders function * on it to produce a MathML node. */ export const buildGroup = function(group, options) { @@ -126,9 +121,9 @@ export const buildGroup = function(group, options) { return new mathMLTree.MathNode("mrow"); } - if (groupTypes[group.type]) { - // Call the groupTypes function - const result = groupTypes[group.type](group, options); + if (groupBuilders[group.type]) { + // Call the groupBuilders function + const result = groupBuilders[group.type](group, options); return result; } else { throw new ParseError( diff --git a/src/defineEnvironment.js b/src/defineEnvironment.js index 1f3e29e4..4d56b259 100644 --- a/src/defineEnvironment.js +++ b/src/defineEnvironment.js @@ -1,6 +1,5 @@ // @flow -import {groupTypes as htmlGroupTypes} from "./buildHTML"; -import {groupTypes as mathmlGroupTypes} from "./buildMathML"; +import {_htmlGroupBuilders, _mathmlGroupBuilders} from "./defineFunction"; import Options from "./Options"; import ParseNode from "./ParseNode"; @@ -114,9 +113,9 @@ export default function defineEnvironment({ _environments[names[i]] = data; } if (htmlBuilder) { - htmlGroupTypes[type] = htmlBuilder; + _htmlGroupBuilders[type] = htmlBuilder; } if (mathmlBuilder) { - mathmlGroupTypes[type] = mathmlBuilder; + _mathmlGroupBuilders[type] = mathmlBuilder; } } diff --git a/src/defineFunction.js b/src/defineFunction.js index ce2059fa..dce08c24 100644 --- a/src/defineFunction.js +++ b/src/defineFunction.js @@ -1,6 +1,4 @@ // @flow -import {groupTypes as htmlGroupTypes} from "./buildHTML"; -import {groupTypes as mathmlGroupTypes} from "./buildMathML"; import {checkNodeType} from "./ParseNode"; import domTree from "./domTree"; @@ -170,6 +168,18 @@ export type FunctionSpec = {| */ export const _functions: {[string]: FunctionSpec<*>} = {}; +/** + * All HTML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ +export const _htmlGroupBuilders: {[string]: HtmlBuilder<*>} = {}; + +/** + * All MathML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ +export const _mathmlGroupBuilders: {[string]: MathMLBuilder<*>} = {}; + export default function defineFunction({ type, nodeType, @@ -199,10 +209,10 @@ export default function defineFunction({ } if (type) { if (htmlBuilder) { - htmlGroupTypes[type] = htmlBuilder; + _htmlGroupBuilders[type] = htmlBuilder; } if (mathmlBuilder) { - mathmlGroupTypes[type] = mathmlBuilder; + _mathmlGroupBuilders[type] = mathmlBuilder; } } } diff --git a/src/functions/raisebox.js b/src/functions/raisebox.js index 7182ed31..005711fb 100644 --- a/src/functions/raisebox.js +++ b/src/functions/raisebox.js @@ -2,11 +2,11 @@ import defineFunction, {ordargument} from "../defineFunction"; import buildCommon from "../buildCommon"; import mathMLTree from "../mathMLTree"; -import {assertNodeType} from "../ParseNode"; +import ParseNode, {assertNodeType} from "../ParseNode"; import {calculateSize} from "../units"; -import * as html from "../buildHTML"; import * as mml from "../buildMathML"; +import * as sizing from "./sizing"; // Box manipulation defineFunction({ @@ -28,16 +28,17 @@ defineFunction({ }; }, htmlBuilder(group, options) { - const body = html.groupTypes.sizing({value: { - value: [{ - type: "text", - value: { - body: group.value.value, - font: "mathrm", // simulate \textrm - }, - }], + const text = new ParseNode("text", { + type: "text", + body: group.value.value, + font: "mathrm", // simulate \textrm + }, group.mode); + const sizedText = new ParseNode("sizing", { + type: "sizing", + value: [text], size: 6, // simulate \normalsize - }}, options); + }, group.mode); + const body = sizing.htmlBuilder(sizedText, options); const dy = calculateSize(group.value.dy.value, options); return buildCommon.makeVList({ positionType: "shift", diff --git a/src/functions/sizing.js b/src/functions/sizing.js index e1b5531e..105bb48e 100644 --- a/src/functions/sizing.js +++ b/src/functions/sizing.js @@ -8,6 +8,7 @@ import * as html from "../buildHTML"; import * as mml from "../buildMathML"; import type Options from "../Options"; +import type {HtmlBuilder} from "../defineFunction"; export function sizingGroup(value: *, options: Options, baseOptions: Options) { const inner = html.buildExpression(value, options, false); @@ -39,6 +40,14 @@ const sizeFuncs = [ "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", ]; +export const htmlBuilder: HtmlBuilder<"sizing"> = (group, options) => { + // Handle sizing operators like \Huge. Real TeX doesn't actually allow + // these functions inside of math expressions, so we do some special + // handling. + const newOptions = options.havingSize(group.value.size); + return sizingGroup(group.value.value, newOptions, options); +}; + defineFunction({ type: "sizing", names: sizeFuncs, @@ -59,13 +68,7 @@ defineFunction({ value: body, }; }, - htmlBuilder: (group, options) => { - // Handle sizing operators like \Huge. Real TeX doesn't actually allow - // these functions inside of math expressions, so we do some special - // handling. - const newOptions = options.havingSize(group.value.size); - return sizingGroup(group.value.value, newOptions, options); - }, + htmlBuilder, mathmlBuilder: (group, options) => { const newOptions = options.havingSize(group.value.size); const inner = mml.buildExpression(group.value.value, newOptions);