393 lines
9 KiB
TypeScript
393 lines
9 KiB
TypeScript
/*************************************************************
|
|
*
|
|
* Copyright (c) 2017-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 Symbol map classes.
|
|
*
|
|
* @author v.sorge@mathjax.org (Volker Sorge)
|
|
*/
|
|
|
|
import {Attributes, Args, ParseMethod, ParseInput, ParseResult} from './Types.js';
|
|
import {Symbol, Macro} from './Symbol.js';
|
|
import {MapHandler} from './MapHandler.js';
|
|
|
|
|
|
/**
|
|
* SymbolMaps are the base components for the input parsers.
|
|
*
|
|
* They provide a contains method that checks if a map is applicable (contains)
|
|
* a particular string. Implementing classes then perform the actual symbol
|
|
* parsing, from simple regular expression test, straight forward symbol mapping
|
|
* to transformational functionality on the parsed string.
|
|
*
|
|
* @interface
|
|
*/
|
|
export interface SymbolMap {
|
|
|
|
/**
|
|
* @return {string} The name of the map.
|
|
*/
|
|
name: string;
|
|
|
|
/**
|
|
* @return {ParseMethod} The default parsing method.
|
|
*/
|
|
parser: ParseMethod;
|
|
|
|
/**
|
|
* @param {string} symbol A symbol to parse.
|
|
* @return {boolean} True if the symbol map applies to the symbol.
|
|
*/
|
|
contains(symbol: string): boolean;
|
|
|
|
/**
|
|
* @param {string} symbol A symbol to parse.
|
|
* @return {ParseMethod} A parse method for the symbol.
|
|
*/
|
|
parserFor(symbol: string): ParseMethod;
|
|
|
|
/**
|
|
* @param {TexParser} env The current parser.
|
|
* @param {string} symbol A symbol to parse.
|
|
* @return {ParseResult} The parsed symbol and the rest of the string.
|
|
*/
|
|
parse([env, symbol]: ParseInput): ParseResult;
|
|
|
|
}
|
|
|
|
/**
|
|
* @param {ParseResult} result The result to check
|
|
* @return {ParseResult} True if result was void, result otherwise
|
|
*/
|
|
export function parseResult(result: ParseResult): ParseResult {
|
|
return result === void 0 ? true : result;
|
|
}
|
|
|
|
/**
|
|
* Abstract implementation of symbol maps.
|
|
* @template T
|
|
*/
|
|
export abstract class AbstractSymbolMap<T> implements SymbolMap {
|
|
|
|
/**
|
|
* @constructor
|
|
* @implements {SymbolMap}
|
|
* @param {string} name Name of the mapping.
|
|
* @param {ParseMethod} parser The parser for the mappiong.
|
|
*/
|
|
constructor(private _name: string, private _parser: ParseMethod) {
|
|
MapHandler.register(this);
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public get name(): string {
|
|
return this._name;
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public abstract contains(symbol: string): boolean;
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parserFor(symbol: string) {
|
|
return this.contains(symbol) ? this.parser : null;
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parse([env, symbol]: ParseInput) {
|
|
let parser = this.parserFor(symbol);
|
|
let mapped = this.lookup(symbol);
|
|
return (parser && mapped) ? parseResult(parser(env, mapped as any)) : null;
|
|
}
|
|
|
|
|
|
public set parser(parser: ParseMethod) {
|
|
this._parser = parser;
|
|
}
|
|
|
|
public get parser(): ParseMethod {
|
|
return this._parser;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param {string} symbol
|
|
* @return {T}
|
|
*/
|
|
public abstract lookup(symbol: string): T;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Regular expressions used for parsing strings.
|
|
*/
|
|
export class RegExpMap extends AbstractSymbolMap<string> {
|
|
|
|
/**
|
|
* @constructor
|
|
* @extends {AbstractSymbolMap}
|
|
* @param {string} name Name of the mapping.
|
|
* @param {ParseMethod} parser The parser for the mappiong.
|
|
* @param {RegExp} regexp The regular expression.
|
|
*/
|
|
constructor(name: string, parser: ParseMethod, private _regExp: RegExp) {
|
|
super(name, parser);
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public contains(symbol: string) {
|
|
return this._regExp.test(symbol);
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public lookup(symbol: string): string {
|
|
return this.contains(symbol) ? symbol : null;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse maps associate strings with parsing functionality.
|
|
* @constructor
|
|
* @extends {AbstractSymbolMap}
|
|
* @template K
|
|
*/
|
|
export abstract class AbstractParseMap<K> extends AbstractSymbolMap<K> {
|
|
|
|
private map: Map<string, K> = new Map<string, K>();
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public lookup(symbol: string): K {
|
|
return this.map.get(symbol);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public contains(symbol: string) {
|
|
return this.map.has(symbol);
|
|
}
|
|
|
|
/**
|
|
* Sets mapping for a symbol.
|
|
* @param {string} symbol The symbol to map.
|
|
* @param {K} object The symbols value in the mapping's codomain.
|
|
*/
|
|
public add(symbol: string, object: K) {
|
|
this.map.set(symbol, object);
|
|
}
|
|
|
|
/**
|
|
* Removes a symbol from the map
|
|
* @param {string} symbol The symbol to remove
|
|
*/
|
|
public remove(symbol: string) {
|
|
this.map.delete(symbol);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Maps symbols that can all be parsed with the same method.
|
|
*
|
|
* @constructor
|
|
* @extends {AbstractParseMap}
|
|
*/
|
|
export class CharacterMap extends AbstractParseMap<Symbol> {
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string} name Name of the mapping.
|
|
* @param {ParseMethod} parser The parser for the mapping.
|
|
* @param {JSON} json The JSON representation of the character mapping.
|
|
*/
|
|
constructor(name: string, parser: ParseMethod,
|
|
json: {[index: string]: string | [string, Attributes]}) {
|
|
super(name, parser);
|
|
for (const key of Object.keys(json)) {
|
|
let value = json[key];
|
|
let [char, attrs] = (typeof(value) === 'string') ? [value, null] : value;
|
|
let character = new Symbol(key, char, attrs);
|
|
this.add(key, character);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Maps symbols that are delimiters, that are all parsed with the same method.
|
|
*
|
|
* @constructor
|
|
* @extends {CharacterMap}
|
|
*/
|
|
export class DelimiterMap extends CharacterMap {
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parse([env, symbol]: ParseInput) {
|
|
return super.parse([env, '\\' + symbol]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Maps macros that all bring their own parsing method.
|
|
*
|
|
* @constructor
|
|
* @extends {AbstractParseMap}
|
|
*/
|
|
export class MacroMap extends AbstractParseMap<Macro> {
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string} name Name of the mapping.
|
|
* @param {JSON} json The JSON representation of the macro map.
|
|
* @param {Record<string, ParseMethod>} functionMap Collection of parse
|
|
* functions for the single macros.
|
|
*/
|
|
constructor(name: string,
|
|
json: {[index: string]: string | Args[]},
|
|
functionMap: Record<string, ParseMethod>) {
|
|
super(name, null);
|
|
for (const key of Object.keys(json)) {
|
|
let value = json[key];
|
|
let [func, ...attrs] = (typeof(value) === 'string') ? [value] : value;
|
|
let character = new Macro(key, functionMap[func as string], attrs);
|
|
this.add(key, character);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parserFor(symbol: string) {
|
|
let macro = this.lookup(symbol);
|
|
return macro ? macro.func : null;
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parse([env, symbol]: ParseInput) {
|
|
let macro = this.lookup(symbol);
|
|
let parser = this.parserFor(symbol);
|
|
if (!macro || !parser) {
|
|
return null;
|
|
}
|
|
return parseResult(parser(env, macro.symbol, ...macro.args));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Maps macros that all bring their own parsing method.
|
|
*
|
|
* @constructor
|
|
* @extends {MacroMap}
|
|
*/
|
|
export class CommandMap extends MacroMap {
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parse([env, symbol]: ParseInput) {
|
|
let macro = this.lookup(symbol);
|
|
let parser = this.parserFor(symbol);
|
|
if (!macro || !parser) {
|
|
return null;
|
|
}
|
|
let saveCommand = env.currentCS;
|
|
env.currentCS = '\\' + symbol;
|
|
let result = parser(env, '\\' + macro.symbol, ...macro.args);
|
|
env.currentCS = saveCommand;
|
|
return parseResult(result);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Maps macros for environments. It has a general parsing method for
|
|
* environments, i.e., one that deals with begin/end, and each environment has
|
|
* its own parsing method returning the content.
|
|
*
|
|
* @constructor
|
|
* @extends {MacroMap}
|
|
*/
|
|
export class EnvironmentMap extends MacroMap {
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string} name Name of the mapping.
|
|
* @param {ParseMethod} parser The parser for the environments.
|
|
* @param {JSON} json The JSON representation of the macro map.
|
|
* @param {Record<string, ParseMethod>} functionMap Collection of parse
|
|
* functions for the single macros.
|
|
*/
|
|
constructor(name: string,
|
|
parser: ParseMethod,
|
|
json: {[index: string]: string | Args[]},
|
|
functionMap: Record<string, ParseMethod>) {
|
|
super(name, json, functionMap);
|
|
this.parser = parser;
|
|
}
|
|
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
public parse([env, symbol]: ParseInput) {
|
|
let macro = this.lookup(symbol);
|
|
let envParser = this.parserFor(symbol);
|
|
if (!macro || !envParser) {
|
|
return null;
|
|
}
|
|
return parseResult(this.parser(env, macro.symbol, envParser, macro.args));
|
|
}
|
|
|
|
}
|