private void ParseComment(BidirectionalCharEnumerator Input)
        {
            bool EndReached = !Input.HasNext();

            while (!EndReached && Input.MoveNext())
            {
                switch (Input.Current)
                {
                case '\r':
                case '\n':
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        if (!Input.HasNext())
                        {
                            ThrowException("Unsupported: Carriage return character, but no newline character.");
                        }
                        Input.MoveNext();
                        if (Input.Current != '\n')
                        {
                            ThrowException("Unsupported: Carriage return character, but no newline character.");
                        }
                        LineNumber++;
                    }
                    EndReached = !EndReached;
                    break;

                default:
                    break;
                }
            }
        }
        public List <List <AbstractSyntaxNode> > Tokenize(string text)
        {
            List <List <AbstractSyntaxNode> > TextArrays = new List <List <AbstractSyntaxNode> >();
            BidirectionalCharEnumerator       Input      = new BidirectionalCharEnumerator(text);

            if (!Input.HasNext())
            {
                ThrowException("You gotta write something, c'mon.");
            }

            while (Input.HasNext())
            {
                Input.MoveNext();
                switch (Input.Current)
                {
                case '#':
                    ParseComment(Input);
                    break;

                case '\r':
                case '\n':
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        if (!Input.HasNext())
                        {
                            ThrowException($"Standalone carriage return character not supported.");
                        }
                        Input.MoveNext();
                        if (Input.Current != '\n')
                        {
                            ThrowException($"Standalone carriage return character not supported.");
                        }
                        LineNumber++;
                    }
                    break;

                case ' ':
                    break;

                case '[':
                case '!':
                    TextArrays.Add(TokenizeArray(Input));
                    break;

                default:
                    ThrowException($"Invalid character \"{Input.Current}\"");
                    break;
                }
            }

            return(TextArrays);
        }
        private AbstractSyntaxNode ParseByteSequence(BidirectionalCharEnumerator Input)
        {
            StringBuilder ByteSequence       = new StringBuilder();
            int           ByteSequenceParsed = 0;

            if (!Input.HasNext())
            {
                ThrowException("\"\\x\" is a reserved directive. It works fine without it being escaped. Remove the escape operator and try again.");
            }

            while (ByteSequenceParsed < 4 && Input.MoveNext())
            {
                if (char.IsLetter(Input.Current))
                {
                    if (char.ToLower(Input.Current) < 97 || char.ToLower(Input.Current) > 102)
                    {
                        ThrowException("Not a valid hexadecimal character!");
                    }
                    else if (char.IsDigit(Input.Current))
                    {
                        if (Input.Current < 48 || Input.Current > 57)
                        {
                            ThrowException("Not a valid hexadecimal character!");
                        }
                    }
                }
                ByteSequence.Append(Input.Current);
                ByteSequenceParsed++;
            }

            if (ByteSequenceParsed < 4)
            {
                ThrowException("Unexpected end of file.");
            }

            return(new AbstractSyntaxNode(TextTokens.ByteSequence, ushort.Parse(ByteSequence.ToString(), System.Globalization.NumberStyles.HexNumber)));
        }
        private List <AbstractSyntaxNode> TokenizeArray(BidirectionalCharEnumerator Input)
        {
            List <AbstractSyntaxNode> Nodes = new List <AbstractSyntaxNode>();
            bool IsCompressed;
            bool EndReached = !Input.HasNext();

            if (EndReached)
            {
                ThrowException("Unexpected end of array.");
            }

            IsCompressed = Input.Current.Equals('!');  // Check if compressed string.

            if (IsCompressed)
            {
                Nodes.Add(new AbstractSyntaxNode(TextTokens.CompressionFlag, '!'));
                Input.MoveNext();
            }

            if (!Input.Current.Equals('['))
            {
                ThrowException("Left bracket is missing.");
            }

            Nodes.Add(new AbstractSyntaxNode(TextTokens.LeftBracket, '['));

            while (!EndReached && Input.MoveNext())
            {
                switch (Input.Current)
                {
                case '!':
                    ThrowException("Only one compression operator is allowed.");
                    break;

                case '[':
                    // Beginning of the text array.
                    ThrowException("Only a 1 dimensional array is allowed.");
                    break;

                case '"':
                    // Beginning of literal.
                    List <AbstractSyntaxNode> QuotationMarkNodes = Nodes.FindAll(x => x.Type == TextTokens.QuotationMark);
                    if (QuotationMarkNodes.Count > 0)
                    {
                        List <AbstractSyntaxNode> OpenQuotationMarkNodes  = QuotationMarkNodes.FindAll(x => x.Attribute.ToString().Equals("Open")),
                                                  CloseQuotationMarkNodes = QuotationMarkNodes.FindAll(x => x.Attribute.ToString().Equals("Close"));

                        if (OpenQuotationMarkNodes.Count != CloseQuotationMarkNodes.Count)
                        {
                            ThrowException("One or more strings in the array were not terminated correctly with a quotation mark.");
                        }
                        else if (Nodes[Nodes.Count - 1].Type != TextTokens.Comma)
                        {
                            ThrowException("Strings containing multiple lines must be separated by a comma.");
                        }
                    }

                    Nodes.Add(new AbstractSyntaxNode(TextTokens.QuotationMark, '"')
                    {
                        Attribute = "Open"
                    });
                    foreach (AbstractSyntaxNode Node in ParseLiteral(Input))
                    {
                        Nodes.Add(Node);
                    }
                    break;

                case ']':
                    // End of the text array.
                    if (Nodes[Nodes.Count - 1].Type != TextTokens.QuotationMark)
                    {
                        ThrowException("String was not terminated before using ']'.");
                    }

                    AbstractSyntaxNode Last = Nodes.FindLast(x => x.Type == TextTokens.StringTerminator);
                    if (Last == null)
                    {
                        ThrowException("You forgot to terminate the last string in the array with '$'. Please do so, or else your text will not save correctly.");
                    }

                    Nodes.Add(new AbstractSyntaxNode(TextTokens.RightBracket, Input.Current));
                    EndReached = !EndReached;
                    break;

                case ',':
                    // Comma, denoting the next literal.
                    if (Nodes[Nodes.Count - 1].Type != TextTokens.QuotationMark)
                    {
                        ThrowException("A comma should only be placed after a quotation mark.");
                    }
                    Nodes.Add(new AbstractSyntaxNode(TextTokens.Comma, Input.Current));
                    break;

                default:
                    // Never seen this character in my life.
                    if (!char.IsWhiteSpace(Input.Current))
                    {
                        ThrowException($"Unrecognized character \"{Input.Current}\".");
                    }
                    if (Input.Current == '\r')
                    {
                        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                        {
                            if (!Input.HasNext())
                            {
                                ThrowException($"Standalone carriage return character not supported.");
                            }
                            Input.MoveNext();
                            if (Input.Current != '\n')
                            {
                                ThrowException($"Standalone carriage return character not supported.");
                            }
                            LineNumber++;
                        }
                    }
                    break;
                }
            }

            if (!EndReached)
            {
                ThrowException("Missing string array terminator.");
            }
            return(Nodes);
        }
        private List <AbstractSyntaxNode> ParseLiteral(BidirectionalCharEnumerator Input)
        {
            List <AbstractSyntaxNode> TokenizedLiteral = new List <AbstractSyntaxNode>();
            bool EndReached = !Input.HasNext();

            if (EndReached)
            {
                ThrowException("Invalid literal.");
            }

            while (!EndReached && Input.MoveNext())
            {
                switch (Input.Current)
                {
                case '\\':
                    if (!Input.HasNext())
                    {
                        ThrowException("Ending quotation mark is missing.");
                    }
                    Input.MoveNext();
                    // Check for control characters.
                    switch (Input.Current)
                    {
                    case 'c':
                        // Macro for text clearing.
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0xF000));
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0xBE01));
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0x0));
                        break;

                    case 'n':
                        // Macro for new line.
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0xFFFE));
                        break;

                    case 'l':
                        // Macro for line scroll.
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0xF000));
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0xBE00));
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.ByteSequence, 0x0));
                        break;

                    case 'x':
                        // Macro for a byte sequence.
                        TokenizedLiteral.Add(ParseByteSequence(Input));
                        break;

                    default:
                        // Escaped character.
                        TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.TextCharacter, Input.Current));
                        break;
                    }
                    break;

                case '{':
                    // String variable.
                    break;

                case '"':
                    // Delimiter for literal.
                    TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.QuotationMark, '"')
                    {
                        Attribute = "Close"
                    });
                    EndReached = !EndReached;
                    break;

                case '$':
                    TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.StringTerminator, 0xFFFF));
                    break;

                default:
                    TokenizedLiteral.Add(new AbstractSyntaxNode(TextTokens.TextCharacter, Input.Current));
                    break;
                }
            }
            if (!EndReached)
            {
                ThrowException("Missing string terminator.");
            }

            return(TokenizedLiteral);
        }