/// <summary>
        /// Generates a <see cref="CodeMemberMethod"/> that can be compiled and used to lex input
        /// </summary>
        /// <param name="dfaTable">The DFA table to use</param>
        /// <param name="errorSymbol">Indicates the error symbol id to use</param>
        /// <returns>A <see cref="CodeMemberMethod"/> representing the lexing procedure</returns>
        public static CodeMemberMethod GenerateLexMethod(CharDfaEntry[] dfaTable, int errorSymbol)
        {
            var result = new CodeMemberMethod();

            result.Name       = "Lex";
            result.Attributes = MemberAttributes.FamilyAndAssembly | MemberAttributes.Static;
            result.Parameters.Add(new CodeParameterDeclarationExpression(typeof(ParseContext), "context"));
            result.ReturnType = new CodeTypeReference(typeof(int));
            result.Statements.Add(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression("context"), "EnsureStarted")));
            // we generate labels for each state except maybe the first.
            // we only generate a label for the first state if any of the
            // states (including itself) reference it. This is to prevent
            // a compiler warning in the case of an unreferenced label
            var isRootLoop = false;
            // we also need to see if any states do not accept
            // if they don't we'll have to generate an error condition
            var hasError = false;

            for (var i = 0; i < dfaTable.Length; i++)
            {
                var trns = dfaTable[i].Transitions;
                for (var j = 0; j < trns.Length; j++)
                {
                    if (0 == trns[j].Destination)
                    {
                        isRootLoop = true;
                        break;
                    }
                }
            }
            var pcr   = new CodeArgumentReferenceExpression(result.Parameters[0].Name);
            var pccr  = new CodePropertyReferenceExpression(pcr, "Current");
            var pccc  = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(pcr, "CaptureCurrent")));
            var exprs = new CodeExpressionCollection();
            var stmts = new CodeStatementCollection();

            for (var i = 0; i < dfaTable.Length; i++)
            {
                stmts.Clear();
                var se   = dfaTable[i];
                var trns = se.Transitions;
                for (var j = 0; j < trns.Length; j++)
                {
                    var cif = new CodeConditionStatement();
                    stmts.Add(cif);
                    exprs.Clear();

                    var trn = trns[j];
                    var pr  = trn.PackedRanges;
                    for (var k = 0; k < pr.Length; k++)
                    {
                        var first = pr[k];
                        ++k;                         // advance an extra place
                        var last = pr[k];
                        if (first != last)
                        {
                            exprs.Add(
                                new CodeBinaryOperatorExpression(
                                    new CodeBinaryOperatorExpression(
                                        pccr,
                                        CodeBinaryOperatorType.GreaterThanOrEqual,
                                        new CodePrimitiveExpression(first)
                                        ),
                                    CodeBinaryOperatorType.BooleanAnd,
                                    new CodeBinaryOperatorExpression(
                                        pccr,
                                        CodeBinaryOperatorType.LessThanOrEqual,
                                        new CodePrimitiveExpression(last)
                                        )
                                    )
                                );
                        }
                        else
                        {
                            exprs.Add(
                                new CodeBinaryOperatorExpression(
                                    pccr,
                                    CodeBinaryOperatorType.ValueEquality,
                                    new CodePrimitiveExpression(first)
                                    )
                                );
                        }
                    }
                    cif.Condition = _MakeBinOps(exprs, CodeBinaryOperatorType.BooleanOr);
                    cif.TrueStatements.Add(pccc);
                    cif.TrueStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(pcr, "Advance")));
                    cif.TrueStatements.Add(new CodeGotoStatement(string.Concat("q", trn.Destination.ToString())));
                }
                if (-1 != se.AcceptSymbolId)                 // is accepting
                {
                    stmts.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(se.AcceptSymbolId)));
                }
                else
                {
                    hasError = true;
                    stmts.Add(new CodeGotoStatement("error"));
                }
                if (0 < i || isRootLoop)
                {
                    result.Statements.Add(new CodeLabeledStatement(string.Concat("q", i.ToString()), stmts[0]));
                    for (int jc = stmts.Count, j = 1; j < jc; ++j)
                    {
                        result.Statements.Add(stmts[j]);
                    }
                }
                else
                {
                    result.Statements.Add(new CodeCommentStatement("q0"));
                    result.Statements.AddRange(stmts);
                }
            }
            if (hasError)
            {
                result.Statements.Add(new CodeLabeledStatement("error", pccc));
                result.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(pcr, "Advance")));
                result.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(errorSymbol)));
            }
            return(result);
        }