forked from KaTeX/KaTeX
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdefineFunction.js
More file actions
174 lines (154 loc) · 5.7 KB
/
defineFunction.js
File metadata and controls
174 lines (154 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// @flow
import {groupTypes as htmlGroupTypes} from "./buildHTML";
import {groupTypes as mathmlGroupTypes} from "./buildMathML";
import type Parser from "./Parser" ;
import type ParseNode from "./ParseNode" ;
import type Options from "./Options";
import type {ArgType} from "./types" ;
import type {Token} from "./Token" ;
/** Context provided to function handlers for error messages. */
export type FunctionContext = {|
funcName: string,
parser: Parser,
token?: Token,
|};
// TODO: Enumerate all allowed output types.
export type FunctionHandler = (
context: FunctionContext,
args: ParseNode[],
optArgs: (?ParseNode)[],
) => *;
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,
};
type FunctionDefSpec = {|
// Unique string to differentiate parse nodes.
type?: string,
// 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.
//
// The function should return an object with the following keys:
// - type: The type of element that this is. This is then used in
// buildHTML/buildMathML to determine which function
// should be called to build this node into a DOM node
// Any other data can be added to the object, which will be passed
// in to the function in buildHTML/buildMathML as `group.value`.
handler: ?FunctionHandler,
// This function returns an object representing the DOM structure to be
// created when rendering the defined LaTeX function.
// TODO: Port buildHTML to flow and make the group and return types explicit.
htmlBuilder?: (group: *, options: Options) => *,
// This function returns an object representing the MathML structure to be
// created when rendering the defined LaTeX function.
// TODO: Port buildMathML to flow and make the group and return types explicit.
mathmlBuilder?: (group: *, options: Options) => *,
|};
/**
* 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 = {|
numArgs: number,
argTypes?: ArgType[],
greediness: number,
allowedInText: boolean,
allowedInMath: boolean,
numOptionalArgs: number,
infix: boolean,
// 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} = {};
export default function defineFunction({
type,
names,
props,
handler,
htmlBuilder,
mathmlBuilder,
}: FunctionDefSpec) {
// Set default values of functions
const data = {
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,
handler: handler,
};
for (let i = 0; i < names.length; ++i) {
_functions[names[i]] = data;
}
if (type) {
if (htmlBuilder) {
htmlGroupTypes[type] = htmlBuilder;
}
if (mathmlBuilder) {
mathmlGroupTypes[type] = mathmlBuilder;
}
}
}
// Since the corresponding buildHTML/buildMathML function expects a
// list of elements, we normalize for different kinds of arguments
export const ordargument = function(arg: ParseNode): ParseNode[] {
if (arg.type === "ordgroup") {
return arg.value;
} else {
return [arg];
}
};