Advertisement
honey_the_codewitch

beginning of DFA engine

Dec 26th, 2023 (edited)
1,451
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { toASCII } from "punycode";
  2.  
  3. const fs = require('fs');
  4. const { graphviz } = require('node-graphviz');
  5.  
  6. class faInputContext {
  7.     public input: Iterable<string>;
  8.     public codepoint: number = -2;
  9.     public position: number = 0;
  10.     public line: number = 1;
  11.     public column: number = 0;
  12.     public tabWidth: number = 4;
  13.     public constructor(input: string, tabWidth:number = 4, position: number = 0, line: number = 1, column: number = 0) {
  14.         this.input = input;
  15.         this.tabWidth = tabWidth;
  16.         this.position = position;
  17.         this.line = line;
  18.         this.column = column;
  19.     }
  20.  
  21.     public advance(): number {
  22.         const it = this.input[Symbol.iterator]();
  23.         const n = it.next();
  24.         if(true===n.done) {
  25.             this.codepoint = -1;
  26.         } else {
  27.             this.codepoint = n.value.codePointAt(0)!;
  28.         }
  29.         switch (this.codepoint) {
  30.             case 10:
  31.                 ++this.line;
  32.                 this.column = 0;
  33.                 break;
  34.             case 13:
  35.                 this.column = 0;
  36.                 break;
  37.             case 9:
  38.                 this.column = (((this.column-1)/this.tabWidth)+1)*this.tabWidth+1;
  39.                 break;
  40.             default:
  41.                 if(this.codepoint>31)
  42.                     ++this.column;
  43.                 break;
  44.         }
  45.         ++this.position;
  46.         return this.codepoint;
  47.     }
  48. }
  49. class faDotGraphOptions {
  50.     public dpi : number = 300;
  51.     public statePrefix: string = "q";
  52.     public debugString: string = null;
  53.     public blockEnds: fa[] = null;
  54.     public debugSourceNfa: fa = null;
  55.     public debugShowNfa: boolean = false;
  56.     public hideAcceptSymbolIds: boolean = false;
  57.     public acceptSymbolNames: string[] = null;
  58.     public vertical: boolean = false;
  59. };
  60. interface faProgressCallbackType { (value: number): void };
  61.  
  62. class faProgress {
  63.  
  64.     private _callback: faProgressCallbackType = null;
  65.     public value: number = 0;
  66.     public constructor(callback: faProgressCallbackType = null) {
  67.         this._callback = callback;
  68.     }
  69.     public report(value: number) : void {
  70.         this.value = value;
  71.         if(this._callback!==null) {
  72.             this._callback(value)
  73.         }
  74.     }
  75. };
  76. class faRange {
  77.     public min: number;
  78.     public max: number;
  79.     public constructor(min: number, max: number) {
  80.         this.min = min;
  81.         this.max = max;
  82.     }
  83.     public intersects(arg : faRange | number) : boolean
  84.     {
  85.         let range: faRange;
  86.         let codepoint: number;
  87.         if (typeof arg === 'number') {
  88.             codepoint = arg;
  89.             return codepoint>=this.min && codepoint<=this.max;
  90.         }
  91.         range = arg;
  92.         return (range.min >= this.min && range.min <= this.max) ||
  93.             (range.max >= this.min && range.max <= this.max);
  94.     }
  95. }
  96. class faTransition {
  97.     public min: number;
  98.     public max: number;
  99.     public to: fa;
  100.     public constructor(to: fa, min: number = -1, max: number = -1) {
  101.         this.min = min;
  102.         this.max = max;
  103.         this.to = to;
  104.     }
  105.     public isEpsilon() : boolean {
  106.         return this.min==-1||this.max==-1;
  107.     }
  108. }
  109. class _FListNode {
  110.     public state: fa = null;
  111.     public stateList: _FList = null;
  112.     public next: _FListNode = null;
  113.     public prev: _FListNode = null;
  114.     public constructor(q: fa, sl: _FList) {
  115.         this.state = q;
  116.         this.stateList = sl;
  117.         if (sl.count++ === 0)
  118.         {
  119.             sl.first = this;
  120.             sl.last = this;
  121.         }
  122.         else
  123.         {
  124.             sl.last.next = this;
  125.             this.prev = sl.last;
  126.             sl.last = this;
  127.         }
  128.     }
  129.     public remove() : void {
  130.         --this.stateList.count;
  131.         if(this.stateList.first === this) {
  132.             this.stateList.first = this.next;
  133.         } else {
  134.             this.prev.next = this.next;
  135.         }
  136.         if(this.stateList.last === this) {
  137.             this.stateList.last = this.prev;
  138.         } else {
  139.             this.next.prev = this.prev;
  140.         }
  141.     }
  142. };
  143. class _FList {
  144.     public count: number = 0;
  145.     public first: _FListNode = null;
  146.     public last: _FListNode = null;
  147.     public add(q: fa) : _FListNode {
  148.         return new _FListNode(q,this);
  149.     }
  150. };
  151. class _FKeyPair  {
  152.     public key: number;
  153.     public value: number;
  154.     constructor(key: number, value: number) {
  155.         this.key = key;
  156.         this.value = value;
  157.     }
  158. };
  159. class fa {
  160.     public isCompact: boolean = true;
  161.     public isDeterministic: boolean = true;
  162.     public tag: number = -1;
  163.     public acceptSymbol: number = -1;
  164.     public id: number = -1;
  165.     public transitions: faTransition[] = [];
  166.     public fromStates : fa[] = null;
  167.     private _minimizationTag : number = -1;
  168.     public fillClosure(result: fa[] = []): fa[] {
  169.         if (result.includes(this)) {
  170.             return result;
  171.         }
  172.         // add this instance
  173.         result.push(this);
  174.         // add sub instances
  175.         for (const transition of this.transitions) {
  176.             const state : fa = transition.to;
  177.             state.fillClosure(result);
  178.         }
  179.         // Return the list of fa instances
  180.         return result;
  181.     }
  182.     public fillEpsilonClosure(result: fa[] = []) : fa[] {
  183.         if (result.includes(this)) {
  184.             return result;
  185.         }
  186.         // add this instance
  187.         result.push(this);
  188.         // add sub instances
  189.         for (const transition of this.transitions) {
  190.             if(transition.isEpsilon()) {
  191.                 const state : fa = transition.to;
  192.                 state.fillEpsilonClosure(result);
  193.             }
  194.         }
  195.         // Return the list of fa instances
  196.         return result;
  197.     }
  198.     public static fillEpsilonClosureStates(states: Iterable<fa>, result: fa[] = []) : fa[] {
  199.         for(let state of states) {
  200.             state.fillEpsilonClosure(result);
  201.         }
  202.         return result;
  203.     }
  204.     public isAccepting(): boolean {
  205.         return this.acceptSymbol > -1;
  206.     }
  207.     public addEpsilon(to: fa, compact: boolean = true): void {
  208.         if(to === undefined || to===null) {
  209.             throw "to was not set";
  210.         }
  211.         if (compact)
  212.         {
  213.             if (this.acceptSymbol < 0 && to.acceptSymbol > -1)
  214.             {
  215.                 this.acceptSymbol = to.acceptSymbol;
  216.             }
  217.             for (let i: number = 0; i < to.transitions.length; ++i)
  218.             {
  219.                 var fat = to.transitions[i];
  220.                 if (!fat.isEpsilon())
  221.                 {
  222.                     this.addTransition(new faRange(fat.min, fat.max), fat.to);
  223.                 }
  224.                 else
  225.                 {
  226.                     this.addEpsilon(fat.to, true);
  227.                 }
  228.             }
  229.         }
  230.         else
  231.         {
  232.             var found =false;
  233.             let i = 0;
  234.             for (;i<this.transitions.length;++i)
  235.             {
  236.                 var fat = this.transitions[i];
  237.                 if (!fat.isEpsilon()) break;
  238.                 if(fat.to===to)
  239.                 {
  240.                     found = true;
  241.                     break;
  242.                 }
  243.             }
  244.             if (!found)
  245.             {
  246.                 this.transitions.splice(i, 0, new faTransition(to));
  247.                 this.isCompact = false;
  248.                 this.isDeterministic = false;
  249.             }
  250.         }
  251.    
  252.     }
  253.     public addTransition(range: faRange, to: fa, compact: boolean = true) : void {
  254.         if(to === undefined || to===null) {
  255.             throw "to was not set";
  256.         }
  257.         if(range.min==-1 || range.max==-1)
  258.             {
  259.                 this.addEpsilon(to,compact);
  260.                 return;
  261.             }
  262.             if(range.min>range.max)
  263.             {
  264.                 const tmp: number = range.min;
  265.                 range.min = range.max;
  266.                 range.max = tmp;
  267.             }
  268.             var insert = -1;
  269.             for (let i: number = 0; i < this.transitions.length; ++i)
  270.             {
  271.                 const fat : faTransition = this.transitions[i];
  272.                 if (to === fat.to)
  273.                 {
  274.                     if(range.min===fat.min &&
  275.                         range.max==fat.max)
  276.                     {
  277.                         return;
  278.                     }
  279.                 }
  280.                 if (this.isDeterministic)
  281.                 {
  282.                     if (range.intersects(
  283.                         new faRange(fat.min, fat.max)))
  284.                     {
  285.                         this.isDeterministic = false;
  286.                     }
  287.                 }
  288.                 if (range.max>fat.max)
  289.                 {
  290.                     insert = i;
  291.                 }
  292.                 if (!this.isDeterministic &&
  293.                     range.max < fat.min)
  294.                 {
  295.                     break;
  296.                 }
  297.             }
  298.             this.transitions.splice(insert+1, 0, new faTransition(to,range.min, range.max));
  299.            
  300.     }
  301.     private static _crackSet(str: string, closure:fa[]): fa[] {
  302.         const result : fa[] = [];
  303.         if(str=="") {
  304.             return result;
  305.         }
  306.         const sa: string[] = str.split(" ");
  307.         for(const s of sa) {
  308.             result.push(closure[Number.parseInt(s,10)]);
  309.         }
  310.         return result;
  311.     }
  312.     private static _makeSet(closure: fa[], states: Iterable<fa>): string {
  313.         let result: string = "";
  314.         let delim: string = "";
  315.         let ns : number[] = [];
  316.         for(const fa of states) {
  317.             const idx = closure.indexOf(fa);
  318.             ns.push(idx);
  319.         }
  320.         ns.sort((x,y)=>x-y);
  321.         for(const i of ns) {
  322.             result+=(delim+i.toString(10));
  323.             delim = " ";
  324.         }
  325.         return result;
  326.     }
  327.     private static _determinize(target: fa, progress: faProgress = null): fa {
  328.         if(progress===null) {
  329.             progress = new faProgress();
  330.         }
  331.         let prog: number = 0;
  332.         progress.report(0);
  333.         const closure: fa[] = target.fillClosure();
  334.         const p: Set<number> = new Set<number>();
  335.         for (const ffa of closure) {
  336.             p.add(0);
  337.             for (const t of ffa.transitions) {
  338.                 p.add(t.min);
  339.                 if (t.max < 0x10ffff) {
  340.                     p.add(t.max + 1);
  341.                 }
  342.             }
  343.         }
  344.         const points: number[] = [...p];
  345.         points.sort((x, y) => x - y);
  346.         ++prog;progress.report(prog);
  347.         const sets: Map<string, Set<fa>> = new Map<string, Set<fa>>();
  348.         const working: string[] = [];
  349.         const dfaMap: Map<string, fa> = new Map<string, fa>();
  350.         const initStates: fa[] = closure[0].fillEpsilonClosure();
  351.         let initial : string = fa._makeSet(closure,initStates);
  352.         sets.set(initial, new Set<fa>(initStates));
  353.         working.push(initial);
  354.         const result: fa = new fa();
  355.         result.isDeterministic = true;
  356.         result.fromStates=initStates;
  357.         result.acceptSymbol = fa.getFirstAcceptSymbol(initStates);
  358.         dfaMap.set(initial, result);
  359.         while (working.length > 0) {
  360.             const s: string = working[0];
  361.             working.shift();
  362.             const dfa: fa = dfaMap.get(s)!;
  363.             const crackedS1: fa[] = fa._crackSet(s,closure);
  364.             for (const q of crackedS1) {
  365.                 if (q.isAccepting()) {
  366.                     dfa.acceptSymbol = q.acceptSymbol;
  367.                     break;
  368.                 }
  369.             }
  370.             let i: number = 0;
  371.             for (const pnt of points) {
  372.                 const set: Set<fa> = new Set<fa>();
  373.                 for (const c of crackedS1) {
  374.                     const ecs: fa[] = c.fillEpsilonClosure();
  375.                     for(const efa of ecs) {
  376.                         for (let trns of efa.transitions) {
  377.                             if(!trns.isEpsilon()) {
  378.                                 if (trns.min <= pnt && pnt <= trns.max) {
  379.                                     for(const eefa of trns.to.fillEpsilonClosure()) {
  380.                                         set.add(eefa);
  381.                                     }
  382.                                 }
  383.                             }
  384.                         }
  385.                     }
  386.                 }
  387.                 const skey: string = fa._makeSet(closure,set);
  388.                 if (!sets.has(skey)) {
  389.                     sets.set(skey, set);
  390.                     working.push(skey);
  391.                     let newFa: fa = new fa();
  392.                     newFa.isDeterministic = true;
  393.                     newFa.isCompact = true;
  394.                     newFa.fromStates = Array.from(set.values());
  395.                     dfaMap.set(skey, newFa);
  396.                 }
  397.                 const dst: fa = dfaMap.get(skey)!;
  398.                 const first: number = pnt;
  399.                 let last: number;
  400.                 if (i + 1 < points.length) {
  401.                     last = points[i + 1] - 1;
  402.                 } else {
  403.                     last = 0x10ffff;
  404.                 }
  405.                 dfa.transitions.push(new faTransition(dst,first, last));
  406.                 ++i;
  407.             }
  408.             ++prog;progress.report(prog);
  409.         }
  410.         for (const ffa of result.fillClosure()) {
  411.             const itrns: faTransition[] = [...ffa.transitions];
  412.             for (const trns of itrns) {
  413.                 const acc: fa[] = trns.to.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
  414.                 if (acc.length == 0) {
  415.                     ffa.transitions.splice(ffa.transitions.indexOf(trns), 1);
  416.                 }
  417.             }
  418.             ++prog;progress.report(prog);
  419.         }
  420.         ++prog;progress.report(prog);
  421.         return result;
  422.     }
  423.     public isDfa(): boolean {
  424.         for(const ffa of this.fillClosure()) {
  425.             if(!ffa.isDeterministic) {
  426.                 return false;
  427.             }
  428.         }
  429.         return true;
  430.     }
  431.     public toDfa(progress:faProgress = null) : fa {
  432.         return fa._determinize(this,progress);
  433.     }
  434.     public totalize(closure: fa[] = null): void {
  435.         if(closure===null) {
  436.             closure = this.fillClosure();
  437.         }
  438.         const s: fa = new fa();
  439.         s.transitions.push(new faTransition(s,0,0x10ffff));
  440.         for(const p of closure) {
  441.             let maxi: number = 0;
  442.             const sortedTrans: faTransition[] = [...p.transitions];
  443.             sortedTrans.sort((x: faTransition,y: faTransition)=>{
  444.                 let c: number = x.max-y.max; if (0 != c) return c; return x.min-y.min;
  445.             });
  446.             for(const t of sortedTrans) {
  447.                 if(!t.isEpsilon()) {
  448.                     if(t.min>maxi) {
  449.                         p.transitions.push(new faTransition(s,maxi,t.min-1));
  450.                     }
  451.                     if(t.max+1>maxi) {
  452.                         maxi = t.max +1;
  453.                     }
  454.                 }
  455.             }
  456.             if(maxi<=0x10ffff) {
  457.                 p.transitions.push(new faTransition(s,maxi,0x10ffff));
  458.             }
  459.         }
  460.     }
  461.     private _step(input: number) : fa {
  462.         for (let ic: number = this.transitions.length, i: number = 0; i < ic; ++i)
  463.         {
  464.             var t = this.transitions[i];
  465.             if (input >= t.min && input <= t.max)
  466.                 return t.to;
  467.        
  468.         }
  469.         return null;
  470.     }
  471.     private static _minimize(a: fa, progress: faProgress) : fa {
  472.         let prog: number = 0;
  473.         if(progress===null) {
  474.             progress = new faProgress();
  475.         }
  476.         progress.report(prog);
  477.         a=a.toDfa(progress);
  478.         const tr: faTransition[] = a.transitions;
  479.         if(tr.length==1) {
  480.             const t: faTransition = tr[0];
  481.             if(t.to=== a && t.min==0&&t.max==0x10ffff) {
  482.                 return a;
  483.             }
  484.         }
  485.         a.totalize();
  486.         ++prog;progress.report(prog);
  487.         const cl: fa[] = a.fillClosure();
  488.         const states: fa[] = new Array<fa>();
  489.         let mtag: number = 0;
  490.         for(const q of cl) {
  491.             q._minimizationTag = mtag;
  492.             states.push(q);
  493.             ++mtag;
  494.         }
  495.         let pp: number[] = [];
  496.         for(let ic: number = cl.length,i=0;i<ic;++i) {
  497.             const ffa: fa=cl[i];
  498.             pp.push(0);
  499.             for(const t of ffa.transitions) {
  500.                 pp.push(t.min);
  501.                 if(t.max<0x10ffff) {
  502.                     pp.push(t.max+1);
  503.                 }
  504.             }
  505.         }
  506.         const sigma: number[] = new Array<number>();
  507.         for(let i = 0;i<pp.length;++i) {
  508.             sigma.push(pp[i]);
  509.         }
  510.         sigma.sort();
  511.  
  512.         // initialize the data structures
  513.         const reverse : fa[][][] = new Array<Array<Array<fa>>>();
  514.         for(const s of states) {
  515.             const arr: fa[][] = new Array<Array<fa>>(sigma.length);
  516.             reverse.push(arr);
  517.         }
  518.         ++prog;progress.report(prog);
  519.         const reverseNonempty: boolean[][] = Array<Array<boolean>>();//[states.length,sigma.length];
  520.         for(let i: number = 0;i<states.length;++i) {
  521.             const arr: boolean[] = new Array<boolean>(sigma.length);
  522.             reverseNonempty.push(arr);
  523.         }
  524.         const partition: fa[][] = new Array<Array<fa>>();
  525.         for(let i: number = 0;i<states.length;++i) {
  526.             partition.push(null);
  527.         }
  528.         ++prog;progress.report(prog);
  529.         const block: number[] = new Array<number>();
  530.         for(let i: number = 0;i<states.length;++i) {          
  531.             block.push(0);
  532.         }
  533.         const active: _FList[][] = new Array<Array<_FList>>(); // [states.length,sigma.length];
  534.         for(let i: number = 0;i<states.length;++i) {
  535.             const arr: _FList[] = new Array<_FList>(sigma.length);
  536.             active.push(arr);
  537.         }
  538.         const active2: _FListNode[][] = new Array<Array<_FListNode>>()//[states.length,sigma.length];
  539.         for(let i: number = 0;i<states.length;++i) {
  540.             const arr: _FListNode[] = new Array<_FListNode>(sigma.length);
  541.             active2.push(arr);
  542.         }
  543.         const pending: _FKeyPair[] = new Array<_FKeyPair>();
  544.         const pending2: boolean[][] = Array<Array<boolean>>();//[sigma.length,states.length];
  545.         for(let i: number = 0;i<sigma.length;++i) {
  546.             pending2.push(new Array<boolean>(states.length));
  547.         }
  548.         let split: fa[] = new Array<fa>();
  549.         const split2: boolean[] = new Array<boolean>(states.length);
  550.         let refine: number[] = new Array<number>();
  551.         const refine2: boolean[] = new Array<boolean>(states.length);
  552.         const splitBlock: fa[][] = new Array<Array<fa>>(states.length);
  553.         ++prog;progress.report(prog);
  554.         for(let q: number = 0;q<states.length;++q) {
  555.             splitBlock[q]= new Array<fa>();
  556.             partition[q]=new Array<fa>();
  557.             for(let x: number = 0;x<sigma.length;++x) {
  558.                 reverse[q][x]=new Array<fa>();
  559.                 active[q][x]=new _FList();
  560.             }
  561.         }
  562.         // Find the initial partition and reverse edges
  563.         for(const qq of states) {
  564.             const j: number = qq.isAccepting()? 0: 1;
  565.             if(qq===null) {
  566.                 console.log("NULL");
  567.             }
  568.             partition[j].push(qq);
  569.             block[qq._minimizationTag]=j;
  570.             for(let x: number = 0;x<sigma.length;++x) {
  571.                 const y: number = sigma[x];
  572.                 const p: fa = qq._step(y);
  573.                 const pn: number = p._minimizationTag;
  574.                 if(reverse[pn] !==null && reverse[pn][x]!==null) {
  575.                     reverse[pn][x].push(qq);
  576.                 }
  577.                 reverseNonempty[pn][x]=true;
  578.             }
  579.             ++prog;progress.report(prog);
  580.         }
  581.         // initialize the active sets
  582.         for(let j: number = 0;j<=1;++j) {
  583.             for(let x: number = 0;x<sigma.length;++x) {
  584.                 const part: fa[] = partition[j];
  585.                 for(const qq of part) {
  586.                     if (reverseNonempty[qq._minimizationTag][x]===true) {
  587.                         active2[qq._minimizationTag][x]=active[j][x].add(qq);
  588.                     }
  589.                 }
  590.             }
  591.             ++prog;progress.report(prog);
  592.         }
  593.         // initialize pending
  594.         for(let x: number = 0; x<sigma.length;++x) {
  595.             const a0: number = active[0][x].count;
  596.             const a1: number = active[1][x].count;
  597.             const j: number = a0<=a1?0:1;
  598.             pending.push(new _FKeyPair(j,x));
  599.             pending2[x][j]=true;
  600.         }
  601.         // process pending until fixed point
  602.         let k: number = 2;
  603.         while(pending.length>0) {
  604.             const ip: _FKeyPair = pending.shift();
  605.             const p: number = ip.key;
  606.             const x: number = ip.value;
  607.             pending2[x][p]=false;
  608.             for(let m: _FListNode = active[p][x].first;m!==null;m=m.next) {
  609.                 for(const s of reverse[m.state._minimizationTag][x]) {
  610.                     if(!split2[s._minimizationTag]) {
  611.                         split2[s._minimizationTag]=true;
  612.                         split.push(s);
  613.                         const j: number = block[s._minimizationTag];
  614.                         splitBlock[j].push(s);
  615.                         if(refine2[j]===false) {
  616.                             refine2[j]=true;
  617.                             refine.push(j);
  618.                         }
  619.                     }
  620.                 }
  621.             }
  622.             ++prog;progress.report(prog);
  623.             // refine blocks
  624.             for(const j of refine) {
  625.                 if(splitBlock[j].length<partition[j].length) {
  626.                     let b1: Array<fa> = partition[j];
  627.                     let b2: Array<fa> = partition[k];
  628.                     const e: Array<fa> = splitBlock[j];
  629.                     for(const s of e) {
  630.                         partition[j] = b1 = b1.splice(b1.indexOf(s),1);
  631.                         b2.push(s);
  632.                         block[s._minimizationTag]=k;
  633.                         for(let c: number = 0;c<sigma.length;++c) {
  634.                             const sn: _FListNode = active2[s._minimizationTag][c];
  635.                             if(sn!==null && sn.stateList===active[j][c]) {
  636.                                 sn.remove();
  637.                                 active2[s._minimizationTag][c]=active[k][c].add(s);
  638.                             }
  639.                         }
  640.                     }
  641.                     // update pending
  642.                     for(let c:number = 0;c<sigma.length;++c) {
  643.                         const aj: number = active[j][c].count;
  644.                         const ak: number = active[k][c].count;
  645.                         if(!pending2[c][j] && 0<aj&&aj<=ak) {
  646.                             pending2[c][j]=true;
  647.                             pending.push(new _FKeyPair(j,c));
  648.                         } else {
  649.                             pending2[c][k]=true;
  650.                             pending.push(new _FKeyPair(k,c));
  651.                         }
  652.                     }
  653.                     ++k;
  654.                 }
  655.                 const sbj: Array<fa> = splitBlock[j];
  656.                 for(const s of sbj) {
  657.                     split2[s._minimizationTag]=false;
  658.                 }
  659.                 refine2[j]=false;
  660.                 sbj.length=0;
  661.                 ++prog;progress.report(prog);
  662.            
  663.             }
  664.             split.length=0;
  665.             refine.length=0;
  666.         }
  667.         ++prog;progress.report(prog);
  668.         // make a new state for each equiv class, set initial state
  669.         const newstates : fa[] = new Array<fa>();
  670.         for(let n: number = 0; n<k;++n) {
  671.             const s: fa = new fa();
  672.             s.isDeterministic = true;
  673.             newstates.push(s);
  674.             const pn: Array<fa> = partition[n];
  675.             for(const q of pn) {
  676.                 if(q === a) {
  677.                     a = s;
  678.                 }
  679.                 s.id = q.id;
  680.                 s.acceptSymbol = q.acceptSymbol;
  681.                 s._minimizationTag = q._minimizationTag;
  682.                 q._minimizationTag = n;
  683.             }
  684.             ++prog;progress.report(prog);
  685.         }
  686.         // build transitions and set acceptance
  687.         for(const s of newstates) {
  688.             const st: fa = states[s._minimizationTag];
  689.             s.acceptSymbol = st.acceptSymbol;
  690.             for(const t of st.transitions) {
  691.                 s.transitions.push(new faTransition(newstates[t.to._minimizationTag],t.min,t.max));
  692.             }
  693.             ++prog;progress.report(prog);
  694.         }
  695.         // remove dead transitions
  696.         for(const ffa of a.fillClosure()) {
  697.             const itrns: faTransition[] = new Array<faTransition>(...ffa.transitions)
  698.             ffa.transitions = new Array<faTransition>();
  699.             for(const trns of itrns) {
  700.                 //if(!trns.to.isTrap()) {
  701.                     ffa.transitions.push(trns);
  702.                 //}
  703.             }
  704.         }
  705.         return a;
  706.     }
  707.     public toMinimizedDfa(progress: faProgress = null) {
  708.         return fa._minimize(this,progress);
  709.     }
  710.     public clone(): fa {
  711.         const result: fa[] = [];
  712.         const closure = this.fillClosure();
  713.         for (let j = 0; j < closure.length; ++j) {
  714.             result.push(new fa());
  715.         }
  716.         let i: number = 0;
  717.         for (const ffa of closure) {
  718.             result[i].isCompact = ffa.isCompact;
  719.             result[i].isDeterministic = ffa.isDeterministic;
  720.             result[i].acceptSymbol = ffa.acceptSymbol;
  721.             result[i].tag = ffa.tag;
  722.             for (const trns of ffa.transitions) {
  723.                 result[i].transitions.push(new faTransition(result[closure.indexOf(trns.to)],trns.min, trns.max));
  724.             }
  725.             ++i;
  726.         }
  727.         return result[0];
  728.     }
  729.     public toString(format: string, options: faDotGraphOptions = null): string {
  730.        
  731.         if(format.toLowerCase()==="dot") {
  732.             if (options!=null && options.debugSourceNfa != null && options.debugShowNfa)
  733.             {
  734.                 return this._buildCompoundDot(this.fillClosure(), options);
  735.             }
  736.             else
  737.             {
  738.                 return this._buildDot(this.fillClosure(),options,-1);
  739.             }
  740.         } else {
  741.             if(this.id < 0) {
  742.                 return "[FA]";
  743.             } else {
  744.                 return "q"+this.id.toString();
  745.             }
  746.         }
  747.     }
  748.     public setIds(): void {
  749.         let i: number = 0;
  750.         for(const fa of this.fillClosure()) {
  751.             fa.id = i;
  752.             ++i;
  753.         }
  754.     }
  755.     public static literal(codepoints: Iterable<number> | string,accept: number = 0): fa {
  756.         if(typeof codepoints=="string") {
  757.             codepoints = this.toUtf32(codepoints);
  758.         }
  759.         const result: fa = new fa();
  760.         let current: fa = result;
  761.         for (const cp of codepoints) {
  762.             current.acceptSymbol = -1;
  763.             const newFa : fa = new fa();
  764.             newFa.acceptSymbol = accept;
  765.             current.addTransition(new faRange(cp,cp),newFa);
  766.             current = newFa;
  767.         }
  768.         return result;
  769.     }
  770.     public static set(ranges: Iterable<faRange>,accept: number=0): fa {
  771.         const result: fa = new fa();
  772.         const final: fa = new fa();
  773.         final.acceptSymbol = accept;
  774.         for (const range of [...ranges].sort((x, y) => x.max - y.max)) {
  775.             result.addTransition(range,final);
  776.         }
  777.         return result;
  778.     }
  779.     public static concat(exprs: Iterable<fa>,accept: number = 0, compact: boolean = true): fa {
  780.         let result: fa | null = null;
  781.         let left: fa | null = null;
  782.         let right: fa | null = null;
  783.         for (const expr of exprs) {
  784.             let nval = expr.clone();
  785.             if (null === left) {
  786.                 if (null === result) {
  787.                     result = nval;
  788.                 }
  789.                 left = nval;
  790.                 continue;
  791.                
  792.             }
  793.             if (null === right) {
  794.                 right = nval;
  795.             }
  796.             nval = right.clone();
  797.             fa._concat(left!, nval,compact);
  798.         }
  799.         const target: fa = null !== right ? right! : result!;
  800.         const acc: fa[] = target.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
  801.         for (const afa of acc) {
  802.             afa.acceptSymbol = accept;
  803.         }
  804.         return result!;
  805.     }
  806.     public static or(exprs: Iterable<fa>, accept: number=0, compact: boolean=true): fa {
  807.         const result: fa = new fa();
  808.         const final: fa = new fa();
  809.         final.acceptSymbol = accept;
  810.         for (const expr of exprs) {
  811.             const nfa = expr.clone();
  812.             const nacc: fa[] = nfa.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
  813.             for (const afa of nacc) {
  814.                 afa.acceptSymbol = -1;
  815.                 afa.addEpsilon(final,compact);
  816.             }
  817.             result.addEpsilon(nfa,compact);
  818.         }
  819.         return result;
  820.     }
  821.     public static optional(expr: fa,accept: number=0,compact: boolean=true): fa {
  822.         const result: fa = expr.clone();
  823.         const acc: fa[] = result.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting());
  824.         for (const afa of acc) {
  825.             afa.acceptSymbol = accept;
  826.             result.addEpsilon(afa,compact);
  827.         }
  828.         return result;
  829.     }
  830.     public static repeat(expr: fa, minOccurs: number = -1, maxOccurs: number = -1, accept: number = 0, compact: boolean = true): fa {
  831.         expr = expr.clone();
  832.         if (minOccurs > 0 && maxOccurs > 0 && minOccurs > maxOccurs) {
  833.             throw Error("Minimum is greater than maximum");
  834.         }
  835.         let result: fa;
  836.         switch (minOccurs) {
  837.             case -1:
  838.             case 0:
  839.                 switch (maxOccurs) {
  840.                     case -1:
  841.                     case 0:
  842.                         result = new fa();
  843.                         result.acceptSymbol = accept;
  844.                         result.addEpsilon(expr,compact);
  845.                         for (const afa of expr.fillClosure()) {
  846.                             if(afa.isAccepting()) {
  847.                                 afa.acceptSymbol = -1;
  848.                                 afa.addEpsilon(result,compact);
  849.                             }
  850.                         }
  851.                         return result;
  852.                     case 1:
  853.                         result = this.optional(expr,accept,compact);
  854.                         return result;
  855.                     default:
  856.                         const l: fa[] = [];
  857.                         expr = this.optional(expr,accept,compact);
  858.                         l.push(expr);
  859.                         for (let i = 1; i < maxOccurs; ++i) {
  860.                             l.push(expr.clone());
  861.                         }
  862.                         result = this.concat(l,accept, compact);
  863.                         return result;
  864.                 }
  865.             case 1:
  866.                 switch (maxOccurs) {
  867.                     case -1:
  868.                     case 0:
  869.                         result = this.concat([expr, fa.repeat(expr, 0, 0,accept,compact)],accept,compact);
  870.                         return result;
  871.                     case 1:
  872.                         return expr;
  873.                     default:
  874.                         result = this.concat([expr, fa.repeat(expr, 0, maxOccurs - 1,accept,compact)],accept,compact);
  875.                         return result;
  876.                 }
  877.             default:
  878.                 switch (maxOccurs) {
  879.                     case -1:
  880.                     case 0:
  881.                         result = this.concat([fa.repeat(expr, minOccurs, minOccurs,accept,compact), fa.repeat(expr, 0, 0,accept,compact)],accept,compact);
  882.                         return result;
  883.                     case 1:
  884.                         throw new Error("Should never get here");
  885.                     default:
  886.                         if (minOccurs == maxOccurs) {
  887.                             const l: fa[] = [];
  888.                             expr = this.optional(expr,accept,compact);
  889.                             l.push(expr);
  890.                             for (let i = 1; i < maxOccurs; ++i) {
  891.                                 l.push(expr.clone());
  892.                             }
  893.                             result = this.concat(l,accept,compact);
  894.                             return result;
  895.                         }
  896.                         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);
  897.                         return result;
  898.                 }
  899.         }
  900.         // throw new Error("Should never get here");
  901.     }
  902.     public isMatch(str: string): boolean {
  903.         let states: fa[] = [];
  904.         this.fillEpsilonClosure(states);
  905.         let remaining: number = str.length - 1;
  906.         for (const cp of fa.toUtf32(str)) {
  907.             const next: fa[] = fa.fillMove(states, cp);
  908.             if (next.length === 0) {
  909.                 if (fa.getFirstAcceptSymbol(states)>=0) {
  910.                     return remaining === 0;
  911.                 }
  912.             }
  913.             states = next;
  914.             --remaining;
  915.         }
  916.         return fa.getFirstAcceptSymbol(states)>=0;
  917.     }
  918.     public isNeutral() : boolean {
  919.         return !this.isAccepting && this.transitions.length==1 && this.transitions[0].isEpsilon();
  920.     }
  921.     public isFinal() : boolean {
  922.         return this.transitions.length==0;
  923.     }
  924.     public isTrap() : boolean {
  925.         if(this.isAccepting()) {
  926.             return false;
  927.         }
  928.         for(const ffa of this.fillClosure()) {
  929.             if(ffa.isAccepting()) {
  930.                 return false;
  931.             }
  932.         }
  933.         return true;
  934.     }
  935.     public fillInputTransitionRangesGroupedByState(includeEpsilonTransitions: boolean = false, result: Map<fa,faRange[]> = null) : Map<fa, faRange[]> {
  936.         if(result===null) {
  937.             result = new Map<fa, faRange[]>();
  938.         }
  939.         for(const fat of this.transitions) {
  940.             if(includeEpsilonTransitions || !fat.isEpsilon()) {
  941.                 const res : faRange[] | undefined = result.get(fat.to);
  942.                 if(res===undefined) {
  943.                     const ndata : faRange[] = [new faRange(fat.min,fat.max)];
  944.                     result.set(fat.to, ndata);
  945.                 } else {
  946.                     res.push(new faRange(fat.min,fat.max));
  947.                 }
  948.             }
  949.         }
  950.         for(const values of result.values()) {
  951.             values.sort((x: faRange,y: faRange)=>{ var c = x.min - y.min; if(0!=c) return c; return x.max-y.max;});
  952.         }
  953.         return result;
  954.     }
  955.     public toDfaTable(): number[] {
  956.         let fa: fa = this;
  957.         if(!this.isDeterministic) {
  958.             fa = this.toDfa();
  959.         }
  960.         const result: number[] = [];
  961.         const closure: fa[] = fa.fillClosure();
  962.         const stateIndices: number[] = [];
  963.         for(let i: number = 0;i<closure.length;++i) {
  964.             const cfa: fa = closure[i];
  965.             stateIndices.push(result.length);
  966.             result.push(cfa.acceptSymbol);
  967.             const itrgp: Map<fa,faRange[]> = cfa.fillInputTransitionRangesGroupedByState();
  968.             result.push(itrgp.size);
  969.             for(const itr of itrgp.entries()) {
  970.                 result.push(closure.indexOf(itr[0]));
  971.                 result.push(itr[1].length);
  972.                 for(const pair of itr[1]) {
  973.                     result.push(pair.min);
  974.                     result.push(pair.max);
  975.                 }
  976.             }
  977.         }
  978.         let state: number = 0;
  979.         while(state< result.length) {
  980.             ++state;
  981.             const tlen = result[state++];
  982.             for(let i: number = 0;i<tlen;++i) {
  983.                 result[state]=stateIndices[result[state]];
  984.                 ++state;
  985.                 const prlen: number = result[state++];
  986.                 state += prlen * 2;
  987.             }
  988.         }
  989.         return result;
  990.     }
  991.     public static fromDfaTable(dfa: number[]) : fa {
  992.        
  993.         if(dfa.length==0) {
  994.             return new fa();
  995.         }
  996.         let si: number = 0;
  997.         const states: Map<number, fa> = new Map<number, fa>();
  998.         while(si<dfa.length) {
  999.             const newFa: fa = new fa();
  1000.             states.set(si,newFa);
  1001.             newFa.acceptSymbol = dfa[si++];
  1002.             const tlen: number = dfa[si++];
  1003.             for(let i: number = 0; i< tlen; ++i) {
  1004.                 ++si; // tto
  1005.                 const prlen: number = dfa[si++];
  1006.                 si+=prlen*2;
  1007.             }
  1008.         }
  1009.         si = 0;
  1010.         while(si<dfa.length) {
  1011.             var state = states.get(si)!;
  1012.             ++si;
  1013.             const tlen: number = dfa[si++];
  1014.             for (let i: number = 0; i < tlen; ++i) {
  1015.                 const tto: number = dfa[si++];
  1016.                 const to: fa = states.get(tto)!;
  1017.                 const prlen: number = dfa[si++];
  1018.                 for(let j: number = 0; j <prlen;++j) {
  1019.                     state.addTransition(new faRange(dfa[si++],dfa[si++]),to);
  1020.                 }
  1021.             }
  1022.         }
  1023.         return states.get(0)!;
  1024.     }
  1025.     public static fillMove(states: Iterable<fa>, codepoint: number, result: fa[] = []): fa[] {
  1026.         for (const ffa of states) {
  1027.             for (const fat of ffa.transitions) {
  1028.                 if (!fat.isEpsilon() && (codepoint>=fat.min && codepoint <= fat.max)) {
  1029.                     fat.to.fillEpsilonClosure(result);
  1030.                 }
  1031.             }
  1032.         }
  1033.         return result;
  1034.     }
  1035.     public static getFirstAcceptSymbol(states: Iterable<fa>): number {
  1036.         for (const fa of states) {
  1037.             if (fa.isAccepting()) {
  1038.                 return fa.acceptSymbol;
  1039.             }
  1040.         }
  1041.         return -1;
  1042.     }
  1043.    
  1044.     private static _concat(lhs: fa, rhs: fa, compact: boolean ): void {
  1045.         const acc: fa[] = lhs.fillClosure().filter((value: fa, index: number, array: fa[]) => value.isAccepting() );
  1046.         for (const afa of acc) {
  1047.             afa.acceptSymbol = -1;
  1048.             afa.addEpsilon(rhs,compact);
  1049.         }
  1050.     }
  1051.  
  1052.     public static *toUtf32(str: string): Iterable<number> {
  1053.         for (const character of str) {
  1054.             let cp: number = character.codePointAt(0)!;
  1055.             if(cp>=0xD800 && cp<=0xDBFF) { // hi surrogate
  1056.                 const cpl: number = character.codePointAt(1)!
  1057.                 if(!(cpl>=0xDC00 && cpl<=0xDFFF)) { // not a low surrogate
  1058.                     throw new Error("Unicode stream error. Unterminated high surrogate");
  1059.                 }
  1060.                 const highValue = cp & 0b11_11111111;
  1061.                 const lowValue = cpl & 0b11_11111111;
  1062.                 const magicAdd = 0b1_00000000_00000000;
  1063.                 cp = (highValue << 10) | lowValue + magicAdd;
  1064.             }
  1065.             yield cp;
  1066.         }
  1067.     }
  1068.     private static *_invertRanges(ranges : Iterable<faRange>) : Iterable<faRange>
  1069.     {
  1070.         if (ranges == null)
  1071.         {
  1072.             return;
  1073.         }
  1074.         let last : number = 0x10ffff;
  1075.  
  1076.         let e : Iterator<faRange> = ranges[Symbol.iterator]();
  1077.         let cur = e.next();
  1078.         if (cur.done)
  1079.         {
  1080.             yield new faRange(0,0x10ffff);
  1081.             return;
  1082.         }
  1083.         if (cur.value.min > 0)
  1084.         {
  1085.             yield new faRange(0,cur.value.min-1);
  1086.             last = cur.value.max;
  1087.             if (0x10ffff <= last)
  1088.                 return;
  1089.         }
  1090.         else if (cur.value.min == 0)
  1091.         {
  1092.             last = cur.value.max;
  1093.             if (0x10ffff <= last)
  1094.                 return;
  1095.         }
  1096.         cur = e.next();
  1097.         while (!cur.done)
  1098.         {
  1099.             if (0x10ffff <= last)
  1100.                 return;
  1101.             if ((last + 1) < cur.value.min)
  1102.             {
  1103.                 yield new faRange(last+1,cur.value.min-1);
  1104.             }
  1105.             last = cur.value.max;
  1106.             cur = e.next();
  1107.         }
  1108.         if (0x10ffff > last)
  1109.         {
  1110.             yield new faRange(last+1,0x10ffff);
  1111.         }
  1112.     }
  1113.     private static _appendRangeTo( ranges : faRange[], index: number=-1) : string
  1114.     {
  1115.         let result: string = "";
  1116.         if(index===-1) {
  1117.             for(let i: number = 0;i < ranges.length;++i)
  1118.             {
  1119.                 result+=fa._appendRangeTo(ranges, i);
  1120.             }
  1121.             return result;
  1122.         }
  1123.         var first = ranges[index].min;
  1124.         var last = ranges[index].max;
  1125.         result += fa._appendRangeCharTo(String.fromCodePoint(first));
  1126.         if (last===first) return result;
  1127.         if (last === first + 1) // spit out 1 and 2 length ranges as flat chars
  1128.         {
  1129.             result+=fa._appendRangeCharTo(String.fromCodePoint(last));
  1130.             return result;
  1131.         }
  1132.         else if(last === first + 2)
  1133.         {
  1134.             result+=fa._appendRangeCharTo(String.fromCodePoint(first+1));
  1135.             result+=fa._appendRangeCharTo(String.fromCodePoint(last));
  1136.             return result;
  1137.         }
  1138.         result+="-";
  1139.         result+=fa._appendRangeCharTo(String.fromCodePoint(last));
  1140.         return result;
  1141.     }
  1142.     private static _appendCharTo(ch: string) : string
  1143.     {
  1144.         let result: string = "";
  1145.         switch(ch)
  1146.         {
  1147.             case '.':
  1148.             case '[':
  1149.             case ']':
  1150.             case '^':
  1151.             case '-':
  1152.             case '+':
  1153.             case '?':
  1154.             case '(':
  1155.             case ')':
  1156.             case '\\':
  1157.                 result+='\\';
  1158.                 result+=ch;
  1159.                 break;
  1160.             case '\t':
  1161.                 result+="\\t";
  1162.                 break;
  1163.             case '\n':
  1164.                 result+="\\n";
  1165.                 break;
  1166.             case '\r':
  1167.                 result+="\\r";
  1168.                 break;
  1169.             case '\0':
  1170.                 result+="\\0";
  1171.                 break;
  1172.             case '\f':
  1173.                 result+="\\f";
  1174.                 break;
  1175.             case '\v':
  1176.                 result+="\\v";
  1177.                 break;
  1178.             case '\b':
  1179.                 result+="\\b";
  1180.                 break;
  1181.             default:
  1182.                 const s:string = ch;
  1183.                 const isletter: boolean = s.toLowerCase() != s.toUpperCase();
  1184.                 const isdigit = !isletter && (s >= '0' && s <= '9');
  1185.                 const issym = !isletter && !isdigit && ",._-<>/\\=+~`!@#$%^&*(){}[]\"\';:?".indexOf(s)>-1;
  1186.                 if (!isletter && !isdigit && !issym)
  1187.                 {
  1188.                     if (s.length == 1)
  1189.                     {
  1190.                         result+="\\u";
  1191.                         const d: number = s.codePointAt(0);
  1192.                         result+=("0000" + (+d).toString(16)).slice(-4);
  1193.                     }
  1194.                     else
  1195.                     {
  1196.                         result+="\\U";
  1197.                         const d: number = s.codePointAt(0);
  1198.                         result+=("00000000" + (+d).toString(16)).slice(-8);
  1199.                     }
  1200.  
  1201.                 }
  1202.                 else {
  1203.                     result+=s;
  1204.                 }
  1205.                 break;
  1206.         }
  1207.         return result;
  1208.     }
  1209.  
  1210.     private static _appendRangeCharTo(ch: string) : string
  1211.     {
  1212.         let result : string = "";
  1213.         switch(ch)
  1214.         {
  1215.             case '.':
  1216.             case '[':
  1217.             case ']':
  1218.             case '^':
  1219.             case '-':
  1220.             case '(':
  1221.             case ')':
  1222.             case '{':
  1223.             case '}':
  1224.             case '\\':
  1225.                 result+='\\';
  1226.                 result+=ch;
  1227.                 break;
  1228.             case '\t':
  1229.                 result+="\\t";
  1230.                 break;
  1231.             case '\n':
  1232.                 result+="\\n";
  1233.                 break;
  1234.             case '\r':
  1235.                 result+="\\r";
  1236.                 break;
  1237.             case '\0':
  1238.                 result+="\\0";
  1239.                 break;
  1240.             case '\f':
  1241.                 result+="\\f";
  1242.                 break;
  1243.             case '\v':
  1244.                 result+="\\v";
  1245.                 break;
  1246.             case '\b':
  1247.                 result+="\\b";
  1248.                 break;
  1249.             default:
  1250.                 const s:string = ch;
  1251.                 const isletter: boolean = s.toLowerCase() != s.toUpperCase();
  1252.                 const isdigit = !isletter && (s >= '0' && s <= '9');
  1253.                 const issym = !isletter && !isdigit && ",._-<>/\\=+~`!@#$%^&*(){}[]\"\';:?".indexOf(s)>-1;
  1254.                 if (!isletter && !isdigit && !issym)
  1255.                 {
  1256.                     if (s.length == 1)
  1257.                     {
  1258.                         result+="\\u";
  1259.                         const d: number = s.codePointAt(0);
  1260.                         result+=("0000" + (+d).toString(16)).slice(-4);
  1261.                     }
  1262.                     else
  1263.                     {
  1264.                         result+="\\U";
  1265.                         const d: number = s.codePointAt(0);
  1266.                         result+=("00000000" + (+d).toString(16)).slice(-8);
  1267.                     }
  1268.  
  1269.                 }
  1270.                 else
  1271.                     result+=s;
  1272.                 break;
  1273.         }
  1274.         return result;
  1275.     }
  1276.     private static _escapeLabel(label: string) : string {
  1277.         if (label===null || label.length==0) return label;
  1278.         let result: string = label.replace("\\", "\\\\");
  1279.         result = result.replace("\"", "\\\"");
  1280.         result = result.replace("\n", "\\n");
  1281.         result = result.replace("\r", "\\r");
  1282.         result = result.replace("\0", "\\0");
  1283.         result = result.replace("\v", "\\v");
  1284.         result = result.replace("\t", "\\t");
  1285.         result = result.replace("\f", "\\f");
  1286.         return result;
  1287.     }
  1288.     private _buildDot(closure: fa[], options: faDotGraphOptions, clusterIndex: number) : string {
  1289.         let result: string = "";
  1290.         if(options===null) {
  1291.             options = new faDotGraphOptions();
  1292.         }
  1293.         const hasBlockEnds: boolean = options.debugShowNfa===false && options.debugString===null&&options.blockEnds!==null;
  1294.         const spfx: string = options.statePrefix===null?"q":options.statePrefix;
  1295.         let pfx: string = "";
  1296.         if(clusterIndex!==-1) {
  1297.             result+=("subgraph cluster_" + clusterIndex.toString(10)+ " {\n");
  1298.             pfx = "c" + clusterIndex.toString(10);
  1299.         } else {
  1300.             result+="digraph FA {\n";
  1301.         }
  1302.         if(!options.vertical) {
  1303.             result+="rankdir=LR\n";
  1304.         }
  1305.         result+="node [shape=circle]\n";
  1306.         let accepting: fa[] = [];
  1307.         let finals: fa[] = [];
  1308.         let neutrals: fa[] = [];
  1309.         for(let i: number=0;i<closure.length;++i) {
  1310.             let ffa = closure[i];
  1311.             if(ffa.isAccepting()) {
  1312.                 accepting.push(ffa);
  1313.             } else if(ffa.isNeutral()) {
  1314.                 neutrals.push(ffa);
  1315.             } else if(ffa.isFinal()) {
  1316.                 finals.push(ffa);
  1317.             }
  1318.         }
  1319.         let fromStates: fa[] = null;
  1320.         let toStates: fa[] = null;
  1321.         let tchar: number = -1;
  1322.         if(options.debugString!=null) {
  1323.             for(let ch of fa.toUtf32(options.debugString)) {
  1324.                 if(fromStates===null) {
  1325.                     fromStates = [];
  1326.                     closure[0].fillEpsilonClosure(fromStates);
  1327.                 } else {
  1328.                     fromStates = toStates;
  1329.                 }
  1330.                 tchar = ch;
  1331.                 toStates = fa.fillMove(fromStates,ch);
  1332.                 if(0==toStates.length) {
  1333.                     break;
  1334.                 }
  1335.             }
  1336.         }
  1337.         if(fromStates==null) {
  1338.             fromStates = [];
  1339.             closure[0].fillEpsilonClosure(fromStates);
  1340.         }
  1341.         if(toStates!=null) {
  1342.             toStates = fa.fillEpsilonClosureStates(toStates);
  1343.         }
  1344.         let i: number = 0;
  1345.         for(const ffa of closure) {
  1346.             const isfrom: boolean = fromStates!==null && fa.fillEpsilonClosureStates(fromStates).includes(ffa);
  1347.             const rngGrps: Map<fa,faRange[]> = ffa.fillInputTransitionRangesGroupedByState();
  1348.             for(const rngGrp of rngGrps) {
  1349.                 const istrns : boolean = isfrom && toStates!==null&&options.debugString!==null && toStates.includes(rngGrp[0]);
  1350.                 const di : number = closure.indexOf(rngGrp[0]);
  1351.                 result += (pfx+spfx);
  1352.                 result+= i.toString(10);
  1353.                 result+="->";
  1354.                 result+=(pfx+spfx);
  1355.                 result+= di.toString(10);
  1356.                 result+=" [label=\"";
  1357.                 let sb: string;
  1358.                 let notRanges: faRange[] = Array.from(fa._invertRanges(rngGrp[1]));
  1359.                 if(notRanges.length>rngGrp[1].length) {
  1360.                     sb=fa._appendRangeTo(rngGrp[1]);
  1361.                 } else {
  1362.                     result+="^";
  1363.                     sb=fa._appendRangeTo(notRanges);
  1364.                 }
  1365.                 if(sb.length!=1 || sb===" ") {
  1366.                     result+='[';
  1367.                     if(sb.length>16) {
  1368.                         sb = sb.substring(0,16);
  1369.                         sb += "...";
  1370.                     }
  1371.                     result+=fa._escapeLabel(sb);
  1372.                     result+="]";
  1373.                 } else {
  1374.                     result+=fa._escapeLabel(sb);
  1375.                 }
  1376.                 if(!istrns) {
  1377.                     result+="\"]\n";
  1378.                 } else {
  1379.                     result+="\",color=green]\n";
  1380.                 }
  1381.             }
  1382.             // do epsilons
  1383.             for (const fat of ffa.transitions)
  1384.             {
  1385.                 if (fat.isEpsilon())
  1386.                 {
  1387.                     var istrns = null != toStates && options.debugString != null && toStates.includes(ffa) && toStates.includes(fat.to);
  1388.                     result+=(pfx + spfx);
  1389.                     result+=i.toString(10);
  1390.                     result+="->";
  1391.                     result+=(pfx+spfx);
  1392.                     result+=(closure.indexOf(fat.to)).toString(10);
  1393.                     if (!istrns)
  1394.                     {
  1395.                         result+=" [style=dashed,color=gray]\n";
  1396.                     } else
  1397.                     {
  1398.                         result+=" [style=dashed,color=green]\n";
  1399.                     }
  1400.                 }
  1401.             }
  1402.             // do block ends
  1403.             if(hasBlockEnds && ffa.isAccepting && options.blockEnds?.length > ffa.acceptSymbol && options.blockEnds[ffa.acceptSymbol]!==null) {
  1404.                 result+=(pfx+spfx+i.toString(10)+"->");
  1405.                 result+=(pfx+"blockEnd"+ffa.acceptSymbol.toString(10)+spfx+"0");
  1406.                 result+=(" [style=dashed,label=\".*?\"]\n");
  1407.             }
  1408.             ++i;
  1409.         }
  1410.         let delim : string;
  1411.         if(hasBlockEnds) {
  1412.             for(i = 0;i<options.blockEnds?.length;++i) {
  1413.                 const bfa : fa = options.blockEnds[i];
  1414.                 if(bfa!==null) {
  1415.                     const bclose = bfa.fillClosure();
  1416.                     delim = "";
  1417.                     for(let qi: number = 0;qi<bclose.length;++qi) {
  1418.                         const cbfa: fa = bclose[qi];
  1419.                         const rngGrps = cbfa.fillInputTransitionRangesGroupedByState();
  1420.                         for(const rngGrp of rngGrps) {
  1421.                             const di: number = bclose.indexOf(rngGrp[0]);
  1422.                             result+=(pfx+"blockEnd"+i.toString(10)+spfx+qi.toString(10));
  1423.                             result+="->";
  1424.                             result+=(pfx+"blockEnd"+i.toString(10)+spfx+di.toString(10));
  1425.                             result+=" [label=\"";
  1426.                             let sb: string =fa._appendRangeTo(rngGrp[1]);
  1427.                             if(sb.length!=1|| sb===" ") {
  1428.                                 result+="[";
  1429.                                 if(sb.length>16) {
  1430.                                     sb=sb.substring(0,16);
  1431.                                     sb+="...";
  1432.                                 }
  1433.                                 result+=fa._escapeLabel(sb);
  1434.                                 result+="]";
  1435.                             } else {
  1436.                                 result+=fa._escapeLabel(sb);
  1437.                             }
  1438.                             result+="\"]\n";
  1439.                         }
  1440.                         // do epsilons
  1441.                         for(const fat of cbfa.transitions) {
  1442.                             if(fat.isEpsilon()) {
  1443.                                 result+=("pfx"+"blockEnd"+i.toString(10)+spfx+qi.toString(10));
  1444.                                 result+="->";
  1445.                                 const di: number = bclose.indexOf(fat.to);
  1446.                                 result+=("pfx"+"blockEnd"+i.toString(10)+spfx+di.toString(10));
  1447.                                 result+=" [style=dashed,color=gray]\n";
  1448.                             }
  1449.                         }
  1450.                     }
  1451.                     for(let qi:number = 0;qi<bclose.length;++qi) {
  1452.                         const cbfa: fa = bclose[qi];
  1453.                         result+=(pfx+"blockEnd"+i.toString(10)+spfx+qi.toString(10)+" [label=<");
  1454.                         result+="<TABLE BORDER=\"0\"><TR><TD>";
  1455.                         result+=("(be)"+spfx);
  1456.                         result+="<SUB>";
  1457.                         result+=qi.toString(10);
  1458.                         result+="</SUB></TD></TR>";
  1459.                         if(cbfa.isAccepting() && !options.hideAcceptSymbolIds) {
  1460.                             result+="<TD><TD>";
  1461.                             let acc: string = null;
  1462.                             if(options.acceptSymbolNames!=null && options.acceptSymbolNames.length>i) {
  1463.                                 acc = options.acceptSymbolNames[i];
  1464.                             }
  1465.                             if(acc===null) {
  1466.                                 acc = i.toString(10);
  1467.                             }
  1468.                             result+=acc.replace("\"","&quot;");
  1469.                             result+="</TD></TR>";
  1470.                         }
  1471.                         result+="</TABLE>";
  1472.                         result+=">";
  1473.                         if(cbfa.isAccepting()) {
  1474.                             result+=",shape=doublecircle";
  1475.                         } else if(cbfa.isFinal()||cbfa.isNeutral()) {
  1476.                             result+=",color=gray";
  1477.                         }
  1478.                         result+="]";
  1479.                     }
  1480.                 }
  1481.             }
  1482.         }
  1483.         delim = "";
  1484.         i = 0;
  1485.         for(const ffa of closure) {
  1486.             result+=(pfx+spfx);
  1487.             result+=i.toString(10);
  1488.             result+=" [";
  1489.             if(options.debugString!==null) {
  1490.                 if(toStates!==null) {
  1491.                     const epstates: fa[] = fa.fillEpsilonClosureStates(toStates,null);
  1492.                     if(epstates.includes(ffa)) {
  1493.                         if(!toStates.includes(ffa)) {
  1494.                             result+="color=darkgreen,"
  1495.                         } else {
  1496.                             result+="color=greeen,";
  1497.                         }
  1498.                     }
  1499.                 }
  1500.             }
  1501.             result+="label=<";
  1502.             result+="<TABLE BORDER=\"0\"><TR><TD>";
  1503.             result+=spfx;
  1504.             result+="<SUB>";
  1505.             result+=i.toString(10);
  1506.             result+="</SUB></TD></TR>";
  1507.             if(options.debugSourceNfa!==null) {
  1508.                 const from: fa[] = ffa.fromStates;
  1509.                 let brk: number = Math.floor(Math.sqrt(from.length));
  1510.                 if(from.length<=3) brk = 3;
  1511.                 for(let j: number = 0;j<from.length;++j) {
  1512.                     if(j===0) {
  1513.                         result+="<TR><TD>";
  1514.                         result+="{ ";
  1515.                         delim = "";
  1516.                     } else if((j%brk)==0) {
  1517.                         delim="";
  1518.                         result+="</TD></TR><TR><TD>";
  1519.                     }
  1520.                     const fromFa: fa = from[j];
  1521.                     result+=delim;
  1522.                     result+="q<SUB>";
  1523.                     result+=options.debugSourceNfa.fillClosure().indexOf(fromFa).toString(10);
  1524.                     result+="</SUB>";
  1525.                     delim = " ";
  1526.                     if(j===from.length-1) {
  1527.                         result+=" }";
  1528.                         result+="</TD></TR>";
  1529.                     }
  1530.                 }
  1531.             }
  1532.             if(ffa.isAccepting() && !options.hideAcceptSymbolIds && !(hasBlockEnds && options.blockEnds?.length>ffa.acceptSymbol && options.blockEnds[ffa.acceptSymbol]!==null)) {
  1533.                 result+="<TR><TD>";
  1534.                 let acc: string = null;
  1535.                 if(options.acceptSymbolNames!==null && options.acceptSymbolNames.length>ffa.acceptSymbol) {
  1536.                     acc = options.acceptSymbolNames [ffa.acceptSymbol];
  1537.                 }
  1538.                 if(acc===null) {
  1539.                     acc = ffa.acceptSymbol.toString(10);
  1540.                 }
  1541.                 result+=acc.replace("\"","&quot;");
  1542.                 result+="</TD></TR>";
  1543.             }
  1544.             result+="</TABLE>";
  1545.             result+=">";
  1546.             let isFinal: boolean = false;
  1547.             if(accepting.includes(ffa) && ((!hasBlockEnds || options.blockEnds?.length<=ffa.acceptSymbol || options.blockEnds[ffa.acceptSymbol]===null))) {
  1548.                 result+=",shape=doublecircle";
  1549.             } else if(isFinal || neutrals.includes(ffa)) {
  1550.                 if((fromStates===null||!fromStates.includes(ffa)) &&
  1551.                     (toStates===null) ||!toStates.includes(ffa)) {
  1552.                         result+=",color=gray";
  1553.                     }
  1554.             }
  1555.             result+="]\n";
  1556.             ++i;
  1557.         }
  1558.         delim="";
  1559.         if(accepting.length>0) {
  1560.             for(const ntfa of accepting) {
  1561.                 if(!hasBlockEnds || options.blockEnds?.length <=ntfa.acceptSymbol || options.blockEnds[ntfa.acceptSymbol]===null) {
  1562.                     result+=delim;
  1563.                     result+=(pfx+spfx);
  1564.                     result+=closure.indexOf(ntfa).toString(10);
  1565.                     delim=",";
  1566.                 }
  1567.             }
  1568.             if(delim!="") {
  1569.                 result+=" [shape=doublecircle]\n";
  1570.             }
  1571.         }
  1572.         delim="";
  1573.         if(neutrals.length>0) {
  1574.             for(const ntfa of neutrals) {
  1575.                 if((fromStates===null || !fromStates.includes(ntfa)) &&
  1576.                  (toStates==null || !toStates.includes(ntfa))) {
  1577.                     result+=delim;
  1578.                     result+=(pfx+spfx);
  1579.                     result+=closure.indexOf(ntfa).toString(10);
  1580.                     delim=",";
  1581.                 }
  1582.             }
  1583.             result+=" [color=gray]\n";
  1584.             delim="";
  1585.         }
  1586.         delim="";
  1587.         if(finals.length>0) {
  1588.             for(const ntfa of finals) {
  1589.                 result+=delim;
  1590.                 result+=(pfx+spfx);
  1591.                 result+=closure.indexOf(ntfa).toString(10);
  1592.                 delim=",";
  1593.             }
  1594.             result+=" [shape=circle,color=gray]\n";
  1595.         }
  1596.         result+="}\n";
  1597.         return result;
  1598.     }
  1599.     private _buildCompoundDot(closure: fa[], options: faDotGraphOptions) : string {
  1600.         let result: string = "digraph FA {\n";
  1601.         let vert: boolean = true;
  1602.         if(!options.vertical) {
  1603.             result+="rank=LR\n";
  1604.         }
  1605.         result+="node [shape=circle]\n";
  1606.         const opt2: faDotGraphOptions = new faDotGraphOptions();
  1607.         opt2.debugSourceNfa = null;
  1608.         opt2.statePrefix = options.statePrefix;
  1609.         opt2.debugString = options.debugString;
  1610.         opt2.debugShowNfa = false;
  1611.         opt2.dpi = options.dpi;
  1612.         opt2.acceptSymbolNames = options.acceptSymbolNames;
  1613.         opt2.hideAcceptSymbolIds = options.hideAcceptSymbolIds;
  1614.         opt2.blockEnds  = options.blockEnds;
  1615.         if(!vert) {
  1616.             result+=this._buildDot(closure, options, 2);
  1617.             result+=this._buildDot(options.debugSourceNfa.fillClosure(), opt2, 1);
  1618.         } else {
  1619.             result+=this._buildDot(options.debugSourceNfa.fillClosure(), opt2, 2);
  1620.             result+=this._buildDot(closure, options, 1);
  1621.         }
  1622.         result+="}\n";
  1623.         return result;
  1624.     }
  1625.    
  1626. }
  1627.  
  1628. console.log("Visual FA");
  1629. const rAZ: faRange = new faRange("A".codePointAt(0),"Z".codePointAt(0));
  1630. const raz: faRange = new faRange("a".codePointAt(0),"z".codePointAt(0));
  1631. const r_: faRange = new faRange("_".codePointAt(0),"_".codePointAt(0));
  1632. const rdig: faRange = new faRange("0".codePointAt(0),"9".codePointAt(0));
  1633. let rngs: faRange[] = new Array<faRange>(r_,rAZ,raz);
  1634. const identFirst: fa = fa.set(rngs);
  1635. rngs.push(rdig);
  1636. const identNext: fa = fa.set(rngs);
  1637. const ident: fa = fa.concat(new Array<fa>(identFirst,fa.repeat(identNext,0,-1))).toMinimizedDfa();
  1638. //fooOrBarz = fa.fromDfaTable(table);
  1639. console.log(ident.isMatch(""));
  1640. console.log(ident.isMatch("foo"));
  1641. console.log(ident.isMatch("foofoo"));
  1642. console.log(ident.isMatch("foofoobar"));
  1643. //console.log(fooLoop.toString("dot"));
  1644. graphviz.dot(ident.toString("dot"), 'svg').then((svg) => {
  1645.     // Write the SVG to file
  1646.     fs.writeFileSync('graph.svg', svg);
  1647.     //console.log(svg);
  1648.   });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement