Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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()));
        }