private Statement ParseStatement()
        {
            if (index == Tokens.Count)
            {
                throw new ParserException("Unexpected EOF - Expected Statement");
            }

            Statement result;
            var currentToken = Tokens[index];

            if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.print)
            {
                // print statement 

                index++;
                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                var print = new Print { Expr = ParseExpression(endIndex) };

                result = print;
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.var)
            {
                // variable declaration

                index++;
                var declareVar = new DeclareVariable();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    declareVar.Ident = (string)Tokens[index].Item2.Lexeme;
                }
                else
                {
                    throw new ParserException("Expected variable name after 'var' keyword");
                }

                index++;

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Expected assigning in variable declaration");
                }

                index++;

                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                declareVar.Expr = ParseExpression(endIndex);

                result = declareVar;
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.read_integer)
            {
                // read_integer statement
 
                index++;
                var readInt = new ReadInteger();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    readInt.Ident = (string)Tokens[index++].Item2.Lexeme;

                    result = readInt;
                }
                else
                {
                    throw new ParserException("Expected variable name after 'read_integer' expression");
                }
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.@for)
            {
                // for-loop statement

                index++;
                var forLoop = new ForLoop();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    forLoop.Ident = (string)Tokens[index].Item2.Lexeme;
                }
                else
                {
                    throw new ParserException("Expected loop counter-variable after 'for'");
                }

                index++;

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Missing '=' in for loop");
                }

                index++;

                forLoop.From = ParseExpression();

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords) Tokens[index].Item2.Lexeme !=
                                                                  Keywords.to)))
                {

                    throw new ParserException("Expected 'to' keyword in for-loop statement");
                }

                index++;

                forLoop.To = ParseExpression();

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords) Tokens[index].Item2.Lexeme !=
                                                                  Keywords.@do)))
                {
                    throw new ParserException("Expected 'do' keyword in for-loop statement");
                }

                index++;

                forLoop.Body = ParseStatement();
                result = forLoop;

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords) Tokens[index].Item2.Lexeme !=
                                                                  Keywords.end)))
                {
                    throw new ParserException("Unterminated for-loop body");
                }

                index++;
            }
            else if (Tokens[index].Item1 == TokenType.Identifier)
            {
                // variable assigning statement

                var assignStatement = new Assign { Ident = (string)Tokens[index++].Item2.Lexeme };

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Expected '=' operator");
                }

                index++;

                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                assignStatement.Expr = ParseExpression(endIndex);

                result = assignStatement;
            }
            else
            {
                // unknown statement
                throw new ParserException("Parse error at token " + index + ": " + Tokens[index]);
            }

            if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Semicolon)
            {
                index++;

                if (index < Tokens.Count)
                {
                    if (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                        (Keywords) Tokens[index].Item2.Lexeme != Keywords.end))
                    {
                        var sequenceStatement = new StatementList {First = result, Second = ParseStatement()};

                        result = sequenceStatement;
                    }
                }
            }

            return result;
        }
        private Statement ParseStatement()
        {
            if (index == Tokens.Count)
            {
                throw new ParserException("Unexpected EOF - Expected Statement");
            }

            Statement result;
            var       currentToken = Tokens[index];

            if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.print)
            {
                // print statement

                index++;
                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                var print = new Print {
                    Expr = ParseExpression(endIndex)
                };

                result = print;
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.var)
            {
                // variable declaration

                index++;
                var declareVar = new DeclareVariable();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    declareVar.Ident = (string)Tokens[index].Item2.Lexeme;
                }
                else
                {
                    throw new ParserException("Expected variable name after 'var' keyword");
                }

                index++;

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Expected assigning in variable declaration");
                }

                index++;

                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                declareVar.Expr = ParseExpression(endIndex);

                result = declareVar;
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.read_integer)
            {
                // read_integer statement

                index++;
                var readInt = new ReadInteger();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    readInt.Ident = (string)Tokens[index++].Item2.Lexeme;

                    result = readInt;
                }
                else
                {
                    throw new ParserException("Expected variable name after 'read_integer' expression");
                }
            }
            else if (currentToken.Item1 == TokenType.Keyword && (Keywords)currentToken.Item2.Lexeme == Keywords.@for)
            {
                // for-loop statement

                index++;
                var forLoop = new ForLoop();

                if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Identifier)
                {
                    forLoop.Ident = (string)Tokens[index].Item2.Lexeme;
                }
                else
                {
                    throw new ParserException("Expected loop counter-variable after 'for'");
                }

                index++;

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Missing '=' in for loop");
                }

                index++;

                forLoop.From = ParseExpression();

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords)Tokens[index].Item2.Lexeme !=
                                                                  Keywords.to)))
                {
                    throw new ParserException("Expected 'to' keyword in for-loop statement");
                }

                index++;

                forLoop.To = ParseExpression();

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords)Tokens[index].Item2.Lexeme !=
                                                                  Keywords.@do)))
                {
                    throw new ParserException("Expected 'do' keyword in for-loop statement");
                }

                index++;

                forLoop.Body = ParseStatement();
                result       = forLoop;

                if (index == Tokens.Count ||
                    (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                  (Keywords)Tokens[index].Item2.Lexeme !=
                                                                  Keywords.end)))
                {
                    throw new ParserException("Unterminated for-loop body");
                }

                index++;
            }
            else if (Tokens[index].Item1 == TokenType.Identifier)
            {
                // variable assigning statement

                var assignStatement = new Assign {
                    Ident = (string)Tokens[index++].Item2.Lexeme
                };

                if (index == Tokens.Count || Tokens[index].Item1 != TokenType.EqualOp)
                {
                    throw new ParserException("Expected '=' operator");
                }

                index++;

                var endIndex = index;

                while (Tokens[endIndex].Item1 != TokenType.Semicolon)
                {
                    endIndex++;
                }

                assignStatement.Expr = ParseExpression(endIndex);

                result = assignStatement;
            }
            else
            {
                // unknown statement
                throw new ParserException("Parse error at token " + index + ": " + Tokens[index]);
            }

            if (index < Tokens.Count && Tokens[index].Item1 == TokenType.Semicolon)
            {
                index++;

                if (index < Tokens.Count)
                {
                    if (Tokens[index].Item1 != TokenType.Keyword || (Tokens[index].Item1 == TokenType.Keyword &&
                                                                     (Keywords)Tokens[index].Item2.Lexeme != Keywords.end))
                    {
                        var sequenceStatement = new StatementList {
                            First = result, Second = ParseStatement()
                        };

                        result = sequenceStatement;
                    }
                }
            }

            return(result);
        }