/// <summary> /// Retrieval of key properties regarding a specific operator /// </summary> /// <param name="operatorToken">The operator token.</param> /// <returns>A locally used object containing the operator properties.</returns> private static OperatorInfo Properties(this IOperatorToken operatorToken) { switch (operatorToken.Type) { case Operator.UnaryPlus: case Operator.UnaryMinus: return(new OperatorInfo { OperatorAssociativity = MathParser.Associativity.Right2Left, OperatorPrecedence = MathParser.Precedence.Unary }); case Operator.Multiplication: case Operator.Division: return(new OperatorInfo { OperatorAssociativity = MathParser.Associativity.Left2Right, OperatorPrecedence = MathParser.Precedence.Multiplicative }); default: return(new OperatorInfo { OperatorAssociativity = MathParser.Associativity.Left2Right, OperatorPrecedence = MathParser.Precedence.Additive }); } }
/// <summary> /// Asserts if the type of the token is as expected /// </summary> /// <param name="expectedType">The expected operator type</param> /// <param name="operatorToken">The operator token containing the actual type value</param> private static void AssertTypeOfOperator(Operator expectedType, IOperatorToken operatorToken) { if (null == operatorToken) { throw new Exception("no operator token was provided"); } Assert.Equal(expectedType, operatorToken.Type); }
string PrintOperator(IOperatorToken opToken) { if (opToken == null) { return(null); } var left = opToken.Left?.Accept(this); var right = opToken.IsUnary ? null : opToken.Right?.Accept(this); var rightIsNull = right == null; if (left == null) { left = "{}"; } if (right == null) { right = "{}"; } switch (opToken.Operator) { case OperatorType.Grouping: return($"{{\\left({left}\\right)}}"); case OperatorType.Add: return($"{left}+{right}"); case OperatorType.Subtract: return($"{left}-{right}"); case OperatorType.Multiply: return($"{left}\\times{right}"); case OperatorType.Divide: return($"\\frac{left}{right}"); case OperatorType.Power: return($"{left}^{right}"); case OperatorType.Root: return(rightIsNull || right == "{2}" ? $"\\sqrt{left}" : $"\\sqrt[{right}]{left}"); case OperatorType.Modulo: return($"{left}\\text{{ mod }}{right}"); case OperatorType.LeftShift: return($"{left}\\ll{right}"); case OperatorType.RightShift: return($"{left}\\gg{right}"); case OperatorType.And: return($"{left}\\land{right}"); case OperatorType.Or: return($"{left}\\lor{right}"); case OperatorType.Xor: return($"{left}\\oplus{right}"); default: return(opToken.IsUnary ? $"?{left}" : $"{left}?{right}"); } }
string PrintOperator(IOperatorToken opToken) { if (opToken == null) { return(null); } var left = opToken.Left?.Accept(this); var right = opToken.IsUnary ? null : opToken.Right?.Accept(this); switch (opToken.Operator) { case OperatorType.Grouping: switch (_fixType) { case FixType.Prefix: case FixType.Postfix: return(left); case FixType.Infix: return($"( {left}) "); default: throw new UnrecognisedTokenException($"Unrecognised fix type {_fixType}"); } default: var enumDesc = opToken.Operator.GetEnumDescription(); switch (_fixType) { case FixType.Prefix: return($"{enumDesc} {left}{right}"); case FixType.Infix: return(opToken.IsUnary ? $"{enumDesc} {left}" : $"{left}{enumDesc} {right}"); case FixType.Postfix: return($"{left}{right}{enumDesc} "); default: throw new UnrecognisedTokenException($"Unrecognised fix type {_fixType}"); } } }
/// <summary> /// Determines the Associativity of a specific operator /// </summary> /// <param name="operatorToken">The operator token.</param> /// <returns>The associativity of the operator.</returns> public static Associativity Associativity(this IOperatorToken operatorToken) { return(operatorToken.Properties().OperatorAssociativity); }
/// <summary> /// Determines the Precedence of a specific operator /// </summary> /// <param name="operatorToken">The operator token.</param> /// <returns>The precedence of the operator.</returns> public static Precedence Precedence(this IOperatorToken operatorToken) { return(operatorToken.Properties().OperatorPrecedence); }
/// <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); }