Ejemplo n.º 1
0
        /**
         * Check that the expression has enough numbers and variables to fit the
         * requirements of the operators and functions, also check
         * for only 1 result stored at the end of the evaluation.
         */
        internal void Validate(List <Token> rpn)
        {
            /*-
             * Thanks to Norman Ramsey:
             * http://http://stackoverflow.com/questions/789847/postfix-notation-validation
             */
            // each push on to this stack is a new function scope, with the value of each
            // layer on the stack being the count of the number of parameters in that scope
            Stack <int> stack = new Stack <int>();

            // push the 'global' scope
            stack.Push(0);

            foreach (Token token in rpn)
            {
                switch (token.type)
                {
                case TokenType.UNARY_OPERATOR:
                    if (stack.Peek() < 1)
                    {
                        throw new ExpressionException("Missing parameter(s) for operator " + token);
                    }
                    break;

                case TokenType.OPERATOR:
                    if (stack.Peek() < 2)
                    {
                        throw new ExpressionException("Missing parameter(s) for operator " + token);
                    }
                    // pop the operator's 2 parameters and add the result
                    int v = stack.Pop();
                    stack.Push(v - 2 + 1);
                    break;

                case TokenType.FUNCTION:
                    bool   isMap = false, isReduce = false;
                    string s = token.surface;
                    if (s[0] == '$')
                    {
                        isMap = true;
                        s     = s.Substring(1);
                    }
                    if (s[0] == '@')
                    {
                        isReduce = true;
                        s        = s.Substring(1);
                    }
                    if (!functions.TryGetValue(s, out LazyFunction tmplazyfun))
                    {
                        throw new UnknownFunctionInExpressionException(string.Format("Unknown function: {0}", s), s);
                    }

                    LazyFunction f = tmplazyfun;
                    if (f == null)
                    {
                        throw new ExpressionException("Unknown function '" + token + "' at position " + (token.pos + 1));
                    }
                    int numParams = stack.Pop();
                    if (isReduce)
                    {
                        numParams++;
                    }
                    if (!f.NumParamsVaries())
                    {
                        if (numParams != f.NumParams)
                        {
                            throw new ExpressionException("Function " + token + " expected " + f.NumParams + " parameters, got " + numParams);
                        }
                        // TODO check numero parametri della funzione mappata
                        if (isMap && numParams == 0)
                        {
                            throw new ExpressionException("Mapping requires at least one parameter, calling function " + token);
                        }
                        // TODO check numero parametri della funzione ridotta
                        if (isReduce && numParams < 2)
                        {
                            throw new ExpressionException("Reduce requires at least two parameters, calling function " + token);
                        }
                    }
                    if (stack.Count <= 0)
                    {
                        throw new ExpressionException("Too many function calls, maximum scope exceeded");
                    }
                    // push the result of the function
                    int val = stack.Pop();
                    stack.Push(val + 1);
                    break;

                case TokenType.OPEN_PAREN:
                    stack.Push(0);
                    break;

                case TokenType.OBJPATH:
                    break;

                default:
                    val = stack.Pop();
                    stack.Push(val + 1);
                    break;
                }
            }

            if (stack.Count > 1)
            {
                throw new ExpressionException("Too many unhandled function parameter lists");
            }
            else if (stack.Peek() > 1)
            {
                throw new ExpressionException("Too many numbers or variables");
            }
            else if (stack.Peek() < 1)
            {
                throw new ExpressionException("Empty expression");
            }
        }
