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); }
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); }
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])); }