// expr {',' expr}
        SequenceExpression ParseSequence(InitFileLexer lexer)
        {
            if(has_error_reported) return null;

            Token token = lexer.Current;
            var start_loc = token.StartLoc;
            Expression expr = null;
            var exprs = new List<Expression>();
            while(token.Kind != TokenKind.EOL){
                switch(token.Kind){
                case TokenKind.Identifier:
                    if(token.Literal.Contains("."))
                        expr = ParsePathLiteral(lexer);
                    else if(token.Literal.StartsWith("#"))
                        expr = ParseColorLiteral(lexer);
                    else
                        expr = ParseLiteral(lexer);

                    break;

                case TokenKind.IntegerLiteral:
                case TokenKind.FloatLiteral:
                    expr = ParseLiteral(lexer);
                    break;

                default:
                    AddError(ErrorCode.Other, token.Line, 1, string.Format("Line {0}: Invalid definition!", token.Line));
                    break;
                }

                exprs.Add(expr);
                token = lexer.Current;
                if(token.Literal == ","){
                    lexer.Advance();
                    token = lexer.Current;
                }
            }

            return AstNode.MakeSequence(exprs, start_loc, token.StartLoc);
        }
        // section-statement | definition '\n'
        BVE5Language.Ast.Statement ParseStatement(InitFileLexer lexer)
        {
            Token token = lexer.Current;
            BVE5Language.Ast.Expression expr = null;

            if(token.Kind == TokenKind.Identifier){
                expr = ParseDefinition(lexer);
            }else if(token.Literal == "["){
                return ParseSectionStatement(lexer);
            }else{
                AddError(ErrorCode.SyntaxError, token.Line, token.Column,
                         "A statement must start with an identifier or the sign '['.");
                while(token.Kind != TokenKind.EOL){
                    if(token.Kind == TokenKind.EOF){
                        AddError(ErrorCode.UnexpectedEOF, token.Line, token.Column, "Unexpected EOF!");
                        return null;
                    }
                    lexer.Advance();
                    token = lexer.Current;
                }
                lexer.Advance();
                return null;
            }

            token = lexer.Current;
            if(token.Kind != TokenKind.EOL){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected EOL but got " + token.Literal + ".");
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            token = lexer.Current;
            return AstNode.MakeStatement(expr, expr.StartLocation, token.StartLoc);
        }
        // path-literal
        LiteralExpression ParsePathLiteral(InitFileLexer lexer)
        {
            Token token = lexer.Current;
            var start_loc = token.StartLoc;
            Debug.Assert(token.Kind == TokenKind.Identifier, "Really meant a path literal?");
            var sb = new StringBuilder();

            while(token.Kind != TokenKind.EOL && token.Literal != ","){
                if(token.Kind == TokenKind.EOF){
                    AddError(ErrorCode.UnexpectedEOF, token.Line, token.Column, "Unexpected EOF!");
                    if(enable_strict_parsing)
                        return null;
                    else
                        break;
                }
                sb.Append(token.Literal);
                lexer.Advance();
                token = lexer.Current;
            }
            return AstNode.MakeLiteral(sb.ToString(), start_loc, token.StartLoc);
        }
        // '[' identifier ']' '\n'
        SectionStatement ParseSectionStatement(InitFileLexer lexer)
        {
            if(has_error_reported) return null;

            Token token = lexer.Current;
            var start_loc = token.StartLoc;
            Debug.Assert(token.Literal == "[", "Really meant a section statement?");

            lexer.Advance();
            Identifier ident = ParseIdent(lexer);
            token = lexer.Current;
            if(token.Literal != "]"){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected ']' but got " + token.Literal + ".");
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            token = lexer.Current;
            if(token.Kind != TokenKind.EOL){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected EOL but got " + token.Literal + ".");
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            token = lexer.Current;
            return AstNode.MakeSectionStatement(ident, start_loc, token.StartLoc);
        }
 // number | any-string
 LiteralExpression ParseLiteral(InitFileLexer lexer)
 {
     Token token = lexer.Current;
     Debug.Assert(token.Kind == TokenKind.IntegerLiteral || token.Kind == TokenKind.FloatLiteral || token.Kind == TokenKind.Identifier, "Really meant a literal?");
     lexer.Advance();
     if(token.Kind == TokenKind.FloatLiteral)
         return AstNode.MakeLiteral(Convert.ToDouble(token.Literal), token.StartLoc, token.EndLoc);
     else if(token.Kind == TokenKind.IntegerLiteral)
         return AstNode.MakeLiteral(Convert.ToInt32(token.Literal), token.StartLoc, token.EndLoc);
     else
         return AstNode.MakeLiteral(token.Literal, token.StartLoc, token.EndLoc);
 }
        SyntaxTree ParseImpl(string src, string fileName, bool parseHeader)
        {
            lock(parse_lock){
                if(!src.EndsWith("\n"))
                    src += "\n";

                using(var reader = new StringReader(src)){
                    var lexer = new InitFileLexer(reader);
                    lexer.Advance();

                    string version_str = "unknown";
                    if(parseHeader){
                        var meta_header = new StringBuilder();
                        while(lexer.Current != Token.EOF && lexer.Current.Kind != TokenKind.EOL){
                            meta_header.Append(lexer.Current.Literal);
                            meta_header.Append(' ');
                            lexer.Advance();
                        }
                        var meta_header_match = MetaHeaderRegexp.Match(meta_header.ToString());
                        if(!meta_header_match.Success){
                            AddError(ErrorCode.InvalidFileHeader, 1, 1, "Invalid " + FileKind + " file!");
                            return null;
                        }else{
                            version_str = meta_header_match.Groups[1].Value;
                        }
                    }
                    if(lexer.Current.Kind == TokenKind.EOL)
                        lexer.Advance();

                    BVE5Language.Ast.Statement stmt = null;
                    var stmts = new List<BVE5Language.Ast.Statement>();
                    while(lexer.Current != Token.EOF){
                        stmt = ParseStatement(lexer);
                        if(enable_strict_parsing && !has_error_reported || !enable_strict_parsing)
                            stmts.Add(stmt);

                        if(has_error_reported)
                            has_error_reported = false;
                    }

                    return AstNode.MakeSyntaxTree(stmts, fileName, version_str, FileKind, new TextLocation(1, 1), stmts.Last().EndLocation, Errors.ToList());
                }
            }
        }
 // anyCharacterExcept-'='-']'-','
 Identifier ParseIdent(InitFileLexer lexer)
 {
     Debug.Assert(lexer.Current.Kind == TokenKind.Identifier, "Really meant an identifier?");
     Token token = lexer.Current;
     lexer.Advance();
     return AstNode.MakeIdent(token.Literal, token.StartLoc, lexer.Current.StartLoc);
 }
        // identifier '=' sequence
        DefinitionExpression ParseDefinition(InitFileLexer lexer)
        {
            if(has_error_reported) return null;

            Identifier lhs = ParseIdent(lexer);
            Token token = lexer.Current;
            if(token.Literal != "="){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected '=' but got " + token.Literal + ".");
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            SequenceExpression rhs = ParseSequence(lexer);
            Expression expr = rhs;
            if(rhs.Expressions.Count() == 1){		//Here, we'll simplify a list of one expression to a solo expression.
                var tmp = rhs.Expressions.First();	//This is needed because some definitions have more than one right-hand-side expressions.
                tmp.Remove();
                expr = tmp;
            }
            return AstNode.MakeDefinition(lhs, expr, lhs.StartLocation, lexer.Current.StartLoc);
        }
 // '#' {number}{3,6}
 LiteralExpression ParseColorLiteral(InitFileLexer lexer)
 {
     Token token = lexer.Current;
     Debug.Assert(token.Literal.StartsWith("#"), "Really meant a color literal?");
     lexer.Advance();
     return AstNode.MakeLiteral(token.Literal, token.StartLoc, lexer.Current.StartLoc);
 }