public Expr BuildExpressionTree(string text)
        {
            var tokenList       = new List <TokenInfo>();
            int tokenListNumber = 0;

            // 1. read all tokens and build initial tree by Parens
            using (TextReader sr = new StringReader(text))
            {
                var t              = new SqlTokenizer(sr, _resolver);
                var current        = t.Token;
                var openParenStack = new List <int>();

                while (current != Token.EOF)
                {
                    tokenList.Add(t.Info);

                    // read expression
                    if (current == Token.OpenParens)
                    {
                        openParenStack.Add(tokenListNumber);
                    }

                    if (current == Token.CloseParens)
                    {
                        if (openParenStack.Count < 1)
                        {
                            throw new Exception($"Cannot find open parens for close parens ')'");
                        }

                        var openParensIndex = openParenStack.Last();
                        openParenStack.RemoveAt(openParenStack.Count - 1);
                        //var sb = new StringBuilder();
                        var expr = new List <TokenInfo>();

                        for (int i = openParensIndex + 1; i <= tokenListNumber; i++)
                        {
                            var ti = tokenList[i];
                            //sb.Append($"{ti.Field}{ti.Param}{ti.Operator}{ti.Number}{ti.StringLiteral}");

                            if (ti != null && ti.Token != Token.CloseParens)
                            {
                                expr.Add(ti);
                                tokenList[i] = null;
                            }

                            //if(ti?.Token == Token.CloseParens)
                            //{
                            //    tokenList[i] = null;
                            //}
                        }

                        tokenList[openParensIndex] = new TokenInfo {
                            Token = Token.ParensExpression, Expression = expr
                        };
                        tokenList[tokenListNumber] = null;
                    }

                    // next
                    t.NextToken();
                    current = t.Token;
                    tokenListNumber++;
                }

                tokenList = tokenList.Where(x => x != null && x.Token != Token.CloseParens).ToList();
            }

            if (!tokenList.Any())
            {
                return(new Expr {
                    Type = ExprType.None
                });
            }

            // 2. Apply logical operators in order : = AND OR
            var prioDict = _resolver.GetPriorityOperators();
            var min      = prioDict.Keys.Min();
            var max      = prioDict.Keys.Max();

            for (int i = min; i <= max; i++)
            {
                ApplyOperators(tokenList, prioDict[i]);
            }

            var sb = new StringBuilder();

            PrintExpressions(tokenList, sb);
            Console.WriteLine(sb.ToString());

            var mainExpr = new Expr {
                Type = ExprType.Expr
            };

            BuildTree(tokenList, mainExpr);
            return(mainExpr);
        }