/// <summary> /// Implement a JSON-Path like navigation on BsonDocument. Support a simple range of paths /// </summary> private static Expression ParsePath(StringScanner s, Expression expr, ParameterExpression root) { if (s.Match(@"\.[\$\-\w]+")) { var method = typeof(BsonExpression).GetMethod("Member"); var name = Expression.Constant(s.Scan(@"\.([\$\-\w]+)", 1)); return(Expression.Call(method, expr, name)); } else if (s.Match(@"\[")) { var method = typeof(BsonExpression).GetMethod("Array"); var i = s.Scan(@"\[\s*(-?[\d+\*])\s*\]", 1); var index = i != "*" && i != "" ? Convert.ToInt32(i) : int.MaxValue; var inner = new BsonExpression(null); if (i == "") // if array operation are not index based, read expression { s.Scan(@"\[\s*"); // read expression with full support to all operators/formulas inner = ReadExpression(s, true, false, false); if (inner == null) { throw LiteException.SyntaxError(s, "Invalid expression formula"); } s.Scan(@"\s*\]"); } return(Expression.Call(method, expr, Expression.Constant(index), Expression.Constant(inner), root)); } else { return(null); } }
/// <summary> /// Throw syntax exception if not terminate string /// </summary> public void ThrowIfNotFinish() { Scan(@"\s*"); if (!HasTerminated) { throw LiteException.SyntaxError(this); } }
/// <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+"), CultureInfo.InvariantCulture.NumberFormat); 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+"), CultureInfo.InvariantCulture.NumberFormat); 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(@"(.+?)\s*:\s*", 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); }