Exemplo n.º 1
0
        /// <summary>
        /// Build abstract syntax tree from tokenized string
        /// using extended shunting yard algorithm
        /// </summary>
        public Tree <AbstractToken> Convert(string[] tokens)
        {
            if (tokens == default ||
                !tokens.Any())
            {
                return(new Tree <AbstractToken>());
            }

            var stack  = new Stack <Operator>(tokens.Length);
            var forest = new List <Tree <AbstractToken> >();

            for (var i = 0; i < tokens.Length; i++)
            {
                var token = new StringToken(tokens, i);

                if (grammarService.IsOperand(token.Current))
                {
                    var operand = new Operand(token.Current);

                    var tree = new Tree <AbstractToken>(operand);

                    forest.Add(tree);

                    continue;
                }

                if (grammarService.IsOperator(token.Current))
                {
                    var possibleOperators = grammarService.GetPossibleOperators(token.Current);

                    var @operator = operatorService.Resolve(token, possibleOperators);

                    // check brackets
                    if (@operator == grammarService.OpenPriorityBracket)
                    {
                        stack.Push(@operator);

                        continue;
                    }

                    if (@operator == grammarService.ClosePriorityBracket)
                    {
                        while (stack.Count >= 0)
                        {
                            if (stack.Count == 0)
                            {
                                throw new InvalidOperationException();
                            }

                            var abstractToken = stack.Pop();

                            if (grammarService.OpenPriorityBracket == abstractToken)
                            {
                                break;
                            }

                            MergeTreesByOperator(forest, abstractToken);
                        }

                        continue;
                    }

                    // if we're here, the operator is not any type of bracket, so it's an evaluatable operator
                    switch (@operator.Arity)
                    {
                    case Arity.Unary when @operator.Fixity == Fixity.Postfix:
                    {
                        MergeTreesByOperator(forest, @operator);

                        break;
                    }

                    case Arity.Unary when @operator.Fixity == Fixity.Prefix:
                    {
                        stack.Push(@operator);

                        break;
                    }

                    case Arity.Binary:
                    {
                        if (stack.Count == 0)
                        {
                            stack.Push(@operator);

                            break;
                        }

                        switch (@operator.Associativity)
                        {
                        case Associativity.Left:
                        {
                            var topOperator = stack.Peek();

                            if (topOperator.Priority <= @operator.Priority &&
                                stack.Count > 0)
                            {
                                topOperator = stack.Pop();
                            }

                            while (topOperator.Priority <= @operator.Priority)
                            {
                                MergeTreesByOperator(forest, topOperator);

                                if (stack.Count == 0)
                                {
                                    break;
                                }

                                topOperator = stack.Peek();

                                if (topOperator.Priority <= @operator.Priority &&
                                    stack.Count > 0)
                                {
                                    topOperator = stack.Pop();
                                }
                            }

                            stack.Push(@operator);

                            break;
                        }

                        case Associativity.Right:
                        {
                            var topOperator = stack.Peek();

                            if (topOperator.Priority <= @operator.Priority &&
                                stack.Count > 0)
                            {
                                MergeTreesByOperator(forest, stack.Pop());
                            }

                            stack.Push(@operator);

                            break;
                        }

                        default:
                            throw new ArgumentOutOfRangeException();
                        }

                        break;
                    }

                    case Arity.Multiarity:
                    {
                        // right operand of multiarity operator should be wrapped in priority brackets
                        // like a in (1, 2)
                        //           ^----^
                        // there's a way to avoid it: https://blog.kallisti.net.nz/2008/02/extension-to-the-shunting-yard-algorithm-to-allow-variable-numbers-of-arguments-to-functions/
                        // [email protected] has a copy of the article

                        var nextOperators = grammarService.GetPossibleOperators(token.Next);

                        var isOpenPriorityBracket = nextOperators.Length == 1 &&
                                                    nextOperators.First().OperatorType ==
                                                    OperatorType.OpenPriorityBracket;

                        if (!isOpenPriorityBracket)
                        {
                            throw new
                                  InvalidOperationException($"The {nameof(OperatorType.InRange)} operator should be followed by {nameof(OperatorType.OpenPriorityBracket)}");
                        }

                        stack.Push(@operator);

                        break;
                    }

                    case Arity.Nulary:
                        throw new
                              NotSupportedException($"Ast service doesn't support {nameof(Arity.Nulary)} operator {@operator} implicitly");

                    case Arity.Ternary:
                        throw new
                              NotSupportedException($"Ast service doesn't support {nameof(Arity.Ternary)} operator {@operator} implicitly");

                    case Arity.Kvatery:
                        throw new
                              NotSupportedException($"Ast service doesn't support {nameof(Arity.Kvatery)} operator {@operator} implicitly");

                    default:
                    {
                        throw new ArgumentOutOfRangeException();
                    }
                    }
                }
            }

            while (stack.Count > 0)
            {
                MergeTreesByOperator(forest, stack.Pop());
            }

            if (forest.Count != 1)
            {
                throw new
                      InvalidOperationException("The forest is broken. That means that some operators is unable to find required operands");
            }

            return(forest[0]);
        }
Exemplo n.º 2
0
        private bool IsInfixBinary(StringToken token, Operator[] possibleOperators)
        {
            if (!possibleOperators.Any(x => x.Fixity == Fixity.Infix && x.Arity == Arity.Binary))
            {
                return(false);
            }

            var isPrecededByCloseBracket = grammarService.ClosePriorityBracket.Denotations.Contains(token.Previous);
            var isFollowedByOpenBracket  = grammarService.OpenPriorityBracket.Denotations.Contains(token.Next);

            // like 5 + 2
            //        ^
            var isInfixBinaryCase1 = grammarService.IsOperand(token.Previous) && grammarService.IsOperand(token.Next);

            // like 5 + (2 + 3)
            //        ^
            var isInfixBinaryCase2 = grammarService.IsOperand(token.Previous) &&
                                     isFollowedByOpenBracket;

            // like (2 + 3) + 5
            //              ^
            var isInfixBinaryCase3 = isPrecededByCloseBracket &&
                                     grammarService.IsOperand(token.Next);

            // like 5! + 3
            //         ^
            var isInfixBinaryCase4 = grammarService.IsOperator(token.Previous) &&
                                     grammarService.IsOperand(token.Next) &&
                                     grammarService.IsUniqueByFixity(token.Previous, Fixity.Postfix);

            // like (1 + 2) + (3 + 4)
            //              ^
            var isInfixBinaryCase5 = isPrecededByCloseBracket &&
                                     grammarService.IsOperator(token.Current) &&
                                     isFollowedByOpenBracket;

            return(isInfixBinaryCase1 ||
                   isInfixBinaryCase2 ||
                   isInfixBinaryCase3 ||
                   isInfixBinaryCase4 ||
                   isInfixBinaryCase5);
        }