123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- /*************************************************************
- *
- * Copyright (c) 2018-2022 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Mappings for TeX parsing for definitorial commands.
- *
- * @author v.sorge@mathjax.org (Volker Sorge)
- */
- import {ParseMethod} from '../Types.js';
- import TexError from '../TexError.js';
- import TexParser from '../TexParser.js';
- import * as sm from '../SymbolMap.js';
- import {Symbol, Macro} from '../Symbol.js';
- import BaseMethods from '../base/BaseMethods.js';
- import ParseUtil from '../ParseUtil.js';
- import {StackItem} from '../StackItem.js';
- import NewcommandUtil from './NewcommandUtil.js';
- // Namespace
- let NewcommandMethods: Record<string, ParseMethod> = {};
- /**
- * Implements \newcommand{\name}[n][default]{...}
- * @param {TexParser} parser The calling parser.
- * @param {string} name The name of the calling command.
- */
- NewcommandMethods.NewCommand = function(parser: TexParser, name: string) {
- // @test Newcommand Simple
- let cs = NewcommandUtil.GetCsNameArgument(parser, name);
- let n = NewcommandUtil.GetArgCount(parser, name);
- let opt = parser.GetBrackets(name);
- let def = parser.GetArgument(name);
- NewcommandUtil.addMacro(parser, cs, NewcommandMethods.Macro, [def, n, opt]);
- };
- /**
- * Implements \newenvironment{name}[n][default]{begincmd}{endcmd}
- * @param {TexParser} parser The calling parser.
- * @param {string} name The name of the calling command.
- */
- NewcommandMethods.NewEnvironment = function(parser: TexParser, name: string) {
- // @test Newenvironment Empty, Newenvironment Content
- let env = ParseUtil.trimSpaces(parser.GetArgument(name));
- let n = NewcommandUtil.GetArgCount(parser, name);
- let opt = parser.GetBrackets(name);
- let bdef = parser.GetArgument(name);
- let edef = parser.GetArgument(name);
- NewcommandUtil.addEnvironment(parser, env, NewcommandMethods.BeginEnv, [true, bdef, edef, n, opt]);
- };
- /**
- * Implements \def command.
- * @param {TexParser} parser The calling parser.
- * @param {string} name The name of the calling command.
- */
- NewcommandMethods.MacroDef = function(parser: TexParser, name: string) {
- // @test Def DoubleLet, DefReDef
- let cs = NewcommandUtil.GetCSname(parser, name);
- let params = NewcommandUtil.GetTemplate(parser, name, '\\' + cs);
- let def = parser.GetArgument(name);
- !(params instanceof Array) ?
- // @test Def DoubleLet, DefReDef
- NewcommandUtil.addMacro(parser, cs, NewcommandMethods.Macro, [def, params]) :
- // @test Def Let
- NewcommandUtil.addMacro(parser, cs, NewcommandMethods.MacroWithTemplate, [def].concat(params));
- };
- /**
- * Implements the \let command.
- *
- * All \let commands create either new delimiters or macros in the extension
- * maps. In the latter case if the let binds a symbol we have to generate a
- * macro with the appropriate parse methods from the SymbolMap. Otherwise we
- * simply copy the macro under a new name.
- *
- * Let does not always work on special characters as TeX does. For example
- * "\let\car^ a\car b" will yield a superscript, on the otherhand
- * \let\bgroup={ is possible and will work fine in \bgroup a } but will fail
- * in \sqrt\bgroup a}.
- *
- * @param {TexParser} parser The calling parser.
- * @param {string} name The name of the calling command.
- */
- NewcommandMethods.Let = function(parser: TexParser, name: string) {
- const cs = NewcommandUtil.GetCSname(parser, name);
- let c = parser.GetNext();
- // @test Let Bar, Let Caret
- if (c === '=') {
- // @test Let Brace Equal, Let Brace Equal Stretchy
- parser.i++;
- c = parser.GetNext();
- }
- const handlers = parser.configuration.handlers;
- if (c === '\\') {
- // @test Let Bar, Let Brace Equal Stretchy
- name = NewcommandUtil.GetCSname(parser, name);
- let macro = handlers.get('delimiter').lookup('\\' + name) as Symbol;
- if (macro) {
- // @test Let Bar, Let Brace Equal Stretchy
- NewcommandUtil.addDelimiter(parser, '\\' + cs, macro.char, macro.attributes);
- return;
- }
- const map = handlers.get('macro').applicable(name);
- if (!map) {
- // @test Let Undefined CS
- return;
- }
- if (map instanceof sm.MacroMap) {
- // @test Def Let, Newcommand Let
- const macro = (map as sm.CommandMap).lookup(name) as Macro;
- NewcommandUtil.addMacro(parser, cs, macro.func, macro.args, macro.symbol);
- return;
- }
- macro = (map as sm.CharacterMap).lookup(name) as Symbol;
- const newArgs = NewcommandUtil.disassembleSymbol(cs, macro);
- const method = (p: TexParser, _cs: string, ...rest: any[]) => {
- // @test Let Relet, Let Let, Let Circular Macro
- const symb = NewcommandUtil.assembleSymbol(rest);
- return map.parser(p, symb);
- };
- NewcommandUtil.addMacro(parser, cs, method, newArgs);
- return;
- }
- // @test Let Brace Equal, Let Caret
- parser.i++;
- const macro = handlers.get('delimiter').lookup(c) as Symbol;
- if (macro) {
- // @test Let Paren Delim, Let Paren Stretchy
- NewcommandUtil.addDelimiter(parser, '\\' + cs, macro.char, macro.attributes);
- return;
- }
- // @test Let Brace Equal, Let Caret
- NewcommandUtil.addMacro(parser, cs, NewcommandMethods.Macro, [c]);
- };
- /**
- * Process a macro with a parameter template by replacing parameters in the
- * parser's string.
- * @param {TexParser} parser The calling parser.
- * @param {string} name The name of the calling command.
- * @param {string} text The text template of the macro.
- * @param {string} n The number of parameters.
- * @param {string[]} ...params The parameter values.
- */
- NewcommandMethods.MacroWithTemplate = function (parser: TexParser, name: string,
- text: string, n: string,
- ...params: string[]) {
- const argCount = parseInt(n, 10);
- // @test Def Let
- if (argCount) {
- // @test Def Let
- let args = [];
- parser.GetNext();
- if (params[0] && !NewcommandUtil.MatchParam(parser, params[0])) {
- // @test Missing Arguments
- throw new TexError('MismatchUseDef',
- 'Use of %1 doesn\'t match its definition', name);
- }
- for (let i = 0; i < argCount; i++) {
- // @test Def Let
- args.push(NewcommandUtil.GetParameter(parser, name, params[i + 1]));
- }
- text = ParseUtil.substituteArgs(parser, args, text);
- }
- parser.string = ParseUtil.addArgs(parser, text,
- parser.string.slice(parser.i));
- parser.i = 0;
- ParseUtil.checkMaxMacros(parser);
- };
- /**
- * Process a user-defined environment.
- * @param {TexParser} parser The calling parser.
- * @param {StackItem} begin The begin stackitem.
- * @param {string} bdef The begin definition in the newenvironment macro.
- * @param {string} edef The end definition in the newenvironment macro.
- * @param {number} n The number of parameters.
- * @param {string} def Default for an optional parameter.
- */
- NewcommandMethods.BeginEnv = function(parser: TexParser, begin: StackItem,
- bdef: string, edef: string, n: number, def: string) {
- // @test Newenvironment Empty, Newenvironment Content
- // We have an end item, and we are supposed to close this environment.
- if (begin.getProperty('end') && parser.stack.env['closing'] === begin.getName()) {
- // @test Newenvironment Empty, Newenvironment Content
- delete parser.stack.env['closing'];
- // Parse the commands in the end environment definition.
- let rest = parser.string.slice(parser.i);
- parser.string = edef;
- parser.i = 0;
- parser.Parse();
- // Reset to parsing the remainder of the expression.
- parser.string = rest;
- parser.i = 0;
- // Close this environment.
- return parser.itemFactory.create('end').setProperty('name', begin.getName());
- }
- if (n) {
- // @test Newenvironment Optional, Newenvironment Arg Optional
- let args: string[] = [];
- if (def != null) {
- // @test Newenvironment Optional, Newenvironment Arg Optional
- let optional = parser.GetBrackets('\\begin{' + begin.getName() + '}');
- args.push(optional == null ? def : optional);
- }
- for (let i = args.length; i < n; i++) {
- // @test Newenvironment Arg Optional
- args.push(parser.GetArgument('\\begin{' + begin.getName() + '}'));
- }
- bdef = ParseUtil.substituteArgs(parser, args, bdef);
- edef = ParseUtil.substituteArgs(parser, [], edef); // no args, but get errors for #n in edef
- }
- parser.string = ParseUtil.addArgs(parser, bdef,
- parser.string.slice(parser.i));
- parser.i = 0;
- return parser.itemFactory.create('beginEnv').setProperty('name', begin.getName());
- };
- NewcommandMethods.Macro = BaseMethods.Macro;
- export default NewcommandMethods;
|