Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { toASCII } from "punycode";
- const fs = require('fs');
- const { graphviz } = require('node-graphviz');
- class faInputContext {
- public input: Iterable<string>;
- public codepoint: number = -2;
- public position: number = 0;
- public line: number = 1;
- public column: number = 0;
- public tabWidth: number = 4;
- public constructor(input: string, tabWidth:number = 4, position: number = 0, line: number = 1, column: number = 0) {
- this.input = input;
- this.tabWidth = tabWidth;
- this.position = position;
- this.line = line;
- this.column = column;
- }
- public advance(): number {
- const it = this.input[Symbol.iterator]();
- const n = it.next();
- if(true===n.done) {
- this.codepoint = -1;
- } else {
- this.codepoint = n.value.codePointAt(0)!;
- }
- switch (this.codepoint) {
- case 10:
- ++this.line;
- this.column = 0;
- break;
- case 13:
- this.column = 0;
- break;
- case 9:
- this.column = (((this.column-1)/this.tabWidth)+1)*this.tabWidth+1;
- break;
- default:
- if(this.codepoint>31)
- ++this.column;
- break;
- }
- ++this.position;
- return this.codepoint;
- }
- }
- class faDotGraphOptions {
- public dpi : number = 300;
- public statePrefix: string = "q";
- public debugString: string = null;
- public blockEnds: fa[] = null;
- public debugSourceNfa: fa = null;
- public debugShowNfa: boolean = false;
- public hideAcceptSymbolIds: boolean = false;
- public acceptSymbolNames: string[] = null;
- public vertical: boolean = false;
- };
- interface faProgressCallbackType { (value: number): void };
- class faProgress {
- private _callback: faProgressCallbackType = null;
- public value: number = 0;
- public constructor(callback: faProgressCallbackType = null) {
- this._callback = callback;
- }
- public report(value: number) : void {
- this.value = value;
- if(this._callback!==null) {
- this._callback(value)
- }
- }
- };
- class faRange {
- public min: number;
- public max: number;
- public constructor(min: number, max: number) {
- this.min = min;
- this.max = max;
- }
- public intersects(arg : faRange | number) : boolean
- {
- let range: faRange;
- let codepoint: number;
- if (typeof arg === 'number') {
- codepoint = arg;
- return codepoint>=this.min && codepoint<=this.max;
- }
- range = arg;
- return (range.min >= this.min && range.min <= this.max) ||
- (range.max >= this.min && range.max <= this.max);
- }
- }
- class faTransition {
- public min: number;
- public max: number;
- public to: fa;
- public constructor(to: fa, min: number = -1, max: number = -1) {
- this.min = min;
- this.max = max;
- this.to = to;
- }
- public isEpsilon() : boolean {
- return this.min==-1||this.max==-1;
- }
- }
- class _FListNode {
- public state: fa = null;
- public stateList: _FList = null;
- public next: _FListNode = null;
- public prev: _FListNode = null;
- public constructor(q: fa, sl: _FList) {
- this.state = q;
- this.stateList = sl;
- if (sl.count++ === 0)
- {
- sl.first = this;
- sl.last = this;
- }
- else
- {
- sl.last.next = this;
- this.prev = sl.last;
- sl.last = this;
- }
- }
- public remove() : void {
- --this.stateList.count;
- if(this.stateList.first === this) {
- this.stateList.first = this.next;
- } else {
- this.prev.next = this.next;
- }
- if(this.stateList.last === this) {
- this.stateList.last = this.prev;
- } else {
- this.next.prev = this.prev;
- }
- }
- };
- class _FList {
- public count: number = 0;
- public first: _FListNode = null;
- public last: _FListNode = null;
- public add(q: fa) : _FListNode {
- return new _FListNode(q,this);
- }
- };
- class _FKeyPair {
- public key: number;
- public value: number;
- constructor(key: number, value: number) {
- this.key = key;
- this.value = value;
- }
- };
- class fa {
- public isCompact: boolean = true;
- public isDeterministic: boolean = true;
- public tag: number = -1;
- public acceptSymbol: number = -1;
- public id: number = -1;
- public transitions: faTransition[] = [];
- public fromStates : fa[] = null;
- private _minimizationTag : number = -1;
- public fillClosure(result: fa[] = []): fa[] {
- if (result.includes(this)) {
- return result;
- }
- // add this instance
- result.push(this);
- // add sub instances
- for (const transition of this.transitions) {
- const state : fa = transition.to;
- state.fillClosure(result);
- }
- // Return the list of fa instances
- return result;
- }
- public fillEpsilonClosure(result: fa[] = []) : fa[] {
- if (result.includes(this)) {
- return result;
- }
- // add this instance
- result.push(this);
- // add sub instances
- for (const transition of this.transitions) {
- if(transition.isEpsilon()) {
- const state : fa = transition.to;
- state.fillEpsilonClosure(result);
- }
- }
- // Return the list of fa instances
- return result;
- }
- public static fillEpsilonClosureStates(states: Iterable<fa>, result: fa[] = []) : fa[] {
- for(let state of states) {
- state.fillEpsilonClosure(result);
- }
- return result;
- }
- public isAccepting(): boolean {
- return this.acceptSymbol > -1;
- }
- public addEpsilon(to: fa, compact: boolean = true): void {
- if(to === undefined || to===null) {
- throw "to was not set";
- }
- if (compact)
- {
- if (this.acceptSymbol < 0 && to.acceptSymbol > -1)
- {
- this.acceptSymbol = to.acceptSymbol;
- }
- for (let i: number = 0; i < to.transitions.length; ++i)
- {
- var fat = to.transitions[i];
- if (!fat.isEpsilon())
- {
- this.addTransition(new faRange(fat.min, fat.max), fat.to);
- }
- else
- {
- this.addEpsilon(fat.to, true);
- }
- }
- }
- else
- {
- var found =false;
- let i = 0;
- for (;i<this.transitions.length;++i)
- {
- var fat = this.transitions[i];
- if (!fat.isEpsilon()) break;
- if(fat.to===to)
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- this.transitions.splice(i, 0, new faTransition(to));
- this.isCompact = false;
- this.isDeterministic = false;
- }
- }
- }
- public addTransition(range: faRange, to: fa, compact: boolean = true) : void {
- if(to === undefined || to===null) {
- throw "to was not set";
- }
- if(range.min==-1 || range.max==-1)
- {
- this.addEpsilon(to,compact);
- return;
- }
- if(range.min>range.max)
- {
- const tmp: number = range.min;
- range.min = range.max;
- range.max = tmp;
- }
- var insert = -1;
- for (let i: number = 0; i < this.transitions.length; ++i)
- {
- const fat : faTransition = this.transitions[i];
- if (to === fat.to)
- {
- if(range.min===fat.min &&
- range.max==fat.max)
- {
- return;
- }
- }
- if (this.isDeterministic)
- {
- if (range.intersects(
- new faRange(fat.min, fat.max)))
- {
- this.isDeterministic = false;
- }
- }
- if (range.max>fat.max)
- {
- insert = i;
- }
- if (!this.isDeterministic &&
- range.max < fat.min)
- {
- break;
- }
- }
- this.transitions.splice(insert+1, 0, new faTransition(to,range.min, range.max));
- }
- private static _crackSet(str: string, closure:fa[]): fa[] {
- const result : fa[] = [];
- if(str=="") {
- return result;
- }
- const sa: string[] = str.split(" ");
- for(const s of sa) {
- result.push(closure[Number.parseInt(s,10)]);
- }
- return result;
- }
- private static _makeSet(closure: fa[], states: Iterable<fa>): string {
- let result: string = "";
- let delim: string = "";
- let ns : number[] = [];
- for(const fa of states) {
- const idx = closure.indexOf(fa);
- ns.push(idx);
- }
- ns.sort((x,y)=>x-y);
- for(const i of ns) {
- result+=(delim+i.toString(10));
- delim = " ";
- }
- return result;
- }
- private static _determinize(target: fa, progress: faProgress = null): fa {
- if(progress===null) {
- progress = new faProgress();
- }
- let prog: number = 0;
- progress.report(0);
- const closure: fa[] = target.fillClosure();
- const p: Set<number> = new Set<number>();
- for (const ffa of closure) {
- p.add(0);
- for (const t of ffa.transitions) {
- p.add(t.min);
- if (t.max < 0x10ffff) {
- p.add(t.max + 1);
- }
- }
- }
- const points: number[] = [...p];
- points.sort((x, y) => x - y);
- ++prog;progress.report(prog);
- const sets: Map<string, Set<fa>> = new Map<string, Set<fa>>();
- const working: string[] = [];
- const dfaMap: Map<string, fa> = new Map<string, fa>();
- const initStates: fa[] = closure[0].fillEpsilonClosure();
- let initial : string = fa._makeSet(closure,initStates);
- sets.set(initial, new Set<fa>(initStates));
- working.push(initial);
- const result: fa = new fa();
- result.isDeterministic = true;
- result.fromStates=initStates;
- result.acceptSymbol = fa.getFirstAcceptSymbol(initStates);
- dfaMap.set(initial, result);
- while (working.length > 0) {
- const s: string = working[0];
- working.shift();
- const dfa: fa = dfaMap.get(s)!;
- const crackedS1: fa[] = fa._crackSet(s,closure);
- for (const q of crackedS1) {
- if (q.isAccepting()) {
- dfa.acceptSymbol = q.acceptSymbol;
- break;
- }
- }
- let i: number = 0;
- for (const pnt of points) {
- const set: Set<fa> = new Set<fa>();
- for (const c of crackedS1) {
- const ecs: fa[] = c.fillEpsilonClosure();
- for(const efa of ecs) {
- for (let trns of efa.transitions) {
- if(!trns.isEpsilon()) {
- if (trns.min <= pnt && pnt <= trns.max) {
- for(const eefa of trns.to.fillEpsilonClosure()) {
- set.add(eefa);
- }
- }
- }
- }
- }
- }
- const skey: string = fa._makeSet(closure,set);
- if (!sets.has(skey)) {
- sets.set(skey, set);
- working.push(skey);
- let newFa: fa = new fa();
- newFa.isDeterministic = true;
- newFa.isCompact = true;
- newFa.fromStates = Array.from(set.values());
- dfaMap.set(skey, newFa);
- }
- const dst: fa = dfaMap.get(skey)!;
- const first: number = pnt;
- let last: number;
- if (i + 1 < points.length) {
- last = points[i + 1] - 1;
- } else {
- last = 0x10ffff;
- }
- dfa.transitions.push(new faTransition(dst,first, last));
- ++i;
- }
- ++prog;progress.report(prog);
- }
- for (const ffa of result.fillClosure()) {
- const itrns: faTransition[] = [...ffa.transitions];
- for (const trns of itrns) {
- const acc: fa[] = trns.to.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
- if (acc.length == 0) {
- ffa.transitions.splice(ffa.transitions.indexOf(trns), 1);
- }
- }
- ++prog;progress.report(prog);
- }
- ++prog;progress.report(prog);
- return result;
- }
- public isDfa(): boolean {
- for(const ffa of this.fillClosure()) {
- if(!ffa.isDeterministic) {
- return false;
- }
- }
- return true;
- }
- public toDfa(progress:faProgress = null) : fa {
- return fa._determinize(this,progress);
- }
- public totalize(closure: fa[] = null): void {
- if(closure===null) {
- closure = this.fillClosure();
- }
- const s: fa = new fa();
- s.transitions.push(new faTransition(s,0,0x10ffff));
- for(const p of closure) {
- let maxi: number = 0;
- const sortedTrans: faTransition[] = [...p.transitions];
- sortedTrans.sort((x: faTransition,y: faTransition)=>{
- let c: number = x.max-y.max; if (0 != c) return c; return x.min-y.min;
- });
- for(const t of sortedTrans) {
- if(!t.isEpsilon()) {
- if(t.min>maxi) {
- p.transitions.push(new faTransition(s,maxi,t.min-1));
- }
- if(t.max+1>maxi) {
- maxi = t.max +1;
- }
- }
- }
- if(maxi<=0x10ffff) {
- p.transitions.push(new faTransition(s,maxi,0x10ffff));
- }
- }
- }
- private _step(input: number) : fa {
- for (let ic: number = this.transitions.length, i: number = 0; i < ic; ++i)
- {
- var t = this.transitions[i];
- if (input >= t.min && input <= t.max)
- return t.to;
- }
- return null;
- }
- private static _minimize(a: fa, progress: faProgress) : fa {
- let prog: number = 0;
- if(progress===null) {
- progress = new faProgress();
- }
- progress.report(prog);
- a=a.toDfa(progress);
- const tr: faTransition[] = a.transitions;
- if(tr.length==1) {
- const t: faTransition = tr[0];
- if(t.to=== a && t.min==0&&t.max==0x10ffff) {
- return a;
- }
- }
- a.totalize();
- ++prog;progress.report(prog);
- const cl: fa[] = a.fillClosure();
- const states: fa[] = new Array<fa>();
- let mtag: number = 0;
- for(const q of cl) {
- q._minimizationTag = mtag;
- states.push(q);
- ++mtag;
- }
- let pp: number[] = [];
- for(let ic: number = cl.length,i=0;i<ic;++i) {
- const ffa: fa=cl[i];
- pp.push(0);
- for(const t of ffa.transitions) {
- pp.push(t.min);
- if(t.max<0x10ffff) {
- pp.push(t.max+1);
- }
- }
- }
- const sigma: number[] = new Array<number>();
- for(let i = 0;i<pp.length;++i) {
- sigma.push(pp[i]);
- }
- sigma.sort();
- // initialize the data structures
- const reverse : fa[][][] = new Array<Array<Array<fa>>>();
- for(const s of states) {
- const arr: fa[][] = new Array<Array<fa>>(sigma.length);
- reverse.push(arr);
- }
- ++prog;progress.report(prog);
- const reverseNonempty: boolean[][] = Array<Array<boolean>>();//[states.length,sigma.length];
- for(let i: number = 0;i<states.length;++i) {
- const arr: boolean[] = new Array<boolean>(sigma.length);
- reverseNonempty.push(arr);
- }
- const partition: fa[][] = new Array<Array<fa>>();
- for(let i: number = 0;i<states.length;++i) {
- partition.push(null);
- }
- ++prog;progress.report(prog);
- const block: number[] = new Array<number>();
- for(let i: number = 0;i<states.length;++i) {
- block.push(0);
- }
- const active: _FList[][] = new Array<Array<_FList>>(); // [states.length,sigma.length];
- for(let i: number = 0;i<states.length;++i) {
- const arr: _FList[] = new Array<_FList>(sigma.length);
- active.push(arr);
- }
- const active2: _FListNode[][] = new Array<Array<_FListNode>>()//[states.length,sigma.length];
- for(let i: number = 0;i<states.length;++i) {
- const arr: _FListNode[] = new Array<_FListNode>(sigma.length);
- active2.push(arr);
- }
- const pending: _FKeyPair[] = new Array<_FKeyPair>();
- const pending2: boolean[][] = Array<Array<boolean>>();//[sigma.length,states.length];
- for(let i: number = 0;i<sigma.length;++i) {
- pending2.push(new Array<boolean>(states.length));
- }
- let split: fa[] = new Array<fa>();
- const split2: boolean[] = new Array<boolean>(states.length);
- let refine: number[] = new Array<number>();
- const refine2: boolean[] = new Array<boolean>(states.length);
- const splitBlock: fa[][] = new Array<Array<fa>>(states.length);
- ++prog;progress.report(prog);
- for(let q: number = 0;q<states.length;++q) {
- splitBlock[q]= new Array<fa>();
- partition[q]=new Array<fa>();
- for(let x: number = 0;x<sigma.length;++x) {
- reverse[q][x]=new Array<fa>();
- active[q][x]=new _FList();
- }
- }
- // Find the initial partition and reverse edges
- for(const qq of states) {
- const j: number = qq.isAccepting()? 0: 1;
- if(qq===null) {
- console.log("NULL");
- }
- partition[j].push(qq);
- block[qq._minimizationTag]=j;
- for(let x: number = 0;x<sigma.length;++x) {
- const y: number = sigma[x];
- const p: fa = qq._step(y);
- const pn: number = p._minimizationTag;
- if(reverse[pn] !==null && reverse[pn][x]!==null) {
- reverse[pn][x].push(qq);
- }
- reverseNonempty[pn][x]=true;
- }
- ++prog;progress.report(prog);
- }
- // initialize the active sets
- for(let j: number = 0;j<=1;++j) {
- for(let x: number = 0;x<sigma.length;++x) {
- const part: fa[] = partition[j];
- for(const qq of part) {
- if (reverseNonempty[qq._minimizationTag][x]===true) {
- active2[qq._minimizationTag][x]=active[j][x].add(qq);
- }
- }
- }
- ++prog;progress.report(prog);
- }
- // initialize pending
- for(let x: number = 0; x<sigma.length;++x) {
- const a0: number = active[0][x].count;
- const a1: number = active[1][x].count;
- const j: number = a0<=a1?0:1;
- pending.push(new _FKeyPair(j,x));
- pending2[x][j]=true;
- }
- // process pending until fixed point
- let k: number = 2;
- while(pending.length>0) {
- const ip: _FKeyPair = pending.shift();
- const p: number = ip.key;
- const x: number = ip.value;
- pending2[x][p]=false;
- for(let m: _FListNode = active[p][x].first;m!==null;m=m.next) {
- for(const s of reverse[m.state._minimizationTag][x]) {
- if(!split2[s._minimizationTag]) {
- split2[s._minimizationTag]=true;
- split.push(s);
- const j: number = block[s._minimizationTag];
- splitBlock[j].push(s);
- if(refine2[j]===false) {
- refine2[j]=true;
- refine.push(j);
- }
- }
- }
- }
- ++prog;progress.report(prog);
- // refine blocks
- for(const j of refine) {
- if(splitBlock[j].length<partition[j].length) {
- let b1: Array<fa> = partition[j];
- let b2: Array<fa> = partition[k];
- const e: Array<fa> = splitBlock[j];
- for(const s of e) {
- partition[j] = b1 = b1.splice(b1.indexOf(s),1);
- b2.push(s);
- block[s._minimizationTag]=k;
- for(let c: number = 0;c<sigma.length;++c) {
- const sn: _FListNode = active2[s._minimizationTag][c];
- if(sn!==null && sn.stateList===active[j][c]) {
- sn.remove();
- active2[s._minimizationTag][c]=active[k][c].add(s);
- }
- }
- }
- // update pending
- for(let c:number = 0;c<sigma.length;++c) {
- const aj: number = active[j][c].count;
- const ak: number = active[k][c].count;
- if(!pending2[c][j] && 0<aj&&aj<=ak) {
- pending2[c][j]=true;
- pending.push(new _FKeyPair(j,c));
- } else {
- pending2[c][k]=true;
- pending.push(new _FKeyPair(k,c));
- }
- }
- ++k;
- }
- const sbj: Array<fa> = splitBlock[j];
- for(const s of sbj) {
- split2[s._minimizationTag]=false;
- }
- refine2[j]=false;
- sbj.length=0;
- ++prog;progress.report(prog);
- }
- split.length=0;
- refine.length=0;
- }
- ++prog;progress.report(prog);
- // make a new state for each equiv class, set initial state
- const newstates : fa[] = new Array<fa>();
- for(let n: number = 0; n<k;++n) {
- const s: fa = new fa();
- s.isDeterministic = true;
- newstates.push(s);
- const pn: Array<fa> = partition[n];
- for(const q of pn) {
- if(q === a) {
- a = s;
- }
- s.id = q.id;
- s.acceptSymbol = q.acceptSymbol;
- s._minimizationTag = q._minimizationTag;
- q._minimizationTag = n;
- }
- ++prog;progress.report(prog);
- }
- // build transitions and set acceptance
- for(const s of newstates) {
- const st: fa = states[s._minimizationTag];
- s.acceptSymbol = st.acceptSymbol;
- for(const t of st.transitions) {
- s.transitions.push(new faTransition(newstates[t.to._minimizationTag],t.min,t.max));
- }
- ++prog;progress.report(prog);
- }
- // remove dead transitions
- for(const ffa of a.fillClosure()) {
- const itrns: faTransition[] = new Array<faTransition>(...ffa.transitions)
- ffa.transitions = new Array<faTransition>();
- for(const trns of itrns) {
- //if(!trns.to.isTrap()) {
- ffa.transitions.push(trns);
- //}
- }
- }
- return a;
- }
- public toMinimizedDfa(progress: faProgress = null) {
- return fa._minimize(this,progress);
- }
- public clone(): fa {
- const result: fa[] = [];
- const closure = this.fillClosure();
- for (let j = 0; j < closure.length; ++j) {
- result.push(new fa());
- }
- let i: number = 0;
- for (const ffa of closure) {
- result[i].isCompact = ffa.isCompact;
- result[i].isDeterministic = ffa.isDeterministic;
- result[i].acceptSymbol = ffa.acceptSymbol;
- result[i].tag = ffa.tag;
- for (const trns of ffa.transitions) {
- result[i].transitions.push(new faTransition(result[closure.indexOf(trns.to)],trns.min, trns.max));
- }
- ++i;
- }
- return result[0];
- }
- public toString(format: string, options: faDotGraphOptions = null): string {
- if(format.toLowerCase()==="dot") {
- if (options!=null && options.debugSourceNfa != null && options.debugShowNfa)
- {
- return this._buildCompoundDot(this.fillClosure(), options);
- }
- else
- {
- return this._buildDot(this.fillClosure(),options,-1);
- }
- } else {
- if(this.id < 0) {
- return "[FA]";
- } else {
- return "q"+this.id.toString();
- }
- }
- }
- public setIds(): void {
- let i: number = 0;
- for(const fa of this.fillClosure()) {
- fa.id = i;
- ++i;
- }
- }
- public static literal(codepoints: Iterable<number> | string,accept: number = 0): fa {
- if(typeof codepoints=="string") {
- codepoints = this.toUtf32(codepoints);
- }
- const result: fa = new fa();
- let current: fa = result;
- for (const cp of codepoints) {
- current.acceptSymbol = -1;
- const newFa : fa = new fa();
- newFa.acceptSymbol = accept;
- current.addTransition(new faRange(cp,cp),newFa);
- current = newFa;
- }
- return result;
- }
- public static set(ranges: Iterable<faRange>,accept: number=0): fa {
- const result: fa = new fa();
- const final: fa = new fa();
- final.acceptSymbol = accept;
- for (const range of [...ranges].sort((x, y) => x.max - y.max)) {
- result.addTransition(range,final);
- }
- return result;
- }
- public static concat(exprs: Iterable<fa>,accept: number = 0, compact: boolean = true): fa {
- let result: fa | null = null;
- let left: fa | null = null;
- let right: fa | null = null;
- for (const expr of exprs) {
- let nval = expr.clone();
- if (null === left) {
- if (null === result) {
- result = nval;
- }
- left = nval;
- continue;
- }
- if (null === right) {
- right = nval;
- }
- nval = right.clone();
- fa._concat(left!, nval,compact);
- }
- const target: fa = null !== right ? right! : result!;
- const acc: fa[] = target.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
- for (const afa of acc) {
- afa.acceptSymbol = accept;
- }
- return result!;
- }
- public static or(exprs: Iterable<fa>, accept: number=0, compact: boolean=true): fa {
- const result: fa = new fa();
- const final: fa = new fa();
- final.acceptSymbol = accept;
- for (const expr of exprs) {
- const nfa = expr.clone();
- const nacc: fa[] = nfa.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
- for (const afa of nacc) {
- afa.acceptSymbol = -1;
- afa.addEpsilon(final,compact);
- }
- result.addEpsilon(nfa,compact);
- }
- return result;
- }
- public static optional(expr: fa,accept: number=0,compact: boolean=true): fa {
- const result: fa = expr.clone();
- const acc: fa[] = result.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
- for (const afa of acc) {
- afa.acceptSymbol = accept;
- result.addEpsilon(afa,compact);
- }
- return result;
- }
- public static repeat(expr: fa, minOccurs: number = -1, maxOccurs: number = -1, accept: number = 0, compact: boolean = true): fa {
- expr = expr.clone();
- if (minOccurs > 0 && maxOccurs > 0 && minOccurs > maxOccurs) {
- throw Error("Minimum is greater than maximum");
- }
- let result: fa;
- switch (minOccurs) {
- case -1:
- case 0:
- switch (maxOccurs) {
- case -1:
- case 0:
- result = new fa();
- result.acceptSymbol = accept;
- result.addEpsilon(expr,compact);
- for (const afa of expr.fillClosure()) {
- if(afa.isAccepting()) {
- afa.acceptSymbol = -1;
- afa.addEpsilon(result,compact);
- }
- }
- return result;
- case 1:
- result = this.optional(expr,accept,compact);
- return result;
- default:
- const l: fa[] = [];
- expr = this.optional(expr,accept,compact);
- l.push(expr);
- for (let i = 1; i < maxOccurs; ++i) {
- l.push(expr.clone());
- }
- result = this.concat(l,accept, compact);
- return result;
- }
- case 1:
- switch (maxOccurs) {
- case -1:
- case 0:
- result = this.concat([expr, fa.repeat(expr, 0, 0,accept,compact)],accept,compact);
- return result;
- case 1:
- return expr;
- default:
- result = this.concat([expr, fa.repeat(expr, 0, maxOccurs - 1,accept,compact)],accept,compact);
- return result;
- }
- default:
- switch (maxOccurs) {
- case -1:
- case 0:
- result = this.concat([fa.repeat(expr, minOccurs, minOccurs,accept,compact), fa.repeat(expr, 0, 0,accept,compact)],accept,compact);
- return result;
- case 1:
- throw new Error("Should never get here");
- default:
- if (minOccurs == maxOccurs) {
- const l: fa[] = [];
- expr = this.optional(expr,accept,compact);
- l.push(expr);
- for (let i = 1; i < maxOccurs; ++i) {
- l.push(expr.clone());
- }
- result = this.concat(l,accept,compact);
- return result;
- }
- result = this.concat([this.repeat(expr, minOccurs, minOccurs,accept,compact), fa.repeat(fa.optional(expr,accept,compact), maxOccurs - minOccurs, maxOccurs - minOccurs,accept,compact)],accept,compact);
- return result;
- }
- }
- // throw new Error("Should never get here");
- }
- public isMatch(str: string): boolean {
- let states: fa[] = [];
- this.fillEpsilonClosure(states);
- let remaining: number = str.length - 1;
- for (const cp of fa.toUtf32(str)) {
- const next: fa[] = fa.fillMove(states, cp);
- if (next.length === 0) {
- if (fa.getFirstAcceptSymbol(states)>=0) {
- return remaining === 0;
- }
- }
- states = next;
- --remaining;
- }
- return fa.getFirstAcceptSymbol(states)>=0;
- }
- public isNeutral() : boolean {
- return !this.isAccepting && this.transitions.length==1 && this.transitions[0].isEpsilon();
- }
- public isFinal() : boolean {
- return this.transitions.length==0;
- }
- public isTrap() : boolean {
- if(this.isAccepting()) {
- return false;
- }
- for(const ffa of this.fillClosure()) {
- if(ffa.isAccepting()) {
- return false;
- }
- }
- return true;
- }
- public fillInputTransitionRangesGroupedByState(includeEpsilonTransitions: boolean = false, result: Map<fa,faRange[]> = null) : Map<fa, faRange[]> {
- if(result===null) {
- result = new Map<fa, faRange[]>();
- }
- for(const fat of this.transitions) {
- if(includeEpsilonTransitions || !fat.isEpsilon()) {
- const res : faRange[] | undefined = result.get(fat.to);
- if(res===undefined) {
- const ndata : faRange[] = [new faRange(fat.min,fat.max)];
- result.set(fat.to, ndata);
- } else {
- res.push(new faRange(fat.min,fat.max));
- }
- }
- }
- for(const values of result.values()) {
- values.sort((x: faRange,y: faRange)=>{ var c = x.min - y.min; if(0!=c) return c; return x.max-y.max;});
- }
- return result;
- }
- public toDfaTable(): number[] {
- let fa: fa = this;
- if(!this.isDeterministic) {
- fa = this.toDfa();
- }
- const result: number[] = [];
- const closure: fa[] = fa.fillClosure();
- const stateIndices: number[] = [];
- for(let i: number = 0;i<closure.length;++i) {
- const cfa: fa = closure[i];
- stateIndices.push(result.length);
- result.push(cfa.acceptSymbol);
- const itrgp: Map<fa,faRange[]> = cfa.fillInputTransitionRangesGroupedByState();
- result.push(itrgp.size);
- for(const itr of itrgp.entries()) {
- result.push(closure.indexOf(itr[0]));
- result.push(itr[1].length);
- for(const pair of itr[1]) {
- result.push(pair.min);
- result.push(pair.max);
- }
- }
- }
- let state: number = 0;
- while(state< result.length) {
- ++state;
- const tlen = result[state++];
- for(let i: number = 0;i<tlen;++i) {
- result[state]=stateIndices[result[state]];
- ++state;
- const prlen: number = result[state++];
- state += prlen * 2;
- }
- }
- return result;
- }
- public static fromDfaTable(dfa: number[]) : fa {
- if(dfa.length==0) {
- return new fa();
- }
- let si: number = 0;
- const states: Map<number, fa> = new Map<number, fa>();
- while(si<dfa.length) {
- const newFa: fa = new fa();
- states.set(si,newFa);
- newFa.acceptSymbol = dfa[si++];
- const tlen: number = dfa[si++];
- for(let i: number = 0; i< tlen; ++i) {
- ++si; // tto
- const prlen: number = dfa[si++];
- si+=prlen*2;
- }
- }
- si = 0;
- while(si<dfa.length) {
- var state = states.get(si)!;
- ++si;
- const tlen: number = dfa[si++];
- for (let i: number = 0; i < tlen; ++i) {
- const tto: number = dfa[si++];
- const to: fa = states.get(tto)!;
- const prlen: number = dfa[si++];
- for(let j: number = 0; j <prlen;++j) {
- state.addTransition(new faRange(dfa[si++],dfa[si++]),to);
- }
- }
- }
- return states.get(0)!;
- }
- public static fillMove(states: Iterable<fa>, codepoint: number, result: fa[] = []): fa[] {
- for (const ffa of states) {
- for (const fat of ffa.transitions) {
- if (!fat.isEpsilon() && (codepoint>=fat.min && codepoint <= fat.max)) {
- fat.to.fillEpsilonClosure(result);
- }
- }
- }
- return result;
- }
- public static getFirstAcceptSymbol(states: Iterable<fa>): number {
- for (const fa of states) {
- if (fa.isAccepting()) {
- return fa.acceptSymbol;
- }
- }
- return -1;
- }
- private static _concat(lhs: fa, rhs: fa, compact: boolean ): void {
- const acc: fa[] = lhs.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting() );
- for (const afa of acc) {
- afa.acceptSymbol = -1;
- afa.addEpsilon(rhs,compact);
- }
- }
- public static *toUtf32(str: string): Iterable<number> {
- for (const character of str) {
- let cp: number = character.codePointAt(0)!;
- if(cp>=0xD800 && cp<=0xDBFF) { // hi surrogate
- const cpl: number = character.codePointAt(1)!
- if(!(cpl>=0xDC00 && cpl<=0xDFFF)) { // not a low surrogate
- throw new Error("Unicode stream error. Unterminated high surrogate");
- }
- const highValue = cp & 0b11_11111111;
- const lowValue = cpl & 0b11_11111111;
- const magicAdd = 0b1_00000000_00000000;
- cp = (highValue << 10) | lowValue + magicAdd;
- }
- yield cp;
- }
- }
- private static *_invertRanges(ranges : Iterable<faRange>) : Iterable<faRange>
- {
- if (ranges == null)
- {
- return;
- }
- let last : number = 0x10ffff;
- let e : Iterator<faRange> = ranges[Symbol.iterator]();
- let cur = e.next();
- if (cur.done)
- {
- yield new faRange(0,0x10ffff);
- return;
- }
- if (cur.value.min > 0)
- {
- yield new faRange(0,cur.value.min-1);
- last = cur.value.max;
- if (0x10ffff <= last)
- return;
- }
- else if (cur.value.min == 0)
- {
- last = cur.value.max;
- if (0x10ffff <= last)
- return;
- }
- cur = e.next();
- while (!cur.done)
- {
- if (0x10ffff <= last)
- return;
- if ((last + 1) < cur.value.min)
- {
- yield new faRange(last+1,cur.value.min-1);
- }
- last = cur.value.max;
- cur = e.next();
- }
- if (0x10ffff > last)
- {
- yield new faRange(last+1,0x10ffff);
- }
- }
- private static _appendRangeTo( ranges : faRange[], index: number=-1) : string
- {
- let result: string = "";
- if(index===-1) {
- for(let i: number = 0;i < ranges.length;++i)
- {
- result+=fa._appendRangeTo(ranges, i);
- }
- return result;
- }
- var first = ranges[index].min;
- var last = ranges[index].max;
- result += fa._appendRangeCharTo(String.fromCodePoint(first));
- if (last===first) return result;
- if (last === first + 1) // spit out 1 and 2 length ranges as flat chars
- {
- result+=fa._appendRangeCharTo(String.fromCodePoint(last));
- return result;
- }
- else if(last === first + 2)
- {
- result+=fa._appendRangeCharTo(String.fromCodePoint(first+1));
- result+=fa._appendRangeCharTo(String.fromCodePoint(last));
- return result;
- }
- result+="-";
- result+=fa._appendRangeCharTo(String.fromCodePoint(last));
- return result;
- }
- private static _appendCharTo(ch: string) : string
- {
- let result: string = "";
- switch(ch)
- {
- case '.':
- case '[':
- case ']':
- case '^':
- case '-':
- case '+':
- case '?':
- case '(':
- case ')':
- case '\\':
- result+='\\';
- result+=ch;
- break;
- case '\t':
- result+="\\t";
- break;
- case '\n':
- result+="\\n";
- break;
- case '\r':
- result+="\\r";
- break;
- case '\0':
- result+="\\0";
- break;
- case '\f':
- result+="\\f";
- break;
- case '\v':
- result+="\\v";
- break;
- case '\b':
- result+="\\b";
- break;
- default:
- const s:string = ch;
- const isletter: boolean = s.toLowerCase() != s.toUpperCase();
- const isdigit = !isletter && (s >= '0' && s <= '9');
- const issym = !isletter && !isdigit && ",._-<>/\\=+~`!@#$%^&*(){}[]\"\';:?".indexOf(s)>-1;
- if (!isletter && !isdigit && !issym)
- {
- if (s.length == 1)
- {
- result+="\\u";
- const d: number = s.codePointAt(0);
- result+=("0000" + (+d).toString(16)).slice(-4);
- }
- else
- {
- result+="\\U";
- const d: number = s.codePointAt(0);
- result+=("00000000" + (+d).toString(16)).slice(-8);
- }
- }
- else {
- result+=s;
- }
- break;
- }
- return result;
- }
- private static _appendRangeCharTo(ch: string) : string
- {
- let result : string = "";
- switch(ch)
- {
- case '.':
- case '[':
- case ']':
- case '^':
- case '-':
- case '(':
- case ')':
- case '{':
- case '}':
- case '\\':
- result+='\\';
- result+=ch;
- break;
- case '\t':
- result+="\\t";
- break;
- case '\n':
- result+="\\n";
- break;
- case '\r':
- result+="\\r";
- break;
- case '\0':
- result+="\\0";
- break;
- case '\f':
- result+="\\f";
- break;
- case '\v':
- result+="\\v";
- break;
- case '\b':
- result+="\\b";
- break;
- default:
- const s:string = ch;
- const isletter: boolean = s.toLowerCase() != s.toUpperCase();
- const isdigit = !isletter && (s >= '0' && s <= '9');
- const issym = !isletter && !isdigit && ",._-<>/\\=+~`!@#$%^&*(){}[]\"\';:?".indexOf(s)>-1;
- if (!isletter && !isdigit && !issym)
- {
- if (s.length == 1)
- {
- result+="\\u";
- const d: number = s.codePointAt(0);
- result+=("0000" + (+d).toString(16)).slice(-4);
- }
- else
- {
- result+="\\U";
- const d: number = s.codePointAt(0);
- result+=("00000000" + (+d).toString(16)).slice(-8);
- }
- }
- else
- result+=s;
- break;
- }
- return result;
- }
- private static _escapeLabel(label: string) : string {
- if (label===null || label.length==0) return label;
- let result: string = label.replace("\\", "\\\\");
- result = result.replace("\"", "\\\"");
- result = result.replace("\n", "\\n");
- result = result.replace("\r", "\\r");
- result = result.replace("\0", "\\0");
- result = result.replace("\v", "\\v");
- result = result.replace("\t", "\\t");
- result = result.replace("\f", "\\f");
- return result;
- }
- private _buildDot(closure: fa[], options: faDotGraphOptions, clusterIndex: number) : string {
- let result: string = "";
- if(options===null) {
- options = new faDotGraphOptions();
- }
- const hasBlockEnds: boolean = options.debugShowNfa===false && options.debugString===null&&options.blockEnds!==null;
- const spfx: string = options.statePrefix===null?"q":options.statePrefix;
- let pfx: string = "";
- if(clusterIndex!==-1) {
- result+=("subgraph cluster_" + clusterIndex.toString(10)+ " {\n");
- pfx = "c" + clusterIndex.toString(10);
- } else {
- result+="digraph FA {\n";
- }
- if(!options.vertical) {
- result+="rankdir=LR\n";
- }
- result+="node [shape=circle]\n";
- let accepting: fa[] = [];
- let finals: fa[] = [];
- let neutrals: fa[] = [];
- for(let i: number=0;i<closure.length;++i) {
- let ffa = closure[i];
- if(ffa.isAccepting()) {
- accepting.push(ffa);
- } else if(ffa.isNeutral()) {
- neutrals.push(ffa);
- } else if(ffa.isFinal()) {
- finals.push(ffa);
- }
- }
- let fromStates: fa[] = null;
- let toStates: fa[] = null;
- let tchar: number = -1;
- if(options.debugString!=null) {
- for(let ch of fa.toUtf32(options.debugString)) {
- if(fromStates===null) {
- fromStates = [];
- closure[0].fillEpsilonClosure(fromStates);
- } else {
- fromStates = toStates;
- }
- tchar = ch;
- toStates = fa.fillMove(fromStates,ch);
- if(0==toStates.length) {
- break;
- }
- }
- }
- if(fromStates==null) {
- fromStates = [];
- closure[0].fillEpsilonClosure(fromStates);
- }
- if(toStates!=null) {
- toStates = fa.fillEpsilonClosureStates(toStates);
- }
- let i: number = 0;
- for(const ffa of closure) {
- const isfrom: boolean = fromStates!==null && fa.fillEpsilonClosureStates(fromStates).includes(ffa);
- const rngGrps: Map<fa,faRange[]> = ffa.fillInputTransitionRangesGroupedByState();
- for(const rngGrp of rngGrps) {
- const istrns : boolean = isfrom && toStates!==null&&options.debugString!==null && toStates.includes(rngGrp[0]);
- const di : number = closure.indexOf(rngGrp[0]);
- result += (pfx+spfx);
- result+= i.toString(10);
- result+="->";
- result+=(pfx+spfx);
- result+= di.toString(10);
- result+=" [label=\"";
- let sb: string;
- let notRanges: faRange[] = Array.from(fa._invertRanges(rngGrp[1]));
- if(notRanges.length>rngGrp[1].length) {
- sb=fa._appendRangeTo(rngGrp[1]);
- } else {
- result+="^";
- sb=fa._appendRangeTo(notRanges);
- }
- if(sb.length!=1 || sb===" ") {
- result+='[';
- if(sb.length>16) {
- sb = sb.substring(0,16);
- sb += "...";
- }
- result+=fa._escapeLabel(sb);
- result+="]";
- } else {
- result+=fa._escapeLabel(sb);
- }
- if(!istrns) {
- result+="\"]\n";
- } else {
- result+="\",color=green]\n";
- }
- }
- // do epsilons
- for (const fat of ffa.transitions)
- {
- if (fat.isEpsilon())
- {
- var istrns = null != toStates && options.debugString != null && toStates.includes(ffa) && toStates.includes(fat.to);
- result+=(pfx + spfx);
- result+=i.toString(10);
- result+="->";
- result+=(pfx+spfx);
- result+=(closure.indexOf(fat.to)).toString(10);
- if (!istrns)
- {
- result+=" [style=dashed,color=gray]\n";
- } else
- {
- result+=" [style=dashed,color=green]\n";
- }
- }
- }
- // do block ends
- if(hasBlockEnds && ffa.isAccepting && options.blockEnds?.length > ffa.acceptSymbol && options.blockEnds[ffa.acceptSymbol]!==null) {
- result+=(pfx+spfx+i.toString(10)+"->");
- result+=(pfx+"blockEnd"+ffa.acceptSymbol.toString(10)+spfx+"0");
- result+=(" [style=dashed,label=\".*?\"]\n");
- }
- ++i;
- }
- let delim : string;
- if(hasBlockEnds) {
- for(i = 0;i<options.blockEnds?.length;++i) {
- const bfa : fa = options.blockEnds[i];
- if(bfa!==null) {
- const bclose = bfa.fillClosure();
- delim = "";
- for(let qi: number = 0;qi<bclose.length;++qi) {
- const cbfa: fa = bclose[qi];
- const rngGrps = cbfa.fillInputTransitionRangesGroupedByState();
- for(const rngGrp of rngGrps) {
- const di: number = bclose.indexOf(rngGrp[0]);
- result+=(pfx+"blockEnd"+i.toString(10)+spfx+qi.toString(10));
- result+="->";
- result+=(pfx+"blockEnd"+i.toString(10)+spfx+di.toString(10));
- result+=" [label=\"";
- let sb: string =fa._appendRangeTo(rngGrp[1]);
- if(sb.length!=1|| sb===" ") {
- result+="[";
- if(sb.length>16) {
- sb=sb.substring(0,16);
- sb+="...";
- }
- result+=fa._escapeLabel(sb);
- result+="]";
- } else {
- result+=fa._escapeLabel(sb);
- }
- result+="\"]\n";
- }
- // do epsilons
- for(const fat of cbfa.transitions) {
- if(fat.isEpsilon()) {
- result+=("pfx"+"blockEnd"+i.toString(10)+spfx+qi.toString(10));
- result+="->";
- const di: number = bclose.indexOf(fat.to);
- result+=("pfx"+"blockEnd"+i.toString(10)+spfx+di.toString(10));
- result+=" [style=dashed,color=gray]\n";
- }
- }
- }
- for(let qi:number = 0;qi<bclose.length;++qi) {
- const cbfa: fa = bclose[qi];
- result+=(pfx+"blockEnd"+i.toString(10)+spfx+qi.toString(10)+" [label=<");
- result+="<TABLE BORDER=\"0\"><TR><TD>";
- result+=("(be)"+spfx);
- result+="<SUB>";
- result+=qi.toString(10);
- result+="</SUB></TD></TR>";
- if(cbfa.isAccepting() && !options.hideAcceptSymbolIds) {
- result+="<TD><TD>";
- let acc: string = null;
- if(options.acceptSymbolNames!=null && options.acceptSymbolNames.length>i) {
- acc = options.acceptSymbolNames[i];
- }
- if(acc===null) {
- acc = i.toString(10);
- }
- result+=acc.replace("\"",""");
- result+="</TD></TR>";
- }
- result+="</TABLE>";
- result+=">";
- if(cbfa.isAccepting()) {
- result+=",shape=doublecircle";
- } else if(cbfa.isFinal()||cbfa.isNeutral()) {
- result+=",color=gray";
- }
- result+="]";
- }
- }
- }
- }
- delim = "";
- i = 0;
- for(const ffa of closure) {
- result+=(pfx+spfx);
- result+=i.toString(10);
- result+=" [";
- if(options.debugString!==null) {
- if(toStates!==null) {
- const epstates: fa[] = fa.fillEpsilonClosureStates(toStates,null);
- if(epstates.includes(ffa)) {
- if(!toStates.includes(ffa)) {
- result+="color=darkgreen,"
- } else {
- result+="color=greeen,";
- }
- }
- }
- }
- result+="label=<";
- result+="<TABLE BORDER=\"0\"><TR><TD>";
- result+=spfx;
- result+="<SUB>";
- result+=i.toString(10);
- result+="</SUB></TD></TR>";
- if(options.debugSourceNfa!==null) {
- const from: fa[] = ffa.fromStates;
- let brk: number = Math.floor(Math.sqrt(from.length));
- if(from.length<=3) brk = 3;
- for(let j: number = 0;j<from.length;++j) {
- if(j===0) {
- result+="<TR><TD>";
- result+="{ ";
- delim = "";
- } else if((j%brk)==0) {
- delim="";
- result+="</TD></TR><TR><TD>";
- }
- const fromFa: fa = from[j];
- result+=delim;
- result+="q<SUB>";
- result+=options.debugSourceNfa.fillClosure().indexOf(fromFa).toString(10);
- result+="</SUB>";
- delim = " ";
- if(j===from.length-1) {
- result+=" }";
- result+="</TD></TR>";
- }
- }
- }
- if(ffa.isAccepting() && !options.hideAcceptSymbolIds && !(hasBlockEnds && options.blockEnds?.length>ffa.acceptSymbol && options.blockEnds[ffa.acceptSymbol]!==null)) {
- result+="<TR><TD>";
- let acc: string = null;
- if(options.acceptSymbolNames!==null && options.acceptSymbolNames.length>ffa.acceptSymbol) {
- acc = options.acceptSymbolNames [ffa.acceptSymbol];
- }
- if(acc===null) {
- acc = ffa.acceptSymbol.toString(10);
- }
- result+=acc.replace("\"",""");
- result+="</TD></TR>";
- }
- result+="</TABLE>";
- result+=">";
- let isFinal: boolean = false;
- if(accepting.includes(ffa) && ((!hasBlockEnds || options.blockEnds?.length<=ffa.acceptSymbol || options.blockEnds[ffa.acceptSymbol]===null))) {
- result+=",shape=doublecircle";
- } else if(isFinal || neutrals.includes(ffa)) {
- if((fromStates===null||!fromStates.includes(ffa)) &&
- (toStates===null) ||!toStates.includes(ffa)) {
- result+=",color=gray";
- }
- }
- result+="]\n";
- ++i;
- }
- delim="";
- if(accepting.length>0) {
- for(const ntfa of accepting) {
- if(!hasBlockEnds || options.blockEnds?.length <=ntfa.acceptSymbol || options.blockEnds[ntfa.acceptSymbol]===null) {
- result+=delim;
- result+=(pfx+spfx);
- result+=closure.indexOf(ntfa).toString(10);
- delim=",";
- }
- }
- if(delim!="") {
- result+=" [shape=doublecircle]\n";
- }
- }
- delim="";
- if(neutrals.length>0) {
- for(const ntfa of neutrals) {
- if((fromStates===null || !fromStates.includes(ntfa)) &&
- (toStates==null || !toStates.includes(ntfa))) {
- result+=delim;
- result+=(pfx+spfx);
- result+=closure.indexOf(ntfa).toString(10);
- delim=",";
- }
- }
- result+=" [color=gray]\n";
- delim="";
- }
- delim="";
- if(finals.length>0) {
- for(const ntfa of finals) {
- result+=delim;
- result+=(pfx+spfx);
- result+=closure.indexOf(ntfa).toString(10);
- delim=",";
- }
- result+=" [shape=circle,color=gray]\n";
- }
- result+="}\n";
- return result;
- }
- private _buildCompoundDot(closure: fa[], options: faDotGraphOptions) : string {
- let result: string = "digraph FA {\n";
- let vert: boolean = true;
- if(!options.vertical) {
- result+="rank=LR\n";
- }
- result+="node [shape=circle]\n";
- const opt2: faDotGraphOptions = new faDotGraphOptions();
- opt2.debugSourceNfa = null;
- opt2.statePrefix = options.statePrefix;
- opt2.debugString = options.debugString;
- opt2.debugShowNfa = false;
- opt2.dpi = options.dpi;
- opt2.acceptSymbolNames = options.acceptSymbolNames;
- opt2.hideAcceptSymbolIds = options.hideAcceptSymbolIds;
- opt2.blockEnds = options.blockEnds;
- if(!vert) {
- result+=this._buildDot(closure, options, 2);
- result+=this._buildDot(options.debugSourceNfa.fillClosure(), opt2, 1);
- } else {
- result+=this._buildDot(options.debugSourceNfa.fillClosure(), opt2, 2);
- result+=this._buildDot(closure, options, 1);
- }
- result+="}\n";
- return result;
- }
- }
- console.log("Visual FA");
- const rAZ: faRange = new faRange("A".codePointAt(0),"Z".codePointAt(0));
- const raz: faRange = new faRange("a".codePointAt(0),"z".codePointAt(0));
- const r_: faRange = new faRange("_".codePointAt(0),"_".codePointAt(0));
- const rdig: faRange = new faRange("0".codePointAt(0),"9".codePointAt(0));
- let rngs: faRange[] = new Array<faRange>(r_,rAZ,raz);
- const identFirst: fa = fa.set(rngs);
- rngs.push(rdig);
- const identNext: fa = fa.set(rngs);
- const ident: fa = fa.concat(new Array<fa>(identFirst,fa.repeat(identNext,0,-1))).toMinimizedDfa();
- //fooOrBarz = fa.fromDfaTable(table);
- console.log(ident.isMatch(""));
- console.log(ident.isMatch("foo"));
- console.log(ident.isMatch("foofoo"));
- console.log(ident.isMatch("foofoobar"));
- //console.log(fooLoop.toString("dot"));
- graphviz.dot(ident.toString("dot"), 'svg').then((svg) => {
- // Write the SVG to file
- fs.writeFileSync('graph.svg', svg);
- //console.log(svg);
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement