* If `optional` is false or absent, this parses an ordinary group, * which is either a single nucleus (like "x") or an expression * in braces (like "{x+y}") or an implicit group, a group that starts * at the current position, and ends right before a higher explicit * group ends, or at EO
(name, // For error reporting. optional, greediness, breakOnTokenText, mode)
| 15630 | |
| 15631 | |
| 15632 | parseGroup(name, // For error reporting. |
| 15633 | optional, greediness, breakOnTokenText, mode) { |
| 15634 | const outerMode = this.mode; |
| 15635 | const firstToken = this.nextToken; |
| 15636 | const text = firstToken.text; // Switch to specified mode |
| 15637 | |
| 15638 | if (mode) { |
| 15639 | this.switchMode(mode); |
| 15640 | } |
| 15641 | |
| 15642 | let groupEnd; |
| 15643 | let result; // Try to parse an open brace or \begingroup |
| 15644 | |
| 15645 | if (optional ? text === "[" : text === "{" || text === "\\begingroup") { |
| 15646 | groupEnd = Parser.endOfGroup[text]; // Start a new group namespace |
| 15647 | |
| 15648 | this.gullet.beginGroup(); // If we get a brace, parse an expression |
| 15649 | |
| 15650 | this.consume(); |
| 15651 | const expression = this.parseExpression(false, groupEnd); |
| 15652 | const lastToken = this.nextToken; // End group namespace before consuming symbol after close brace |
| 15653 | |
| 15654 | this.gullet.endGroup(); |
| 15655 | result = { |
| 15656 | type: "ordgroup", |
| 15657 | mode: this.mode, |
| 15658 | loc: SourceLocation.range(firstToken, lastToken), |
| 15659 | body: expression, |
| 15660 | // A group formed by \begingroup...\endgroup is a semi-simple group |
| 15661 | // which doesn't affect spacing in math mode, i.e., is transparent. |
| 15662 | // https://tex.stackexchange.com/questions/1930/when-should-one- |
| 15663 | // use-begingroup-instead-of-bgroup |
| 15664 | semisimple: text === "\\begingroup" || undefined |
| 15665 | }; |
| 15666 | } else if (optional) { |
| 15667 | // Return nothing for an optional group |
| 15668 | result = null; |
| 15669 | } else { |
| 15670 | // If there exists a function with this name, parse the function. |
| 15671 | // Otherwise, just return a nucleus |
| 15672 | result = this.parseFunction(breakOnTokenText, name, greediness) || this.parseSymbol(); |
| 15673 | |
| 15674 | if (result == null && text[0] === "\\" && !implicitCommands.hasOwnProperty(text)) { |
| 15675 | if (this.settings.throwOnError) { |
| 15676 | throw new ParseError("Undefined control sequence: " + text, firstToken); |
| 15677 | } |
| 15678 | |
| 15679 | result = this.handleUnsupportedCmd(); |
| 15680 | } |
| 15681 | } // Switch mode back |
| 15682 | |
| 15683 | |
| 15684 | if (mode) { |
| 15685 | this.switchMode(outerMode); |
| 15686 | } // Make sure we got a close brace |
| 15687 | |
| 15688 | |
| 15689 | if (groupEnd) { |
no test coverage detected