123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- /**
- * This module is a simple state machine implementation. We model states as a
- * graph, and the state machine simply keeps track of the current and final
- * state. The state also has a display field as metadata. This serves as the
- * state name and is also used to represent the remaining text to be input at
- * that particular state.
- */
- namespace state {
- export enum TransitionResult { FAILED, SUCCESS, FINISHED }
- interface StateMap {
- [index: string]: State
- }
- interface StateTransitionList {
- [index: string]: State
- }
- export interface Observer {
- (result: TransitionResult): void
- }
- export class State {
- display: string;
- transitions: StateTransitionList;
- constructor(display: string) {
- this.display = display;
- this.transitions = {};
- }
- addTransition(input: string, state: State): void {
- this.transitions[input] = state;
- }
- transition(input: string): State | null {
- return this.transitions[input];
- }
- }
- export class StateMachine {
- initialState: State;
- finalState: State;
- currentState: State;
- observers: Set<Observer>;
- constructor(initialState: State, finalState: State) {
- this.initialState = initialState;
- this.currentState = initialState;
- this.finalState = finalState;
- this.observers = new Set();
- }
- transition(input: string): TransitionResult {
- let result = this._transition(input);
- this.notify(result);
- return result;
- }
- private _transition(input: string): TransitionResult {
- let newState = this.currentState.transition(input);
- if (newState == null) {
- return TransitionResult.FAILED;
- } else {
- this.currentState = newState;
- if (this.finalState === newState) {
- return TransitionResult.FINISHED;
- } else {
- return TransitionResult.SUCCESS;
- }
- }
- }
- isNew(): boolean {
- return this.currentState === this.initialState;
- }
- isFinished(): boolean {
- return this.currentState === this.finalState;
- }
- reset(): void {
- this.currentState = this.initialState;
- }
- clone(): StateMachine {
- return new StateMachine(this.initialState, this.finalState);
- }
- getWord(): string {
- return this.initialState.display;
- }
- getDisplay(): string {
- return this.currentState.display;
- }
- addObserver(observer: Observer): void {
- this.observers.add(observer);
- }
- removeObserver(observer: Observer): void {
- this.observers.delete(observer);
- }
- notify(result: TransitionResult): void {
- this.observers.forEach(o => o(result));
- }
- }
- interface Transition {
- from: string,
- input: string,
- to: string
- }
- export function buildFromTransitions(initial: string, transitions: Transition[]): StateMachine {
- let states: StateMap = {};
- function getState(name: string): State {
- if (states[name] === undefined) {
- states[name] = new State(name);
- }
- return states[name];
- }
- transitions.forEach(t => {
- let fromState = getState(t.from);
- let toState = getState(t.to);
- fromState.addTransition(t.input, toState);
- })
- let initialState = getState(initial);
- let finalState = getState('');
- return new StateMachine(initialState, finalState);
- }
- export function makeTransition(from: string, input: string, to: string): Transition {
- return { from, input, to };
- }
- }
|