/// <summary>
        /// Initializes a new instance of the <see cref="CustomExpression"/> class from a given expression.
        /// </summary>
        /// <param name="expression">The custom math expression.</param>
        /// <exception cref="ArgumentNullException">Thrown, when the passed argument is null.</exception>
        /// <exception cref="ArgumentException">Thrown, when the expression is invalid.</exception>
        public CustomExpression(string expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            this.Name = expression.GetRootFunctionName();
            if (this.Name == null)
            {
                throw new ArgumentException(
                          "The expression does not contain a custom function!");
            }

            int indexOfFunctionName = expression.IndexOf(this.Name);

            this.Expression = expression;

            if (this.Expression[indexOfFunctionName + this.Name.Length] != '(')
            {
                throw new ArgumentException(
                          "The function's opening bracket must be right after the name of the function!");
            }

            this.replaceStartIndex = indexOfFunctionName;

            // Removing the leading chars
            var trimmedExpression = this.Expression.Substring(indexOfFunctionName);

            var openingBracketIndex = trimmedExpression.IndexOf('(');
            var closingBracketIndex = this.FindClosingBracketIndex(trimmedExpression, openingBracketIndex);

            this.Argument        = trimmedExpression.Substring(openingBracketIndex + 1, closingBracketIndex - openingBracketIndex - 1);
            this.replaceEndIndex = this.replaceStartIndex + closingBracketIndex + 1;

            var value = NumericalParser.ParseDouble(this.Argument);

            if (value.HasValue)
            {
                this.ArgumentValue = value;
            }
            else
            {
                try
                {
                    this.InnerExpression = new CustomExpression(this.Argument);
                }
                catch (Exception)
                {
                    // Probably this is a normal math expression
                    this.ArgumentValue = Operations.Compute(this.Argument);
                }
            }
        }
        /// <summary>
        /// Pre-calculates the custom math expression and removes the custom function calls.
        /// </summary>
        /// <returns>A simplified math expression.</returns>
        public string Simplify()
        {
            if (this.Name == null)
            {
                throw new InvalidOperationException(
                          "Can not compute the function, because its name is null.");
            }

            if (!Custom.Functions.ContainsKey(this.Name))
            {
                throw new InvalidOperationException(
                          "Undefined custom function: " + this.Name);
            }

            var children = this.GetChildren(this).ToList();

            if (children.Count == 0)
            {
                var result   = Custom.Functions[this.Name].Invoke(this.ArgumentValue.Value);
                var leading  = this.Expression.Substring(0, this.replaceStartIndex);
                var trailing = this.Expression.Substring(this.replaceEndIndex);

                return(leading + result + trailing);
            }
            else
            {
                double?result = null;
                for (int i = children.Count - 1; i >= 0; i--)
                {
                    var funct = children[i];
                    if (funct.ArgumentValue.HasValue)
                    {
                        result = Operations.Compute(funct.Simplify());
                    }
                    else
                    {
                        if (!result.HasValue)
                        {
                            continue;
                        }

                        result = Custom.Functions[funct.Name].Invoke(result.Value);
                    }
                }

                var finalResult = Custom.Functions[this.Name].Invoke(result.Value);

                var leading  = this.Expression.Substring(0, this.replaceStartIndex);
                var trailing = this.Expression.Substring(this.replaceEndIndex);

                return(leading + finalResult + trailing);
            }
        }
Example #3
0
        /// <summary>
        /// Pre-calculates the extra operators (defined at the <see cref="Custom.LeftAssociativeOperators"/> and <see cref="Custom.RightAssociativeOperators"/> for a given math expression.
        /// </summary>
        /// <param name="expression">The math expression to translate.</param>
        /// <param name="rightAssociative">Determines the operator associativity.</param>
        private static void TranslateCustomOperators(ref string expression, bool rightAssociative)
        {
            char? @operator = expression.GetOperator(rightAssociative);

            if (@operator == null)
            {
                return;
            }

            int operatorIndex;

            if (rightAssociative)
            {
                operatorIndex = expression.LastIndexOf(@operator.Value);
            }
            else
            {
                operatorIndex = expression.IndexOf(@operator.Value);
            }

            int    leftIndex             = operatorIndex - 1;
            int    rightIndex            = operatorIndex + 1;
            double?left                  = null;
            double?right                 = null;
            int    replaceLeftIndex      = -1;
            int    replaceRightIndex     = -1;
            bool   isLeftSideExpression  = false;
            bool   hasLeftSideOperator   = false;
            bool   isRightSideExpression = false;
            bool   hasRightSideOperator  = false;

            while (leftIndex >= 0)
            {
                var ls = expression.Substring(leftIndex, operatorIndex - leftIndex).Replace(" ", string.Empty);
                if (ls.Contains(')'))
                {
                    isLeftSideExpression = true;
                }

                if (ls.Contains('+') || ls.Contains('-') || ls.Contains('*') || ls.Contains('/'))
                {
                    hasLeftSideOperator = true;
                }

                if (hasLeftSideOperator && !isLeftSideExpression)
                {
                    break;
                }

                double?computed = null;
                try
                {
                    computed         = Operations.Compute(ls);
                    left             = computed.Value;
                    replaceLeftIndex = leftIndex;
                }
                catch (Exception)
                {
                    if (left != null)
                    {
                        break;
                    }
                }

                leftIndex--;
            }

            while (rightIndex < expression.Length)
            {
                var rs = expression.Substring(operatorIndex + 1, rightIndex - operatorIndex).Replace(" ", string.Empty);
                if (rs.Contains(')'))
                {
                    isRightSideExpression = true;
                }

                if (rs.Contains('+') || rs.Contains('-') || rs.Contains('*') || rs.Contains('/'))
                {
                    hasRightSideOperator = true;
                }

                if (hasRightSideOperator && !isRightSideExpression)
                {
                    break;
                }

                double?computed = null;
                try
                {
                    computed          = Operations.Compute(rs);
                    right             = computed.Value;
                    replaceRightIndex = rightIndex;
                }
                catch (Exception)
                {
                    if (right != null)
                    {
                        break;
                    }
                }

                rightIndex++;
            }

            if (left != null && right != null)
            {
                var    textToReplace = expression.Substring(replaceLeftIndex, replaceRightIndex - replaceLeftIndex + 1);
                string result;
                if (rightAssociative)
                {
                    result = Custom.RightAssociativeOperators[@operator.Value].Invoke(left.Value, right.Value).ToString();
                }
                else
                {
                    result = Custom.LeftAssociativeOperators[@operator.Value].Invoke(left.Value, right.Value).ToString();
                }

                expression = expression.Replace(textToReplace, result);
                TranslateCustomOperators(ref expression, rightAssociative);
            }
        }