/// <summary> /// Print the state machine as DOT notation suitable for drawing graphs. /// This is a useful debug functionality!! /// </summary> /// <param name="automata">Automata to generate graph for</param> /// <param name="input">Input to highlight the current state with</param> /// <param name="graphName">Graph name as specified in notation</param> /// <returns></returns> internal static string AsDotNotation <TState>(this FiniteAutomata <TState> automata, string input, string graphName = "automata") where TState : FiniteAutomata <TState> .BaseState { // Draw the *FA as a directed graph with the state numbers in circles // Use a double circle for accepting states // // digraph graphname { // digraph G { // [node shape="circle"] // 1 [shape="doublecircle"] // 1 -> 2 [label=a] //} StringBuilder sb = new StringBuilder(); sb.Append("digraph " + graphName + " {\n"); sb.Append("\t[node shape=\"circle\"]\n"); sb.Append("\tgraph [rankdir=\"LR\"]\n"); IEnumerable <TState> currentStates = Enumerable.Empty <TState>(); bool matchSuccessful = false; if (!string.IsNullOrEmpty(input)) { StimulateResult <TState> stimulateResult = automata.Stimulate(input); matchSuccessful = (input == stimulateResult.Matched); sb.AppendFormat("\tlabel=\"Matched: {0}\"\n", stimulateResult.Matched.Replace("\"", "\\\"")); sb.Append("\tlabelloc=top;\n"); sb.Append("\tlabeljust=center;\n"); currentStates = stimulateResult.ActiveStates; } foreach (Transition <TState> transition in automata.Transitions) { sb.Append(string.Format("\t{0} -> {1} [label=\"{2}\"]\n", transition.From.StateNumber, transition.To.StateNumber, transition.TransitionLabel().Replace("\\", "\\\\").Replace("\"", "\\\""))); } foreach (TState state in automata.States.Where(f => f.AcceptState || currentStates.Contains(f))) { sb.AppendFormat("\t{0} [{1}{2}]\n", state.StateNumber, state.AcceptState ? "shape=\"doublecircle\"" : "", currentStates.Contains(state) ? string.Format(" fillcolor=\"{0}\" style=\"filled\"", matchSuccessful ? "green" : "red") : ""); } sb.Append("}"); return(sb.ToString()); }