/// <summary> /// Deserialize a json using a StringScanner and returns BsonValue /// </summary> public static BsonValue Deserialize(StringScanner s) { if (s == null) throw new ArgumentNullException("s"); if (s.HasTerminated) return BsonValue.Null; using (var sr = new StringReader(s.ToString())) { var reader = new JsonReader(sr); var value = reader.Deserialize(); s.Seek((int)(reader.Position - 1)); return value; } }
/// <summary> /// Parse and compile an expression from a stringscanner. Must define if will read a path only or support for full expression. Can parse only arithmetic (+/-/*/..) or full logic operators (=/!=/>/...) /// </summary> private static Func <BsonDocument, BsonValue, IEnumerable <BsonValue> > Compile(StringScanner s, bool pathOnly, bool arithmeticOnly) { var isRoot = pathOnly; var root = Expression.Parameter(typeof(BsonDocument), "root"); var current = Expression.Parameter(typeof(BsonValue), "current"); Expression expr; if (pathOnly) { // if read path, read first expression only // support missing $ as root s.Scan(@"\$\.?"); expr = ParseSingleExpression(s, root, current, true); } else { // read all expression (a + b) // if include operator, support = > < && || too expr = ParseExpression(s, root, current, arithmeticOnly); } var lambda = Expression.Lambda <Func <BsonDocument, BsonValue, IEnumerable <BsonValue> > >(expr, root, current); return(lambda.Compile()); }
/// <summary> /// Start parse string into linq expression. Read path, function or base type bson values (int, double, bool, string) /// </summary> internal static Expression ParseSingleExpression(StringScanner s, ParameterExpression root, ParameterExpression current, bool isRoot) { if (s.Match(@"[\$@]") || isRoot) // read root path { var r = s.Scan(@"[\$@]"); // read root/current var method = typeof(BsonExpression).GetMethod("Root"); var name = Expression.Constant(s.Scan(@"\.([\$\-\w]+)", 1)); var expr = Expression.Call(method, r == "@" ? current : root, name) as Expression; // parse the rest of path while (!s.HasTerminated) { var result = ParsePath(s, expr, root); if (result == null) { break; } expr = result; } return(expr); } else if (s.Match(@"-?\d*\.\d+")) // read double { var number = Convert.ToDouble(s.Scan(@"-?\d*\.\d+")); var value = Expression.Constant(new BsonValue(number)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"-?\d+")) // read int { var number = Convert.ToInt32(s.Scan(@"-?\d+")); var value = Expression.Constant(new BsonValue(number)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"(true|false)")) // read bool { var boolean = Convert.ToBoolean(s.Scan(@"(true|false)")); var value = Expression.Constant(new BsonValue(boolean)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"null")) // read null { var value = Expression.Constant(BsonValue.Null); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"'")) // read string { var str = s.Scan(@"'([\s\S]*?)'", 1); var value = Expression.Constant(new BsonValue(str)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"\w+\s*\(")) // read function { // get static method from this class var name = s.Scan(@"(\w+)\s*\(", 1).ToUpper(); var method = typeof(BsonExpression).GetMethod(name); var parameters = new List <Expression>(); if (method == null) { throw LiteException.SyntaxError(s, "Method " + name + " not exist"); } while (!s.HasTerminated) { var parameter = ParseExpression(s, root, current, false); parameters.Add(parameter); if (s.Scan(@"\s*,\s*").Length > 0) { continue; } else if (s.Scan(@"\s*\)\s*").Length > 0) { break; } throw LiteException.SyntaxError(s); } return(Expression.Call(method, parameters.ToArray())); } throw LiteException.SyntaxError(s); }
/// <summary> /// Start parse string into linq expression. Read path, function or base type bson values (int, double, bool, string) /// </summary> internal static Expression ParseSingleExpression(StringScanner s, ParameterExpression root, ParameterExpression current, bool isRoot) { if (s.Match(@"[\$@]") || isRoot) // read root path { var r = s.Scan(@"[\$@]"); // read root/current var method = typeof(BsonExpression).GetMethod("Root"); var name = Expression.Constant(s.Scan(@"\.?([\$\-\w]+)", 1)); var expr = Expression.Call(method, r == "@" ? current : root, name) as Expression; // parse the rest of path while (!s.HasTerminated) { var result = ParsePath(s, expr, root); if (result == null) { break; } expr = result; } return(expr); } else if (s.Match(@"-?\d*\.\d+")) // read double { var number = Convert.ToDouble(s.Scan(@"-?\d*\.\d+")); var value = Expression.Constant(new BsonValue(number)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"-?\d+")) // read int { var number = Convert.ToInt32(s.Scan(@"-?\d+")); var value = Expression.Constant(new BsonValue(number)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"(true|false)")) // read bool { var boolean = Convert.ToBoolean(s.Scan(@"(true|false)")); var value = Expression.Constant(new BsonValue(boolean)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"null")) // read null { var value = Expression.Constant(BsonValue.Null); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Match(@"'")) // read string { var str = s.Scan(@"'([\s\S]*?)'", 1); var value = Expression.Constant(new BsonValue(str)); return(Expression.NewArrayInit(typeof(BsonValue), value)); } else if (s.Scan(@"\{\s*").Length > 0) // read document { { // read key value var method = typeof(ExpressionOperators).GetMethod("DOCUMENT"); var keys = new List <Expression>(); var values = new List <Expression>(); while (!s.HasTerminated) { // read key + value var key = s.Scan("(.+?):", 1).ThrowIfEmpty("Invalid token", s); var value = ParseExpression(s, root, current, false); // add key and value to parameter list (as an expression) keys.Add(Expression.Constant(new BsonValue(key))); values.Add(value); if (s.Scan(@"\s*,\s*").Length > 0) { continue; } else if (s.Scan(@"\s*\}\s*").Length > 0) { break; } throw LiteException.SyntaxError(s); } var arrKeys = Expression.NewArrayInit(typeof(BsonValue), keys.ToArray()); var arrValues = Expression.NewArrayInit(typeof(IEnumerable <BsonValue>), values.ToArray()); return(Expression.Call(method, new Expression[] { arrKeys, arrValues })); } else if (s.Scan(@"\[\s*").Length > 0) // read array [ { var method = typeof(ExpressionOperators).GetMethod("ARRAY"); var values = new List <Expression>(); while (!s.HasTerminated) { // read value expression var value = ParseExpression(s, root, current, false); values.Add(value); if (s.Scan(@"\s*,\s*").Length > 0) { continue; } else if (s.Scan(@"\s*\]\s*").Length > 0) { break; } throw LiteException.SyntaxError(s); } var arrValues = Expression.NewArrayInit(typeof(IEnumerable <BsonValue>), values.ToArray()); return(Expression.Call(method, new Expression[] { arrValues })); } else if (s.Scan(@"\(\s*").Length > 0) // read inner ( { // read a inner expression inside ( and ) var inner = ParseExpression(s, root, current, false); if (s.Scan(@"\s*\)").Length == 0) { throw LiteException.SyntaxError(s); } return(inner); } else if (s.Match(@"\w+\s*\(")) // read function { // get static method from this class var name = s.Scan(@"(\w+)\s*\(", 1).ToUpper(); var parameters = new List <Expression>(); if (s.Scan(@"\s*\)\s*").Length == 0) { while (!s.HasTerminated) { var parameter = ParseExpression(s, root, current, false); parameters.Add(parameter); if (s.Scan(@"\s*,\s*").Length > 0) { continue; } else if (s.Scan(@"\s*\)\s*").Length > 0) { break; } throw LiteException.SyntaxError(s); } } var method = _methods.FirstOrDefault(x => x.Name == name && x.GetParameters().Count() == parameters.Count); if (method == null) { throw LiteException.SyntaxError(s, "Method " + name + " not exist or invalid parameter count"); } return(Expression.Call(method, parameters.ToArray())); } throw LiteException.SyntaxError(s); }