diff --git a/src/environments.js b/src/environments.js index 726941f781..2beebd9925 100644 --- a/src/environments.js +++ b/src/environments.js @@ -1,18 +1,96 @@ -/* eslint no-constant-condition:0 */ +// @flow import ParseNode from "./ParseNode"; import ParseError from "./ParseError"; +import type Parser from "./Parser"; +import type {ArgType, Mode, StyleStr} from "./types"; + +/** + * The context contains the following properties: + * - mode: current parsing mode. + * - envName: the name of the environment, one of the listed names. + * - parser: the parser object. + * - positions: the positions associated with these arguments from args. + */ +type EnvContext = { + mode: Mode, + envName: string, + parser: Parser, + positions: number[], +}; + +/** + * List of ParseNodes followed by an array of positions. + * Returned by `Parser`'s `parseArguments`. + */ +type ParseResult = (ParseNode | number[])[] | ParseNode; + +/** + * The handler function receives two arguments + * - context: information and references provided by the parser + * - args: an array of arguments passed to \begin{name} + */ +type EnvHandler = (context: EnvContext, args: ParseNode[]) => ParseResult; + +/** + * - numArgs: (default 0) The number of arguments after the \begin{name} function. + * - argTypes: (optional) Just like for a function + * - allowedInText: (default false) Whether or not the environment is allowed + * inside text mode (not enforced yet). + * - numOptionalArgs: (default 0) Just like for a function + */ +type EnvProps = { + numArgs?: number, + argTypes?: ArgType[], + allowedInText?: boolean, + numOptionalArgs?: number, +}; + +type EnvData = { + numArgs: number, + argTypes?: ArgType[], + greediness: number, + allowedInText: boolean, + numOptionalArgs: number, + handler: EnvHandler, +}; +const environments: {[string]: EnvData} = {}; +export default environments; + +// Data stored in the ParseNode associated with the environment. +type AlignSpec = { type: "separator", separator: string } | { + type: "align", + align: string, + pregap?: number, + postgap?: number, +}; +type ArrayEnvNodeData = { + type: "array", + hskipBeforeAndAfter?: boolean, + arraystretch?: number, + addJot?: boolean, + cols?: AlignSpec[], + // These fields are always set, but not on struct construction + // initialization. + body?: ParseNode[][], // List of rows in the (2D) array. + rowGaps?: number[], +}; + /** * Parse the body of the environment, with rows delimited by \\ and * columns delimited by &, and create a nested list in row-major order * with one group per cell. If given an optional argument style * ("text", "display", etc.), then each cell is cast into that style. */ -function parseArray(parser, result, style) { +function parseArray( + parser: Parser, + result: ArrayEnvNodeData, + style: StyleStr, +) { let row = []; const body = [row]; const rowGaps = []; - while (true) { + for (;;) { let cell = parser.parseExpression(false, null); cell = new ParseNode("ordgroup", cell, parser.mode); if (style) { @@ -42,36 +120,17 @@ function parseArray(parser, result, style) { return new ParseNode(result.type, result, parser.mode); } + /* * An environment definition is very similar to a function definition: - * it is declared with a name or a list of names, a set of properties - * and a handler containing the actual implementation. - * - * The properties include: - * - numArgs: The number of arguments after the \begin{name} function. - * - argTypes: (optional) Just like for a function - * - allowedInText: (optional) Whether or not the environment is allowed inside - * text mode (default false) (not enforced yet) - * - numOptionalArgs: (optional) Just like for a function - * A bare number instead of that object indicates the numArgs value. - * - * The handler function will receive two arguments - * - context: information and references provided by the parser - * - args: an array of arguments passed to \begin{name} - * The context contains the following properties: - * - envName: the name of the environment, one of the listed names. - * - parser: the parser object - * - lexer: the lexer object - * - positions: the positions associated with these arguments from args. - * The handler must return a ParseResult. + * it is declared with a list of names, a set of properties and a handler + * containing the actual implementation. */ -function defineEnvironment(names, props, handler) { - if (typeof names === "string") { - names = [names]; - } - if (typeof props === "number") { - props = { numArgs: props }; - } +function defineEnvironment( + names: string[], + props: EnvProps, + handler: EnvHandler, +) { // Set default values of environments const data = { numArgs: props.numArgs || 0, @@ -82,7 +141,7 @@ function defineEnvironment(names, props, handler) { handler: handler, }; for (let i = 0; i < names.length; ++i) { - module.exports[names[i]] = data; + environments[names[i]] = data; } } @@ -207,7 +266,7 @@ defineEnvironment([ // except it operates within math mode. // Note that we assume \nomallineskiplimit to be zero, // so that \strut@ is the same as \strut. -defineEnvironment("aligned", { +defineEnvironment(["aligned"], { }, function(context) { let res = { type: "array", @@ -252,7 +311,7 @@ defineEnvironment("aligned", { // A gathered environment is like an array environment with one centered // column, but where rows are considered lines so get \jot line spacing // and contents are set in \displaystyle. -defineEnvironment("gathered", { +defineEnvironment(["gathered"], { }, function(context) { let res = { type: "array", diff --git a/src/types.js b/src/types.js index f67da72bc8..30b55e887f 100644 --- a/src/types.js +++ b/src/types.js @@ -16,3 +16,6 @@ export type Mode = "math" | "text"; // first argument is special and the second // argument is parsed normally) export type ArgType = "color" | "size" | "original"; + +// LaTeX display style. +export type StyleStr = "text" | "display";