Beispiel #1
0
        internal static IList <Expression> ParseQueryParameters(ref Utf8JsonReader reader, Stack <Expression> stack, JsonLogicOptions options)
        {
            if (!reader.Read())
            {
                throw new JsonException();
            }
            var l = new List <Expression>(2);

            if (reader.TokenType != JsonTokenType.EndArray)
            {
                var arrayExpr = Parse(ref reader, stack);
                if (!arrayExpr.Type.IsArray)
                {
                    throw new JsonLogicException($"Querie's first parameter MUST be an array.");
                }
                l.Add(arrayExpr);
                var etype = arrayExpr.Type.GetElementType();
                if (!reader.Read())
                {
                    throw new JsonException();
                }
                var predicateParameter = Expression.Parameter(etype, "p");
                stack.Push(predicateParameter);
                var predicateExpr = Parse(ref reader, stack);
                stack.Pop();
                l.Add(predicateExpr);
                if (!reader.Read())
                {
                    throw new JsonException();
                }
                if (reader.TokenType != JsonTokenType.EndArray)
                {
                    reader.Skip();
                }
            }
            return(l);
        }
Beispiel #2
0
        internal static IList <Expression> ParseOpParameters(ref Utf8JsonReader reader, Stack <Expression> parameters, JsonLogicOptions options)
        {
            if (!reader.Read())
            {
                throw new JsonException();
            }
            var l = new List <Expression>(2);

            while (reader.TokenType != JsonTokenType.EndArray)
            {
                l.Add(Parse(ref reader, parameters));
                if (!reader.Read())
                {
                    throw new JsonException();
                }
            }
            return(l);
        }
