Skip to content

Commit

Permalink
Port environments.js to @flow. (#862)
Browse files Browse the repository at this point in the history
* Port environment.js to @flow.

* Updated per comments.
  • Loading branch information
marcianx authored and kevinbarabash committed Sep 10, 2017
1 parent b4a0082 commit 1c344ed
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 33 deletions.
125 changes: 92 additions & 33 deletions 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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions src/types.js
Expand Up @@ -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";

0 comments on commit 1c344ed

Please sign in to comment.