// 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); }