コード例 #1
0
        public Evaluation EvaluateOperator(
            ExpressionOptions options, ValueToken op, Token?left, Token?right)
        {
            var validationError = ValidateOperatorTokens(options, op, left, right);

            if (validationError != null)
            {
                return(validationError);
            }

            var leftEval = left != null?Evaluate(options, left) : null;

            if (leftEval?.Error != null)
            {
                return(leftEval);
            }

            var rightEval = right != null?Evaluate(options, right) : null;

            if (rightEval?.Error != null)
            {
                return(rightEval);
            }

            return(ExecuteOperator.Invoke(op.Value, leftEval?.Result, rightEval?.Result));
        }
コード例 #2
0
        public static ResultCode AssignParents(ExpressionOptions options, List <Token> root)
        {
            var stack = new Stack <CollectionToken>();

            // Tokens in the root list have 'null' parent.
            foreach (var token in root)
            {
                if (token is CollectionToken collection)
                {
                    stack.Push(collection);
                }
            }

            while (stack.Count > 0)
            {
                var parent = stack.Pop();
                foreach (var token in parent)
                {
                    token.Parent = parent;

                    if (token is CollectionToken collection)
                    {
                        stack.Push(collection);
                    }
                }
            }

            return(ResultCode.Ok);
        }
コード例 #3
0
        private Evaluation ListToCollection(ExpressionOptions options, List <Token> tokens)
        {
            int valueCount = 0;

            for (int i = 0; i < tokens.Count; i++)
            {
                if (tokens[i].Type != TokenType.ListSeparator)
                {
                    valueCount++;
                }
            }

            var evaluations = new object?[valueCount];
            int evalIndex   = 0;

            for (int i = 0; i < tokens.Count; i++)
            {
                if (tokens[i].Type == TokenType.ListSeparator)
                {
                    continue;
                }

                var eval = Evaluate(options, tokens[i]);
                if (eval.Error != null)
                {
                    return(eval);
                }

                evaluations[evalIndex++] = eval.Result;
            }
            return(new Evaluation(evaluations));
        }
