Spaces:
Sleeping
Sleeping
| 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 extends NodeType> = ( | |
| context: FunctionContext, | |
| args: AnyParseNode[], | |
| optArgs: (AnyParseNode | null | undefined)[], | |
| ) => 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 extends NodeType> = | |
| (group: ParseNode<NODETYPE>, options: Options) => HtmlDomNode; | |
| export type MathMLBuilder<NODETYPE extends 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 extends NodeType> = | |
| (group: ParseNode<"supsub"> | ParseNode<NODETYPE>, options: 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[]; | |
| // Whether it expands to a single token or a braced group of tokens. | |
| // If it's grouped, it can be used as an argument to primitive commands, | |
| // such as \sqrt (without the optional argument) and super/subscript. | |
| allowedInArgument?: boolean; | |
| // 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; | |
| // Whether or not the function is a TeX primitive. | |
| primitive?: boolean; | |
| }; | |
| type FunctionDefSpec<NODETYPE extends 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> | null | undefined; | |
| // 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 extends NodeType> = { | |
| type: NODETYPE; // Need to use the type to avoid error. See NOTES below. | |
| numArgs: number; | |
| argTypes?: ArgType[]; | |
| allowedInArgument: boolean; | |
| allowedInText: boolean; | |
| allowedInMath: boolean; | |
| numOptionalArgs: number; | |
| infix: boolean; | |
| primitive: 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<any> | null | undefined; | |
| }; | |
| /** | |
| * All registered functions. | |
| * `functions.js` just exports this same dictionary again and makes it public. | |
| * `Parser.js` requires this dictionary. | |
| */ | |
| export const _functions: Record<string, FunctionSpec<any>> = {}; | |
| /** | |
| * All HTML builders. Should be only used in the `define*` and the `build*ML` | |
| * functions. | |
| */ | |
| export const _htmlGroupBuilders: Record<string, HtmlBuilder<any>> = {}; | |
| /** | |
| * All MathML builders. Should be only used in the `define*` and the `build*ML` | |
| * functions. | |
| */ | |
| export const _mathmlGroupBuilders: Record<string, MathMLBuilder<any>> = {}; | |
| export default function defineFunction<NODETYPE extends NodeType>({ | |
| type, | |
| names, | |
| props, | |
| handler, | |
| htmlBuilder, | |
| mathmlBuilder, | |
| }: FunctionDefSpec<NODETYPE>) { | |
| // Set default values of functions | |
| const data = { | |
| type, | |
| numArgs: props.numArgs, | |
| argTypes: props.argTypes, | |
| allowedInArgument: !!props.allowedInArgument, | |
| allowedInText: !!props.allowedInText, | |
| allowedInMath: (props.allowedInMath === undefined) | |
| ? true | |
| : props.allowedInMath, | |
| numOptionalArgs: props.numOptionalArgs || 0, | |
| infix: !!props.infix, | |
| primitive: !!props.primitive, | |
| handler, | |
| }; | |
| 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 extends 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, | |
| }); | |
| } | |
| export const normalizeArgument = function(arg: AnyParseNode): AnyParseNode { | |
| return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg; | |
| }; | |
| // 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]; | |
| }; | |