private static string GetDFAInfo(DfaExplorer explorer, CharSetSolver solver) { StringBuilder sb = new(); sb.Append($"States = {explorer.StateCount} "); sb.Append($"Transitions = {explorer.TransitionCount} "); sb.Append($"Min Terms ({explorer._builder._solver.GetMinterms()!.Length}) = ").AppendJoin(',', DescribeLabels(explorer, explorer.Alphabet, solver)); return(sb.ToString()); }
/// <summary>Write the DFA or NFA in DGML format into the TextWriter.</summary> /// <param name="matcher">The <see cref="SymbolicRegexMatcher"/> for the regular expression.</param> /// <param name="writer">Writer to which the DGML is written.</param> /// <param name="nfa">True to create an NFA instead of a DFA.</param> /// <param name="addDotStar">True to prepend .*? onto the pattern (outside of the implicit root capture).</param> /// <param name="reverse">If true, then unwind the regex backwards (and <paramref name="addDotStar"/> is ignored).</param> /// <param name="maxStates">The approximate maximum number of states to include; less than or equal to 0 for no maximum.</param> /// <param name="maxLabelLength">maximum length of labels in nodes anything over that length is indicated with .. </param> public static void Write( TextWriter writer, SymbolicRegexMatcher <TSet> matcher, bool nfa = false, bool addDotStar = true, bool reverse = false, int maxStates = -1, int maxLabelLength = -1) { var charSetSolver = new CharSetSolver(); var explorer = new DfaExplorer(matcher, nfa, addDotStar, reverse, maxStates); var nonEpsilonTransitions = new Dictionary <(int SourceState, int TargetState), List <(SymbolicRegexNode <TSet>?, TSet)> >(); var epsilonTransitions = new List <Transition>(); foreach (Transition transition in explorer.GetTransitions()) { if (transition.IsEpsilon) { epsilonTransitions.Add(transition); } else { (int SourceState, int TargetState)p = (transition.SourceState, transition.TargetState); if (!nonEpsilonTransitions.TryGetValue(p, out List <(SymbolicRegexNode <TSet>?, TSet)>?rules)) { nonEpsilonTransitions[p] = rules = new List <(SymbolicRegexNode <TSet>?, TSet)>(); } rules.Add(transition.Label); } } writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); writer.WriteLine("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\" ZoomLevel=\"1.5\" GraphDirection=\"TopToBottom\" >"); writer.WriteLine(" <Nodes>"); writer.WriteLine(" <Node Id=\"dfa\" Label=\" \" Group=\"Collapsed\" Category=\"DFA\" DFAInfo=\"{0}\" />", GetDFAInfo(explorer, charSetSolver)); writer.WriteLine(" <Node Id=\"dfainfo\" Category=\"DFAInfo\" Label=\"{0}\"/>", GetDFAInfo(explorer, charSetSolver)); foreach (int state in explorer.GetStates()) { writer.WriteLine(" <Node Id=\"{0}\" Label=\"{0}\" Category=\"State\" Group=\"Collapsed\" StateInfo=\"{1}\">", state, explorer.DescribeState(state)); if (state == explorer.InitialState) { writer.WriteLine(" <Category Ref=\"InitialState\" />"); } if (explorer.IsFinalState(state)) { writer.WriteLine(" <Category Ref=\"FinalState\" />"); } writer.WriteLine(" </Node>"); writer.WriteLine(" <Node Id=\"{0}info\" Label=\"{1}\" Category=\"StateInfo\"/>", state, explorer.DescribeState(state)); } writer.WriteLine(" </Nodes>"); writer.WriteLine(" <Links>"); writer.WriteLine(" <Link Source=\"dfa\" Target=\"{0}\" Label=\"\" Category=\"StartTransition\" />", explorer.InitialState); writer.WriteLine(" <Link Source=\"dfa\" Target=\"dfainfo\" Label=\"\" Category=\"Contains\" />"); foreach (Transition transition in epsilonTransitions) { writer.WriteLine(" <Link Source=\"{0}\" Target=\"{1}\" Category=\"EpsilonTransition\" />", transition.SourceState, transition.TargetState); } foreach (KeyValuePair <(int, int), List <(SymbolicRegexNode <TSet>?, TSet)> > transition in nonEpsilonTransitions) { string label = string.Join($",{Environment.NewLine} ", DescribeLabels(explorer, transition.Value, charSetSolver)); string info = ""; if (label.Length > (uint)maxLabelLength) { info = $"FullLabel = \"{label}\" "; label = string.Concat(label.AsSpan(0, maxLabelLength), ".."); } writer.WriteLine($" <Link Source=\"{transition.Key.Item1}\" Target=\"{transition.Key.Item2}\" Label=\"{label}\" Category=\"NonEpsilonTransition\" {info}/>"); } foreach (int state in explorer.GetStates()) { writer.WriteLine(" <Link Source=\"{0}\" Target=\"{0}info\" Category=\"Contains\" />", state); } writer.WriteLine(" </Links>"); writer.WriteLine(" <Categories>"); writer.WriteLine(" <Category Id=\"DFA\" Label=\"DFA\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"EpsilonTransition\" Label=\"Epsilon transition\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"StartTransition\" Label=\"Initial transition\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"FinalLabel\" Label=\"Final transition\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"FinalState\" Label=\"Final\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"SinkState\" Label=\"Sink state\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"EpsilonState\" Label=\"Epsilon state\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"InitialState\" Label=\"Initial\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"NonEpsilonTransition\" Label=\"Nonepsilon transition\" IsTag=\"True\" />"); writer.WriteLine(" <Category Id=\"State\" Label=\"State\" IsTag=\"True\" />"); writer.WriteLine(" </Categories>"); writer.WriteLine(" <Styles>"); writer.WriteLine(" <Style TargetType=\"Node\" GroupLabel=\"InitialState\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('InitialState')\" />"); writer.WriteLine(" <Setter Property=\"Background\" Value=\"lightgray\" />"); writer.WriteLine(" <Setter Property=\"MinWidth\" Value=\"0\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Node\" GroupLabel=\"FinalState\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('FinalState')\" />"); writer.WriteLine(" <Setter Property=\"Background\" Value=\"lightgreen\" />"); writer.WriteLine(" <Setter Property=\"StrokeThickness\" Value=\"4\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Node\" GroupLabel=\"State\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('State')\" />"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"black\" />"); writer.WriteLine(" <Setter Property=\"Background\" Value=\"white\" />"); writer.WriteLine(" <Setter Property=\"MinWidth\" Value=\"0\" />"); writer.WriteLine(" <Setter Property=\"FontSize\" Value=\"12\" />"); writer.WriteLine(" <Setter Property=\"FontFamily\" Value=\"Arial\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Link\" GroupLabel=\"NonEpsilonTransition\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('NonEpsilonTransition')\" />"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"black\" />"); writer.WriteLine(" <Setter Property=\"FontSize\" Value=\"18\" />"); writer.WriteLine(" <Setter Property=\"FontFamily\" Value=\"Arial\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Link\" GroupLabel=\"StartTransition\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('StartTransition')\" />"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"black\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Link\" GroupLabel=\"EpsilonTransition\" ValueLabel=\"True\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('EpsilonTransition')\" />"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"black\" />"); writer.WriteLine(" <Setter Property=\"StrokeDashArray\" Value=\"8 8\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Link\" GroupLabel=\"FinalLabel\" ValueLabel=\"False\">"); writer.WriteLine(" <Condition Expression=\"HasCategory('FinalLabel')\" />"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"black\" />"); writer.WriteLine(" <Setter Property=\"StrokeDashArray\" Value=\"8 8\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Node\" GroupLabel=\"StateInfo\" ValueLabel=\"True\">"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"white\" />"); writer.WriteLine(" <Setter Property=\"FontSize\" Value=\"18\" />"); writer.WriteLine(" <Setter Property=\"FontFamily\" Value=\"Arial\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" <Style TargetType=\"Node\" GroupLabel=\"DFAInfo\" ValueLabel=\"True\">"); writer.WriteLine(" <Setter Property=\"Stroke\" Value=\"white\" />"); writer.WriteLine(" <Setter Property=\"FontSize\" Value=\"18\" />"); writer.WriteLine(" <Setter Property=\"FontFamily\" Value=\"Arial\" />"); writer.WriteLine(" </Style>"); writer.WriteLine(" </Styles>"); writer.WriteLine("</DirectedGraph>"); }
private static IEnumerable <string> DescribeLabels(DfaExplorer explorer, IList <(SymbolicRegexNode <TSet>?, TSet)> items, CharSetSolver solver)
private static IEnumerable <string> DescribeLabels(DfaExplorer explorer, IList <(SymbolicRegexNode <T>?, T)> items)