/// <summary>
        /// Scans the input stream and returns scanned <see cref="Token"/> objects.
        /// </summary>
        /// <returns></returns>
        public TokenSequence Scan()
        {
            TokenSequence tokens = new TokenSequence();

            //
            // Scan indents in the beginning
            if(!sourceReader.Empty)
                ScanIndent(tokens);

            //
            // Main loop
            while(!sourceReader.Empty)
            {
                ScanInternal(tokens);
            } // while

            //
            // Terminate statements, if any
            if(tokens.Count > 0 && tokens.Last.Type != TokenType.EndStatement)
                tokens.Add(new Token(TokenType.EndStatement, null));

            //
            // Now postprocess what we have scanned so far and
            // remove extraneous Indents & stuff
            tokens = PostProcessTokens(new TokenSequence(tokens));

            return tokens;
        }
        public void Last()
        {
            TokenSequence sequence = new TokenSequence();
            sequence.Add(new Token(TokenType.BeginBlock, null));
            sequence.Add(new Token(TokenType.Colon, null));
            sequence.Add(new Token(TokenType.Comma, null));

            Assert.AreEqual(new Token(TokenType.Comma, null), sequence.Last);
        }
        public void AddAndCount()
        {
            TokenSequence sequence = new TokenSequence();
            sequence.Add(new Token(TokenType.BeginBlock, null));
            sequence.Add(new Token(TokenType.Colon, null));
            sequence.Add(new Token(TokenType.Comma, null));

            Assert.AreEqual(3, sequence.Count);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="TokenSequence"/> class.
 /// </summary>
 /// <param name="tokens"></param>
 public TokenSequence(TokenSequence tokens)
 {
     foreach(Token token in tokens.tokens)
         Add(token);
 }
        private void ScanInternal(TokenSequence tokens)
        {
            char lookahead = sourceReader.Next;

            if(char.IsDigit(lookahead) || lookahead == '-')
                tokens.Add(ScanIntegerConstant());
            else if(lookahead == '"')
                tokens.Add(ScanStringConstant());
            else if(char.IsLetter(lookahead))
                tokens.Add(ScanSymbolOrKeyword());
            else if(lookahead == ':')
                tokens.Add(ScanColon());
            else if(lookahead == '=')
                tokens.Add(ScanPropertyAssignment());
            else if(lookahead == '[')
                tokens.Add(ScanLeftSquareBracket());
            else if(lookahead == ']')
                tokens.Add(ScanRightSquareBracket());
            else if(lookahead == '{')
                tokens.Add(ScanLeftBrace());
            else if(lookahead == '}')
                tokens.Add(ScanRightBrace());
            else if(lookahead == '(')
                tokens.Add(ScanLeftBracket());
            else if(lookahead == ')')
                tokens.Add(ScanRightBracket());
            else if(lookahead == ',')
                tokens.Add(ScanComma());
            else if(lookahead == '/')
                ScanComment();
            else if(lookahead == '\r')
            {
                tokens.Add(new Token(TokenType.EndStatement, new Location(sourceReader.Line, sourceReader.Column + 1)));

                sourceReader.ReadNext();
                sourceReader.ReadNext();

                ScanIndent(tokens);
            } // else if
            else if(!sourceReader.Empty)
                sourceReader.ReadNext();
        }
        private void ScanIndent(TokenSequence tokens)
        {
            int indent = 0;

            while(!sourceReader.Empty && (sourceReader.Next == ' ' || sourceReader.Next == '\t' ))
            {
                if(sourceReader.Next == ' ')
                    indent += 1;
                else if(sourceReader.Next == '\t')
                    indent += 4;

                sourceReader.ReadNext();
            } // while

            if(indent == 0)
                return;

            tokens.Add(new Token(TokenType.Indent, new StringBuilder().Insert(0, " ", indent).ToString(), 
                new Location(sourceReader.Line, sourceReader.Column + 1)));

            //
            // Return if we reached the end of the line - nothing to do here
            /*if(!sourceReader.Empty && sourceReader.Next == '\r')
                return;

            if(indents.Count == 0 || indents.Peek() < indent)
            {
                if(indents.Count == 0 || tokens.Count == 0)
                {
                    indents.Push(indent);
                    tokens.Add(new Token(TokenType.BeginBlock, null));
                } // if
                else
                {
                    if(tokens.Last.Type == TokenType.BeginBlock)
                    {
                        if(indents.Peek() != indent)
                        {
                            indents.Pop();
                            indents.Push(indent);
                        } // if
                    } // if
                    else
                    {
                        indents.Push(indent);
                        tokens.Add(new Token(TokenType.BeginBlock, null));
                    } // else
                } // else
            } // if
            else if(indents.Peek() > indent)
            {
                while(indents.Count != 0 && indents.Peek() != indent)
                {
                    indents.Pop();
                    tokens.Add(new Token(TokenType.EndBlock, null));
                } // while
            } // else if
             * */
        }
        private static TokenSequence OptimizeTokens(TokenSequence tokens)
        {
            TokenSequence optimizedTokens = new TokenSequence();

            //
            // Removing sequences of Indent(EndStatement)+ tokens
            // and compacting EndStatement+ tokens to just one
            while(!tokens.Empty)
            {
                Token token = tokens.RemoveFirst();

                if(token.Type == TokenType.Indent && !tokens.Empty && tokens.First.Type == TokenType.EndStatement)
                    while(!tokens.Empty && tokens.First.Type == TokenType.EndStatement)
                        tokens.RemoveFirst();
                else if(token.Type == TokenType.EndStatement && !tokens.Empty && tokens.First.Type == TokenType.EndStatement)
                {
                    while(!tokens.Empty && tokens.First.Type == TokenType.EndStatement)
                        tokens.RemoveFirst();

                    optimizedTokens.Add(new Token(TokenType.EndStatement, null));
                } // else if
                else
                    optimizedTokens.Add(token);
            } // while

            return optimizedTokens;
        }
        private static TokenSequence LayoutTokens(TokenSequence tokens)
        {
            TokenSequence laidOutTokens = new TokenSequence();

            Stack<int> indents = new Stack<int>();
            indents.Push(0);

            while(!tokens.Empty)
            {
                Token token = tokens.RemoveFirst();

                if(token.Type == TokenType.Indent)
                {
                    if(token.Lexeme.Length == indents.Peek())
                        continue;

                    if(token.Lexeme.Length > indents.Peek())
                    {
                        indents.Push(token.Lexeme.Length);
                        laidOutTokens.Add(new Token(TokenType.BeginBlock, token.Location));
                    } // if
                    else
                    {
                        while(indents.Peek() != 0 && indents.Peek() != token.Lexeme.Length)
                        {
                            indents.Pop();
                            laidOutTokens.Add(new Token(TokenType.EndBlock, token.Location));
                        } // while
                    } // else
                } // if
                else 
                    laidOutTokens.Add(token);
            } // while

            //
            // Close remaining blocks
            while(indents.Peek() != 0)
            {
                indents.Pop();
                laidOutTokens.Add(new Token(TokenType.EndBlock, null));
            } // while

            return laidOutTokens;
        }
 private static TokenSequence PostProcessTokens(TokenSequence tokens)
 {
     return LayoutTokens(OptimizeTokens(tokens));
 }
        public void RemoveFirst()
        {
            TokenSequence sequence = new TokenSequence();
            sequence.Add(new Token(TokenType.BeginBlock, null));
            sequence.Add(new Token(TokenType.Colon, null));
            sequence.Add(new Token(TokenType.Comma, null));

            Assert.AreEqual(new Token(TokenType.BeginBlock, null), sequence.RemoveFirst());
            Assert.AreEqual(2, sequence.Count);
        }
 public void LastThrowsInvalidOperationExceptionOnEmptySequence()
 {
     TokenSequence sequence = new TokenSequence();
     Token last = sequence.Last;
 }