Beispiel #3
0
        internal static Expression Parse(ref Utf8JsonReader reader, Stack <Expression> stack, JsonLogicOptions options = null)
        {
            Expression exp = default;

            switch (reader.TokenType)
            {
            case JsonTokenType.StartObject:
            {
                if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName)
                {
                    throw new JsonException();
                }
                var opName = reader.GetString()?.Trim()?.ToLower();
                if (options?.Aliases != null && options.Aliases.TryGetValue(opName, out string alias))
                {
                    opName = alias;
                }
                if (!reader.Read())
                {
                    throw new JsonException();
                }
                switch (reader.TokenType)
                {
                case JsonTokenType.StartArray:
                {
                    // this migh be an expression of type { operation : [params...] }
                    // 1 - try to get a standard operations
                    if (KnownOpFactories.TryGetValue(opName, out OpFactory factory))
                    {
                        var values = ParseOpParameters(ref reader, stack, options);
                        exp = factory?.Invoke(values, stack, options);
                        break;
                    }
                    // 2 - Else it's operations on Array. We must parse it in different way, because
                    // underlying predicate need ExpressionParameter to be passed instead as
                    // data.
                    if (KnownQueryFactories.TryGetValue(opName, out factory))
                    {
                        var values = ParseQueryParameters(ref reader, stack, options);
                        exp = factory?.Invoke(values, stack, options);
                        break;
                    }

                    throw new JsonLogicException($"Unknown operation {opName}");
                }

                case JsonTokenType.String:
                {
                    // using Syntactic sugar such "var"="xxx" instead as "var" = ["xxx"]
                    switch (opName)
                    {
                    case TerminalVocabulary.Var:
                    {
                        exp = GetProperty(reader.GetString(), stack, options);
                        break;
                    }

                    default: throw new JsonLogicException($"Syntax error.");
                    }
                    break;
                }

                case JsonTokenType.Number:
                {
                    // using Syntactic sugar such "var"=n instead as "var" = [n]
                    switch (opName)
                    {
                    case TerminalVocabulary.Var:
                    {
                        exp = Expression.ArrayAccess(stack.Peek(), Expression.Constant(reader.GetInt32()));
                        break;
                    }

                    default: throw new JsonLogicException($"Syntax error.");
                    }
                    break;
                }

                default: throw new JsonException();
                }
                if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject)
                {
                    throw new JsonException();
                }
                break;
            }

            case JsonTokenType.String:
            {
                var value = reader.GetString();
                exp = Expression.Constant((object)value);
                break;
            }

            case JsonTokenType.Number:
            {
                var value = reader.GetDouble();
                exp = Expression.Constant((object)value);
                break;
            }

            case JsonTokenType.StartArray:
            {
                exp = ParseArray(ref reader);
                break;
            }

            default: throw new JsonException();
            }
            return(exp);
        }
        public static Expression LessThanOrEqual(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
        {
            if (values.Count < 2)
            {
                throw new JsonLogicException();
            }
            if (values.Count == 2)
            {
                return(Expression.LessThanOrEqual(values[0].EnsureJsonNumber(), values[1].EnsureJsonNumber()));
            }

            // special case to test between
            var v0 = values[0].EnsureJsonNumber();
            var v1 = values[1].EnsureJsonNumber();
            var v2 = values[2].EnsureJsonNumber();
            var a  = Expression.LessThanOrEqual(v0, v1);
            var b  = Expression.LessThanOrEqual(v1, v2);

            return(Expression.And(a, b));
        }
        public static Expression And(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
        {
            if (values.Count < 2)
            {
                throw new JsonLogicException();
            }
            Expression exp = Expression.AndAlso(values[0], values[1]);

            for (int i = 2; i != values.Count; i++)
            {
                exp = Expression.AndAlso(exp, values[i]);
            }
            return(exp);
        }
        private static Expression MinMax(IList <Expression> values, JsonLogicOptions options, bool op)
        {
            Expression array = default;
            Type       etype = default;

            switch (values.Count)
            {
            case 0: throw new JsonLogicException();

            case 1:
            {
                if (values[0].Type.IsArray)
                {
                    array = values[0];
                    etype = array.Type.GetElementType();
                    break;
                }
                return(values[0]);
            }

            case 2:
            {
                // fast track..
                var a = values[0].EnsureJsonNumber();
                var b = values[1].EnsureJsonNumber();
                var p = Expression.Variable(a.Type);
                return(Expression.Block(a.Type,
                                        new[] { p },
                                        Expression.IfThenElse(Expression.LessThan(a, b),
                                                              Expression.Assign(p, op ? b : a),
                                                              Expression.Assign(p, op ? a : b)),
                                        p));
            }

            default:
            {
                if (!values.All(v => v is ConstantExpression ce && ce.Type.IsNumericType()))
                {
                    throw new JsonLogicException("Array must be of Number");
                }
                array = Expression.Constant(values.Cast <ConstantExpression>().Select <ConstantExpression, double>(v => (double)Convert.ChangeType(v.Value, typeof(double))).ToArray());
                etype = typeof(double);
                break;
            }
            }
            var indexVariable = Expression.Variable(typeof(int), "index");
            var arrayValue    = Expression.Variable(etype, "value");
            var result        = Expression.Variable(etype, "result");
            var lengthExp     = Expression.Property(array, typeof(Array).GetPublicInstanceDeclaredOnlyReadProperty(nameof(Array.Length)) !);

            return(Expression.Block(
                       new[] { indexVariable, arrayValue, result },
                       Expression.Assign(result, Expression.ArrayIndex(array, Expression.Constant(0))),
                       ExpressionHelpers.For(
                           Expression.Assign(indexVariable, Expression.Constant(0)),
                           Expression.LessThan(indexVariable, lengthExp),
                           Expression.PostIncrementAssign(indexVariable),
                           Expression.Block(
                               Expression.Assign(arrayValue, Expression.ArrayIndex(array, indexVariable)),
                               Expression.IfThenElse(Expression.LessThan(result, arrayValue),
                                                     Expression.Assign(result, op ? arrayValue : result),
                                                     Expression.Assign(result, op ? result : arrayValue))
                               )
                           ),
                       result));
        }
 public static Expression NotEqual(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
 {
     if (values.Count < 2)
     {
         throw new JsonLogicException();
     }
     if (values[0].Type.IsNumericType())
     {
         return(Expression.Equal(values[0].EnsureJsonNumber(), values[1].EnsureJsonNumber()));
     }
     return(Expression.NotEqual(values[0], values[1]));
 }
        public static Expression Some(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
        {
            if (values.Count < 2)
            {
                throw new JsonLogicException();
            }
            if (!values[0].Type.IsArray)
            {
                throw new JsonLogicException();
            }

            Expression arrayExpr = values[0];
            Type       etype     = arrayExpr.Type.GetElementType();

            var indexVariable = Expression.Variable(typeof(int), "index");
            var lengthExp     = Expression.Property(arrayExpr, typeof(Array).GetPublicInstanceDeclaredOnlyReadProperty(nameof(Array.Length)) !);
            var result        = Expression.Variable(typeof(bool), "result");
            var label         = Expression.Label();
            // note we have to replace all parameters into the underlying expression to reflect the item
            var itemParameter          = Expression.Parameter(etype, "item");
            ParameterReplacer replacer = new ParameterReplacer(itemParameter);
            var predicate = replacer.Visit(values[1]);

            // Parameter for the catch block
            var exception = Expression.Parameter(typeof(Exception));

            var expr = Expression.Block(
                new[] { indexVariable, itemParameter, result },
                Expression.TryCatch(
                    Expression.Block(
                        Expression.Assign(result, Expression.Constant(false)),
                        Expression.Assign(indexVariable, Expression.Constant(0)),
#if DEBUG
                        ExpressionHelpers.DebugExpression(lengthExp),
#endif
                        Expression.Loop(
                            Expression.IfThenElse(
                                Expression.LessThan(indexVariable, lengthExp),
                                Expression.Block(
                                    Expression.Assign(itemParameter, Expression.ArrayIndex(arrayExpr, indexVariable)),
#if DEBUG
                                    ExpressionHelpers.DebugExpression(predicate),
#endif
                                    Expression.IfThen(
                                        predicate,
                                        Expression.Block(
                                            Expression.Assign(result, Expression.Constant(true)),
                                            Expression.Break(label)
                                            )
                                        ),
                                    Expression.PostIncrementAssign(indexVariable)
                                    ),
                                Expression.Break(label)),
                            label)
                        ),
                    Expression.Catch(
                        exception,
#if DEBUG
                        ExpressionHelpers.DebugExpression(exception)
#else
                        Expression.Throw(exception)
#endif
                        )
                    ),
                result);

            return(expr);
        }
        public static Expression Var(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
        {
            if (values.Count < 1)
            {
                throw new JsonLogicException();
            }
            var a = values[0];

            if (a is ConstantExpression ce)
            {
                if (ce.Value is string str)
                {
                    try
                    {
                        return(JsonLogic.GetProperty(str, stack, options));
                    }
                    catch (JsonLogicException)
                    {
                        if (values.Count > 1)
                        {
                            return(values[1]);
                        }
                        throw;
                    }
                }

                if (ce.Type.IsNumericType())
                {
                    var array = stack?.FirstOrDefault(e => e.Type.IsArray);
                    if (array != default)
                    {
                        return(Expression.ArrayAccess(array, ce.EnsureIndexNumber()));
                    }
                }
            }
            throw new JsonLogicException();
        }
 public static Expression Max(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
 {
     return(MinMax(values, options, true));
 }
 public static Expression Modulo(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
 {
     if (values.Count < 2)
     {
         throw new JsonLogicException();
     }
     return(Expression.Modulo(values[0].EnsureJsonNumber(), values[1].EnsureJsonNumber()));
 }
 public static Expression GreaterThan(IList <Expression> values, Stack <Expression> stack = null, JsonLogicOptions options = null)
 {
     if (values.Count < 2)
     {
         throw new JsonLogicException();
     }
     return(Expression.GreaterThan(values[0], values[1]));
 }