void DumpFiniteAutomaton(FiniteAutomatonState<char> state, ISet<FiniteAutomatonState<char>> visited)
        {
            if (visited.Contains(state))
                return;
            visited.Add(state);

            Debug.WriteLine(state.GetHashCode() + ": ");
            if (state.AcceptTerminals != null)
            {
                foreach (var term in state.AcceptTerminals)
                {
                    Debug.WriteLine("  Accept " + term.Name);
                }
            }
            if (state.RejectTerminals != null)
            {
                foreach (var term in state.RejectTerminals)
                {
                    Debug.WriteLine("  Reject " + term.Name);
                }
            }
            if (state.Transitions != null)
            {
                foreach (var trans in state.Transitions)
                {
                    Debug.WriteLine("  Transition:");
                    Debug.WriteLine("    Match Expression: " + trans.Characters);
                    Debug.WriteLine("    MatchEof: " + trans.MatchEpsilon);
                    Debug.WriteLine("    MatchEpsilon: " + trans.MatchEpsilon);
                    Debug.WriteLine("    Target: " + trans.Target.GetHashCode());
                }
                foreach (var trans in state.Transitions)
                {
                    DumpFiniteAutomaton(trans.Target, visited);
                }
            }
        }
        public void Initialize()
        {
            _expressionHelper = new ExpressionHelper();
            var matchOpen = new HashSet<char> { '<' };
            var matchClose = new HashSet<char> { '>' };
            var matchSlash = new HashSet<char> { '/' };
            var matchLetter = Utilities.AllLetters();
            var matchLetterOrDigit = Utilities.Union(matchLetter, Utilities.AllDigits());
            var matchw = new HashSet<char> { 'w' };
            var matchh = new HashSet<char> { 'h' };
            var matchi = new HashSet<char> { 'i' };
            var matchl = new HashSet<char> { 'l' };
            var matche = new HashSet<char> { 'e' };
            var matchf = new HashSet<char> { 'f' };
            var matchSpace = Utilities.AllWhitespace();

            //_parserGenerator = new TerminalClassifier<char>();
            // Simple XML tags (no attributes)
            _openTag = new Terminal<char>
            {
                Name = "OpenTag",
                ValueType = typeof(string)
            };
            _closeTag = new Terminal<char>
            {
                Name = "CloseTag",
                ValueType = typeof(string)
            };
            _leafTag = new Terminal<char>
            {
                Name = "LeafTag",
                ValueType = typeof(string)
            };

            // Other terminals commonly found in programming languages.
            _if = new Terminal<char>
            {
                Name = "If",
                ValueType = typeof(void)
            };

            _while = new Terminal<char>
            {
                Name = "While",
                ValueType = typeof(void)
            };

            _identifier = new Terminal<char>
            {
                Name = "Identifier",
                ValueType = typeof(string)
            };

            _whitespace = new Terminal<char>
            {
                Name = "Whitespace",
                ValueType = typeof(void)
            };

            var openTagAccept = new FiniteAutomatonState<char>
            {
                IsAccepting = true,
                IsRejecting = false,
            };
            var closeTagAccept = new FiniteAutomatonState<char>
            {
                IsAccepting = true,
                IsRejecting = false
            };
            var leafTagAccept = new FiniteAutomatonState<char>
            {
                IsAccepting = true,
                IsRejecting = false
            };

            var leafClose = new FiniteAutomatonState<char>
            {
                Transitions = new[] {new FiniteAutomatonStateTransition<char> {
                    Characters = matchClose,
                    Target = leafTagAccept
                }
                }
            };

            var leafName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchSlash,
                        Target = leafClose},
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetterOrDigit}}
            };
            leafName.Transitions.Skip(1).First().Target = leafName;

            var beginLeafName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetter,
                        Target = leafName}}
            };

            var leafOpen = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchOpen,
                        Target = beginLeafName}
                }
            };

            var closeClose = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchClose,
                        Target = closeTagAccept}
                }
            };

            var closeName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char>
                    {
                        MatchEpsilon = true,
                        Target = closeClose
                    },
                    new FiniteAutomatonStateTransition<char>
                    {
                        Characters = matchLetterOrDigit
                    }
                }
            };
            closeName.Transitions.Skip(1).First().Target = closeName;

            var beginCloseName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char>
                    {
                        Characters = matchLetter,
                        Target = closeName
                    }
                }
            };
            var closeSlash = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchSlash,
                        Target = beginCloseName}
                }
            };
            var closeOpen = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchOpen,
                        Target = closeSlash}}
            };

            var openName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchClose,
                        Target = openTagAccept},
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetterOrDigit}
                }
            };
            openName.Transitions.Skip(1).First().Target = openName;

            var beginOpenName = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetter,
                        Target = openName
                    }
                }
            };

            var openOpen = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchOpen,
                        Target = beginOpenName}}
            };

            _openTag.InitialState = openOpen;
            _closeTag.InitialState = closeOpen;
            _leafTag.InitialState = leafOpen;

            // Keywords & identifiers
            var acceptIf = new FiniteAutomatonState<char>
            {
                IsAccepting = true
            };
            var fIf = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchf,
                        Target = acceptIf
                    }
                }
            };
            var iIf = new FiniteAutomatonState<char>
            {

                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchi,
                        Target = fIf
                    }
                }
            };
            _if.InitialState = iIf;

            var acceptWhile = new FiniteAutomatonState<char>
            {
                IsAccepting = true
            };

            var eWhile = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matche,
                        Target = acceptWhile
                    }
                }
            };

            var lWhile = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchl,
                        Target = eWhile}
                }
            };

            var iWhile = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchi,
                        Target = lWhile}
                }
            };

            var hWhile = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchh,
                        Target = iWhile}
                }
            };

            var wWhile = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchw,
                        Target = hWhile}
                }
            };
            _while.InitialState = wWhile;

            var identifierChar = new FiniteAutomatonState<char>
            {
                IsAccepting = true,
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetterOrDigit
                    }
                }
            };
            identifierChar.Transitions.First().Target = identifierChar;

            var identifierBegin = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchLetter,
                        Target = identifierChar
                    }
                }
            };
            _identifier.InitialState = identifierBegin;

            var whitespaceChar = new FiniteAutomatonState<char>
            {
                IsAccepting = true,
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchSpace
                    }
                }
            };

            whitespaceChar.Transitions.First().Target = whitespaceChar;

            var firstWhitespace = new FiniteAutomatonState<char>
            {
                Transitions = new[] {
                    new FiniteAutomatonStateTransition<char> {
                        Characters = matchSpace,
                        Target=whitespaceChar
                    }
                }
            };
            _whitespace.InitialState = firstWhitespace;
        }
 void DumpFiniteAutomaton(FiniteAutomatonState<char> initial)
 {
     DumpFiniteAutomaton(initial, new HashSet<FiniteAutomatonState<char>>());
 }