mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Port functions.js to @flow. (#867)
* Make types in defineFunction stricter and prep for porting functions.js to flow. * Add more explicit types to functions/delimsizing.js. * Port functions.js to @flow.
This commit is contained in:
@@ -1,77 +1,83 @@
|
||||
// @flow
|
||||
import functions from "./functions";
|
||||
import {groupTypes as htmlGroupTypes} from "./buildHTML";
|
||||
import {groupTypes as mathmlGroupTypes} from "./buildMathML";
|
||||
|
||||
import type ParseNode from "./ParseNode" ;
|
||||
import type Options from "./Options";
|
||||
import type {ArgType} from "./types" ;
|
||||
import type {Parser} from "./Parser" ;
|
||||
import type {Token} from "./Token" ;
|
||||
|
||||
type FunctionSpec<T> = {
|
||||
/** 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[]) => *;
|
||||
|
||||
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 `numArgs + numOptionalArgs`.
|
||||
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,
|
||||
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: {
|
||||
// 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 `numArgs + numOptionalArgs`.
|
||||
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,
|
||||
},
|
||||
props: FunctionPropSpec,
|
||||
|
||||
// The handler is called to handle these functions and their arguments.
|
||||
// It receives two arguments:
|
||||
// - context contains information and references provided by the parser
|
||||
// - args is an array of arguments obtained from TeX input
|
||||
// The context contains the following properties:
|
||||
// - funcName: the text (i.e. name) of the function, including \
|
||||
// - parser: the parser object
|
||||
// - lexer: the lexer object
|
||||
// The latter three should only be used to produce error messages.
|
||||
//
|
||||
// The function should return an object with the following keys:
|
||||
// - type: The type of element that this is. This is then used in
|
||||
@@ -79,16 +85,44 @@ type FunctionSpec<T> = {
|
||||
// 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: (context: any, args: any) => T,
|
||||
handler: ?FunctionHandler,
|
||||
|
||||
// This function returns an object representing the DOM structure to be
|
||||
// created when rendering the defined LaTeX function.
|
||||
htmlBuilder: (group: T, options: Options) => any,
|
||||
// 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.
|
||||
mathmlBuilder: (group: T, options: Options) => any,
|
||||
}
|
||||
// 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.
|
||||
*/
|
||||
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,
|
||||
@@ -97,7 +131,7 @@ export default function defineFunction({
|
||||
handler,
|
||||
htmlBuilder,
|
||||
mathmlBuilder,
|
||||
}: FunctionSpec<*>) {
|
||||
}: FunctionDefSpec) {
|
||||
// Set default values of functions
|
||||
const data = {
|
||||
numArgs: props.numArgs,
|
||||
@@ -112,7 +146,7 @@ export default function defineFunction({
|
||||
handler: handler,
|
||||
};
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
functions[names[i]] = data;
|
||||
_functions[names[i]] = data;
|
||||
}
|
||||
if (type) {
|
||||
if (htmlBuilder) {
|
||||
@@ -126,7 +160,7 @@ export default function defineFunction({
|
||||
|
||||
// Since the corresponding buildHTML/buildMathML function expects a
|
||||
// list of elements, we normalize for different kinds of arguments
|
||||
export const ordargument = function(arg: any) {
|
||||
export const ordargument = function(arg: ParseNode) {
|
||||
if (arg.type === "ordgroup") {
|
||||
return arg.value;
|
||||
} else {
|
||||
|
@@ -1,24 +1,33 @@
|
||||
// @flow
|
||||
/** Include this to ensure that all functions are defined. */
|
||||
import utils from "./utils";
|
||||
import ParseError from "./ParseError";
|
||||
import ParseNode from "./ParseNode";
|
||||
import {default as _defineFunction, ordargument} from "./defineFunction";
|
||||
import {
|
||||
default as _defineFunction,
|
||||
ordargument,
|
||||
_functions,
|
||||
} from "./defineFunction";
|
||||
|
||||
// WARNING: New functions should be added to src/functions.
|
||||
import type {FunctionPropSpec, FunctionHandler} from "./defineFunction" ;
|
||||
|
||||
// WARNING: New functions should be added to src/functions and imported here.
|
||||
|
||||
const functions = _functions;
|
||||
export default functions;
|
||||
|
||||
// Define a convenience function that mimcs the old semantics of defineFunction
|
||||
// to support existing code so that we can migrate it a little bit at a time.
|
||||
const defineFunction = function(names, props, handler) {
|
||||
if (typeof names === "string") {
|
||||
names = [names];
|
||||
}
|
||||
if (typeof props === "number") {
|
||||
props = { numArgs: props };
|
||||
}
|
||||
const defineFunction = function(
|
||||
names: string[],
|
||||
props: FunctionPropSpec,
|
||||
handler: ?FunctionHandler, // null only if handled in parser
|
||||
) {
|
||||
_defineFunction({names, props, handler});
|
||||
};
|
||||
|
||||
// A normal square root
|
||||
defineFunction("\\sqrt", {
|
||||
defineFunction(["\\sqrt"], {
|
||||
numArgs: 1,
|
||||
numOptionalArgs: 1,
|
||||
}, function(context, args) {
|
||||
@@ -56,7 +65,7 @@ defineFunction([
|
||||
});
|
||||
|
||||
// A two-argument custom color
|
||||
defineFunction("\\textcolor", {
|
||||
defineFunction(["\\textcolor"], {
|
||||
numArgs: 2,
|
||||
allowedInText: true,
|
||||
greediness: 3,
|
||||
@@ -72,7 +81,7 @@ defineFunction("\\textcolor", {
|
||||
});
|
||||
|
||||
// \color is handled in Parser.js's parseImplicitGroup
|
||||
defineFunction("\\color", {
|
||||
defineFunction(["\\color"], {
|
||||
numArgs: 1,
|
||||
allowedInText: true,
|
||||
greediness: 3,
|
||||
@@ -80,7 +89,7 @@ defineFunction("\\color", {
|
||||
}, null);
|
||||
|
||||
// An overline
|
||||
defineFunction("\\overline", {
|
||||
defineFunction(["\\overline"], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
const body = args[0];
|
||||
@@ -91,7 +100,7 @@ defineFunction("\\overline", {
|
||||
});
|
||||
|
||||
// An underline
|
||||
defineFunction("\\underline", {
|
||||
defineFunction(["\\underline"], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
const body = args[0];
|
||||
@@ -102,7 +111,7 @@ defineFunction("\\underline", {
|
||||
});
|
||||
|
||||
// A box of the width and height
|
||||
defineFunction("\\rule", {
|
||||
defineFunction(["\\rule"], {
|
||||
numArgs: 2,
|
||||
numOptionalArgs: 1,
|
||||
argTypes: ["size", "size", "size"],
|
||||
@@ -131,7 +140,7 @@ defineFunction(["\\kern", "\\mkern"], {
|
||||
});
|
||||
|
||||
// A KaTeX logo
|
||||
defineFunction("\\KaTeX", {
|
||||
defineFunction(["\\KaTeX"], {
|
||||
numArgs: 0,
|
||||
}, function(context) {
|
||||
return {
|
||||
@@ -157,7 +166,7 @@ defineFunction([
|
||||
});
|
||||
|
||||
// Build a relation by placing one symbol on top of another
|
||||
defineFunction("\\stackrel", {
|
||||
defineFunction(["\\stackrel"], {
|
||||
numArgs: 2,
|
||||
}, function(context, args) {
|
||||
const top = args[0];
|
||||
@@ -185,7 +194,7 @@ defineFunction("\\stackrel", {
|
||||
});
|
||||
|
||||
// \mod-type functions
|
||||
defineFunction("\\bmod", {
|
||||
defineFunction(["\\bmod"], {
|
||||
numArgs: 0,
|
||||
}, function(context, args) {
|
||||
return {
|
||||
@@ -308,7 +317,7 @@ defineFunction([
|
||||
});
|
||||
|
||||
// \mathop class command
|
||||
defineFunction("\\mathop", {
|
||||
defineFunction(["\\mathop"], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
const body = args[0];
|
||||
@@ -392,7 +401,7 @@ defineFunction(["\\mathllap", "\\mathrlap", "\\mathclap"], {
|
||||
});
|
||||
|
||||
// smash, with optional [tb], as in AMS
|
||||
defineFunction("\\smash", {
|
||||
defineFunction(["\\smash"], {
|
||||
numArgs: 1,
|
||||
numOptionalArgs: 1,
|
||||
allowedInText: true,
|
||||
@@ -437,19 +446,19 @@ import "./functions/delimsizing";
|
||||
defineFunction([
|
||||
"\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
|
||||
"\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
|
||||
], 0, null);
|
||||
], {numArgs: 0}, null);
|
||||
|
||||
// Style changing functions (handled in Parser.js explicitly, hence no
|
||||
// handler)
|
||||
defineFunction([
|
||||
"\\displaystyle", "\\textstyle", "\\scriptstyle",
|
||||
"\\scriptscriptstyle",
|
||||
], 0, null);
|
||||
], {numArgs: 0}, null);
|
||||
|
||||
// Old font changing functions
|
||||
defineFunction([
|
||||
"\\rm", "\\sf", "\\tt", "\\bf", "\\it", //"\\sl", "\\sc",
|
||||
], 0, null);
|
||||
], {numArgs: 0}, null);
|
||||
|
||||
defineFunction([
|
||||
// styles
|
||||
|
@@ -9,6 +9,9 @@ import utils from "../utils";
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
import type ParseNode from "../ParseNode";
|
||||
import type {FunctionContext} from "../defineFunction";
|
||||
|
||||
// Extra data needed for the delimiter handler down below
|
||||
const delimiterSizes = {
|
||||
"\\bigl" : {mclass: "mopen", size: 1},
|
||||
@@ -45,7 +48,7 @@ const delimiters = [
|
||||
];
|
||||
|
||||
// Delimiter functions
|
||||
const checkDelimiter = function(delim, context) {
|
||||
function checkDelimiter(delim: ParseNode, context: FunctionContext): ParseNode {
|
||||
if (utils.contains(delimiters, delim.value)) {
|
||||
return delim;
|
||||
} else {
|
||||
@@ -53,7 +56,7 @@ const checkDelimiter = function(delim, context) {
|
||||
"Invalid delimiter: '" + delim.value + "' after '" +
|
||||
context.funcName + "'", delim);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
defineFunction({
|
||||
type: "delimsizing",
|
||||
|
@@ -15,7 +15,8 @@ export type Mode = "math" | "text";
|
||||
// bodies of functions like \textcolor where the
|
||||
// first argument is special and the second
|
||||
// argument is parsed normally)
|
||||
export type ArgType = "color" | "size" | "original";
|
||||
// - "text": Node group parsed as in text mode.
|
||||
export type ArgType = "color" | "size" | "original" | "text";
|
||||
|
||||
// LaTeX display style.
|
||||
export type StyleStr = "text" | "display";
|
||||
|
Reference in New Issue
Block a user