public void SyntaxFactsMethods() { Assert.AreEqual("protected internal", SyntaxFacts.GetText(Accessibility.ProtectedOrInternal)); Assert.AreEqual("??", SyntaxFacts.GetText(SyntaxKind.QuestionQuestionToken)); Assert.AreEqual("this", SyntaxFacts.GetText(SyntaxKind.ThisKeyword)); Assert.AreEqual(SyntaxKind.CharacterLiteralExpression, SyntaxFacts.GetLiteralExpression(SyntaxKind.CharacterLiteralToken)); Assert.AreEqual(SyntaxKind.CoalesceExpression, SyntaxFacts.GetBinaryExpression(SyntaxKind.QuestionQuestionToken)); Assert.AreEqual(SyntaxKind.None, SyntaxFacts.GetBinaryExpression(SyntaxKind.UndefDirectiveTrivia)); Assert.AreEqual(false, SyntaxFacts.IsPunctuation(SyntaxKind.StringLiteralToken)); }
private ExpressionSyntax ParseEquality() { var left = this.ParseLogicalNot(); while (this.CurrentToken.Kind == SyntaxKind.EqualsEqualsToken || this.CurrentToken.Kind == SyntaxKind.ExclamationEqualsToken) { var op = this.EatToken(); var right = this.ParseEquality(); left = SyntaxFactory.BinaryExpression(SyntaxFacts.GetBinaryExpression(op.Kind), left, op, right); } return left; }
private static bool BinaryTokenNeedsSeparator(SyntaxKind kind) { switch (kind) { case SyntaxKind.DotToken: case SyntaxKind.MinusGreaterThanToken: return(false); default: return(SyntaxFacts.GetBinaryExpression(kind) != SyntaxKind.None); } }
public void TestBinaryOperators(SyntaxKind kind) { var text = "(a) " + kind.GetText() + " b"; var expr = ParseExpression(text); Assert.NotNull(expr); var opKind = SyntaxFacts.GetBinaryExpression(kind); Assert.Equal(opKind, expr.Kind); Assert.Equal(text, expr.ToString()); Assert.Equal(0, expr.GetDiagnostics().Count()); var b = (BinaryExpressionSyntax)expr; Assert.NotNull(b.OperatorToken); Assert.Equal(kind, b.OperatorToken.Kind); Assert.NotNull(b.Left); Assert.NotNull(b.Right); Assert.Equal("(a)", b.Left.ToString()); Assert.Equal("b", b.Right.ToString()); }
private ExpressionSyntax ParseSubExpression(Precedence precedence) { ExpressionSyntax leftOperand = null; Precedence newPrecedence = 0; SyntaxKind opKind = SyntaxKind.None; var tk = CurrentToken.Kind; leftOperand = ParseTerm(precedence); while (true) { tk = CurrentToken.Kind; if (IsExpectedBinaryOperator(tk)) { opKind = SyntaxFacts.GetBinaryExpression(tk); } else { break; } newPrecedence = GetPrecedence(opKind); if (newPrecedence <= precedence) { break; } var opToken = EatToken(tk); leftOperand = SyntaxFactory.BinaryExpression(opKind, leftOperand, opToken, ParseSubExpression(newPrecedence)); } return(leftOperand); }
// Priority is the TypeSyntax. It might return TypeSyntax which might be a constant pattern such as enum 'Days.Sunday' // We handle such cases in the binder of is operator. // It is used for parsing patterns in the is operators. private CSharpSyntaxNode ParseTypeOrPattern() { var tk = this.CurrentToken.Kind; CSharpSyntaxNode node = null; switch (tk) { case SyntaxKind.CloseParenToken: case SyntaxKind.CloseBracketToken: case SyntaxKind.CloseBraceToken: case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: // HACK: for error recovery, we prefer a (missing) type. return(this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false)); default: // attempt to disambiguate. break; } // If it is a nameof, skip the 'if' and parse as a constant pattern. if (SyntaxFacts.IsPredefinedType(tk) || (tk == SyntaxKind.IdentifierToken && this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword)) { var resetPoint = this.GetResetPoint(); try { TypeSyntax type = this.ParseTypeCore(parentIsParameter: false, isOrAs: true, expectSizes: false, isArrayCreation: false); tk = this.CurrentToken.ContextualKind; if (!type.IsMissing) { if (this.IsTrueIdentifier()) { var identifier = ParseIdentifierToken(); node = _syntaxFactory.DeclarationPattern(type, identifier); } } if (node == null) { Debug.Assert(Precedence.Shift == Precedence.Relational + 1); if ((IsExpectedBinaryOperator(tk) && GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) > Precedence.Relational) || tk == SyntaxKind.DotToken) // member selection is not formally a binary operator but has higher precedence than relational { this.Reset(ref resetPoint); // We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression // So x is y < z should be parsed as (x is y) < z // But x is y << z should be parsed as x is (y << z) node = _syntaxFactory.ConstantPattern(this.ParseSubExpression(Precedence.Shift)); } // it is a typical "is Type" operator else { // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- // as those are never semantically valid constant expressions for a pattern node = type; } } } finally { this.Release(ref resetPoint); } } else { // In places where a pattern is supported, we do not support tuple types // due to both syntactic and semantic ambiguities between tuple types and positional patterns. // But it still might be a pattern such as (operand is 3) or (operand is nameof(x)) node = _syntaxFactory.ConstantPattern(this.ParseExpressionCore()); } return(node); }
private ExpressionSyntax ParseSubExpression(uint precedence) { if (Current.Kind == SyntaxKind.CompileKeyword) { var compile = Match(SyntaxKind.CompileKeyword); var shaderTarget = Match(SyntaxKind.IdentifierToken); var shaderFunctionName = ParseIdentifier(); var shaderFunction = new FunctionInvocationExpressionSyntax(shaderFunctionName, ParseParenthesizedArgumentList(false)); return(new CompileExpressionSyntax(compile, shaderTarget, shaderFunction)); } ExpressionSyntax leftOperand; SyntaxKind opKind; // No left operand, so we need to parse one -- possibly preceded by a // unary operator. var tk = Current.Kind; if (SyntaxFacts.IsPrefixUnaryExpression(tk)) { opKind = SyntaxFacts.GetPrefixUnaryExpression(tk); leftOperand = ParsePrefixUnaryExpression(opKind); } else { // Not a unary operator - get a primary expression. leftOperand = ParseTerm(); } while (true) { // We either have a binary or assignment or compound operator here, or we're finished. tk = Current.Kind; ExpressionOperatorType operatorType; if (SyntaxFacts.IsBinaryExpression(tk) && (!_greaterThanTokenIsNotOperator || tk != SyntaxKind.GreaterThanToken) && (tk != SyntaxKind.GreaterThanToken || !_allowGreaterThanTokenAroundRhsExpression || Lookahead.Kind != SyntaxKind.SemiToken)) { operatorType = ExpressionOperatorType.BinaryExpression; opKind = SyntaxFacts.GetBinaryExpression(tk); } else if (SyntaxFacts.IsAssignmentExpression(tk)) { operatorType = ExpressionOperatorType.AssignmentExpression; opKind = SyntaxFacts.GetAssignmentExpression(tk); } else if (tk == SyntaxKind.CommaToken && CommaIsSeparatorStack.Peek() == false) { operatorType = ExpressionOperatorType.CompoundExpression; opKind = SyntaxKind.CompoundExpression; } else { break; } var newPrecedence = SyntaxFacts.GetOperatorPrecedence(opKind); Debug.Assert(newPrecedence > 0); // All binary operators must have precedence > 0! // Check the precedence to see if we should "take" this operator if (newPrecedence < precedence) { break; } // Same precedence, but not right-associative -- deal with this "later" if (newPrecedence == precedence && !SyntaxFacts.IsRightAssociative(opKind)) { break; } // Precedence is okay, so we'll "take" this operator. var opToken = NextToken(); SyntaxToken lessThanToken = null; if (operatorType == ExpressionOperatorType.AssignmentExpression && _allowGreaterThanTokenAroundRhsExpression) { lessThanToken = NextTokenIf(SyntaxKind.LessThanToken); } var rightOperand = ParseSubExpression(newPrecedence); SyntaxToken greaterThanToken = null; if (lessThanToken != null) { greaterThanToken = NextTokenIf(SyntaxKind.GreaterThanToken); } switch (operatorType) { case ExpressionOperatorType.BinaryExpression: leftOperand = new BinaryExpressionSyntax(opKind, leftOperand, opToken, rightOperand); break; case ExpressionOperatorType.AssignmentExpression: leftOperand = new AssignmentExpressionSyntax(opKind, leftOperand, opToken, lessThanToken, rightOperand, greaterThanToken); break; case ExpressionOperatorType.CompoundExpression: leftOperand = new CompoundExpressionSyntax(opKind, leftOperand, opToken, rightOperand); break; default: throw new ArgumentOutOfRangeException(); } } var conditionalPrecedence = SyntaxFacts.GetOperatorPrecedence(SyntaxKind.ConditionalExpression); if (tk == SyntaxKind.QuestionToken && precedence <= conditionalPrecedence) { var questionToken = NextToken(); var colonLeft = ParseSubExpression(conditionalPrecedence); var colon = Match(SyntaxKind.ColonToken); var colonRight = ParseSubExpression(conditionalPrecedence); leftOperand = new ConditionalExpressionSyntax(leftOperand, questionToken, colonLeft, colon, colonRight); } return(leftOperand); }
private CSharpSyntaxNode ParseTypeOrPatternForIsOperatorCore() { var tk = this.CurrentToken.Kind; Precedence precedence = GetPrecedence(SyntaxKind.IsPatternExpression); // We will parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression // So x is y < z should be parsed as (x is y) < z // But x is y << z should be parsed as x is (y << z) Debug.Assert(Precedence.Shift == precedence + 1); // For totally broken syntax, parse a type for error recovery purposes switch (tk) { case SyntaxKind.IdentifierToken when this.CurrentToken.ContextualKind == SyntaxKind.UnderscoreToken: // We permit a type named `_` on the right-hand-side of an is operator, but not inside of a pattern. case SyntaxKind.CloseParenToken: case SyntaxKind.CloseBracketToken: case SyntaxKind.CloseBraceToken: case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: // HACK: for error recovery, we prefer a (missing) type. return(this.ParseType(ParseTypeMode.AfterIs)); default: // attempt to disambiguate. break; } // If it starts with 'nameof(', skip the 'if' and parse as a constant pattern. if (LooksLikeTypeOfPattern(tk)) { var resetPoint = this.GetResetPoint(); try { TypeSyntax type = this.ParseType(ParseTypeMode.AfterIs); if (!type.IsMissing) { PatternSyntax p = ParsePatternContinued(type, precedence, whenIsKeyword: false); if (p != null) { return(p); } } tk = this.CurrentToken.ContextualKind; if ((!IsExpectedBinaryOperator(tk) || GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) <= precedence) && // member selection is not formally a binary operator but has higher precedence than relational tk != SyntaxKind.DotToken) { // it is a typical "is Type" operator. // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- // as those are never semantically valid constant expressions for a pattern return(type); } this.Reset(ref resetPoint); } finally { this.Release(ref resetPoint); } } // check to see if it looks like a recursive pattern. if (tk == SyntaxKind.OpenParenToken || tk == SyntaxKind.OpenBraceToken) { var resetPoint = this.GetResetPoint(); try { PatternSyntax p = ParsePatternContinued(type: null, precedence, whenIsKeyword: false); if (p != null) { return(p); } // this can occur when we encounter a misplaced lambda expression. this.Reset(ref resetPoint); } finally { this.Release(ref resetPoint); } } // In places where a pattern is supported, we do not support tuple types // due to both syntactic and semantic ambiguities between tuple types and positional patterns. // But it still might be a pattern such as (operand is 3) or (operand is nameof(x)) return(_syntaxFactory.ConstantPattern(this.ParseSubExpressionCore(precedence))); }
private ExpressionSyntax ParseDirectiveSubExpression(uint precedence) { ExpressionSyntax leftOperand; SyntaxKind opKind; // No left operand, so we need to parse one -- possibly preceded by a // unary operator. var tk = Current.Kind; if (SyntaxFacts.IsPrefixUnaryExpression(tk, true)) { opKind = SyntaxFacts.GetPrefixUnaryExpression(tk); leftOperand = ParseDirectivePrefixUnaryExpression(opKind); } else { // Not a unary operator - get a primary expression. leftOperand = ParseDirectiveTerm(); } while (true) { // We either have a binary operator here, or we're finished. tk = Current.Kind; if (SyntaxFacts.IsBinaryExpression(tk)) { opKind = SyntaxFacts.GetBinaryExpression(tk); } else { break; } var newPrecedence = SyntaxFacts.GetOperatorPrecedence(opKind); Debug.Assert(newPrecedence > 0); // All binary operators must have precedence > 0! // Check the precedence to see if we should "take" this operator if (newPrecedence < precedence) { break; } // Same precedence, but not right-associative -- deal with this "later" if (newPrecedence == precedence && !SyntaxFacts.IsRightAssociative(opKind)) { break; } // Precedence is okay, so we'll "take" this operator. var opToken = NextToken(); var rightOperand = ParseDirectiveSubExpression(newPrecedence); leftOperand = new BinaryExpressionSyntax(opKind, leftOperand, opToken, rightOperand); } var conditionalPrecedence = SyntaxFacts.GetOperatorPrecedence(SyntaxKind.ConditionalExpression); if (tk == SyntaxKind.QuestionToken && precedence <= conditionalPrecedence) { var questionToken = NextToken(); var colonLeft = ParseDirectiveSubExpression(conditionalPrecedence); var colon = Match(SyntaxKind.ColonToken); var colonRight = ParseDirectiveSubExpression(conditionalPrecedence); leftOperand = new ConditionalExpressionSyntax(leftOperand, questionToken, colonLeft, colon, colonRight); } return(leftOperand); }
/// <summary> /// Parses the type, or pattern, right-hand operand of an is expression. /// Priority is the TypeSyntax. It may return a TypeSyntax which turns out in binding to /// be a constant pattern such as enum 'Days.Sunday'. We handle such cases in the binder of the is operator. /// </summary> private CSharpSyntaxNode ParseTypeOrPatternForIsOperator() { var tk = this.CurrentToken.Kind; Precedence precedence = GetPrecedence(SyntaxKind.IsPatternExpression); switch (tk) { case SyntaxKind.CloseParenToken: case SyntaxKind.CloseBracketToken: case SyntaxKind.CloseBraceToken: case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: // HACK: for error recovery, we prefer a (missing) type. return(this.ParseType(ParseTypeMode.AfterIs)); default: // attempt to disambiguate. break; } // If it starts with 'nameof(', skip the 'if' and parse as a constant pattern. if (SyntaxFacts.IsPredefinedType(tk) || (tk == SyntaxKind.IdentifierToken && (this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword || this.PeekToken(1).Kind != SyntaxKind.OpenParenToken))) { var resetPoint = this.GetResetPoint(); try { TypeSyntax type = this.ParseType(ParseTypeMode.AfterIs); if (!type.IsMissing && this.IsTrueIdentifier()) { var designation = ParseSimpleDesignation(); return(_syntaxFactory.DeclarationPattern(type, designation)); } tk = this.CurrentToken.ContextualKind; if ((!IsExpectedBinaryOperator(tk) || GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) <= precedence) && // member selection is not formally a binary operator but has higher precedence than relational tk != SyntaxKind.DotToken) { // it is a typical "is Type" operator. // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- // as those are never semantically valid constant expressions for a pattern return(type); } this.Reset(ref resetPoint); } finally { this.Release(ref resetPoint); } } // We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression // So x is y < z should be parsed as (x is y) < z // But x is y << z should be parsed as x is (y << z) Debug.Assert(Precedence.Shift == precedence + 1); // In places where a pattern is supported, we do not support tuple types // due to both syntactic and semantic ambiguities between tuple types and positional patterns. // But it still might be a pattern such as (operand is 3) or (operand is nameof(x)) return(_syntaxFactory.ConstantPattern(this.ParseSubExpressionCore(precedence))); }