123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- // @flow
- import {checkNodeType} from "./parseNode";
- import type Parser from "./Parser";
- import type {ParseNode, AnyParseNode, NodeType} from "./parseNode";
- import type Options from "./Options";
- import type {ArgType, BreakToken, Mode} from "./types";
- import type {HtmlDomNode} from "./domTree";
- import type {Token} from "./Token";
- import type {MathDomNode} from "./mathMLTree";
- /** Context provided to function handlers for error messages. */
- export type FunctionContext = {|
- funcName: string,
- parser: Parser,
- token?: Token,
- breakOnTokenText?: BreakToken,
- |};
- export type FunctionHandler<NODETYPE: NodeType> = (
- context: FunctionContext,
- args: AnyParseNode[],
- optArgs: (?AnyParseNode)[],
- ) => ParseNode<NODETYPE>;
- export type HtmlBuilder<NODETYPE> = (ParseNode<NODETYPE>, Options) => HtmlDomNode;
- export type MathMLBuilder<NODETYPE> = (
- group: ParseNode<NODETYPE>,
- options: Options,
- ) => MathDomNode;
- // More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types)
- // whose presence impacts super/subscripting. In this case, ParseNode<"supsub">
- // delegates its HTML building to the HtmlBuilder corresponding to these nodes.
- export type HtmlBuilderSupSub<NODETYPE> =
- (ParseNode<"supsub"> | ParseNode<NODETYPE>, Options) => HtmlDomNode;
- export type FunctionPropSpec = {
- // The number of arguments the function takes.
- numArgs: number,
- // An array corresponding to each argument of the function, giving the
- // type of argument that should be parsed. Its length should be equal
- // to `numOptionalArgs + numArgs`, and types for optional arguments
- // should appear before types for mandatory arguments.
- argTypes?: ArgType[],
- // The greediness of the function to use ungrouped arguments.
- //
- // E.g. if you have an expression
- // \sqrt \frac 1 2
- // since \frac has greediness=2 vs \sqrt's greediness=1, \frac
- // will use the two arguments '1' and '2' as its two arguments,
- // then that whole function will be used as the argument to
- // \sqrt. On the other hand, the expressions
- // \frac \frac 1 2 3
- // and
- // \frac \sqrt 1 2
- // will fail because \frac and \frac have equal greediness
- // and \sqrt has a lower greediness than \frac respectively. To
- // make these parse, we would have to change them to:
- // \frac {\frac 1 2} 3
- // and
- // \frac {\sqrt 1} 2
- //
- // The default value is `1`
- greediness?: number,
- // Whether or not the function is allowed inside text mode
- // (default false)
- allowedInText?: boolean,
- // Whether or not the function is allowed inside text mode
- // (default true)
- allowedInMath?: boolean,
- // (optional) The number of optional arguments the function
- // should parse. If the optional arguments aren't found,
- // `null` will be passed to the handler in their place.
- // (default 0)
- numOptionalArgs?: number,
- // Must be true if the function is an infix operator.
- infix?: boolean,
- // Switch to the specified mode while consuming the command token.
- // This is useful for commands that switch between math and text mode,
- // for making sure that a switch happens early enough. Note that the
- // mode is switched immediately back to its original value after consuming
- // the command token, so that the argument parsing and/or function handler
- // can easily access the old mode while doing their own mode switching.
- consumeMode?: ?Mode,
- };
- type FunctionDefSpec<NODETYPE: NodeType> = {|
- // Unique string to differentiate parse nodes.
- // Also determines the type of the value returned by `handler`.
- type: NODETYPE,
- // The first argument to defineFunction is a single name or a list of names.
- // All functions named in such a list will share a single implementation.
- names: Array<string>,
- // Properties that control how the functions are parsed.
- props: FunctionPropSpec,
- // The handler is called to handle these functions and their arguments and
- // returns a `ParseNode`.
- handler: ?FunctionHandler<NODETYPE>,
- // This function returns an object representing the DOM structure to be
- // created when rendering the defined LaTeX function.
- // This should not modify the `ParseNode`.
- htmlBuilder?: HtmlBuilder<NODETYPE>,
- // This function returns an object representing the MathML structure to be
- // created when rendering the defined LaTeX function.
- // This should not modify the `ParseNode`.
- mathmlBuilder?: MathMLBuilder<NODETYPE>,
- |};
- /**
- * Final function spec for use at parse time.
- * This is almost identical to `FunctionPropSpec`, except it
- * 1. includes the function handler, and
- * 2. requires all arguments except argTypes.
- * It is generated by `defineFunction()` below.
- */
- export type FunctionSpec<NODETYPE: NodeType> = {|
- type: NODETYPE, // Need to use the type to avoid error. See NOTES below.
- numArgs: number,
- argTypes?: ArgType[],
- greediness: number,
- allowedInText: boolean,
- allowedInMath: boolean,
- numOptionalArgs: number,
- infix: boolean,
- consumeMode: ?Mode,
- // FLOW TYPE NOTES: Doing either one of the following two
- //
- // - removing the NODETYPE type parameter in FunctionSpec above;
- // - using ?FunctionHandler<NODETYPE> below;
- //
- // results in a confusing flow typing error:
- // "string literal `styling`. This type is incompatible with..."
- // pointing to the definition of `defineFunction` and finishing with
- // "some incompatible instantiation of `NODETYPE`"
- //
- // Having FunctionSpec<NODETYPE> above and FunctionHandler<*> below seems to
- // circumvent this error. This is not harmful for catching errors since
- // _functions is typed FunctionSpec<*> (it stores all TeX function specs).
- // Must be specified unless it's handled directly in the parser.
- handler: ?FunctionHandler<*>,
- |};
- /**
- * All registered functions.
- * `functions.js` just exports this same dictionary again and makes it public.
- * `Parser.js` requires this dictionary.
- */
- 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<NODETYPE: NodeType>({
- type,
- nodeType,
- names,
- props,
- handler,
- htmlBuilder,
- mathmlBuilder,
- }: FunctionDefSpec<NODETYPE>) {
- // Set default values of functions
- const data = {
- type,
- numArgs: props.numArgs,
- argTypes: props.argTypes,
- greediness: (props.greediness === undefined) ? 1 : props.greediness,
- allowedInText: !!props.allowedInText,
- allowedInMath: (props.allowedInMath === undefined)
- ? true
- : props.allowedInMath,
- numOptionalArgs: props.numOptionalArgs || 0,
- infix: !!props.infix,
- consumeMode: props.consumeMode,
- handler: handler,
- };
- for (let i = 0; i < names.length; ++i) {
- // TODO: The value type of _functions should be a type union of all
- // possible `FunctionSpec<>` possibilities instead of `FunctionSpec<*>`,
- // which is an existential type.
- // $FlowFixMe
- _functions[names[i]] = data;
- }
- if (type) {
- if (htmlBuilder) {
- _htmlGroupBuilders[type] = htmlBuilder;
- }
- if (mathmlBuilder) {
- _mathmlGroupBuilders[type] = mathmlBuilder;
- }
- }
- }
- /**
- * Use this to register only the HTML and MathML builders for a function (e.g.
- * if the function's ParseNode is generated in Parser.js rather than via a
- * stand-alone handler provided to `defineFunction`).
- */
- export function defineFunctionBuilders<NODETYPE: NodeType>({
- type, htmlBuilder, mathmlBuilder,
- }: {|
- type: NODETYPE,
- htmlBuilder?: HtmlBuilder<NODETYPE>,
- mathmlBuilder: MathMLBuilder<NODETYPE>,
- |}) {
- defineFunction({
- type,
- names: [],
- props: {numArgs: 0},
- handler() { throw new Error('Should never be called.'); },
- htmlBuilder,
- mathmlBuilder,
- });
- }
- // Since the corresponding buildHTML/buildMathML function expects a
- // list of elements, we normalize for different kinds of arguments
- export const ordargument = function(arg: AnyParseNode): AnyParseNode[] {
- const node = checkNodeType(arg, "ordgroup");
- return node ? node.body : [arg];
- };
|