Example #1
0
        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);
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
        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");
        }
Example #4
0
        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");
        }
Example #5
0
        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;
        }
Example #6
0
        /// <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());
        }
Example #7
0
        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);
        }
Example #8
0
        /// <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);
            }
        }
Example #9
0
        /// <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;
            }
        }
Example #11
0
        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)));
            }
        }
Example #12
0
        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);
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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);
        }