UNPKG

docutils-ts

Version:

Port of the Python Docutils library to TypeScript

164 lines (163 loc) 6.41 kB
import { InvalidArgumentsError } from "../exceptions.js"; import UnknownTransitionError from "../error/unknownTransitionError.js"; import DuplicateTransitionError from "../error/duplicateTransitionError.js"; import NestedStateMachine from "../parsers/rst/nestedStateMachine.js"; /** * State superclass. Contains a list of transitions, and transition methods. * * Transition methods all have the same signature. They take 3 parameters: * * - An `re` match object. ``match.string`` contains the matched input line, * ``match.start()`` gives the start index of the match, and * ``match.end()`` gives the end index. * - A context object, whose meaning is application-defined (initial value * ``None``). It can be used to store any information required by the state * machine, and the retured context is passed on to the next transition * method unchanged. * - The name of the next state, a string, taken from the transitions list; * normally it is returned unchanged, but it may be altered by the * transition method if necessary. * * Transition methods all return a 3-tuple: * * - A context object, as (potentially) modified by the transition method. * - The next state name (a return value of ``None`` means no state change). * - The processing result, a list, which is accumulated by the state * machine. * * Transition methods may raise an `EOFError` to cut processing short. * * There are two implicit transitions, and corresponding transition methods * are defined: `bof()` handles the beginning-of-file, and `eof()` handles * the end-of-file. These methods have non-standard signatures and return * values. `bof()` returns the initial context and results, and may be used * to return a header string, or do any other processing needed. `eof()` * should handle any remaining context and wrap things up; it returns the * final processing result. * * Typical applications need only subclass `State` (or a subclass), set the * `patterns` and `initial_transitions` class attributes, and provide * corresponding transition methods. The default object initialization will * take care of constructing the list of transitions. * */ class State { constructor(stateMachine, debug = false) { /** * {Name: pattern} mapping, used by `make_transition()`. Each pattern may * be a string or a compiled `re` pattern. Override in subclasses. */ this.patterns = {}; //protected knownIndentSmKwargs: any; //protected indentSmKwargs: any; this.transitionOrder = []; this.transitions = {}; this.stateName = ''; this.stateMachine = stateMachine; this.logger = stateMachine.logger; this.debug = debug; if (this.createNestedStateMachine === undefined) { this.createNestedStateMachine = () => NestedStateMachine.createStateMachine(this.stateMachine, undefined, this.stateMachine.stateFactory.withStateClasses(["QuotedLiteralBlock"])); } } runtimeInit() { /* empty */ } unlink() { this.stateMachine = undefined; } addInitialTransitions() { //this.logger.silly('addInitialTransitions'); if (this.initialTransitions) { //this.logger.silly('got initial transitions', { value: this.initialTransitions}); const [names, transitions] = this.makeTransitions(this.initialTransitions); this.addTransitions(names, transitions); } } addTransitions(names, transitions) { //this.logger.silly('addTransitions', { value: {names,transitions}}); names.forEach(((name) => { if (name in this.transitions) { throw new DuplicateTransitionError(name); } if (!(name in transitions)) { throw new UnknownTransitionError(name); } })); this.transitionOrder.splice(0, 0, ...names); Object.keys(transitions).forEach((key) => { //this.logger.silly('addTransition', { value:key}); this.transitions[key] = transitions[key]; }); //this.logger.silly('done addTransitions'); } addTransition(name, transition) { this.transitionOrder.splice(0, 0, name); this.transitions[name] = transition; } removeTransition(name) { delete this.transitions[name]; this.transitionOrder.splice(this.transitionOrder.indexOf(name), 1); } makeTransition(name, nextState) { if (name == null) { throw new InvalidArgumentsError('need transition name'); } if (nextState === undefined) { nextState = this.constructor.name; } // @ts-ignore let pattern = this.patterns[name]; if (!(pattern instanceof RegExp)) { try { pattern = new RegExp(`^${pattern}`); } catch (error) { throw error; } } // @ts-ignore if (typeof (this[name]) !== 'function') { throw new Error(`cant find method ${name} on ${this.constructor.name}`); } // @ts-ignore const method = this[name]; return [pattern, method, nextState]; } makeTransitions(nameList) { const names = []; const transitions = {}; if (!Array.isArray(nameList)) { // console.log('warning, not an array'); throw new Error(`not array ${nameList}`); } /* check what happens with throw inside here */ nameList.forEach((namestate) => { if (namestate == null) { throw new InvalidArgumentsError('nameList contains null'); } if (!Array.isArray(namestate)) { transitions[namestate.toString()] = this.makeTransition(namestate); names.push(namestate); } else { transitions[namestate[0]] = this.makeTransition(namestate[0], namestate[1]); names.push(namestate[0]); } }); return [names, transitions]; } noMatch(context, transitions) { return [context, undefined, []]; } bof(context) { return [context, []]; } eof(context) { return []; } nop(match, context, nextState) { return [context, nextState, []]; } } export default State;