/// <summary>
        /// Output the ANTLR translation of the specified character value node
        /// </summary>
        protected override void WriteCharValNode(ITree node)
        {
            var isCaseSensitive = IsCaseSensitive(node);
            var text            = GetStringValue(node);

            var length = text.Length;

            if (isCaseSensitive && length > 0)
            {
                Write("'");
                Write(text);
                Write("'");
            }
            else
            {
                if (length > 1)
                {
                    Write("(");
                }

                for (int index = 0; index < length; index++)
                {
                    if (index > 0)
                    {
                        Write(" ");
                    }

                    var upperCharacter = char.ToUpperInvariant(text[index]);
                    var lowerCharacter = char.ToLowerInvariant(text[index]);

                    if (upperCharacter == lowerCharacter)
                    {
                        Write("'");
                        Write(AntlrHelper.CharEscape(lowerCharacter));
                        Write("'");
                    }
                    else
                    {
                        Write("(");

                        Write("'");
                        Write(AntlrHelper.CharEscape(upperCharacter));
                        Write("'");

                        Write(" | ");

                        Write("'");
                        Write(AntlrHelper.CharEscape(lowerCharacter));
                        Write("'");

                        Write(")");
                    }
                }

                if (length > 1)
                {
                    Write(")");
                }
            }
        }
        void OutputLiteralRules(IDictionary<char, NamedCharacter> literals, TextWriter writer, INamedCharacterLookup lookup)
        {
            var knownValues = 
                literals.Values
                .Where(x => lookup.IsKnownCharacter(x.Character))
                .OrderBy(x => x.Character)
                .Select(x => x);

            var unknownValues = 
                literals.Values
                .Where(x => !(lookup.IsKnownCharacter(x.Character)))
                .OrderBy(x => x.Character)
                .Select(x => x);

            if (literals.Count > 0)
            {
                writer.WriteLine("");
                writer.WriteLine("");
                writer.WriteLine(@"////////////////////////////////////////////////////////////////////////////////////////////");
                writer.WriteLine(@"// Lexer rules generated for each distinct character in original grammar");
                writer.WriteLine(@"// " + lookup.Description);
                writer.WriteLine(@"////////////////////////////////////////////////////////////////////////////////////////////");
                writer.WriteLine("");
            }

            // output known (named) literals first
            foreach (var value in knownValues)
            {
                writer.Write(value.Name);
                writer.Write(" : ");

                writer.Write("'");

                var character = value.Character;

                writer.Write(AntlrHelper.CharEscape(character));

                writer.Write("'");

                writer.WriteLine(";");
            }

            // output unknown literals
            foreach (var value in unknownValues)
            {
                writer.Write(value.Name);
                writer.Write(" : ");

                int number = value.Character;

                writer.Write(@"'\u");
                writer.Write(number.ToString("X4"));
                writer.Write("'");

                writer.WriteLine(";");
            }
        }