static FA _Determinize(FA fa, IProgress <FAProgress> progress = null) { if (null != progress) { progress.Report(new FAProgress(FAStatus.DfaTransform, 0)); } var p = new HashSet <int>(); var closure = new List <FA>(); fa.FillClosure(closure); for (int ic = closure.Count, i = 0; i < ic; ++i) { var ffa = closure[i]; p.Add(0); foreach (var t in ffa.InputTransitions) { p.Add(t.Key.Key); if (t.Key.Value < 0x10ffff) { p.Add((t.Key.Value + 1)); } } } var points = new int[p.Count]; p.CopyTo(points, 0); Array.Sort(points); var comparer = _SetComparer.Default; var sets = new Dictionary <HashSet <FA>, HashSet <FA> >(comparer); var working = new Queue <HashSet <FA> >(); var dfaMap = new Dictionary <HashSet <FA>, FA>(comparer); var initial = new HashSet <FA>(); fa.FillEpsilonClosure(initial); sets.Add(initial, initial); working.Enqueue(initial); var result = new FA(); foreach (var afa in initial) { if (afa.IsAccepting) { result.IsAccepting = true; result.AcceptSymbol = afa.AcceptSymbol; break; } } dfaMap.Add(initial, result); var j = 1; while (working.Count > 0) { var s = working.Dequeue(); var ecs = FillEpsilonClosure(s); FA dfa; dfaMap.TryGetValue(s, out dfa); foreach (FA q in ecs) { if (q.IsAccepting) { dfa.IsAccepting = true; dfa.AcceptSymbol = q.AcceptSymbol; break; } } for (var i = 0; i < points.Length; i++) { var set = new HashSet <FA>(); foreach (FA c in ecs) { foreach (var trns in c.InputTransitions) { if (trns.Key.Key <= points[i] && points[i] <= trns.Key.Value) { foreach (var efa in trns.Value.FillEpsilonClosure()) { set.Add(efa); } } } } if (!sets.ContainsKey(set)) { sets.Add(set, set); working.Enqueue(set); dfaMap.Add(set, new FA()); } FA dst; dfaMap.TryGetValue(set, out dst); int first = points[i]; int last; if (i + 1 < points.Length) { last = (points[i + 1] - 1); } else { last = 0x10ffff; } dfa.InputTransitions.Add(new KeyValuePair <int, int>(first, last), dst); } if (null != progress) { progress.Report(new FAProgress(FAStatus.DfaTransform, j)); } ++j; } // remove dead transitions foreach (var ffa in result.FillClosure()) { var itrns = new List <KeyValuePair <KeyValuePair <int, int>, FA> >(ffa.InputTransitions); foreach (var trns in itrns) { if (null == trns.Value.FirstAcceptingState) { ffa.InputTransitions.Remove(trns.Key); } } if (null != progress) { progress.Report(new FAProgress(FAStatus.DfaTransform, j)); } ++j; } return(result); }
/// <summary> /// Returns a DFA table that can be used to lex or match /// </summary> /// <param name="symbolTable">The symbol table to use, or null to just implicitly tag symbols with integer ids</param> /// <returns>A DFA table that can be used to efficiently match or lex input</returns> public DfaEntry[] ToDfaStateTable(IList <int> symbolTable = null, IProgress <FAProgress> progress = null) { // only convert to a DFA if we haven't already // ToDfa() already checks but it always copies // the state information so this performs better FA dfa = null; if (!IsDfa) { dfa = ToDfa(progress); dfa.TrimDuplicates(progress); } else { dfa = this; } var closure = new List <FA>(); dfa.FillClosure(closure); var symbolLookup = new Dictionary <int, int>(); // if we don't have a symbol table, build // the symbol lookup from the states. if (null == symbolTable) { // go through each state, looking for accept symbols // and then add them to the new symbol table is we // haven't already var i = 0; for (int jc = closure.Count, j = 0; j < jc; ++j) { var fa = closure[j]; if (fa.IsAccepting && !symbolLookup.ContainsKey(fa.AcceptSymbol)) { if (0 > fa.AcceptSymbol) { throw new InvalidOperationException("An accept symbol was never specified for state q" + jc.ToString()); } symbolLookup.Add(fa.AcceptSymbol, i); ++i; } } } else // build the symbol lookup from the symbol table { for (int ic = symbolTable.Count, i = 0; i < ic; ++i) { symbolLookup.Add(symbolTable[i], i); } } // build the root array var result = new DfaEntry[closure.Count]; for (var i = 0; i < result.Length; i++) { var fa = closure[i]; #if DEBUG if (fa.IsAccepting) { System.Diagnostics.Debug.Assert(-1 < fa.AcceptSymbol, "Illegal accept symbol " + fa.AcceptSymbol.ToString() + " was found on state state q" + i.ToString()); } #endif // get all the transition ranges for each destination state var trgs = fa.FillInputTransitionRangesGroupedByState(); // make a new transition entry array for our DFA state table var trns = new DfaTransitionEntry[trgs.Count]; var j = 0; // for each transition range foreach (var trg in trgs) { // add the transition entry using // the packed ranges from CharRange trns[j] = new DfaTransitionEntry( trg.Value, closure.IndexOf(trg.Key)); ++j; } // now add the state entry for the state above #if DEBUG if (fa.IsAccepting && !symbolLookup.ContainsKey(fa.AcceptSymbol)) { try { dfa.RenderToFile(@"dfastatetable_crashdump_dfa.jpg"); } catch { } System.Diagnostics.Debug.Assert(false, "The symbol table did not contain an entry for state q" + i.ToString()); } #endif result[i] = new DfaEntry( fa.IsAccepting ? symbolLookup[fa.AcceptSymbol] : -1, trns); } return(result); }