Ejemplo n.º 2
0
        private LazyNumber LazyEval(string expression)
        {
            if (string.IsNullOrEmpty(expression))
            {
                throw new ExpressionException("Empty expression");
            }

            Stack <LazyNumber> stack = new Stack <LazyNumber>();

            foreach (Token token in GetRPN(expression))
            {
                switch (token.type)
                {
                case TokenType.UNARY_OPERATOR:
                    LazyNumber value = stack.Pop();
                    stack.Push(operators[token.surface].Eval(value, null));
                    break;

                case TokenType.OPERATOR:
                    LazyNumber v2 = stack.Pop();
                    LazyNumber v1 = stack.Pop();
                    stack.Push(operators[token.surface].Eval(v1, v2));
                    break;

                case TokenType.VARIABLE:
                    LazyNumber lazyNumber;
                    if (variables.ContainsKey(token.surface))
                    {
                        lazyNumber = new FunLazyNumberS(() => variables[token.surface]);
                    }
                    else
                    {
                        if (vararrays.ContainsKey(token.surface))
                        {
                            lazyNumber = new FunLazyNumberA(() => vararrays[token.surface]);
                        }
                        else
                        {
                            if (stringVariables.ContainsKey(token.surface))
                            {
                                lazyNumber = new FunLazyString(() => stringVariables[token.surface]);
                            }
                            else
                            {
                                throw new UnknownVariableInExpressionException("Unknown variable: " + token.ToString(), token.ToString());
                            }
                        }
                    }
                    stack.Push(lazyNumber);
                    break;

                case TokenType.OBJPATH:
                    if (!string.IsNullOrEmpty(token.surface))
                    {
                        LazyNumber avar = stack.Peek();
                        if (avar is FunLazyString)
                        {
                            var lazyjson = new FunLazyJSON((FunLazyString)avar)
                            {
                                ObjectPath = token.surface
                            };
                            stack.Pop();
                            stack.Push(lazyjson);
                        }
                        else
                        {
                            throw new ExpressionException("Object path set to a variable that is not a JSON object");
                        }
                    }
                    else
                    {
                        throw new ExpressionException("Empty or invalid object path");
                    }
                    break;

                case TokenType.FUNCTION:
                    bool   isMap = false, isReduce = false;
                    string s = token.surface;
                    if (s[0] == MapChar)
                    {
                        s     = s.Substring(1);
                        isMap = true;
                    }
                    if (s[0] == ReduceChar)
                    {
                        s        = s.Substring(1);
                        isReduce = true;
                    }
                    LazyFunction      f = functions[s];
                    List <LazyNumber> p = new List <LazyNumber>(
                        !f.NumParamsVaries() ? f.NumParams : 0);
                    // pop parameters off the stack until we hit the start of
                    // this function's parameter list
                    while (!stack.IsEmpty() && stack.Peek() != PARAMS_START)
                    {
                        p.Insert(0, stack.Pop());
                    }

                    if (stack.Peek() == PARAMS_START)
                    {
                        stack.Pop();
                    }

                    LazyNumber fResult;
                    if (isMap)
                    {
                        fResult = new FunLazyNumberA(() =>
                        {
                            double[] vals = p[0].EvalArray();
                            if (vals.Length == 0)
                            {
                                return(new double[0]);
                            }
                            double[] res = new double[vals.Length];
                            int i        = 0;
                            foreach (double val in vals)
                            {
                                p[0]     = new FunLazyNumberS(() => val);
                                res[i++] = f.LazyEval(p).Eval();
                            }
                            return(res);
                        });
                    }
                    else
                    {
                        if (isReduce)
                        {
                            fResult = new FunLazyNumberS(() =>
                            {
                                double[] vals = p[0].EvalArray();
                                if (vals.Length == 0)
                                {
                                    return(0.0D);
                                }
                                if (vals.Length == 1)
                                {
                                    return(vals[0]);
                                }
                                double res             = 0.0D;
                                List <LazyNumber> pred = new List <LazyNumber>(p.Count + 1)
                                {     // collection initialization ... ugly ....:)
                                    null,
                                    null
                                };
                                if (p.Count > 1)
                                {
                                    pred.AddRange(p.GetRange(1, p.Count - 1));
                                }
                                pred[0] = new FunLazyNumberS(() => vals[0]);
                                pred[1] = new FunLazyNumberS(() => vals[1]);
                                res     = f.LazyEval(pred).Eval();
                                for (int i = 2; i < vals.Length; i++)
                                {
                                    pred[0] = new FunLazyNumberS(() => res);
                                    pred[1] = new FunLazyNumberS(() => vals[i]);
                                    res     = f.LazyEval(pred).Eval();
                                }
                                return(res);
                            });
                        }
                        else
                        {
                            fResult = f.LazyEval(p);
                        }
                    }
                    stack.Push(fResult);
                    break;

                case TokenType.OPEN_PAREN:
                    stack.Push(PARAMS_START);
                    break;

                case TokenType.LITERAL:
                    stack.Push(new FunLazyNumberS(() => StoD(token.surface)));
                    break;

                case TokenType.STRINGPARAM:
                    FunLazyString tmpNumber = new FunLazyString(() => token.surface);
                    stack.Push(tmpNumber);
                    break;

                case TokenType.HEX_LITERAL:
                    double hexd = double.NaN;
                    try
                    {
                        hexd = (double)Convert.ToInt64(token.surface, 16);
                    }
                    catch (Exception) { }     // no need to worry, just NaN
                    stack.Push(new FunLazyNumberS(() => hexd));
                    break;
                }
            }
            return(stack.Pop());
        }