mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 03:08:40 +00:00
committed by
Ashish Myles
parent
63733f796a
commit
5cc795eaaa
@@ -1,5 +1,4 @@
|
||||
// @flow
|
||||
import {Token} from "./Token";
|
||||
import SourceLocation from "./SourceLocation";
|
||||
import type {Mode} from "./types";
|
||||
|
||||
@@ -21,14 +20,14 @@ export default class ParseNode {
|
||||
type: string, // type of node, like e.g. "ordgroup"
|
||||
value: mixed, // type-specific representation of the node
|
||||
mode: Mode, // parse mode in action for this node, "math" or "text"
|
||||
firstToken?: Token, // first token of the input for this node,
|
||||
// will omit position information if unset
|
||||
lastToken?: Token, // last token of the input for this node,
|
||||
// will default to firstToken if unset
|
||||
first?: {loc: ?SourceLocation}, // first token or node of the input for
|
||||
// this node, will omit position information if unset
|
||||
last?: {loc: ?SourceLocation}, // last token or node of the input for this
|
||||
// node, will default to firstToken if unset
|
||||
) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.mode = mode;
|
||||
this.loc = SourceLocation.range(firstToken, lastToken);
|
||||
this.loc = SourceLocation.range(first, last);
|
||||
}
|
||||
}
|
||||
|
266
src/Parser.js
266
src/Parser.js
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
/* eslint no-constant-condition:0 */
|
||||
/* eslint no-console:0 */
|
||||
import functions from "./functions";
|
||||
@@ -12,6 +13,12 @@ import unicodeSymbols from "./unicodeSymbols";
|
||||
import ParseNode from "./ParseNode";
|
||||
import ParseError from "./ParseError";
|
||||
import { combiningDiacriticalMarksEndRegex } from "./Lexer.js";
|
||||
import Settings from "./Settings";
|
||||
import { Token } from "./Token";
|
||||
|
||||
import type { Mode, ArgType } from "./types";
|
||||
import type { FunctionContext, FunctionSpec } from "./defineFunction" ;
|
||||
import type { EnvSpec } from "./defineEnvironment";
|
||||
|
||||
/**
|
||||
* This file contains the parser used to parse out a TeX expression from the
|
||||
@@ -47,8 +54,6 @@ import { combiningDiacriticalMarksEndRegex } from "./Lexer.js";
|
||||
* standalone object which can be used as an argument to another function.
|
||||
*/
|
||||
|
||||
/* TODO: Uncomment when porting to flow.
|
||||
type ParsedType = "fn" | "arg" | "$"
|
||||
type ParsedFunc = {|
|
||||
type: "fn",
|
||||
result: string, // Function name defined via defineFunction (e.g. "\\frac").
|
||||
@@ -66,38 +71,21 @@ type ParsedDollar = {|
|
||||
token: Token,
|
||||
|};
|
||||
type ParsedFuncOrArgOrDollar = ParsedFunc | ParsedArg | ParsedDollar;
|
||||
*/
|
||||
type ParsedFuncOrArg = ParsedFunc | ParsedArg;
|
||||
|
||||
/**
|
||||
* @param {ParseNode} result
|
||||
* @param {Token} token
|
||||
* @return {ParsedArg}
|
||||
*/
|
||||
function newArgument(result, token) {
|
||||
function newArgument(result: ParseNode, token: Token): ParsedArg {
|
||||
return {type: "arg", result, token};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Token} token
|
||||
* @return {ParsedFunc}
|
||||
*/
|
||||
function newFunction(token) {
|
||||
function newFunction(token: Token): ParsedFunc {
|
||||
return {type: "fn", result: token.text, token};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Token} token
|
||||
* @return {ParsedDollar}
|
||||
*/
|
||||
function newDollar(token) {
|
||||
function newDollar(token: Token): ParsedDollar {
|
||||
return {type: "$", result: "$", token};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ParsedFuncOrArgOrDollar} parsed
|
||||
* @return {ParsedFuncOrArg}
|
||||
*/
|
||||
function assertFuncOrArg(parsed) {
|
||||
function assertFuncOrArg(parsed: ParsedFuncOrArgOrDollar): ParsedFuncOrArg {
|
||||
if (parsed.type === "$") {
|
||||
throw new ParseError("Unexpected $", parsed.token);
|
||||
}
|
||||
@@ -105,7 +93,13 @@ function assertFuncOrArg(parsed) {
|
||||
}
|
||||
|
||||
export default class Parser {
|
||||
constructor(input, settings) {
|
||||
mode: Mode;
|
||||
gullet: MacroExpander;
|
||||
settings: Settings;
|
||||
leftrightDepth: number;
|
||||
nextToken: Token;
|
||||
|
||||
constructor(input: string, settings: Settings) {
|
||||
// Start in math mode
|
||||
this.mode = "math";
|
||||
// Create a new macro expander (gullet) and (indirectly via that) also a
|
||||
@@ -125,18 +119,15 @@ export default class Parser {
|
||||
/**
|
||||
* Checks a result to make sure it has the right type, and throws an
|
||||
* appropriate error otherwise.
|
||||
*
|
||||
* @param {boolean=} consume whether to consume the expected token,
|
||||
* defaults to true
|
||||
*/
|
||||
expect(text, consume) {
|
||||
expect(text: string, consume?: boolean = true) {
|
||||
if (this.nextToken.text !== text) {
|
||||
throw new ParseError(
|
||||
"Expected '" + text + "', got '" + this.nextToken.text + "'",
|
||||
this.nextToken
|
||||
);
|
||||
}
|
||||
if (consume !== false) {
|
||||
if (consume) {
|
||||
this.consume();
|
||||
}
|
||||
}
|
||||
@@ -152,17 +143,15 @@ export default class Parser {
|
||||
/**
|
||||
* Switches between "text" and "math" modes.
|
||||
*/
|
||||
switchMode(newMode) {
|
||||
switchMode(newMode: Mode) {
|
||||
this.mode = newMode;
|
||||
this.gullet.switchMode(newMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main parsing function, which parses an entire input.
|
||||
*
|
||||
* @return {Array.<ParseNode>}
|
||||
*/
|
||||
parse() {
|
||||
parse(): ParseNode[] {
|
||||
// Try to parse the input
|
||||
this.consume();
|
||||
const parse = this.parseInput();
|
||||
@@ -172,7 +161,7 @@ export default class Parser {
|
||||
/**
|
||||
* Parses an entire input tree.
|
||||
*/
|
||||
parseInput() {
|
||||
parseInput(): ParseNode[] {
|
||||
// Parse an expression
|
||||
const expression = this.parseExpression(false);
|
||||
// If we succeeded, make sure there's an EOF at the end
|
||||
@@ -185,17 +174,18 @@ export default class Parser {
|
||||
/**
|
||||
* Parses an "expression", which is a list of atoms.
|
||||
*
|
||||
* @param {boolean} breakOnInfix Should the parsing stop when we hit infix
|
||||
* nodes? This happens when functions have higher precendence
|
||||
* than infix nodes in implicit parses.
|
||||
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
|
||||
* happens when functions have higher precendence han infix
|
||||
* nodes in implicit parses.
|
||||
*
|
||||
* @param {?string} breakOnTokenText The text of the token that the expression
|
||||
* should end with, or `null` if something else should end the
|
||||
* expression.
|
||||
*
|
||||
* @return {Array<ParseNode>}
|
||||
* `breakOnTokenText`: The text of the token that the expression should end
|
||||
* with, or `null` if something else should end the
|
||||
* expression.
|
||||
*/
|
||||
parseExpression(breakOnInfix, breakOnTokenText) {
|
||||
parseExpression(
|
||||
breakOnInfix: boolean,
|
||||
breakOnTokenText?: "]" | "}" | "$",
|
||||
): ParseNode[] {
|
||||
const body = [];
|
||||
// Keep adding atoms to the body until we can't parse any more atoms (either
|
||||
// we reached the end, a }, or a \right)
|
||||
@@ -235,11 +225,8 @@ export default class Parser {
|
||||
*
|
||||
* There can only be one infix operator per group. If there's more than one
|
||||
* then the expression is ambiguous. This can be resolved by adding {}.
|
||||
*
|
||||
* @param {Array<ParseNode>} body
|
||||
* @return {Array<ParseNode>}
|
||||
*/
|
||||
handleInfixNodes(body) {
|
||||
handleInfixNodes(body: ParseNode[]): ParseNode[] {
|
||||
let overIndex = -1;
|
||||
let funcName;
|
||||
|
||||
@@ -256,7 +243,7 @@ export default class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
if (overIndex !== -1) {
|
||||
if (overIndex !== -1 && funcName) {
|
||||
let numerNode;
|
||||
let denomNode;
|
||||
|
||||
@@ -287,10 +274,10 @@ export default class Parser {
|
||||
|
||||
/**
|
||||
* Handle a subscript or superscript with nice errors.
|
||||
* @param {string} name For error reporting.
|
||||
* @return {ParsedNode}
|
||||
*/
|
||||
handleSupSubscript(name) {
|
||||
handleSupSubscript(
|
||||
name: string, // For error reporting.
|
||||
): ParseNode {
|
||||
const symbolToken = this.nextToken;
|
||||
const symbol = symbolToken.text;
|
||||
this.consume();
|
||||
@@ -312,16 +299,16 @@ export default class Parser {
|
||||
if (arg.type === "fn") {
|
||||
// ^ and _ have a greediness, so handle interactions with functions'
|
||||
// greediness
|
||||
const funcGreediness = functions[group.result].greediness;
|
||||
const funcGreediness = functions[arg.result].greediness;
|
||||
if (funcGreediness > Parser.SUPSUB_GREEDINESS) {
|
||||
return this.parseGivenFunction(group);
|
||||
} else {
|
||||
throw new ParseError(
|
||||
"Got function '" + group.result + "' with no arguments " +
|
||||
"Got function '" + arg.result + "' with no arguments " +
|
||||
"as " + name, symbolToken);
|
||||
}
|
||||
} else {
|
||||
return group.result;
|
||||
return arg.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +316,7 @@ export default class Parser {
|
||||
* Converts the textual input of an unsupported command into a text node
|
||||
* contained within a color node whose color is determined by errorColor
|
||||
*/
|
||||
handleUnsupportedCmd() {
|
||||
handleUnsupportedCmd(): ParseNode {
|
||||
const text = this.nextToken.text;
|
||||
const textordArray = [];
|
||||
|
||||
@@ -360,11 +347,8 @@ export default class Parser {
|
||||
|
||||
/**
|
||||
* Parses a group with optional super/subscripts.
|
||||
*
|
||||
* @param {"]" | "}"} breakOnTokenText - character to stop parsing the group on.
|
||||
* @return {?ParseNode}
|
||||
*/
|
||||
parseAtom(breakOnTokenText) {
|
||||
parseAtom(breakOnTokenText?: "]" | "}" | "$"): ?ParseNode {
|
||||
// The body of an atom is an implicit group, so that things like
|
||||
// \left(x\right)^2 work correctly.
|
||||
const base = this.parseImplicitGroup(breakOnTokenText);
|
||||
@@ -481,21 +465,39 @@ export default class Parser {
|
||||
* implicit grouping after it until the end of the group. E.g.
|
||||
* small text {\Large large text} small text again
|
||||
* It is also used for \left and \right to get the correct grouping.
|
||||
*
|
||||
* @param {"]" | "}"} breakOnTokenText - character to stop parsing the group on.
|
||||
* @return {?ParseNode}
|
||||
*/
|
||||
parseImplicitGroup(breakOnTokenText) {
|
||||
parseImplicitGroup(breakOnTokenText?: "]" | "}" | "$"): ?ParseNode {
|
||||
const start = this.parseSymbol();
|
||||
|
||||
if (start == null) {
|
||||
// If we didn't get anything we handle, fall back to parseFunction
|
||||
return this.parseFunction();
|
||||
} else if (start.type === "arg") {
|
||||
// Defer to parseGivenFunction if it's not a function we handle
|
||||
return this.parseGivenFunction(start);
|
||||
}
|
||||
|
||||
const func = start.result;
|
||||
|
||||
if (func === "\\left") {
|
||||
if (func === "$") {
|
||||
if (this.mode === "math") {
|
||||
throw new ParseError("$ within math mode");
|
||||
}
|
||||
const outerMode = this.mode;
|
||||
this.switchMode("math");
|
||||
// Expand next symbol now that we're in math mode.
|
||||
this.consume();
|
||||
const body = this.parseExpression(false, "$");
|
||||
// We can't expand the next symbol after the $ until after
|
||||
// switching modes back. So don't consume within expect.
|
||||
this.expect("$", false);
|
||||
this.switchMode(outerMode);
|
||||
this.consume();
|
||||
return new ParseNode("styling", {
|
||||
style: "text",
|
||||
value: body,
|
||||
}, "math");
|
||||
} else if (func === "\\left") {
|
||||
// If we see a left:
|
||||
// Parse the entire left function (including the delimiter)
|
||||
const left = this.parseGivenFunction(start);
|
||||
@@ -506,6 +508,9 @@ export default class Parser {
|
||||
// Check the next token
|
||||
this.expect("\\right", false);
|
||||
const right = this.parseFunction();
|
||||
if (!right) {
|
||||
throw new ParseError('failed to parse function after \\right');
|
||||
}
|
||||
return new ParseNode("leftright", {
|
||||
body: body,
|
||||
left: left.value.value,
|
||||
@@ -515,13 +520,13 @@ export default class Parser {
|
||||
// begin...end is similar to left...right
|
||||
const begin = this.parseGivenFunction(start);
|
||||
const envName = begin.value.name;
|
||||
if (!environments.has(envName)) {
|
||||
if (!environments.hasOwnProperty(envName)) {
|
||||
throw new ParseError(
|
||||
"No such environment: " + envName, begin.value.nameGroup);
|
||||
}
|
||||
// Build the environment object. Arguments and other information will
|
||||
// be made available to the begin and end methods using properties.
|
||||
const env = environments.get(envName);
|
||||
const env = environments[envName];
|
||||
const {args, optArgs} =
|
||||
this.parseArguments("\\begin{" + envName + "}", env);
|
||||
const context = {
|
||||
@@ -533,13 +538,14 @@ export default class Parser {
|
||||
this.expect("\\end", false);
|
||||
const endNameToken = this.nextToken;
|
||||
const end = this.parseFunction();
|
||||
if (end.value.name !== envName) {
|
||||
if (!end) {
|
||||
throw new ParseError("failed to parse function after \\end");
|
||||
} else if (end.value.name !== envName) {
|
||||
throw new ParseError(
|
||||
"Mismatch: \\begin{" + envName + "} matched " +
|
||||
"by \\end{" + end.value.name + "}",
|
||||
endNameToken);
|
||||
}
|
||||
result.position = end.position;
|
||||
return result;
|
||||
} else if (utils.contains(Parser.sizeFuncs, func)) {
|
||||
// If we see a sizing function, parse out the implicit body
|
||||
@@ -588,24 +594,6 @@ export default class Parser {
|
||||
color: color.result.value,
|
||||
value: body,
|
||||
}, this.mode);
|
||||
} else if (func === "$") {
|
||||
if (this.mode === "math") {
|
||||
throw new ParseError("$ within math mode");
|
||||
}
|
||||
const outerMode = this.mode;
|
||||
this.switchMode("math");
|
||||
// Expand next symbol now that we're in math mode.
|
||||
this.consume();
|
||||
const body = this.parseExpression(false, "$");
|
||||
// We can't expand the next symbol after the $ until after
|
||||
// switching modes back. So don't consume within expect.
|
||||
this.expect("$", false);
|
||||
this.switchMode(outerMode);
|
||||
this.consume();
|
||||
return new ParseNode("styling", {
|
||||
style: "text",
|
||||
value: body,
|
||||
}, "math");
|
||||
} else {
|
||||
// Defer to parseGivenFunction if it's not a function we handle
|
||||
return this.parseGivenFunction(start);
|
||||
@@ -615,10 +603,8 @@ export default class Parser {
|
||||
/**
|
||||
* Parses an entire function, including its base and all of its arguments.
|
||||
* It also handles the case where the parsed node is not a function.
|
||||
*
|
||||
* @return {?ParseNode}
|
||||
*/
|
||||
parseFunction() {
|
||||
parseFunction(): ?ParseNode {
|
||||
const baseGroup = this.parseGroup();
|
||||
return baseGroup ? this.parseGivenFunction(baseGroup) : null;
|
||||
}
|
||||
@@ -626,11 +612,8 @@ export default class Parser {
|
||||
/**
|
||||
* Same as parseFunction(), except that the base is provided, guaranteeing a
|
||||
* non-nullable result.
|
||||
*
|
||||
* @param {ParsedFuncOrArgOrDollar} baseGroup
|
||||
* @return {ParseNode}
|
||||
*/
|
||||
parseGivenFunction(baseGroup) {
|
||||
parseGivenFunction(baseGroup: ParsedFuncOrArgOrDollar): ParseNode {
|
||||
baseGroup = assertFuncOrArg(baseGroup);
|
||||
if (baseGroup.type === "fn") {
|
||||
const func = baseGroup.result;
|
||||
@@ -657,34 +640,36 @@ export default class Parser {
|
||||
|
||||
/**
|
||||
* Call a function handler with a suitable context and arguments.
|
||||
* @param {string} name
|
||||
* @param {Array<ParseNode>} args
|
||||
* @param {Array<?ParseNode>} optArgs
|
||||
* @param {Token=} token
|
||||
*/
|
||||
callFunction(name, args, optArgs, token) {
|
||||
const context = {
|
||||
callFunction(
|
||||
name: string,
|
||||
args: ParseNode[],
|
||||
optArgs: (?ParseNode)[],
|
||||
token?: Token,
|
||||
): * {
|
||||
const context: FunctionContext = {
|
||||
funcName: name,
|
||||
parser: this,
|
||||
token,
|
||||
};
|
||||
return functions[name].handler(context, args, optArgs);
|
||||
const func = functions[name];
|
||||
if (func && func.handler) {
|
||||
return func.handler(context, args, optArgs);
|
||||
} else {
|
||||
throw new ParseError(`No function handler for ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the arguments of a function or environment
|
||||
*
|
||||
* @param {string} func "\name" or "\begin{name}"
|
||||
* @param {{
|
||||
* numArgs: number,
|
||||
* numOptionalArgs: (number|undefined),
|
||||
* }} funcData
|
||||
* @return {{
|
||||
* args: Array<ParseNode>,
|
||||
* optArgs: Array<?ParseNode>,
|
||||
* }}
|
||||
*/
|
||||
parseArguments(func, funcData) {
|
||||
parseArguments(
|
||||
func: string, // Should look like "\name" or "\begin{name}".
|
||||
funcData: FunctionSpec | EnvSpec,
|
||||
): {
|
||||
args: ParseNode[],
|
||||
optArgs: (?ParseNode)[],
|
||||
} {
|
||||
const totalArgs = funcData.numArgs + funcData.numOptionalArgs;
|
||||
if (totalArgs === 0) {
|
||||
return {args: [], optArgs: []};
|
||||
@@ -730,7 +715,7 @@ export default class Parser {
|
||||
"Expected group after '" + func + "'", nextToken);
|
||||
}
|
||||
}
|
||||
let argNode;
|
||||
let argNode: ParseNode;
|
||||
arg = assertFuncOrArg(arg);
|
||||
if (arg.type === "fn") {
|
||||
const argGreediness =
|
||||
@@ -753,10 +738,11 @@ export default class Parser {
|
||||
|
||||
/**
|
||||
* Parses a group when the mode is changing.
|
||||
*
|
||||
* @return {?ParsedFuncOrArgOrDollar}
|
||||
*/
|
||||
parseGroupOfType(type, optional) {
|
||||
parseGroupOfType(
|
||||
type: ArgType, // Used to describe the mode in error messages.
|
||||
optional: boolean,
|
||||
): ?ParsedFuncOrArgOrDollar {
|
||||
// Handle `original` argTypes
|
||||
if (type === "original") {
|
||||
type = this.mode;
|
||||
@@ -786,12 +772,11 @@ export default class Parser {
|
||||
/**
|
||||
* Parses a group, essentially returning the string formed by the
|
||||
* brace-enclosed tokens plus some position information.
|
||||
*
|
||||
* @param {string} modeName Used to describe the mode in error messages
|
||||
* @param {boolean=} optional Whether the group is optional or required
|
||||
* @return {?Token}
|
||||
*/
|
||||
parseStringGroup(modeName, optional) {
|
||||
parseStringGroup(
|
||||
modeName: ArgType, // Used to describe the mode in error messages.
|
||||
optional: boolean,
|
||||
): ?Token {
|
||||
if (optional && this.nextToken.text !== "[") {
|
||||
return null;
|
||||
}
|
||||
@@ -820,12 +805,11 @@ export default class Parser {
|
||||
* Parses a group, essentially returning the string formed by the
|
||||
* brace-enclosed tokens plus some position information, possibly
|
||||
* with nested braces.
|
||||
*
|
||||
* @param {string} modeName Used to describe the mode in error messages
|
||||
* @param {boolean=} optional Whether the group is optional or required
|
||||
* @return {?Token}
|
||||
*/
|
||||
parseStringGroupWithBalancedBraces(modeName, optional) {
|
||||
parseStringGroupWithBalancedBraces(
|
||||
modeName: ArgType, // Used to describe the mode in error messages.
|
||||
optional: boolean,
|
||||
): ?Token {
|
||||
if (optional && this.nextToken.text !== "[") {
|
||||
return null;
|
||||
}
|
||||
@@ -866,12 +850,11 @@ export default class Parser {
|
||||
* Parses a regex-delimited group: the largest sequence of tokens
|
||||
* whose concatenated strings match `regex`. Returns the string
|
||||
* formed by the tokens plus some position information.
|
||||
*
|
||||
* @param {RegExp} regex
|
||||
* @param {string} modeName Used to describe the mode in error messages
|
||||
* @return {Token}
|
||||
*/
|
||||
parseRegexGroup(regex, modeName) {
|
||||
parseRegexGroup(
|
||||
regex: RegExp,
|
||||
modeName: string, // Used to describe the mode in error messages.
|
||||
): Token {
|
||||
const outerMode = this.mode;
|
||||
this.mode = "text";
|
||||
const firstToken = this.nextToken;
|
||||
@@ -895,7 +878,7 @@ export default class Parser {
|
||||
/**
|
||||
* Parses a color description.
|
||||
*/
|
||||
parseColorGroup(optional) {
|
||||
parseColorGroup(optional: boolean): ?ParsedArg {
|
||||
const res = this.parseStringGroup("color", optional);
|
||||
if (!res) {
|
||||
return null;
|
||||
@@ -910,7 +893,7 @@ export default class Parser {
|
||||
/**
|
||||
* Parses a url string.
|
||||
*/
|
||||
parseUrlGroup(optional) {
|
||||
parseUrlGroup(optional: boolean): ?ParsedArg {
|
||||
const res = this.parseStringGroupWithBalancedBraces("url", optional);
|
||||
if (!res) {
|
||||
return null;
|
||||
@@ -927,7 +910,7 @@ export default class Parser {
|
||||
/**
|
||||
* Parses a size specification, consisting of magnitude and unit.
|
||||
*/
|
||||
parseSizeGroup(optional) {
|
||||
parseSizeGroup(optional: boolean): ?ParsedArg {
|
||||
let res;
|
||||
if (!optional && this.nextToken.text !== "{") {
|
||||
res = this.parseRegexGroup(
|
||||
@@ -961,11 +944,8 @@ export default class Parser {
|
||||
* bracket-enclosed group.
|
||||
* If `mode` is present, switches to that mode while parsing the group,
|
||||
* and switches back after.
|
||||
*
|
||||
* @param {boolean=} optional Whether the group is optional or required
|
||||
* @return {?ParsedFuncOrArgOrDollar}
|
||||
*/
|
||||
parseGroup(optional, mode) {
|
||||
parseGroup(optional?: boolean, mode?: Mode): ?ParsedFuncOrArgOrDollar {
|
||||
const outerMode = this.mode;
|
||||
const firstToken = this.nextToken;
|
||||
// Try to parse an open brace
|
||||
@@ -1010,11 +990,9 @@ export default class Parser {
|
||||
* The result will simply replace multiple textord nodes with a single
|
||||
* character in each value by a single textord node having multiple
|
||||
* characters in its value. The representation is still ASCII source.
|
||||
*
|
||||
* @param {Array.<ParseNode>} group the nodes of this group,
|
||||
* list will be moified in place
|
||||
* The group will be modified in place.
|
||||
*/
|
||||
formLigatures(group) {
|
||||
formLigatures(group: ParseNode[]) {
|
||||
let n = group.length - 1;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const a = group[i];
|
||||
@@ -1041,10 +1019,8 @@ export default class Parser {
|
||||
/**
|
||||
* Parse a single symbol out of the string. Here, we handle both the functions
|
||||
* we have defined, as well as the single character symbols
|
||||
*
|
||||
* @return {?ParsedFuncOrArgOrDollar}
|
||||
*/
|
||||
parseSymbol() {
|
||||
parseSymbol(): ?ParsedFuncOrArgOrDollar {
|
||||
const nucleus = this.nextToken;
|
||||
let text = nucleus.text;
|
||||
|
||||
|
@@ -6,11 +6,13 @@
|
||||
|
||||
import utils from "./utils";
|
||||
|
||||
import type { MacroMap } from "./macros";
|
||||
|
||||
export type SettingsOptions = {
|
||||
displayMode?: boolean;
|
||||
throwOnError?: boolean;
|
||||
errorColor?: string;
|
||||
macros?: {[macroName: string]: string};
|
||||
macros?: MacroMap;
|
||||
colorIsTextColor?: boolean;
|
||||
maxSize?: number;
|
||||
};
|
||||
@@ -29,7 +31,7 @@ class Settings {
|
||||
displayMode: boolean;
|
||||
throwOnError: boolean;
|
||||
errorColor: string;
|
||||
macros: {[macroName: string]: string};
|
||||
macros: MacroMap;
|
||||
colorIsTextColor: boolean;
|
||||
maxSize: number;
|
||||
|
||||
|
@@ -49,7 +49,7 @@ type EnvProps = {
|
||||
* 2. requires all arguments except argType
|
||||
* It is generated by `defineEnvironment()` below.
|
||||
*/
|
||||
type EnvSpec = {|
|
||||
export type EnvSpec = {|
|
||||
numArgs: number,
|
||||
argTypes?: ArgType[],
|
||||
greediness: number,
|
||||
|
@@ -1,14 +1,7 @@
|
||||
// @flow
|
||||
import {_environments} from "./defineEnvironment";
|
||||
|
||||
const environments = {
|
||||
has(envName: string) {
|
||||
return _environments.hasOwnProperty(envName);
|
||||
},
|
||||
get(envName: string) {
|
||||
return _environments[envName];
|
||||
},
|
||||
};
|
||||
const environments = _environments;
|
||||
|
||||
export default environments;
|
||||
|
||||
|
@@ -48,7 +48,7 @@ function parseArray(
|
||||
const body = [row];
|
||||
const rowGaps = [];
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
let cell = parser.parseExpression(false, null);
|
||||
let cell = parser.parseExpression(false, undefined);
|
||||
cell = new ParseNode("ordgroup", cell, parser.mode);
|
||||
if (style) {
|
||||
cell = new ParseNode("styling", {
|
||||
@@ -72,6 +72,9 @@ function parseArray(
|
||||
break;
|
||||
} else if (next === "\\\\" || next === "\\cr") {
|
||||
const cr = parser.parseFunction();
|
||||
if (!cr) {
|
||||
throw new ParseError(`Failed to parse function after ${next}`);
|
||||
}
|
||||
rowGaps.push(cr.value.size);
|
||||
row = [];
|
||||
body.push(row);
|
||||
|
Reference in New Issue
Block a user