protected override void VisitUnaryExpressionSyntax(UnaryExpressionSyntax pNode) { base.VisitUnaryExpressionSyntax(pNode); switch (pNode.Operator) { case UnaryExpressionOperator.Not: TrySetImplicitCastType(pNode.Value, SmallTypeCache.Boolean); pNode.SetType(SmallTypeCache.Boolean); break; case UnaryExpressionOperator.Length: pNode.SetType(SmallTypeCache.Int); break; case UnaryExpressionOperator.Negative: case UnaryExpressionOperator.PreDecrement: case UnaryExpressionOperator.PreIncrement: case UnaryExpressionOperator.PostDecrement: case UnaryExpressionOperator.PostIncrement: TrySetImplicitCastType(pNode.Value, pNode.Value.Type); pNode.SetType(pNode.Value.Type); break; } }
protected override void VisitUnaryExpressionSyntax(UnaryExpressionSyntax pNode) { switch (pNode.Operator) { case UnaryExpressionOperator.Not: if (!CanCast(pNode.Value.Type, SmallTypeCache.Boolean)) { CompilerErrors.TypeCastError(pNode.Value.Type, SmallTypeCache.Boolean, pNode.Value.Span); } break; case UnaryExpressionOperator.Length: if (!pNode.Value.Type.IsArray) { CompilerErrors.TypeCastError(pNode.Value.Type.ToString(), "array", pNode.Span); } break; case UnaryExpressionOperator.PreDecrement: case UnaryExpressionOperator.PreIncrement: case UnaryExpressionOperator.PostDecrement: case UnaryExpressionOperator.PostIncrement: case UnaryExpressionOperator.Negative: if (!TypeHelper.IsInt(pNode.Value.Type)) { CompilerErrors.TypeCastError(pNode.Value.Type, SmallTypeCache.Int, pNode.Span); } break; } base.VisitUnaryExpressionSyntax(pNode); }
/// <summary> /// Building the expression tree based on precedence of operators /// "High precedence" roughly means "should be calculated first", e.g. * operator VS + operator /// Syntax nodes with a high precedence will be placed first (lower) in the syntax tree, as syntax trees are evaluated from bottom up /// </summary> private ExpressionSyntax ParseExpression(int inParentPrecedence = 0) { ExpressionSyntax left; var unaryOperatorPrecedence = CurrentToken.Kind.GetUnaryOperatorPrecedence(); // If current token is a unary operator (i.e. UnaryOperatorPrecedenc != 0) and it is more than parent precedence, we put it first (i.e. lower) in the syntax tree if (unaryOperatorPrecedence != 0 && unaryOperatorPrecedence >= inParentPrecedence) { var operatorToken = NextToken(); var operandExpression = ParseExpression(unaryOperatorPrecedence); left = new UnaryExpressionSyntax(operatorToken, operandExpression); } else // The left expression must be a primary expression { left = ParsePrimaryExpression(); } // After settling the unary expression, if there exists more tokens after it (i.e. an operator and right expression), we shall parse this "binary expression" while (true) { var precedence = CurrentToken.Kind.GetBinaryOperatorPrecedence(); // Getting the precedence of the current token if (precedence == 0 || precedence < inParentPrecedence) { break; // If precedence = 0 (i.e. no tokens), or if it is less than the parent (not part of current expression, but the next one), we shall break } var operatorToken = NextToken(); // Get current token and proceed to the next one var right = ParseExpression(precedence); // Recursively parse the "right-side" of the binary expression left = new BinaryExpressionSyntax(left, operatorToken, right); // Now with the left, operator, and right sides of the expression, we shall group (or "collapse") them all together as the same "node" (i.e. left) as a BinaryExpressionSyntax } return(left); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var boundOperand = BindExpression(syntax.Operand); var boundOperatorKind = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundOperand.Type); return(new BoundUnaryExpression(boundOperatorKind, boundOperand)); }
private static int EvaluateUnaryExpression(UnaryExpressionSyntax unary) { return(unary.OperatorToken.Kind switch { SyntaxKind.PlusToken => Evaluate(unary.Operand), SyntaxKind.MinusToken => - Evaluate(unary.Operand), _ => throw new InvalidOperationException($"Unexpected operator token '{unary.OperatorToken.Kind}'") });
public override void VisitUnaryExpression(UnaryExpressionSyntax node) { if (!_featureStatisticsBuilder.HasCBooleanOperators && node.OperatorToken.Kind is SyntaxKind.BangToken) { _featureStatisticsBuilder.HasCBooleanOperators = true; } base.VisitUnaryExpression(node); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var boundOperand = BindExpression(syntax.Operand); var boundOperator = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundOperand.Type); if (boundOperator == null) { _diagnostics.ReportUndefinedUnaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, boundOperand.Type); return(boundOperand); } return(new BoundUnaryExpression(boundOperator, boundOperand)); }
public AnnotatedExpression AnnotateUnaryExpression(UnaryExpressionSyntax syntax) { var annotateOperand = AnnotateExpression(syntax.Operand); var annotateOperatorKind = AnnotatedUnaryOperator.Annotate(syntax.OperatorToken.Kind, annotateOperand.Type); if (annotateOperatorKind == null) { _diagnostics.ReportUndefinedUnaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, annotateOperand.Type); return(annotateOperand); } return(new AnnotatedUnaryExpression(annotateOperatorKind, annotateOperand)); }
private static async Task<Document> ChangeToIsNotAsync(Document document, UnaryExpressionSyntax unary, BinaryExpressionSyntax isExpression, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken); var newRoot = root.ReplaceNode( unary, SyntaxFactory.BinaryExpression( SyntaxKind.IsNotExpression, isExpression.Left, SyntaxFactory.Token(SyntaxKind.IsNotKeyword).WithTriviaFrom(isExpression.OperatorToken), isExpression.Right)); return document.WithSyntaxRoot(newRoot); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var boundOperand = BindExpression(syntax.Operand); var boundOperator = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundOperand.Type); if (boundOperator == null) { _diagnostics.Add($"Unary operator '{syntax.OperatorToken.Text}' is not defined for type {boundOperand.Type}"); return(boundOperand); } return(new BoundUnaryExpression(boundOperator, boundOperand)); }
/// <summary> /// Binding a unary expression. Binds operand recursively while evaluating the unary operator kind of each bound expression /// </summary> private BoundExpression BindUnaryExpression(UnaryExpressionSyntax inSyntax) { var boundOperand = BindExpression(inSyntax.OperandExpression); var boundOperatorKind = BindUnaryOperatorKind(inSyntax.OperatorToken.Kind, boundOperand.Type); if (boundOperatorKind == null) { _diagnostics.Add("Unary operator " + inSyntax.OperatorToken.Text + " is not defined for type " + boundOperand.Type); return(boundOperand); } return(new BoundUnaryExpression(boundOperatorKind.Value, boundOperand)); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var boundExpresion = BindExpression(syntax.Expression); var op = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundExpresion.ReturnType); if (op == null) { _diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.UndefinedUnaryOperator, syntax.OperatorToken.Location, syntax.OperatorToken.ValueText, boundExpresion.ReturnType)); return(boundExpresion); } return(new BoundUnaryExpresion(op, boundExpresion)); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var operand = BindExpression(syntax.Operand); var op = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, operand.Type); if (op == null) { Diagnostics.ReportUndefinedUnaryOperator(syntax.OperatorToken, operand.Type); return(operand); } return(new BoundUnaryExpression(op, operand)); }
private static async Task <Document> ChangeToIsNotAsync(Document document, UnaryExpressionSyntax unary, BinaryExpressionSyntax isExpression, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode( unary, SyntaxFactory.BinaryExpression( SyntaxKind.IsNotExpression, isExpression.Left, SyntaxFactory.Token(SyntaxKind.IsNotKeyword).WithTriviaFrom(isExpression.OperatorToken), isExpression.Right)); return(document.WithSyntaxRoot(newRoot)); }
public override SyntaxNode?VisitUnaryExpression(UnaryExpressionSyntax node) { var operand = (ExpressionSyntax)Visit(node.Operand); var operandFlags = GetFlags(operand); return(node.Kind() switch { SyntaxKind.UnaryMinusExpression when HasEFlag(operandFlags, ExpressionFlags.IsNum) => LiteralExpression(SyntaxKind.NumericalLiteralExpression, Literal(-GetValue <double>(operand))), SyntaxKind.LogicalNotExpression when TryConvertToBool(operand, out var value) => LiteralExpression(value ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression), SyntaxKind.BitwiseNotExpression when HasEFlag(operandFlags, ExpressionFlags.IsNum) && TryGetInt64(operand, out var value) && TryConvertToDouble(~value, out var result) => LiteralExpression(SyntaxKind.NumericalLiteralExpression, Literal(result)), _ => node.Update(node.OperatorToken, operand), });
private Value ConvertUnaryExpressionToValue( BlockBuilder currentBlock, UnaryExpressionSyntax expression) { switch (expression.Operator) { case UnaryOperator.Not: var operand = ConvertToOperand(currentBlock, expression.Operand); return(new UnaryOperation(expression.Operator, operand, expression.Span)); case UnaryOperator.Plus: // This is a no-op return(ConvertToValue(currentBlock, expression.Operand)); default: throw NonExhaustiveMatchException.ForEnum(expression.Operator); } }
private void OutputUnaryExpression(UnaryExpressionSyntax node, string prefix) { bool expressionIsBinary = node.Expression is BinaryExpressionSyntax; builder.AddFragment(new OutputFragment(prefix, DefaultColour)); builder.AddFragment(new OutputFragment(node.OpToken.ToString(), DefaultColour)); if (expressionIsBinary) { builder.AddFragment(new OutputFragment("(", DefaultColour)); } Output(node.Expression, string.Empty); if (expressionIsBinary) { builder.AddFragment(new OutputFragment(")", DefaultColour)); } }
public void Render(UnaryExpressionSyntax unaryExpression) { Render(unaryExpression.UnaryOperatorNode); Render(unaryExpression.ExpressionNode); }
public virtual TResult VisitUnaryExpression(UnaryExpressionSyntax node) { return(DefaultVisit(node)); }
private BoundExpression BindUnaryExpression(UnaryExpressionSyntax node) { var operatorKind = node.Kind.ToUnaryOperatorKind(); return(BindUnaryExpression(node.Span, operatorKind, node.Expression)); }
public virtual void VisitUnaryExpression(UnaryExpressionSyntax node) => this.DefaultVisit(node);
public virtual TResult Visit(UnaryExpressionSyntax syntax) => default(TResult);
protected override SyntaxNode VisitForSyntax(ForSyntax pNode) { //Rewrite for statements with iterator arrays to normal for statements if (pNode.Iterator != null) { var i = SyntaxFactory.Identifier("!i"); i.SetType(SmallTypeCache.Int); var postOp = pNode.Reverse ? UnaryExpressionOperator.PostDecrement : UnaryExpressionOperator.PostIncrement; UnaryExpressionSyntax finalizer = SyntaxFactory.UnaryExpression(i, postOp); SyntaxNode end = null; DeclarationSyntax decl = null; //Save itvar in case we are looping in a case body var rw = _rewrite; var it = _itVar; //Declare our iterator outside the for loop //This will help if our iterator is complex like a function call var iterVar = SyntaxFactory.Identifier("!iter"); iterVar.SetType(pNode.Iterator.Type); var iterDecl = SyntaxFactory.SingleDeclaration(iterVar, pNode.Iterator); if (pNode.Iterator.Type.IsArray) { //We are iterating over an array //Reverse loops will start at Array.Length and decrement to 0 //Normal loops will start at 0 and increment to Array.Length if (pNode.Reverse) { var length = SyntaxFactory.UnaryExpression(iterVar, UnaryExpressionOperator.Length); length.SetType(SmallTypeCache.Int); decl = SyntaxFactory.SingleDeclaration(i, SyntaxFactory.BinaryExpression(length, BinaryExpressionOperator.Subtraction, SyntaxFactory.NumericLiteral(1))); end = SyntaxFactory.NumericLiteral(0); } else { decl = SyntaxFactory.SingleDeclaration(i, SyntaxFactory.NumericLiteral(0)); end = SyntaxFactory.UnaryExpression(iterVar, UnaryExpressionOperator.Length); ((UnaryExpressionSyntax)end).SetType(SmallTypeCache.Int); } _itVar = SyntaxFactory.ArrayAccess(iterVar, i); ((ArrayAccessSyntax)_itVar).SetType(iterVar.Type); } else if (pNode.Iterator.Type.IsAssignableFrom(_enumerable)) { //We are iterating over an enumerable //Reverse loops will start at Count and decrement to 0 //Normal loops will start at 0 and increment to Count if (pNode.Reverse) { var count = SyntaxFactory.MemberAccess(iterVar, SyntaxFactory.Identifier("Count")); decl = SyntaxFactory.SingleDeclaration(i, SyntaxFactory.BinaryExpression(count, BinaryExpressionOperator.Subtraction, SyntaxFactory.NumericLiteral(1))); end = SyntaxFactory.NumericLiteral(0); } else { decl = SyntaxFactory.SingleDeclaration(i, SyntaxFactory.NumericLiteral(0)); end = SyntaxFactory.MemberAccess(iterVar, SyntaxFactory.Identifier("Count")); } _itVar = SyntaxFactory.MemberAccess(iterVar, SyntaxFactory.MethodCall("ItemAt", new List <SyntaxNode>() { SyntaxFactory.Identifier("!i") })); } else { //Some bad type. We can't rewrite if it isn't array or enumerable return(base.VisitForSyntax(pNode)); } var op = pNode.Reverse ? BinaryExpressionOperator.GreaterThanOrEqual : BinaryExpressionOperator.LessThan; var condition = SyntaxFactory.BinaryExpression(i, op, end); condition.SetType(SmallTypeCache.Boolean); var body = (BlockSyntax)Visit(pNode.Body); var forStatement = SyntaxFactory.For(new List <DeclarationSyntax>() { decl }, condition, new List <SyntaxNode>() { finalizer }, body); //Restore our it for any other nested loops _itVar = it; _rewrite = rw; //Return our iterator declaration and for rewrite return(SyntaxFactory.Block(new List <SyntaxNode>() { iterDecl, forStatement })); } return(base.VisitForSyntax(pNode)); }
public virtual void VisitUnaryExpression(UnaryExpressionSyntax unaryExpression, A args) { VisitExpression(unaryExpression.Operand, args); }
private static bool TryCompileUnary(UnaryExpressionSyntax expression, in Temporary innerValue,
private DataType InferUnaryExpressionType(UnaryExpressionSyntax unaryExpression) { var operandType = InferExpressionType(unaryExpression.Operand); var @operator = unaryExpression.Operator; // If either is unknown, then we can't know whether there is a a problem // (technically not true, for example, we could know that one arg should // be a bool and isn't) if (operandType == DataType.Unknown) { return(unaryExpression.Type = DataType.Unknown); } bool typeError; switch (@operator) { case UnaryOperator.Not: typeError = operandType != DataType.Bool; unaryExpression.Type = DataType.Bool; break; case UnaryOperator.At: typeError = false; // TODO check that the expression can have a pointer taken if (operandType is Metatype) { unaryExpression.Type = DataType.Type; // constructing a type } else { unaryExpression.Type = new PointerType(operandType); // taking the address of something } break; case UnaryOperator.Question: typeError = false; // TODO check that the expression can have a pointer taken unaryExpression.Type = new PointerType(operandType); break; case UnaryOperator.Caret: switch (operandType) { case PointerType pointerType: unaryExpression.Type = pointerType.Referent; typeError = false; break; default: unaryExpression.Type = DataType.Unknown; typeError = true; break; } break; case UnaryOperator.Minus: switch (operandType) { case IntegerConstantType integerType: typeError = false; unaryExpression.Type = integerType; break; case SizedIntegerType sizedIntegerType: typeError = false; unaryExpression.Type = sizedIntegerType; break; default: unaryExpression.Type = DataType.Unknown; typeError = true; break; } break; default: throw NonExhaustiveMatchException.ForEnum(@operator); } if (typeError) { diagnostics.Add(TypeError.OperatorCannotBeAppliedToOperandOfType(file, unaryExpression.Span, @operator, operandType)); } return(unaryExpression.Type); }
protected virtual void VisitUnaryExpressionSyntax(UnaryExpressionSyntax pNode) { Visit(pNode.Value); }
protected virtual SyntaxNode VisitUnaryExpressionSyntax(UnaryExpressionSyntax pNode) { return(SyntaxFactory.UnaryExpression(Visit(pNode.Value), pNode.Operator)); }
/// <summary> /// For expressions, we switch to a precedence climbing parser. /// </summary> public ExpressionSyntax ParseExpression(OperatorPrecedence minPrecedence) { var expression = ParseAtom(); for (; ;) { IOperatorToken @operator = null; OperatorPrecedence?precedence = null; var leftAssociative = true; switch (Tokens.Current) { case IEqualsToken _: case IPlusEqualsToken _: case IMinusEqualsToken _: case IAsteriskEqualsToken _: case ISlashEqualsToken _: if (minPrecedence <= OperatorPrecedence.Assignment) { precedence = OperatorPrecedence.Assignment; leftAssociative = false; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IQuestionQuestionToken _: if (minPrecedence <= OperatorPrecedence.Coalesce) { precedence = OperatorPrecedence.Coalesce; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IOrKeywordToken _: if (minPrecedence <= OperatorPrecedence.LogicalOr) { precedence = OperatorPrecedence.LogicalOr; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IAndKeywordToken _: if (minPrecedence <= OperatorPrecedence.LogicalAnd) { precedence = OperatorPrecedence.LogicalAnd; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IEqualsEqualsToken _: case INotEqualToken _: if (minPrecedence <= OperatorPrecedence.Equality) { precedence = OperatorPrecedence.Equality; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case ILessThanToken _: case ILessThanOrEqualToken _: case IGreaterThanToken _: case IGreaterThanOrEqualToken _: case ILessThanColonToken _: // Subtype operator case IAsKeywordToken _: if (minPrecedence <= OperatorPrecedence.Relational) { precedence = OperatorPrecedence.Relational; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IColonToken _: // type kind if (minPrecedence <= OperatorPrecedence.Relational) { var colon = Tokens.Expect <IColonToken>(); TypeKind typeKind; switch (Tokens.Current) { case IClassKeywordToken _: typeKind = TypeKind.Class; break; case IStructKeywordToken _: typeKind = TypeKind.Struct; break; default: Tokens.Expect <ITypeKindKeywordToken>(); // We saw a colon without what we expected after, just assume it is missing continue; } var typeKindSpan = Tokens.Expect <ITypeKindKeywordToken>(); var span = TextSpan.Covering(colon, typeKindSpan); expression = new TypeKindExpressionSyntax(span, typeKind); continue; } break; case IDotDotToken _: case ILessThanDotDotToken _: case IDotDotLessThanToken _: case ILessThanDotDotLessThanToken _: if (minPrecedence <= OperatorPrecedence.Range) { precedence = OperatorPrecedence.Range; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IPlusToken _: case IMinusToken _: if (minPrecedence <= OperatorPrecedence.Additive) { precedence = OperatorPrecedence.Additive; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IAsteriskToken _: case ISlashToken _: if (minPrecedence <= OperatorPrecedence.Multiplicative) { precedence = OperatorPrecedence.Multiplicative; @operator = Tokens.RequiredToken <IOperatorToken>(); } break; case IDollarToken _: if (minPrecedence <= OperatorPrecedence.Lifetime) { Tokens.Expect <IDollarToken>(); var(nameSpan, lifetime) = ParseLifetimeName(); expression = new ReferenceLifetimeSyntax(expression, nameSpan, lifetime); continue; } break; case IQuestionToken _: if (minPrecedence <= OperatorPrecedence.Unary) { var question = Tokens.Required <IQuestionToken>(); var span = TextSpan.Covering(expression.Span, question); expression = new UnaryExpressionSyntax(span, UnaryOperatorFixity.Postfix, UnaryOperator.Question, expression); continue; } break; case IOpenParenToken _: if (minPrecedence <= OperatorPrecedence.Primary) { var callee = expression; Tokens.Expect <IOpenParenToken>(); var arguments = ParseArguments(); var closeParenSpan = Tokens.Expect <ICloseParenToken>(); var span = TextSpan.Covering(callee.Span, closeParenSpan); expression = new InvocationSyntax(span, callee, arguments); continue; } break; case IDotToken _: case ICaretDotToken _: case IQuestionDotToken _: if (minPrecedence <= OperatorPrecedence.Primary) { // Member Access var accessOperator = BuildAccessOperator(Tokens.RequiredToken <IAccessOperatorToken>()); var member = ParseSimpleName(); var span = TextSpan.Covering(expression.Span, member.Span); expression = new MemberAccessExpressionSyntax(span, expression, accessOperator, member); continue; } break; default: return(expression); } if (@operator is IOperatorToken operatorToken && precedence is OperatorPrecedence operatorPrecedence) { if (leftAssociative) { operatorPrecedence += 1; } var rightOperand = ParseExpression(operatorPrecedence); expression = BuildOperatorExpression(expression, operatorToken, rightOperand); }
private bool TryParseFactor([NotNullWhen(true)] out ExpressionSyntax?expressionSyntax) { // Factor := Identifier | Number | Boolean literal | ( Expression ) | -Factor | !Factor | ~Factor expressionSyntax = null; switch (_lexer.PeekTokenType()) { case TokenType.Minus: return(ParseUnary(UnaryOperation.Minus, out expressionSyntax)); case TokenType.Exclamation: return(ParseUnary(UnaryOperation.Negation, out expressionSyntax)); case TokenType.Tilde: return(ParseUnary(UnaryOperation.Complement, out expressionSyntax)); case TokenType.OpenParen: // Eat the '(' _lexer.GetToken(); // Parse the expression. // The inner expression is returned as is, with no enclosing 'parens' node. // The modified precedence/associativity is already reflected in the syntax tree. if (!TryParseExpression(out expressionSyntax)) { return(false); } // Eat the ')' if (!ExpectToken(TokenType.CloseParen, DiagnosticCode.ExpectedClosingParen)) { return(false); } return(true); case TokenType.False: // Eat the token _lexer.GetToken(); expressionSyntax = new BooleanLiteralSyntax(false, _lexer.LastPosition); return(true); case TokenType.True: // Eat the token _lexer.GetToken(); expressionSyntax = new BooleanLiteralSyntax(true, _lexer.LastPosition); return(true); case TokenType.StringLiteral: return(TryParseStringLiteral(out expressionSyntax)); case TokenType.Identifier: if (!TryReadAndValidateIdentifier(out var identifier, allowReservedTypeNames: false)) { return(false); } // This may be either a function call or a variable reference if (_lexer.PeekTokenType() == TokenType.OpenParen) { // Function call if (!TryParseFunctionCall(identifier, out var callSyntax)) { return(false); } else { Debug.Assert(callSyntax != null); expressionSyntax = callSyntax; return(true); } } else { // Variable reference expressionSyntax = identifier; return(true); } default: return(TryParseNumber(out expressionSyntax)); } // Local helper method for sharing code between the various unary paths bool ParseUnary(UnaryOperation op, out ExpressionSyntax?syntax) { // Eat the operator var opPosition = _lexer.Position; _lexer.GetToken(); // Recurse into the inner expression, which is a factor too if (TryParseFactor(out var innerFactor)) { Debug.Assert(innerFactor != null); syntax = new UnaryExpressionSyntax(op, innerFactor, opPosition); return(true); } else { syntax = null; return(false); } } }