/// <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);
        }
Exemple #3
0
        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);
                }