コード例 #4
0
        public static ResultCode Parse(ExpressionOptions options, List <Token> tokens)
        {
            if (tokens == null)
            {
                throw new ArgumentNullException(nameof(tokens));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (tokens.Count == 0)
            {
                return(ResultCode.NoTokens);
            }

            // TODO: check for valid placement of list separators

            ResultCode code;

            if ((code = MakeLists(tokens)) != ResultCode.Ok ||
                (code = MakeFunctions(options, tokens)) != ResultCode.Ok ||
                (code = MakeImplicitMultiplications(options, tokens)) != ResultCode.Ok ||
                (code = MakeOperatorGroups(options, tokens)) != ResultCode.Ok ||
                (code = AssignParents(options, tokens)) != ResultCode.Ok)
            {
                return(code);
            }
            return(code);
        }
コード例 #5
0
        private static ResultCode ReduceLists(ExpressionOptions options, List <Token> tokens)
        {
            var listStack = new Stack <List <Token> >();

            listStack.Push(tokens);

            while (listStack.Count > 0)
            {
                var currentTokens = listStack.Pop();
                for (int i = 0; i < currentTokens.Count; i++)
                {
                    var token = currentTokens[i];
                    if (token.Type == TokenType.List)
                    {
                        var listToken = (ListToken)token;
                        if (listToken.Count == 1)
                        {
                            var listChild = listToken[0];
                            currentTokens[i] = listChild;
                            i--;
                        }
                        listStack.Push(listToken.Children);
                    }
                }
            }
            return(ResultCode.Ok);
        }
コード例 #6
0
        private bool ValidateOperatorList(
            ExpressionOptions options, List <Token> tokens,
            [MaybeNullWhen(false)] out ValueToken opToken,
            out Token?leftToken,
            out Token?rightToken)
        {
            if (tokens.Count == 2)
            {
                for (int opIndex = 0; opIndex < tokens.Count; opIndex++)
                {
                    var token = tokens[opIndex];
                    if (token.Type != TokenType.Operator)
                    {
                        continue;
                    }

                    var opDefinitions = options.OpDefinitions.Span;
                    for (int j = 0; j < opDefinitions.Length; j++)
                    {
                        var opDef = opDefinitions[j];
                        if (opDef.Associativity == OperatorSidedness.Both)
                        {
                            continue;
                        }

                        opToken = (ValueToken)token;
                        for (int k = 0; k < opDef.Names.Length; k++)
                        {
                            var name = opDef.Names[k];
                            if (!name.Span.SequenceEqual(opToken.Value))
                            {
                                continue;
                            }

                            leftToken  = opIndex == 0 ? null : tokens[0];
                            rightToken = opIndex == 1 ? null : tokens[1];
                            return(true);
                        }
                    }
                    break;
                }
            }

            if (tokens.Count == 3 &&
                tokens[1] is ValueToken middleToken &&
                middleToken.Type == TokenType.Operator)
            {
                opToken    = middleToken;
                leftToken  = tokens[0];
                rightToken = tokens[2];
                return(true);
            }

            opToken    = null;
            leftToken  = null;
            rightToken = null;
            return(false);
        }
コード例 #7
0
        private static ResultCode MakeImplicitMultiplications(
            ExpressionOptions options, List <Token> tokens)
        {
            for (int i = 0; i < tokens.Count; i++)
            {
                var token = tokens[i];
                if (token is CollectionToken collectionToken)
                {
                    var code = MakeImplicitMultiplications(options, collectionToken.Children);
                    if (code != ResultCode.Ok)
                    {
                        return(code);
                    }
                }
                else if (token.Type != TokenType.Name)
                {
                    // Skip as this type is not allowed to have an implicit factor prefix.
                    continue;
                }

                if (i - 1 < 0)
                {
                    continue; // We are at the list's beginning.
                }
                var leftToken = tokens[i - 1];
                if (leftToken.Type == TokenType.Operator ||
                    leftToken.Type == TokenType.Name ||
                    leftToken.Type == TokenType.ListSeparator)
                {
                    continue;
                }

                if (leftToken.Type != TokenType.DecimalDigit &&
                    leftToken.Type != TokenType.DecimalNumber &&
                    leftToken.Type != TokenType.List)
                {
                    return(ResultCode.InvalidTokenBeforeList);
                }

                var multiplyOpDef = options.GetOperatorDefinition(OperatorType.Multiply);
                if (multiplyOpDef == null)
                {
                    return(ResultCode.MissingMultiplicationDefinition);
                }

                var opToken = new ValueToken(TokenType.Operator, multiplyOpDef.Names[0]);
                tokens.Insert(i, opToken);
            }
            return(ResultCode.Ok);
        }
コード例 #8
0
        public Evaluation EvaluateList(ExpressionOptions options, List <Token> tokens)
        {
            if (tokens.Count == 0)
            {
                return(new Evaluation(EErrorCode.Empty));
            }

            if (tokens.Count == 1)
            {
                return(Evaluate(options, tokens[0]));
            }

            if (ValidateOperatorList(
                    options, tokens,
                    out var opToken, out var leftToken, out var rightToken))
            {
                return(EvaluateOperator(options, opToken, leftToken, rightToken));
            }

            return(ListToCollection(options, tokens));
        }
コード例 #9
0
        static ExpressionOptions()
        {
            var opDefs = new Dictionary <OperatorType, OperatorDefinition>();

            void AddOpDef(int precedence, OperatorType type, OperatorSidedness sidedness, params char[] names)
            {
                var opDef = new OperatorDefinition(precedence, type, sidedness, names);

                opDefs.Add(type, opDef);
            }

            AddOpDef(0, OperatorType.Add, OperatorSidedness.OptionalLeft, '+');
            AddOpDef(0, OperatorType.Subtract, OperatorSidedness.OptionalLeft, '-', '–');
            AddOpDef(1, OperatorType.Multiply, OperatorSidedness.Both, '*', '×');
            AddOpDef(1, OperatorType.Divide, OperatorSidedness.Both, '/', ':', '÷');
            AddOpDef(1, OperatorType.Modulo, OperatorSidedness.Both, '%');
            AddOpDef(2, OperatorType.Exponent, OperatorSidedness.Both, '^');
            AddOpDef(2, OperatorType.Factorial, OperatorSidedness.Left, '!');

            var opInverses = new List <OperatorInverse>();

            void AddOpInverse(OperatorType source, OperatorType inverse)
            {
                var sourceOp  = opDefs[source];
                var inverseOp = opDefs[inverse];

                opInverses.Add(new OperatorInverse(sourceOp, inverseOp));
            }

            AddOpInverse(OperatorType.Add, OperatorType.Subtract);
            AddOpInverse(OperatorType.Subtract, OperatorType.Add);
            AddOpInverse(OperatorType.Multiply, OperatorType.Divide);
            AddOpInverse(OperatorType.Divide, OperatorType.Multiply);

            Default = new ExpressionOptions(
                opDefs.Values,
                opInverses,
                doImplicitMultiplications: true);
        }
コード例 #10
0
        public Evaluation Evaluate(ExpressionOptions options, Token token)
        {
            if (token is ListToken listToken)
            {
                return(EvaluateList(options, listToken.Children));
            }

            if (token is ValueToken valueToken)
            {
                string value = valueToken.Value;
                switch (valueToken.Type)
                {
                case TokenType.DecimalDigit:
                    return(new Evaluation(CharUnicodeInfo.GetNumericValue(value[0])));

                case TokenType.DecimalNumber:
                    if (value.Length == 1)
                    {
                        return(new Evaluation(CharUnicodeInfo.GetNumericValue(value[0])));
                    }
                    else
                    {
                        var parsed = double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture);
                        return(new Evaluation(parsed));
                    }

                case TokenType.Name:
                    return(ResolveReference.Invoke(valueToken.Value));

                default:
                    return(new Evaluation(EErrorCode.SyntaxError));
                }
            }
            else if (token is FunctionToken funcToken)
            {
                return(EvaluateFunction(options, funcToken));
            }
            return(new Evaluation(EErrorCode.Undefined));
        }
