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!"); } }