MapHandler.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2017-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Singleton class for handling symbol maps.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {AbstractSymbolMap, SymbolMap} from './SymbolMap.js';
  23. import {ParseInput, ParseResult, ParseMethod} from './Types.js';
  24. // import {ParserConfiguration} from './Configuration.js';
  25. import {PrioritizedList} from '../../util/PrioritizedList.js';
  26. import {FunctionList} from '../../util/FunctionList.js';
  27. export type HandlerType = 'delimiter' | 'macro' | 'character' | 'environment';
  28. export type HandlerConfig = {[P in HandlerType]?: string[]};
  29. export type FallbackConfig = {[P in HandlerType]?: ParseMethod};
  30. export namespace MapHandler {
  31. let maps: Map<string, SymbolMap> = new Map();
  32. /**
  33. * Adds a new symbol map to the map handler. Might overwrite an existing
  34. * symbol map of the same name.
  35. *
  36. * @param {SymbolMap} map Registers a new symbol map.
  37. */
  38. export let register = function(map: SymbolMap): void {
  39. maps.set(map.name, map);
  40. };
  41. /**
  42. * Looks up a symbol map if it exists.
  43. *
  44. * @param {string} name The name of the symbol map.
  45. * @return {SymbolMap} The symbol map with the given name or null.
  46. */
  47. export let getMap = function(name: string): SymbolMap {
  48. return maps.get(name);
  49. };
  50. }
  51. /**
  52. * Class of symbol mappings that are active in a configuration.
  53. */
  54. export class SubHandler {
  55. private _configuration: PrioritizedList<SymbolMap> = new PrioritizedList<SymbolMap>();
  56. private _fallback: FunctionList = new FunctionList();
  57. /**
  58. * Adds a list of symbol maps to the handler.
  59. * @param {string[]} maps The names of the symbol maps to add.
  60. * @param {ParseMethod} fallback A fallback method.
  61. * @param {number} priority Optionally a priority.
  62. */
  63. public add(maps: string[], fallback: ParseMethod,
  64. priority: number = PrioritizedList.DEFAULTPRIORITY) {
  65. for (const name of maps.slice().reverse()) {
  66. let map = MapHandler.getMap(name);
  67. if (!map) {
  68. this.warn('Configuration ' + name + ' not found! Omitted.');
  69. return;
  70. }
  71. this._configuration.add(map, priority);
  72. }
  73. if (fallback) {
  74. this._fallback.add(fallback, priority);
  75. }
  76. }
  77. /**
  78. * Parses the given input with the first applicable symbol map.
  79. * @param {ParseInput} input The input for the parser.
  80. * @return {ParseResult} The output of the parsing function.
  81. */
  82. public parse(input: ParseInput): ParseResult {
  83. for (let {item: map} of this._configuration) {
  84. const result = map.parse(input);
  85. if (result) {
  86. return result;
  87. }
  88. }
  89. let [env, symbol] = input;
  90. Array.from(this._fallback)[0].item(env, symbol);
  91. }
  92. /**
  93. * Maps a symbol to its "parse value" if it exists.
  94. *
  95. * @param {string} symbol The symbol to parse.
  96. * @return {T} A boolean, Character, or Macro.
  97. */
  98. public lookup<T>(symbol: string): T {
  99. let map = this.applicable(symbol) as AbstractSymbolMap<T>;
  100. return map ? map.lookup(symbol) : null;
  101. }
  102. /**
  103. * Checks if a symbol is contained in one of the symbol mappings of this
  104. * configuration.
  105. *
  106. * @param {string} symbol The symbol to parse.
  107. * @return {boolean} True if the symbol is contained in the mapping.
  108. */
  109. public contains(symbol: string): boolean {
  110. return this.applicable(symbol) ? true : false;
  111. }
  112. /**
  113. * @override
  114. */
  115. public toString(): string {
  116. let names = [];
  117. for (let {item: map} of this._configuration) {
  118. names.push(map.name);
  119. }
  120. return names.join(', ');
  121. }
  122. /**
  123. * Retrieves the first applicable symbol map in the configuration.
  124. * @param {string} symbol The symbol to parse.
  125. * @return {SymbolMap} A map that can parse the symbol.
  126. */
  127. public applicable(symbol: string): SymbolMap {
  128. for (let {item: map} of this._configuration) {
  129. if (map.contains(symbol)) {
  130. return map;
  131. }
  132. }
  133. return null;
  134. }
  135. /**
  136. * Retrieves the map of the given name.
  137. * @param {string} name Name of the symbol map.
  138. * @return {SymbolMap} The map if it exists.
  139. */
  140. public retrieve(name: string): SymbolMap {
  141. for (let {item: map} of this._configuration) {
  142. if (map.name === name) {
  143. return map;
  144. }
  145. }
  146. return null;
  147. }
  148. /**
  149. * Prints a warning message.
  150. * @param {string} message The warning.
  151. */
  152. private warn(message: string) {
  153. console.log('TexParser Warning: ' + message);
  154. }
  155. }
  156. export class SubHandlers {
  157. private map = new Map<HandlerType, SubHandler>();
  158. /**
  159. * Adds a symbol map to the configuration if it exists.
  160. * @param {string} name of the symbol map.
  161. */
  162. public add(handlers: HandlerConfig, fallbacks: FallbackConfig,
  163. priority: number = PrioritizedList.DEFAULTPRIORITY): void {
  164. for (const key of Object.keys(handlers)) {
  165. let name = key as HandlerType;
  166. let subHandler = this.get(name);
  167. if (!subHandler) {
  168. subHandler = new SubHandler();
  169. this.set(name, subHandler);
  170. }
  171. subHandler.add(handlers[name], fallbacks[name], priority);
  172. }
  173. }
  174. /**
  175. * Setter for subhandlers.
  176. * @param {HandlerType} name The name of the subhandler.
  177. * @param {SubHandler} subHandler The subhandler.
  178. */
  179. public set(name: HandlerType, subHandler: SubHandler) {
  180. this.map.set(name, subHandler);
  181. }
  182. /**
  183. * Getter for subhandler.
  184. * @param {HandlerType} name Name of the subhandler.
  185. * @return {SubHandler} The subhandler by that name if it exists.
  186. */
  187. public get(name: HandlerType): SubHandler {
  188. return this.map.get(name);
  189. }
  190. /**
  191. * Retrieves a symbol map of the given name.
  192. * @param {string} name Name of the symbol map.
  193. * @return {SymbolMap} The map if it exists. O/w null.
  194. */
  195. public retrieve(name: string): SymbolMap {
  196. for (const handler of this.map.values()) {
  197. let map = handler.retrieve(name);
  198. if (map) {
  199. return map;
  200. }
  201. }
  202. return null;
  203. }
  204. /**
  205. * All names of registered subhandlers.
  206. * @return {IterableIterator<string>} Iterable list of keys.
  207. */
  208. public keys(): IterableIterator<string> {
  209. return this.map.keys();
  210. }
  211. }