コード例 #11
0
        public static ResultCode Reduce(ExpressionOptions options, List <Token> tokens)
        {
            // TODO: the reducer can currently break expressions pretty hard, so fix it
            throw new NotImplementedException();

            if (tokens == null)
            {
                throw new ArgumentNullException(nameof(tokens));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            ResultCode code;

            if ((code = ReduceLists(options, tokens)) != ResultCode.Ok)
            {
                return(code);
            }
            return(code);
        }
コード例 #12
0
        public static EError?ValidateOperatorTokens(
            ExpressionOptions options, ValueToken op, Token?left, Token?right)
        {
            var opDef = options.GetOperatorDefinition(op.Value);

            if (opDef != null)
            {
                if (left == null && (
                        opDef.Associativity == OperatorSidedness.Both ||
                        opDef.Associativity.HasFlag(OperatorSidedness.Left)))
                {
                    return(EErrorCode.OperatorMissingLeftValue);
                }

                if (right == null && (
                        opDef.Associativity == OperatorSidedness.Both ||
                        opDef.Associativity.HasFlag(OperatorSidedness.Right)))
                {
                    return(EErrorCode.OperatorMissingRightValue);
                }
            }
            return(null);
        }
コード例 #13
0
        public Evaluation EvaluateFunction(ExpressionOptions options, FunctionToken function)
        {
            var arguments  = new JToken?[function.ArgumentCount];
            int valueIndex = 0;

            for (int i = 0; i < function.Children.Count; i++)
            {
                var arg = function.Children[i];
                if (arg.Type == TokenType.ListSeparator)
                {
                    continue;
                }

                var eval = Evaluate(options, arg);
                if (eval.Error != null)
                {
                    return(eval);
                }

                arguments[valueIndex] = eval.Result;
                valueIndex++;
            }
            return(ExecuteFunction.Invoke(function.Name.Value, arguments));
        }
コード例 #14
0
        private static ResultCode MakeOperatorGroups(ExpressionOptions options, List <Token> tokens)
        {
            var listStack = new Stack <List <Token> >();

            listStack.Push(tokens);

            var opIndices = new List <(int index, ValueToken token, OperatorDefinition definition)>();
            var opShifts  = new List <(int index, int shift)>();

            while (listStack.Count > 0)
            {
                var currentTokens = listStack.Pop();
                for (int j = 0; j < currentTokens.Count; j++)
                {
                    var token = currentTokens[j];
                    if (token is CollectionToken collectionToken)
                    {
                        listStack.Push(collectionToken.Children);
                    }
                }

                // Gather operators so we can sort them by priority rules.
                opIndices.Clear();
                for (int j = 0; j < currentTokens.Count; j++)
                {
                    var token = currentTokens[j];
                    if (token.Type != TokenType.Operator)
                    {
                        continue;
                    }

                    var opToken = (ValueToken)token;
                    var opDef   = options.GetOperatorDefinition(opToken.Value);
                    if (opDef == null)
                    {
                        return(ResultCode.UnknownSymbol);
                    }

                    opIndices.Add((index: j, opToken, opDef));
                }

                opIndices.Sort((x, y) =>
                {
                    int xPriority = x.definition?.Precedence ?? 0;
                    int yPriority = y.definition?.Precedence ?? 0;

                    // Sort types in descending order.
                    int priorityCompare = yPriority.CompareTo(xPriority);
                    if (priorityCompare != 0)
                    {
                        return(priorityCompare);
                    }

                    // Sort indices of same type in ascending order.
                    return(x.index.CompareTo(y.index));
                });

                // Merge token triplets with a center operator or
                // pairs with a leading operator.
                opShifts.Clear();
                for (int i = 0; i < opIndices.Count; i++)
                {
                    var(opIndex, opToken, opDef) = opIndices[i];

                    // Offset "opIndex" by shifts caused by previous operator merges.
                    for (int j = 0; j < opShifts.Count; j++)
                    {
                        var(shiftIndex, shift) = opShifts[j];
                        if (shiftIndex < opIndex)
                        {
                            opIndex += shift;
                        }
                    }

                    Token?leftToken  = null;
                    Token?rightToken = null;

                    int left = opIndex - 1;
                    if (opDef?.Associativity != OperatorSidedness.Right)
                    {
                        if (left < 0)
                        {
                            if (opDef != null && opDef.Associativity.HasFlag(OperatorSidedness.Left))
                            {
                                return(ResultCode.OperatorMissingLeftValue);
                            }
                        }
                        else
                        {
                            leftToken = currentTokens[left];
                        }
                    }
                    if (leftToken?.Type == TokenType.Operator)
                    {
                        continue;
                    }

                    int right = opIndex + 1;
                    if (opDef != null && opDef.Associativity != OperatorSidedness.Left)
                    {
                        if (right >= currentTokens.Count)
                        {
                            if (opDef.Associativity.HasFlag(OperatorSidedness.Right))
                            {
                                return(ResultCode.OperatorMissingRightValue);
                            }
                        }
                        else
                        {
                            rightToken = currentTokens[right];
                            if (rightToken.Type == TokenType.Operator)
                            {
                                continue;
                            }

                            // Mitigates operators with following operators.
                            if (rightToken.Type == TokenType.Operator)
                            {
                                int secondRight = opIndex + 2;
                                if (secondRight < currentTokens.Count)
                                {
                                    var subToken = new ListToken(new List <Token>(2)
                                    {
                                        rightToken,
                                        currentTokens[secondRight]
                                    });

                                    rightToken           = subToken;
                                    currentTokens[right] = rightToken;
                                    currentTokens.RemoveAt(secondRight);

                                    opShifts.Add((right, -1));
                                    opIndices.RemoveAll(x => x.index == right);
                                }
                                else
                                {
                                    return(ResultCode.OperatorMissingRightValue);
                                }
                            }
                        }
                    }

                    int sideTokenCount = 0;
                    if (leftToken != null)
                    {
                        sideTokenCount++;
                    }
                    if (rightToken != null)
                    {
                        sideTokenCount++;
                    }

                    // Try to skip making a 1-item list.
                    int resultCount = 1 + sideTokenCount;
                    if (resultCount == currentTokens.Count)
                    {
                        continue;
                    }

                    var resultList = new List <Token>(resultCount);
                    if (leftToken != null)
                    {
                        resultList.Add(leftToken);
                    }
                    resultList.Add(opToken);
                    if (rightToken != null)
                    {
                        resultList.Add(rightToken);
                    }

                    int firstIndex  = opIndex - (leftToken != null ? 1 : 0);
                    var resultToken = new ListToken(resultList);
                    currentTokens[firstIndex] = resultToken;
                    currentTokens.RemoveRange(firstIndex + 1, resultList.Count - 1);

                    int nextShift = 1 - resultList.Count;
                    opShifts.Add((opIndex, nextShift));
                }
            }
            return(ResultCode.Ok);
        }
コード例 #15
0
        private static ResultCode MakeFunctions(ExpressionOptions options, List <Token> tokens)
        {
            var stack = new Stack <List <Token> >();

            stack.Push(tokens);

            var argumentAccumulator     = new List <Token>();
            var argumentListAccumulator = new List <Token>();

            while (stack.Count > 0)
            {
                bool hasList = false;
                var  list    = stack.Pop();

                for (int i = list.Count; i-- > 0;)
                {
                    var token = list[i];

                    if (hasList)
                    {
                        hasList = false;

                        if (token.Type == TokenType.Operator ||
                            token.Type == TokenType.DecimalDigit ||
                            token.Type == TokenType.DecimalNumber ||
                            token.Type == TokenType.List)
                        {
                            continue;
                        }

                        if (token.Type != TokenType.Name)
                        {
                            return(ResultCode.InvalidTokenBeforeList);
                        }

                        void FlushArgumentAccumulator()
                        {
                            if (argumentAccumulator.Count == 0)
                            {
                                return;
                            }

                            argumentListAccumulator.Add(new ListToken(argumentAccumulator));
                            argumentAccumulator = new List <Token>();
                        }

                        var arguments = (ListToken)list[i + 1];
                        foreach (var argument in arguments)
                        {
                            if (argument.Type == TokenType.ListSeparator)
                            {
                                FlushArgumentAccumulator();
                                argumentListAccumulator.Add(argument);
                            }
                            else
                            {
                                argumentAccumulator.Add(argument);
                            }
                        }
                        FlushArgumentAccumulator();

                        var name = (ValueToken)token;
                        var func = new FunctionToken(name, argumentListAccumulator);
                        argumentListAccumulator = new List <Token>();

                        list[i] = func;
                        list.RemoveAt(i + 1);
                    }

                    if (token is ListToken listToken)
                    {
                        hasList = true;
                        stack.Push(listToken.Children);
                    }
                }
            }

            return(ResultCode.Ok);
        }
コード例 #16
0
 public ExpressionTree(ExpressionOptions options, List <Token> tokens)
 {
     Options  = options ?? throw new ArgumentNullException(nameof(options));
     Tokens   = tokens ?? throw new ArgumentNullException(nameof(tokens));
     MetaList = new ListToken(Tokens);
 }
コード例 #17
0
 public ExpressionTree(ExpressionOptions options) : this(options, new List <Token>())
 {
 }