public void InitializeOpenedInsightWindow(ITextEditor editor, IInsightWindow insightWindow)
        {
            EventHandler<TextChangeEventArgs> on_document_changed = delegate{
                // whenever the document is changed, recalculate EndOffset
                var remaining_doc = editor.Document.CreateReader(insightWindow.StartOffset, editor.Document.TextLength - insightWindow.StartOffset);
                var lexer = new BVE5RouteFileLexer(remaining_doc);
                var line = editor.Document.GetLineForOffset(insightWindow.StartOffset);
                lexer.SetInitialLocation(line.LineNumber, insightWindow.StartOffset - editor.Document.PositionToOffset(line.LineNumber, 1));
                Token token;

                lexer.Advance();
                while((token = lexer.Current) != null && token.Kind != TokenKind.EOF){
                    if(token.Literal == ")"){
                        MarkInsightWindowEndOffset(insightWindow, editor, token.StartLoc);
                        break;
                    }else if(token.Literal == ";"){
                        MarkInsightWindowEndOffset(insightWindow, editor, token.StartLoc);
                        break;
                    }
                    lexer.Advance();
                }
            };

            insightWindow.DocumentChanged += on_document_changed;
            insightWindow.SelectedItemChanged += delegate { HighlightParameter(insightWindow, highlighted_parameter); };
            on_document_changed(null, null);
        }
        // expr '(' [arguments] ')'
        Expression ParseInvokeExpr(BVE5RouteFileLexer lexer, BVE5Language.Ast.Expression callTarget)
        {
            if(has_error_reported) return callTarget;

            Token token = lexer.Current;
            Debug.Assert(token.Literal == "(", "Really meant an invoke expression?");
            lexer.Advance();
            token = lexer.Current;
            var args = new List<BVE5Language.Ast.Expression>();

            while(token.Kind != TokenKind.EOF && token.Literal != ")"){
                args.Add(ParseRValueExpression(lexer));

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

            if(token.Kind == TokenKind.EOF){
                AddError(ErrorCode.UnexpectedEOF, token.Line, token.Column, "Unexpected EOF!");
                if(enable_strict_parsing)
                    return null;
            }else if(token.Literal != ")"){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Missing ')'");
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
                token = lexer.Current;
            }

            return AstNode.MakeInvoke(callTarget, args, callTarget.StartLocation, token.StartLoc);    //token should be pointing to a closing parenthesis token
        }
        // ident '[' ident ']'
        IndexerExpression ParseIndexExpr(BVE5RouteFileLexer lexer, Identifier target)
        {
            Token token = lexer.Current;
            Debug.Assert(token.Literal == "[", "Really meant an index reference?");
            lexer.Advance();

            token = lexer.Current;
            if(token.Kind != TokenKind.Identifier && token.Kind != TokenKind.IntegerLiteral){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Unexpected token: " + token.Literal +
                    "; The operator '[]' can only take a string or an index as its argument.");
                if(enable_strict_parsing)
                    return null;
            }

            LiteralExpression literal = ParseLiteral(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;
            }

            return AstNode.MakeIndexExpr(target, literal, target.StartLocation, token.StartLoc);
        }
        SyntaxTree ParseImpl(string src, string fileName, bool parseHeader)
        {
            lock(parse_lock){
                using(var reader = new StringReader(src)){
                    var lexer = new BVE5RouteFileLexer(reader);
                    lexer.Advance();

                    string version_str = "unknown";
                    if(parseHeader){
                        int cur_line = lexer.CurrentLine;
                        var meta_header = new StringBuilder();
                        while(lexer.Current != Token.EOF && lexer.CurrentLine == cur_line){
                            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 Map file!");
                            return null;
                        }else{
                            version_str = meta_header_match.Groups[1].Value;
                        }
                    }

                    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, BVE5FileKind.RouteFile, new TextLocation(1, 1), stmts.Last().EndLocation, Errors.ToList());
                }
            }
        }
        private SyntaxTree ParseImpl(string src, string fileName, bool parseHeader)
        {
            lock(parse_lock){
                using(var reader = new StringReader(src)){
                    var lexer = new BVE5RouteFileLexer(reader);
                    if(parseHeader){
                        int cur_line = lexer.CurrentLine;
                        var tokens = new List<Token>();
                        while(lexer.Current != Token.EOF && lexer.CurrentLine == cur_line){
                            lexer.Advance();
                            tokens.Add(lexer.Current);
                        }
                        if(tokens.Count != 3 || tokens[0].Literal != "BveTs" || tokens[1].Literal != "Map" ||
                           tokens[2].Literal != "1.00")
                            throw new BVE5ParserException(1, 1, "Invalid Map file!");
                    }else{
                        lexer.Advance();
                    }

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

                    return AstNode.MakeSyntaxTree(stmts, fileName, new TextLocation(1, 1), stmt.EndLocation);
                }
            }
        }
        // number ':' number ':' number
        TimeFormatLiteral ParseTimeLiteral(BVE5RouteFileLexer lexer)
        {
            if(has_error_reported) return null;

            Token token = lexer.Current;
            Debug.Assert(token.Kind == TokenKind.IntegerLiteral, "Really meant a time literal?");
            int[] nums = new int[3];
            Token start_token = lexer.Current;

            for(int i = 0; i < 3; ++i){
                if(token.Kind == TokenKind.EOF){
                    AddError(ErrorCode.UnexpectedEOF, token.Line, token.Column, "Unexpected EOF!");
                    if(enable_strict_parsing)
                        return null;
                    else
                        break;
                }

                nums[i] = Convert.ToInt32(token.Literal);

                lexer.Advance();
                if(i == 2) break;

                token = lexer.Current;
                if(token.Kind == TokenKind.EOF){
                    AddError(ErrorCode.UnexpectedEOF, token.Line, token.Column, "Unexpected EOF!");
                    if(enable_strict_parsing)
                        return null;
                    else
                        break;
                }else if(token.Literal != ":"){
                    AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected ':' but got " + token.Literal);
                }

                lexer.Advance();
                token = lexer.Current;
            }

            return AstNode.MakeTimeFormat(nums[0], nums[1], nums[2], start_token.StartLoc, token.StartLoc);
        }
        // path-literal | identifier | time-literal | literal
        BVE5Language.Ast.Expression ParseRValueExpression(BVE5RouteFileLexer lexer)
        {
            Token token = lexer.Current;
            switch(token.Kind){
            case TokenKind.Identifier:
                if(lexer.Peek.Literal != "," || lexer.Peek.Literal != ")")
                    return ParsePathLiteral(lexer);
                else
                    return ParseIdent(lexer);

            case TokenKind.IntegerLiteral:
            case TokenKind.FloatLiteral:
                var la = lexer.Peek;
                if(la.Literal == ":")
                    return ParseTimeLiteral(lexer);
                else
                    return ParseLiteral(lexer);

            default:
                AddError(ErrorCode.SyntaxError, token.Line, token.Column,
                         "A right-hand-side expression must be an identifier, a file path, a literal or a time format literal!");
                return null;
            }
        }
        // ident '.' ident
        MemberReferenceExpression ParseMemberRef(BVE5RouteFileLexer lexer, BVE5Language.Ast.Expression parent)
        {
            Token token = lexer.Current;
            Debug.Assert(token.Literal == ".", "Really meant a member reference?");
            lexer.Advance();

            token = lexer.Current;
            Identifier ident = null;
            if(token.Kind == TokenKind.Identifier){
                ident = ParseIdent(lexer);
            }else{
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Unexpected token: " + token.Kind);
                if(enable_strict_parsing)
                    return null;
            }

            token = lexer.Current;
            return AstNode.MakeMemRef(parent, ident, parent.StartLocation, token.StartLoc);
        }
        // [\d]+ ';' | ident ['[' ident ']'] '.' ident '(' [args] ')' ';'
        private BVE5Language.Ast.Statement ParseStatement(BVE5RouteFileLexer lexer)
        {
            Token token = lexer.Current;
            BVE5Language.Ast.Expression expr = null;

            if(token.Kind == TokenKind.IntegerLiteral){
                expr = ParseLiteral(lexer);
            }else if(token.Kind == TokenKind.Identifier){
                AstNode res = ParseIdent(lexer);
                if(lexer.Current.Literal == "["){
                    res = ParseIndexExpr(lexer, res as BVE5Language.Ast.Expression);
                }

                token = lexer.Current;
                if(token.Literal != ".")
                    throw new BVE5ParserException(token.Line, token.Column, "Expected '.' but got " + token.Literal);

                res = ParseMemberRef(lexer, res as BVE5Language.Ast.Expression);
                expr = ParseInvokeExpr(lexer, res as BVE5Language.Ast.Expression);
            }else{
                throw new BVE5ParserException(token.Line, token.Column,
                                                "A statement must start with an integer literal or an identifier.");
            }

            token = lexer.Current;
            if(token.Literal != ";")
                throw new BVE5ParserException(token.Line, token.Column, "Unexpected character: " + token.Literal);

            lexer.Advance();

            return AstNode.MakeStatement(expr, expr.StartLocation, token.EndLoc);   //token should be pointing to a semicolon token
        }
        // path-literal
        private LiteralExpression ParsePathLiteral(BVE5RouteFileLexer 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.Literal != "," && token.Literal != ")"){
                sb.Append(token.Literal);
                lexer.Advance();
                token = lexer.Current;
            }
            return AstNode.MakeLiteral(sb.ToString(), start_loc, token.StartLoc);
        }
        // ident '.' ident
        private MemberReferenceExpression ParseMemberRef(BVE5RouteFileLexer lexer, BVE5Language.Ast.Expression parent)
        {
            Debug.Assert(lexer.Current.Literal == ".", "Really meant a member reference?");
            lexer.Advance();
            Token token = lexer.Current;
            if(token.Kind != TokenKind.Identifier)
                throw new BVE5ParserException(token.Line, token.Column, "Unexpected token: " + token.Kind);

            Identifier ident = ParseIdent(lexer);
            return AstNode.MakeMemRef(parent, ident, parent.StartLocation, ident.EndLocation);
        }
 // number
 private LiteralExpression ParseLiteral(BVE5RouteFileLexer lexer)
 {
     Token token = lexer.Current;
     Debug.Assert(token.Kind == TokenKind.IntegerLiteral || token.Kind == TokenKind.FloatLiteral, "Really meant a literal?");
     lexer.Advance();
     return AstNode.MakeLiteral(token.Kind == TokenKind.FloatLiteral ? Convert.ToDouble(token.Literal) : Convert.ToInt32(token.Literal),
                                token.StartLoc, token.EndLoc);
 }
        // expr '(' [arguments] ')'
        private InvocationExpression ParseInvokeExpr(BVE5RouteFileLexer lexer, BVE5Language.Ast.Expression callTarget)
        {
            Debug.Assert(lexer.Current.Literal == "(", "Really meant an invoke expression?");
            lexer.Advance();
            Token token = lexer.Current;
            var args = new List<BVE5Language.Ast.Expression>();

            while(token.Kind != TokenKind.EOF && token.Literal != ")"){
                args.Add(ParseArgument(lexer));

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

            if(token.Kind == TokenKind.EOF)
                throw new BVE5ParserException(token.Line, token.Column, "Unexpected EOF!");

            lexer.Advance();
            return AstNode.MakeInvoke(callTarget, args, callTarget.StartLocation, token.EndLoc);
        }
        // ident '[' ident ']'
        private IndexerExpression ParseIndexExpr(BVE5RouteFileLexer lexer, BVE5Language.Ast.Expression target)
        {
            Debug.Assert(lexer.Current.Literal == "[", "Really meant an index reference?");
            lexer.Advance();
            Token token = lexer.Current;
            if(token.Kind != TokenKind.Identifier)
                throw new BVE5ParserException(token.Line, token.Column, "Unexpected token: " + token.Kind);

            Identifier ident = ParseIdent(lexer);
            token = lexer.Current;
            if(token.Literal != "]")
                throw new BVE5ParserException(token.Line, token.Column, "Expected ']' but got " + token.Literal);

            lexer.Advance();
            return AstNode.MakeIndexExpr(target, ident, target.StartLocation, token.EndLoc);
        }
        // "let" definition ";"
        LetStatement ParseLetStatement(BVE5RouteFileLexer lexer)
        {
            Token token = lexer.Current;
            Debug.Assert(token.Literal == "let", "Really meant a let statement?");
            if(token.Literal != "let"){
                AddError(ErrorCode.UnknownKeyword, token.Line, token.Column, "Unknown keyword " + token.Literal);
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            var expr = ParseDefinition(lexer);
            token = lexer.Current;
            if(token.Literal != ";"){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Unexpected character: " + token.Literal);
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            return AstNode.MakeLetStatement(expr, expr.StartLocation, token.EndLoc);
        }
        // number | any-string
        LiteralExpression ParseLiteral(BVE5RouteFileLexer lexer)
        {
            if(has_error_reported) return null;

            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);
        }
        // number ':' number ':' number
        private TimeFormatLiteral ParseTimeLiteral(BVE5RouteFileLexer lexer)
        {
            Debug.Assert(lexer.Current.Kind == TokenKind.IntegerLiteral, "Really meant a timel literal?");
            int[] nums = new int[3];
            Token token;
            Token start_token = token = lexer.Current;

            for(int i = 0; i < 3; ++i){
                if(token.Kind == TokenKind.EOF)
                    throw new BVE5ParserException(token.Line, token.Column, "Unexpected EOF!");

                nums[i] = Convert.ToInt32(token.Literal);
                if(i == 2) break;

                lexer.Advance();
                token = lexer.Current;
                if(token.Kind == TokenKind.EOF)
                    throw new BVE5ParserException(token.Line, token.Column, "Unexpected EOF!");
                else if(token.Literal != ":")
                    throw new BVE5ParserException(token.Line, token.Column, "Expected ':' but got " + token.Literal);

                lexer.Advance();
                token = lexer.Current;
            }

            return AstNode.MakeTimeFormat(nums[0], nums[1], nums[2], start_token.StartLoc, token.StartLoc);
        }
        // path-literal
        LiteralExpression ParsePathLiteral(BVE5RouteFileLexer lexer)
        {
            if(has_error_reported) return null;

            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.Literal != "," && 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);
        }
        // ident '=' expr
        DefinitionExpression ParseDefinition(BVE5RouteFileLexer lexer)
        {
            Token token = lexer.Current;
            var start_loc = token.StartLoc;
            Debug.Assert(token.Kind == TokenKind.Identifier, "Really meant a definition?");

            var lhs = 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();
            }

            var rhs = ParseRValueExpression(lexer);
            token = lexer.Current;
            return AstNode.MakeDefinition(lhs, rhs, start_loc, token.StartLoc);
        }
        // let-statement | [\d]+ ';' | ident ['[' literal ']'] '.' ident '(' [args] ')' ';'
        BVE5Language.Ast.Statement ParseStatement(BVE5RouteFileLexer lexer)
        {
            Token token = lexer.Current;
            BVE5Language.Ast.Expression expr = null;

            switch(token.Kind){
            case TokenKind.IntegerLiteral:
                expr = ParseLiteral(lexer);
                break;

            case TokenKind.Identifier:
                var ident = ParseIdent(lexer);
                expr = (lexer.Current.Literal == "[") ? (Expression)ParseIndexExpr(lexer, ident) : (Expression)ident;

                token = lexer.Current;
                if(token.Literal != "."){
                    AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Expected '.' but got " + token.Literal);
                    if(enable_strict_parsing)
                        return null;
                }
                expr = ParseMemberRef(lexer, expr);
                expr = ParseInvokeExpr(lexer, expr);
                break;

            case TokenKind.KeywordToken:
                return ParseLetStatement(lexer);

            default:
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "A statement must start with a keyword, an integer literal or an identifier.");
                TryRecovery(lexer);
                return null;
            }

            token = lexer.Current;
            if(token.Literal != ";"){
                AddError(ErrorCode.SyntaxError, token.Line, token.Column, "Unexpected character: " + token.Literal);
                if(enable_strict_parsing)
                    return null;
            }else{
                lexer.Advance();
            }

            return AstNode.MakeStatement(expr, expr.StartLocation, token.EndLoc);   //token should be pointing to a semicolon token
        }
 // any-character-except-(-)-.-;-[-]
 Identifier ParseIdent(BVE5RouteFileLexer lexer)
 {
     Token token = lexer.Current;
     Debug.Assert(token.Kind == TokenKind.Identifier, "Really meant an identifier?");
     lexer.Advance();
     return AstNode.MakeIdent(token.Literal, token.StartLoc, lexer.Current.StartLoc);
 }
        void TryRecovery(BVE5RouteFileLexer lexer)
        {
            var start_line = lexer.CurrentLine;
            Token token = lexer.Current;
            while(token.Kind != TokenKind.EOF){
                if(lexer.CurrentLine != start_line)
                    return;

                lexer.Advance();
                token = lexer.Current;
            }
        }
 private Expression ParseArgument(BVE5RouteFileLexer lexer)
 {
     Token token = lexer.Current;
     if(token.Kind == TokenKind.Identifier){
         if(lexer.Peek.Literal != "," || lexer.Peek.Literal != ")")
             return ParsePathLiteral(lexer);
         else
             return ParseIdent(lexer);
     }else if(token.Kind == TokenKind.IntegerLiteral){
         var la = lexer.Peek;
         if(la.Literal == ":")
             return ParseTimeLiteral(lexer);
         else
             return ParseLiteral(lexer);
     }else{
         throw new BVE5ParserException(token.Line, token.Column,
                                       "An argument must be an identifier, a file path, a literal or a time format literal!");
     }
 }