mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-08 04:28:41 +00:00
Merge pull request #345 from gagern/splitFunctions
Split up functions list into calls to defineFunction
This commit is contained in:
@@ -173,7 +173,7 @@ Parser.prototype.handleInfixNodes = function (body, mode) {
|
||||
}
|
||||
overIndex = i;
|
||||
funcName = node.value.replaceWith;
|
||||
func = functions.funcs[funcName];
|
||||
func = functions[funcName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ Parser.prototype.handleSupSubscript = function(pos, mode, symbol, name) {
|
||||
} else if (group.isFunction) {
|
||||
// ^ and _ have a greediness, so handle interactions with functions'
|
||||
// greediness
|
||||
var funcGreediness = functions.funcs[group.result.result].greediness;
|
||||
var funcGreediness = functions[group.result.result].greediness;
|
||||
if (funcGreediness > SUPSUB_GREEDINESS) {
|
||||
return this.parseFunction(pos, mode);
|
||||
} else {
|
||||
@@ -484,7 +484,7 @@ Parser.prototype.parseFunction = function(pos, mode) {
|
||||
if (baseGroup) {
|
||||
if (baseGroup.isFunction) {
|
||||
var func = baseGroup.result.result;
|
||||
var funcData = functions.funcs[func];
|
||||
var funcData = functions[func];
|
||||
if (mode === "text" && !funcData.allowedInText) {
|
||||
throw new ParseError(
|
||||
"Can't use function '" + func + "' in text mode",
|
||||
@@ -494,7 +494,7 @@ Parser.prototype.parseFunction = function(pos, mode) {
|
||||
var args = [func];
|
||||
var newPos = this.parseArguments(
|
||||
baseGroup.result.position, mode, func, funcData, args);
|
||||
var result = functions.funcs[func].handler.apply(this, args);
|
||||
var result = functions[func].handler.apply(this, args);
|
||||
return new ParseResult(
|
||||
new ParseNode(result.type, result, mode),
|
||||
newPos);
|
||||
@@ -563,7 +563,7 @@ Parser.prototype.parseArguments = function(pos, mode, func, funcData, args) {
|
||||
var argNode;
|
||||
if (arg.isFunction) {
|
||||
var argGreediness =
|
||||
functions.funcs[arg.result.result].greediness;
|
||||
functions[arg.result.result].greediness;
|
||||
if (argGreediness > baseGreediness) {
|
||||
argNode = this.parseFunction(newPos, mode);
|
||||
} else {
|
||||
@@ -695,7 +695,7 @@ Parser.prototype.parseOptionalGroup = function(pos, mode) {
|
||||
Parser.prototype.parseSymbol = function(pos, mode) {
|
||||
var nucleus = this.lexer.lex(pos, mode);
|
||||
|
||||
if (functions.funcs[nucleus.text]) {
|
||||
if (functions[nucleus.text]) {
|
||||
// If there exists a function with this name, we return the function and
|
||||
// say that it is a function.
|
||||
return new ParseFuncOrArgument(
|
||||
|
392
src/functions.js
392
src/functions.js
@@ -1,13 +1,18 @@
|
||||
var utils = require("./utils");
|
||||
var ParseError = require("./ParseError");
|
||||
|
||||
// This file contains a list of functions that we parse. The functions map
|
||||
// contains the following data:
|
||||
|
||||
/*
|
||||
* Keys are the name of the functions to parse
|
||||
* The data contains the following keys:
|
||||
/* This file contains a list of functions that we parse, identified by
|
||||
* the calls to declareFunction.
|
||||
*
|
||||
* The first argument to declareFunction is a single name or a list of names.
|
||||
* All functions named in such a list will share a single implementation.
|
||||
*
|
||||
* Each declared function can have associated properties, which
|
||||
* include the following:
|
||||
*
|
||||
* - numArgs: The number of arguments the function takes.
|
||||
* If this is the only property, it can be passed as a number
|
||||
* instead of an element of a properties object.
|
||||
* - argTypes: (optional) 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`. Valid
|
||||
@@ -50,14 +55,17 @@ var ParseError = require("./ParseError");
|
||||
* should parse. If the optional arguments aren't found,
|
||||
* `null` will be passed to the handler in their place.
|
||||
* (default 0)
|
||||
* - handler: The function that is called to handle this function and its
|
||||
* arguments. The arguments are:
|
||||
*
|
||||
* The last argument is that implementation, the handler for the function(s).
|
||||
* It is called to handle these functions and their arguments.
|
||||
* Its own arguments are:
|
||||
* - func: the text of the function
|
||||
* - [args]: the next arguments are the arguments to the function,
|
||||
* of which there are numArgs of them
|
||||
* - positions: the positions in the overall string of the function
|
||||
* and the arguments. Should only be used to produce
|
||||
* error messages
|
||||
* The handler is called with `this` referring to the parser.
|
||||
* 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
|
||||
@@ -66,26 +74,45 @@ var ParseError = require("./ParseError");
|
||||
* in to the function in buildHTML/buildMathML as `group.value`.
|
||||
*/
|
||||
|
||||
var functions = {
|
||||
// A normal square root
|
||||
"\\sqrt": {
|
||||
function defineFunction(names, props, handler) {
|
||||
if (typeof names === "string") {
|
||||
names = [names];
|
||||
}
|
||||
if (typeof props === "number") {
|
||||
props = { numArgs: props };
|
||||
}
|
||||
// Set default values of functions
|
||||
var data = {
|
||||
numArgs: props.numArgs,
|
||||
argTypes: props.argTypes,
|
||||
greediness: (props.greediness === undefined) ? 1 : props.greediness,
|
||||
allowedInText: !!props.allowedInText,
|
||||
numOptionalArgs: props.numOptionalArgs || 0,
|
||||
handler: handler
|
||||
};
|
||||
for (var i = 0; i < names.length; ++i) {
|
||||
module.exports[names[i]] = data;
|
||||
}
|
||||
}
|
||||
|
||||
// A normal square root
|
||||
defineFunction("\\sqrt", {
|
||||
numArgs: 1,
|
||||
numOptionalArgs: 1,
|
||||
handler: function(func, index, body, positions) {
|
||||
numOptionalArgs: 1
|
||||
}, function(func, index, body, positions) {
|
||||
return {
|
||||
type: "sqrt",
|
||||
body: body,
|
||||
index: index
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Some non-mathy text
|
||||
"\\text": {
|
||||
// Some non-mathy text
|
||||
defineFunction("\\text", {
|
||||
numArgs: 1,
|
||||
argTypes: ["text"],
|
||||
greediness: 2,
|
||||
handler: function(func, body) {
|
||||
greediness: 2
|
||||
}, function(func, body) {
|
||||
// Since the corresponding buildHTML/buildMathML function expects a
|
||||
// list of elements, we normalize for different kinds of arguments
|
||||
// TODO(emily): maybe this should be done somewhere else
|
||||
@@ -100,16 +127,15 @@ var functions = {
|
||||
type: "text",
|
||||
body: inner
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// A two-argument custom color
|
||||
"\\color": {
|
||||
// A two-argument custom color
|
||||
defineFunction("\\color", {
|
||||
numArgs: 2,
|
||||
allowedInText: true,
|
||||
greediness: 3,
|
||||
argTypes: ["color", "original"],
|
||||
handler: function(func, color, body) {
|
||||
argTypes: ["color", "original"]
|
||||
}, function(func, color, body) {
|
||||
// Normalize the different kinds of bodies (see \text above)
|
||||
var inner;
|
||||
if (body.type === "ordgroup") {
|
||||
@@ -123,48 +149,44 @@ var functions = {
|
||||
color: color.value,
|
||||
value: inner
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// An overline
|
||||
"\\overline": {
|
||||
numArgs: 1,
|
||||
handler: function(func, body) {
|
||||
// An overline
|
||||
defineFunction("\\overline", {
|
||||
numArgs: 1
|
||||
}, function(func, body) {
|
||||
return {
|
||||
type: "overline",
|
||||
body: body
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// A box of the width and height
|
||||
"\\rule": {
|
||||
// A box of the width and height
|
||||
defineFunction("\\rule", {
|
||||
numArgs: 2,
|
||||
numOptionalArgs: 1,
|
||||
argTypes: ["size", "size", "size"],
|
||||
handler: function(func, shift, width, height) {
|
||||
argTypes: ["size", "size", "size"]
|
||||
}, function(func, shift, width, height) {
|
||||
return {
|
||||
type: "rule",
|
||||
shift: shift && shift.value,
|
||||
width: width.value,
|
||||
height: height.value
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// A KaTeX logo
|
||||
"\\KaTeX": {
|
||||
numArgs: 0,
|
||||
handler: function(func) {
|
||||
// A KaTeX logo
|
||||
defineFunction("\\KaTeX", {
|
||||
numArgs: 0
|
||||
}, function(func) {
|
||||
return {
|
||||
type: "katex"
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
"\\phantom": {
|
||||
numArgs: 1,
|
||||
handler: function(func, body) {
|
||||
defineFunction("\\phantom", {
|
||||
numArgs: 1
|
||||
}, function(func, body) {
|
||||
var inner;
|
||||
if (body.type === "ordgroup") {
|
||||
inner = body.value;
|
||||
@@ -176,9 +198,7 @@ var functions = {
|
||||
type: "phantom",
|
||||
value: inner
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Extra data needed for the delimiter handler down below
|
||||
var delimiterSizes = {
|
||||
@@ -221,18 +241,8 @@ var fontAliases = {
|
||||
"\\frak": "\\mathfrak"
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a list of functions which each have the same function but have
|
||||
* different names so that we don't have to duplicate the data a bunch of times.
|
||||
* Each element in the list is an object with the following keys:
|
||||
* - funcs: A list of function names to be associated with the data
|
||||
* - data: An objecty with the same data as in each value of the `function`
|
||||
* table above
|
||||
*/
|
||||
var duplicatedFunctions = [
|
||||
// Single-argument color functions
|
||||
{
|
||||
funcs: [
|
||||
// Single-argument color functions
|
||||
defineFunction([
|
||||
"\\blue", "\\orange", "\\pink", "\\red",
|
||||
"\\green", "\\gray", "\\purple",
|
||||
"\\blueA", "\\blueB", "\\blueC", "\\blueD", "\\blueE",
|
||||
@@ -246,12 +256,11 @@ var duplicatedFunctions = [
|
||||
"\\grayA", "\\grayB", "\\grayC", "\\grayD", "\\grayE",
|
||||
"\\grayF", "\\grayG", "\\grayH", "\\grayI",
|
||||
"\\kaBlue", "\\kaGreen"
|
||||
],
|
||||
data: {
|
||||
], {
|
||||
numArgs: 1,
|
||||
allowedInText: true,
|
||||
greediness: 3,
|
||||
handler: function(func, body) {
|
||||
greediness: 3
|
||||
}, function(func, body) {
|
||||
var atoms;
|
||||
if (body.type === "ordgroup") {
|
||||
atoms = body.value;
|
||||
@@ -264,102 +273,82 @@ var duplicatedFunctions = [
|
||||
color: "katex-" + func.slice(1),
|
||||
value: atoms
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// There are 2 flags for operators; whether they produce limits in
|
||||
// displaystyle, and whether they are symbols and should grow in
|
||||
// displaystyle. These four groups cover the four possible choices.
|
||||
// There are 2 flags for operators; whether they produce limits in
|
||||
// displaystyle, and whether they are symbols and should grow in
|
||||
// displaystyle. These four groups cover the four possible choices.
|
||||
|
||||
// No limits, not symbols
|
||||
{
|
||||
funcs: [
|
||||
// No limits, not symbols
|
||||
defineFunction([
|
||||
"\\arcsin", "\\arccos", "\\arctan", "\\arg", "\\cos", "\\cosh",
|
||||
"\\cot", "\\coth", "\\csc", "\\deg", "\\dim", "\\exp", "\\hom",
|
||||
"\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh",
|
||||
"\\tan","\\tanh"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0,
|
||||
handler: function(func) {
|
||||
], {
|
||||
numArgs: 0
|
||||
}, function(func) {
|
||||
return {
|
||||
type: "op",
|
||||
limits: false,
|
||||
symbol: false,
|
||||
body: func
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Limits, not symbols
|
||||
{
|
||||
funcs: [
|
||||
// Limits, not symbols
|
||||
defineFunction([
|
||||
"\\det", "\\gcd", "\\inf", "\\lim", "\\liminf", "\\limsup", "\\max",
|
||||
"\\min", "\\Pr", "\\sup"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0,
|
||||
handler: function(func) {
|
||||
], {
|
||||
numArgs: 0
|
||||
}, function(func) {
|
||||
return {
|
||||
type: "op",
|
||||
limits: true,
|
||||
symbol: false,
|
||||
body: func
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// No limits, symbols
|
||||
{
|
||||
funcs: [
|
||||
// No limits, symbols
|
||||
defineFunction([
|
||||
"\\int", "\\iint", "\\iiint", "\\oint"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0,
|
||||
handler: function(func) {
|
||||
], {
|
||||
numArgs: 0
|
||||
}, function(func) {
|
||||
return {
|
||||
type: "op",
|
||||
limits: false,
|
||||
symbol: true,
|
||||
body: func
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Limits, symbols
|
||||
{
|
||||
funcs: [
|
||||
// Limits, symbols
|
||||
defineFunction([
|
||||
"\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap",
|
||||
"\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes",
|
||||
"\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0,
|
||||
handler: function(func) {
|
||||
], {
|
||||
numArgs: 0
|
||||
}, function(func) {
|
||||
return {
|
||||
type: "op",
|
||||
limits: true,
|
||||
symbol: true,
|
||||
body: func
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Fractions
|
||||
{
|
||||
funcs: [
|
||||
// Fractions
|
||||
defineFunction([
|
||||
"\\dfrac", "\\frac", "\\tfrac",
|
||||
"\\dbinom", "\\binom", "\\tbinom"
|
||||
],
|
||||
data: {
|
||||
], {
|
||||
numArgs: 2,
|
||||
greediness: 2,
|
||||
handler: function(func, numer, denom) {
|
||||
greediness: 2
|
||||
}, function(func, numer, denom) {
|
||||
var hasBarLine;
|
||||
var leftDelim = null;
|
||||
var rightDelim = null;
|
||||
@@ -402,37 +391,29 @@ var duplicatedFunctions = [
|
||||
rightDelim: rightDelim,
|
||||
size: size
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Left and right overlap functions
|
||||
{
|
||||
funcs: ["\\llap", "\\rlap"],
|
||||
data: {
|
||||
// Left and right overlap functions
|
||||
defineFunction(["\\llap", "\\rlap"], {
|
||||
numArgs: 1,
|
||||
allowedInText: true,
|
||||
handler: function(func, body) {
|
||||
allowedInText: true
|
||||
}, function(func, body) {
|
||||
return {
|
||||
type: func.slice(1),
|
||||
body: body
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Delimiter functions
|
||||
{
|
||||
funcs: [
|
||||
// Delimiter functions
|
||||
defineFunction([
|
||||
"\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
|
||||
"\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
|
||||
"\\bigm", "\\Bigm", "\\biggm", "\\Biggm",
|
||||
"\\big", "\\Big", "\\bigg", "\\Bigg",
|
||||
"\\left", "\\right"
|
||||
],
|
||||
data: {
|
||||
numArgs: 1,
|
||||
handler: function(func, delim, positions) {
|
||||
], {
|
||||
numArgs: 1
|
||||
}, function(func, delim, positions) {
|
||||
if (!utils.contains(delimiters, delim.value)) {
|
||||
throw new ParseError(
|
||||
"Invalid delimiter: '" + delim.value + "' after '" +
|
||||
@@ -455,35 +436,22 @@ var duplicatedFunctions = [
|
||||
value: delim.value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Sizing functions (handled in Parser.js explicitly, hence no handler)
|
||||
{
|
||||
funcs: [
|
||||
// Sizing functions (handled in Parser.js explicitly, hence no handler)
|
||||
defineFunction([
|
||||
"\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
|
||||
"\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0
|
||||
}
|
||||
},
|
||||
], 0, null);
|
||||
|
||||
// Style changing functions (handled in Parser.js explicitly, hence no
|
||||
// handler)
|
||||
{
|
||||
funcs: [
|
||||
// Style changing functions (handled in Parser.js explicitly, hence no
|
||||
// handler)
|
||||
defineFunction([
|
||||
"\\displaystyle", "\\textstyle", "\\scriptstyle",
|
||||
"\\scriptscriptstyle"
|
||||
],
|
||||
data: {
|
||||
numArgs: 0
|
||||
}
|
||||
},
|
||||
], 0, null);
|
||||
|
||||
{
|
||||
funcs: [
|
||||
defineFunction([
|
||||
// styles
|
||||
"\\mathrm", "\\mathit", "\\mathbf",
|
||||
|
||||
@@ -493,10 +461,9 @@ var duplicatedFunctions = [
|
||||
|
||||
// aliases
|
||||
"\\Bbb", "\\bold", "\\frak"
|
||||
],
|
||||
data: {
|
||||
numArgs: 1,
|
||||
handler: function (func, body) {
|
||||
], {
|
||||
numArgs: 1
|
||||
}, function (func, body) {
|
||||
if (func in fontAliases) {
|
||||
func = fontAliases[func];
|
||||
}
|
||||
@@ -505,36 +472,28 @@ var duplicatedFunctions = [
|
||||
font: func.slice(1),
|
||||
body: body
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Accents
|
||||
{
|
||||
funcs: [
|
||||
// Accents
|
||||
defineFunction([
|
||||
"\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
|
||||
"\\check", "\\hat", "\\vec", "\\dot"
|
||||
// We don't support expanding accents yet
|
||||
// "\\widetilde", "\\widehat"
|
||||
],
|
||||
data: {
|
||||
numArgs: 1,
|
||||
handler: function(func, base) {
|
||||
], {
|
||||
numArgs: 1
|
||||
}, function(func, base) {
|
||||
return {
|
||||
type: "accent",
|
||||
accent: func,
|
||||
base: base
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Infix generalized fractions
|
||||
{
|
||||
funcs: ["\\over", "\\choose"],
|
||||
data: {
|
||||
numArgs: 0,
|
||||
handler: function (func) {
|
||||
// Infix generalized fractions
|
||||
defineFunction(["\\over", "\\choose"], {
|
||||
numArgs: 0
|
||||
}, function (func) {
|
||||
var replaceWith;
|
||||
switch (func) {
|
||||
case "\\over":
|
||||
@@ -550,33 +509,25 @@ var duplicatedFunctions = [
|
||||
type: "infix",
|
||||
replaceWith: replaceWith
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Row breaks for aligned data
|
||||
{
|
||||
funcs: ["\\\\", "\\cr"],
|
||||
data: {
|
||||
// Row breaks for aligned data
|
||||
defineFunction(["\\\\", "\\cr"], {
|
||||
numArgs: 0,
|
||||
numOptionalArgs: 1,
|
||||
argTypes: ["size"],
|
||||
handler: function(func, size) {
|
||||
argTypes: ["size"]
|
||||
}, function(func, size) {
|
||||
return {
|
||||
type: "cr",
|
||||
size: size
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Environment delimiters
|
||||
{
|
||||
funcs: ["\\begin", "\\end"],
|
||||
data: {
|
||||
// Environment delimiters
|
||||
defineFunction(["\\begin", "\\end"], {
|
||||
numArgs: 1,
|
||||
argTypes: ["text"],
|
||||
handler: function(func, nameGroup, positions) {
|
||||
argTypes: ["text"]
|
||||
}, function(func, nameGroup, positions) {
|
||||
if (nameGroup.type !== "ordgroup") {
|
||||
throw new ParseError(
|
||||
"Invalid environment name",
|
||||
@@ -591,39 +542,4 @@ var duplicatedFunctions = [
|
||||
name: name,
|
||||
namepos: positions[1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var addFuncsWithData = function(funcs, data) {
|
||||
for (var i = 0; i < funcs.length; i++) {
|
||||
functions[funcs[i]] = data;
|
||||
}
|
||||
};
|
||||
|
||||
// Add all of the functions in duplicatedFunctions to the functions map
|
||||
for (var i = 0; i < duplicatedFunctions.length; i++) {
|
||||
addFuncsWithData(duplicatedFunctions[i].funcs, duplicatedFunctions[i].data);
|
||||
}
|
||||
|
||||
// Set default values of functions
|
||||
for (var f in functions) {
|
||||
if (functions.hasOwnProperty(f)) {
|
||||
var func = functions[f];
|
||||
|
||||
functions[f] = {
|
||||
numArgs: func.numArgs,
|
||||
argTypes: func.argTypes,
|
||||
greediness: (func.greediness === undefined) ? 1 : func.greediness,
|
||||
allowedInText: func.allowedInText ? func.allowedInText : false,
|
||||
numOptionalArgs: (func.numOptionalArgs === undefined) ? 0 :
|
||||
func.numOptionalArgs,
|
||||
handler: func.handler
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
funcs: functions
|
||||
};
|
||||
});
|
||||
|
Reference in New Issue
Block a user