private BsonArray ReadArray(StringScanner s) { var arr = new BsonArray(); s.Scan(@"\s*\["); while (!s.Match(@"\s*\]")) { var value = this.ReadValue(s); arr.Add(value); if (s.Scan(@"\s*,").Length == 0) { break; } } if (s.Scan(@"\s*\]").Length == 0) { throw new ArgumentException("Missing close json array symbol"); } return(arr); }
/// <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); } }
internal BsonValue ReadValue(StringScanner s) { s.Scan(@"\s*"); if (s.Scan("null").Length > 0) { return(BsonValue.Null); } else if (s.Match(@"\[")) { return(this.ReadArray(s)); } else if (s.Match(@"{\s*[""]?\$\w+[""]?\s*:\s*""[^""]*""\s*}")) { return(this.ReadExtendDataType(s)); } else if (s.Match(@"{")) { return(this.ReadObject(s)); } else if (s.Match("\"")) { return(new BsonValue(this.ReadString(s))); } else if (s.Match(@"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?")) { return(this.ReadNumber(s)); } else if (s.Match(@"(true|false)")) { return(new BsonValue(bool.Parse(s.Scan("(true|false)")))); } throw new ArgumentException("String is not a valid JsonEx"); }
private BsonValue ReadExtendDataType(StringScanner s) { s.Scan(@"\{\s*"); var dataType = s.Match(@"[$\w]+") ? s.Scan(@"[$\w]+") : this.ReadString(s); s.Scan(@"\s*:\s*"); var value = this.ReadString(s); s.Scan(@"\s*}"); try { switch (dataType) { case "$date": return(new BsonValue(DateTime.Parse(value))); case "$guid": return(new BsonValue(new Guid(value))); case "$binary": return(new BsonValue(Convert.FromBase64String(value))); } } catch (Exception ex) { throw new FormatException("Invalid " + dataType + " value in " + value, ex); } throw new ArgumentException("Invalid JSON extended format"); }
public IEnumerable <BsonValue> ReadEnumerable(string json) { var s = new StringScanner(json); if (s.Scan(@"\s*\[").Length == 0) { throw new ArgumentException("String is not a json array"); } while (!s.Match(@"\s*\]")) { yield return(this.ReadValue(s)); if (s.Scan(@"\s*,").Length == 0) { break; } } if (s.Scan(@"\s*\]").Length == 0) { throw new ArgumentException("Missing close json array symbol"); } yield break; }
/// <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()); }
private string ReadString(StringScanner s) { if (s.Scan(@"\s*\""").Length == 0) { throw new ArgumentException("Invalid json string"); } var str = s.ScanUntil(@"(?:[^""\\]|\\.)*"); if (s.Scan("\"").Length == 0) { throw new ArgumentException("Invalid json string"); } return(str); }
/// <summary> /// Extract expression or a path from a StringScanner. Returns null if is not a Path/Expression /// </summary> internal static string ReadExpression(StringScanner s, bool pathOnly, bool arithmeticOnly = true) { var start = s.Index; try { // if marked to read path only and first char is not $, // enter in parseExpressin marking as RootPath var isRoot = pathOnly; var root = Expression.Parameter(typeof(BsonDocument), "root"); var current = Expression.Parameter(typeof(BsonValue), "current"); if (pathOnly) { // if read path, read first expression only // support missing $ as root s.Scan(@"\$\.?"); ParseSingleExpression(s, root, current, true); } else { // read all expression (a + b) // if include operator, support = > < && || too ParseExpression(s, root, current, arithmeticOnly); } return(s.Source.Substring(start, s.Index - start)); } catch (LiteException ex) when(ex.ErrorCode == LiteException.SYNTAX_ERROR) { s.Index = start; return(null); } }
/// <summary> /// Start parse string into linq expression. Read path, function or base type bson values (int, double, bool, string) /// </summary> internal static Expression ParseExpression(StringScanner s, ParameterExpression root, ParameterExpression current, bool arithmeticOnly) { var first = ParseSingleExpression(s, root, current, false); var values = new List <Expression> { first }; var ops = new List <string>(); // read all blocks and operation first while (!s.HasTerminated) { // checks if must support arithmetic only (+, -, *, /) var op = s.Scan(arithmeticOnly ? _reArithmetic : _reOperator, 1); if (op.Length == 0) { break; } var expr = ParseSingleExpression(s, root, current, false); values.Add(expr); ops.Add(op); } var order = 0; // now, process operator in correct order while (values.Count >= 2) { var op = _operators.ElementAt(order); var n = ops.IndexOf(op.Key); if (n == -1) { order++; } else { // get left/right values to execute operator var left = values.ElementAt(n); var right = values.ElementAt(n + 1); // process result in a single value var result = Expression.Call(op.Value, left, right); // remove left+right and insert result values.Insert(n, result); values.RemoveRange(n + 1, 2); // remove operation ops.RemoveAt(n); } } return(values.Single()); }
public static void ParseKeyValue(this IDictionary <string, string> dict, string connectionString) { var s = new StringScanner(connectionString); while (!s.HasTerminated) { var key = s.Scan(@"(.*?)=", 1).Trim(); var value = ""; s.Scan(@"\s*"); if (s.Match("\"")) { // read a value inside an string " (remove escapes) value = s.Scan(@"""((?:\\""|.)*?)""", 1).Replace("\\\"", "\""); s.Scan(@"\s*;?\s*"); } else { // read value value = s.Scan(@"(.*?);\s*", 1).Trim(); // read last part if (value.Length == 0) { value = s.Scan(".*").Trim(); } } dict[key] = value; } }
private BsonValue ReadNumber(StringScanner s) { var nf = CultureInfo.InvariantCulture.NumberFormat; var value = s.Scan(@"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?"); if (value.Contains(".")) { return(new BsonValue(Convert.ToDouble(value, nf))); } else { return(new BsonValue(Convert.ToInt32(value))); } }
private BsonObject ReadObject(StringScanner s) { var obj = new BsonObject(); s.Scan(@"\s*{"); while (!s.Match(@"\s*}")) { s.Scan(@"\s*"); // accept key without " var key = s.Match(@"[\w$]+") ? s.Scan(@"[\w$]+") : this.ReadString(s); if (key.Trim().Length == 0) { throw new ArgumentException("Invalid json object key"); } s.Scan(@"\s*:\s*"); var value = this.ReadValue(s); obj[key] = value; if (s.Scan(@"\s*,").Length == 0) { break; } } if (s.Scan(@"\s*}").Length == 0) { throw new ArgumentException("Missing close json object symbol"); } return(obj); }
/// <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); }