text: // @flow
  import type Parser from "./Parser";
  import type {ParseNode, AnyParseNode, NodeType, UnsupportedCmdParseNode}
      from "./parseNode";
  import type Options from "./Options";
  import type {ArgType, BreakToken} 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)[],
  ) => UnsupportedCmdParseNode | ParseNode<NODETYPE>;
  // Note: reverse the order of the return type union will cause a flow error.
  // See https://github.com/facebook/flow/issues/3663.
  
  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 \fFunctionDefSpecrac 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,
  };
  
  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,
  
      // 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,
      names,
      props,
      handler,
      htmlBuilder,
      mathmlBuilder,
  }: FunctionDefSpec<NODETYPE>) {
  
      const sourceLocationHandlerWrapper: ?FunctionHandler<NODETYPE> =
      handler == null ? null : (
          context: FunctionContext,
          args: AnyParseNode[],
          optArgs: (?AnyParseNode)[],
      ) => {
          const originalResult = handler.call(this, context, args, optArgs);
  
          // $FlowFixMe
          const result: ParseNode<NODETYPE> = originalResult;
  
          if (result.loc === undefined) {
              if (context.token === undefined || !context.token.loc) {
                  throw new Error('both loc sources are undefined');
              }
              result.loc = context.token.loc;
          }
  
          return result;
      };
  
      // 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: sourceLocationHandlerWrapper,
      };
      for (let i = 0; i < names.length; ++i) {
          _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[] {
      return arg.type === "ordgroup" ? arg.body : [arg];
  };
  