private bool TryParseParameterDeclarations(out ImmutableList <ParameterDeclarationSyntax> parameters) { parameters = ImmutableList <ParameterDeclarationSyntax> .Empty; if (!ExpectToken(TokenType.OpenParen, DiagnosticCode.ExpectedParameterList)) { return(false); } // Read parameters unless the parameter list is empty if (_lexer.PeekTokenType() != TokenType.CloseParen) { while (true) { var paramPosition = _lexer.Position; // Read the type name if (!TryParseType(DiagnosticCode.ExpectedParameterDeclaration, out var paramType)) { return(false); } // Read the parameter name if (!ExpectIdentifier(DiagnosticCode.ExpectedParameterName, out var paramName)) { return(false); } if (!NameParsing.IsValidSimpleName(paramName) || NameParsing.IsReservedTypeName(paramName)) { _diagnosticSink.Add(DiagnosticCode.InvalidVariableName, _lexer.LastPosition, paramName); return(false); } // Add it to the list parameters = parameters.Add(new ParameterDeclarationSyntax(paramType, paramName, paramPosition)); // If the next token is a comma, there is another parameter to parse if (_lexer.PeekTokenType() == TokenType.Comma) { _lexer.GetToken(); } else { break; } } } if (!ExpectToken(TokenType.CloseParen, DiagnosticCode.ExpectedClosingParen)) { return(false); } return(true); }
private bool TryParseType(DiagnosticCode noIdentifierError, [NotNullWhen(true)] out TypeSyntax?type) { type = null; if (!ExpectIdentifier(noIdentifierError, out var typeName)) { return(false); } if (!NameParsing.IsValidFullName(typeName)) { _diagnosticSink.Add(DiagnosticCode.InvalidTypeName, _lexer.LastPosition, typeName); return(false); } type = new TypeNameSyntax(typeName, _lexer.LastPosition); return(true); }
private bool TryParseVariableDeclaration([NotNullWhen(true)] out VariableDeclarationSyntax?declaration) { declaration = null; var startPosition = _lexer.Position; EatAndAssertToken(TokenType.Var); // Following the "var" keyword there is the type name if (!TryParseType(DiagnosticCode.ExpectedType, out var typeSyntax)) { return(false); } // Then there is the variable name, which is a simple name if (_lexer.PeekTokenType() != TokenType.Identifier) { _diagnosticSink.Add(DiagnosticCode.ExpectedIdentifier, _lexer.Position, ReadTokenIntoString()); return(false); } var variableName = ReadTokenIntoString(); if (!NameParsing.IsValidSimpleName(variableName) || NameParsing.IsReservedTypeName(variableName)) { _diagnosticSink.Add(DiagnosticCode.InvalidVariableName, _lexer.LastPosition, variableName); return(false); } // Then there is the initial value (a "=" followed by an expression) if (!ExpectToken(TokenType.Equals, DiagnosticCode.ExpectedInitialValue) || !TryParseExpression(out var initialValue)) { return(false); } Debug.Assert(initialValue != null); // Finally, a semicolon if (!ExpectToken(TokenType.Semicolon, DiagnosticCode.ExpectedSemicolon)) { return(false); } declaration = new VariableDeclarationSyntax(typeSyntax, variableName, initialValue, startPosition); return(true); }
private bool TryReadAndValidateIdentifier([NotNullWhen(true)] out IdentifierSyntax?identifier, bool allowReservedTypeNames) { if (_lexer.PeekTokenType() != TokenType.Identifier) { _diagnosticSink.Add(DiagnosticCode.ExpectedIdentifier, _lexer.Position, ReadTokenIntoString()); identifier = null; return(false); } var token = ReadTokenIntoString(); if (!NameParsing.IsValidFullName(token) || (!allowReservedTypeNames && NameParsing.IsReservedTypeName(token))) { _diagnosticSink.Add(DiagnosticCode.InvalidIdentifier, _lexer.LastPosition, token); identifier = null; return(false); } identifier = new IdentifierSyntax(token, _lexer.LastPosition); return(true); }
private bool TryParseNamespaceDeclaration(out string namespaceName) { // Eat the 'namespace' token EatAndAssertToken(TokenType.Namespace); // Read and validate the namespace name if (!ExpectIdentifier(DiagnosticCode.ExpectedNamespaceName, out namespaceName)) { return(false); } if (!NameParsing.IsValidNamespaceName(namespaceName)) { _diagnosticSink.Add(DiagnosticCode.InvalidNamespaceName, _lexer.LastPosition, namespaceName); return(false); } // Eat the semicolon if (!ExpectToken(TokenType.Semicolon, DiagnosticCode.ExpectedSemicolon)) { return(false); // TODO: Think about error recovery } return(true); }
private SourceFileSyntax?ParseSourceFile() { var namespaceName = string.Empty; var functionListBuilder = ImmutableList <FunctionSyntax> .Empty.ToBuilder(); // Parse source file level items until end-of-file while (!_lexer.PeekToken().IsEmpty) { // First, parse any attributes var attributes = ImmutableList <AttributeSyntax> .Empty; while (_lexer.PeekTokenType() == TokenType.OpenBracket) { if (TryParseAttribute(out var attribute)) { Debug.Assert(attribute != null); attributes = attributes.Add(attribute); } else { return(null); } } // Then read the item var itemPosition = _lexer.Position; if (_lexer.PeekTokenType() == TokenType.Namespace) { // Attributes may not be applied to namespaces if (!attributes.IsEmpty) { _diagnosticSink.Add(DiagnosticCode.AttributesOnlyApplicableToFunctions, itemPosition); return(null); } // Only a single namespace declaration is allowed per file if (namespaceName != string.Empty) { _diagnosticSink.Add(DiagnosticCode.ExpectedOnlyOneNamespace, itemPosition); return(null); } if (!TryParseNamespaceDeclaration(out namespaceName)) { return(null); } continue; } // Other source file items must begin with a visibility modifier var visibility = ParseVisibility(); if (visibility != Visibility.Unknown) { // Namespace must be declared before any definitions if (namespaceName == string.Empty) { _diagnosticSink.Add(DiagnosticCode.ExpectedNamespaceDeclarationBeforeDefinitions, itemPosition); return(null); } // TODO: Class definitions // Parse the function definition: type if (!TryParseType(DiagnosticCode.ExpectedType, out var returnType)) { return(null); // TODO: Recovery } // Name if (!ExpectIdentifier(DiagnosticCode.ExpectedFunctionName, out var functionName)) { return(null); } if (!NameParsing.IsValidSimpleName(functionName)) { _diagnosticSink.Add(DiagnosticCode.InvalidFunctionName, _lexer.LastPosition, functionName); return(null); // TODO: Recovery } // Parameter list if (!TryParseParameterDeclarations(out var parameters)) { return(null); } // Method body; the method may also have no body, as indicated by a semicolon BlockSyntax?methodBody; if (_lexer.PeekTokenType() == TokenType.Semicolon) { _lexer.GetToken(); // Eat the semicolon methodBody = null; } else if (_lexer.PeekTokenType() != TokenType.OpenBrace) { _diagnosticSink.Add(DiagnosticCode.ExpectedMethodBody, _lexer.Position, ReadTokenIntoString()); return(null); } else if (!TryParseBlock(out methodBody)) { return(null); } // Add the parsed function to the syntax tree functionListBuilder.Add(new FunctionSyntax(functionName, returnType, visibility, parameters, attributes, methodBody, itemPosition)); } else { _diagnosticSink.Add(DiagnosticCode.ExpectedSourceFileItem, itemPosition); return(null); } } return(new SourceFileSyntax(namespaceName, _filename, functionListBuilder.ToImmutable())); }