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";
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>;
export type HtmlBuilder<NODETYPE> = (ParseNode<NODETYPE>, Options) => HtmlDomNode;
export type MathMLBuilder<NODETYPE> = (
group: ParseNode<NODETYPE>,
options: Options,
) => MathDomNode;
export type HtmlBuilderSupSub<NODETYPE> =
(ParseNode<"supsub"> | ParseNode<NODETYPE>, Options) => HtmlDomNode;
export type FunctionPropSpec = {
numArgs: number,
argTypes?: ArgType[],
allowedInArgument?: boolean,
allowedInText?: boolean,
allowedInMath?: boolean,
numOptionalArgs?: number,
infix?: boolean,
primitive?: boolean,
};
type FunctionDefSpec<NODETYPE: NodeType> = {|
type: NODETYPE,
names: Array<string>,
props: FunctionPropSpec,
handler: ?FunctionHandler<NODETYPE>,
htmlBuilder?: HtmlBuilder<NODETYPE>,
mathmlBuilder?: MathMLBuilder<NODETYPE>,
|};
export type FunctionSpec<NODETYPE: NodeType> = {|
type: NODETYPE, numArgs: number,
argTypes?: ArgType[],
allowedInArgument: boolean,
allowedInText: boolean,
allowedInMath: boolean,
numOptionalArgs: number,
infix: boolean,
primitive: boolean,
handler: ?FunctionHandler<*>,
|};
export const _functions: {[string]: FunctionSpec<*>} = {};
export const _htmlGroupBuilders: {[string]: HtmlBuilder<*>} = {};
export const _mathmlGroupBuilders: {[string]: MathMLBuilder<*>} = {};
export default function defineFunction<NODETYPE: NodeType>({
type,
names,
props,
handler,
htmlBuilder,
mathmlBuilder,
}: FunctionDefSpec<NODETYPE>) {
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: 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;
}
}
}
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,
});
}
export const normalizeArgument = function(arg: AnyParseNode): AnyParseNode {
return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg;
};
export const ordargument = function(arg: AnyParseNode): AnyParseNode[] {
return arg.type === "ordgroup" ? arg.body : [arg];
};