/// <summary>
 /// Recupera o container condicional.
 /// </summary>
 /// <param name="enumerator"></param>
 /// <param name="configuration"></param>
 /// <returns></returns>
 private static ConditionalTerm GetContainer(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration)
 {
     if (enumerator.MoveNext())
     {
         return(GetTerm(ref enumerator, configuration));
     }
     return(null);
 }
        /// <summary>
        /// Recupera a chamada de função.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <param name="containerStop"></param>
        /// <param name="callName"></param>
        /// <returns></returns>
        private static ConditionalTerm GetFunctionCall(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration, char?containerStop, Column callName)
        {
            var call            = callName;
            var pars            = new List <ConditionalTerm>();
            var commaExpression = _conditionalLexer.TokenParser.GetTerm((int)Colosoft.Text.InterpreterExpression.TokenID.Comma);
            var token           = enumerator.Current;
            var distinctFlag    = false;

            while (!token.ToString().Equals(")"))
            {
                if (!enumerator.MoveNext())
                {
                    break;
                }
                token = enumerator.Current;
                if (token.ToString().Equals(")"))
                {
                    enumerator.MoveNext();
                    break;
                }
                var             termText = enumerator.Current.Text;
                ConditionalTerm term     = null;
                while (true)
                {
                    term = GetTerm(ref enumerator, configuration);
                    if (term is Column && StringComparer.InvariantCultureIgnoreCase.Equals(((Column)term).Name, "DISTINCT"))
                    {
                        enumerator.MoveNext();
                        distinctFlag = true;
                    }
                    else
                    {
                        break;
                    }
                }
                if (StringComparer.InvariantCultureIgnoreCase.Equals(call.Name, "CAST") && pars.Count == 1 && term is Column)
                {
                    term = new Constant(((Column)term).Name);
                }
                if (!enumerator.MoveNext())
                {
                    pars.Add(term);
                    break;
                }
                if (Conditional.IsConditionalOperator(enumerator.Current))
                {
                    var left  = term;
                    var oper  = new Operator(enumerator.Current.Text);
                    var right = GetTerm(ref enumerator);
                    term = new Conditional(left, oper, right);
                    if (!enumerator.MoveNext())
                    {
                        pars.Add(term);
                        break;
                    }
                }
                if (Formula.IsArithmeticOperator(enumerator.Current))
                {
                    term = GetFormula(ref enumerator, configuration, term);
                }
                if ((token.Token == (int)Colosoft.Text.InterpreterExpression.TokenID.Identifier || Colosoft.Query.Parser.SqlTokenParser.IsSqlAnsiFunction(token.Token)) && enumerator.Current.Token == (int)Colosoft.Text.InterpreterExpression.TokenID.LParen)
                {
                    var columnExpression = ParserColumnExpression(termText);
                    term = GetFunctionCall(ref enumerator, configuration, ')', (columnExpression as Column) ?? new Column(termText));
                    if (Formula.IsArithmeticOperator(enumerator.Current))
                    {
                        term = GetFormula(ref enumerator, configuration, term);
                    }
                    pars.Add(term);
                    token = enumerator.Current;
                }
                else
                {
                    pars.Add(term);
                    token = enumerator.Current;
                }
                if (token.ToString().Equals(")"))
                {
                    enumerator.MoveNext();
                    break;
                }
                if (!token.ToString().Equals(commaExpression))
                {
                    throw new ConditionalParserException(string.Format("Expected comma after '{0}'", pars.Last().ToString()));
                }
            }
            return(new FunctionCall()
            {
                Call = call,
                Parameters = pars.ToArray(),
                Options = distinctFlag ? FunctionCallOptions.Distinct : FunctionCallOptions.None
            });
        }
        /// <summary>
        /// Recupera container condicional.
        /// </summary>
        /// <param name="enumerator">Enumerador dos items que serão analizados.</param>
        /// <param name="configuration"></param>
        /// <param name="containerInfo"></param>
        /// <returns></returns>
        private static ConditionalTerm GetContainer(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration, Text.InterpreterExpression.ContainerChars containerInfo)
        {
            ConditionalContainer container = null;
            var logical = Text.InterpreterExpression.TokenID.And;

            while (containerInfo == null || (string.IsNullOrEmpty(enumerator.Current.Text) || enumerator.Current.Text[0] != containerInfo.Stop))
            {
                var conditional = GetConditional(ref enumerator, configuration, containerInfo != null ? (char?)containerInfo.Stop : null);
                if (((conditional is ValuesArray) || (conditional is FunctionCall)) && container == null)
                {
                    return(conditional);
                }
                else if (container == null)
                {
                    container = new ConditionalContainer(conditional);
                }
                else if (logical == Text.InterpreterExpression.TokenID.And)
                {
                    container.And(conditional);
                }
                else if (logical == Text.InterpreterExpression.TokenID.Plus || logical == Text.InterpreterExpression.TokenID.Minus || logical == Text.InterpreterExpression.TokenID.Star || logical == Text.InterpreterExpression.TokenID.Slash)
                {
                    Operator oper = null;
                    switch (logical)
                    {
                    case Text.InterpreterExpression.TokenID.Plus:
                        oper = new Operator("+");
                        break;

                    case Text.InterpreterExpression.TokenID.Minus:
                        oper = new Operator("-");
                        break;

                    case Text.InterpreterExpression.TokenID.Star:
                        oper = new Operator("*");
                        break;

                    case Text.InterpreterExpression.TokenID.Slash:
                        oper = new Operator("/");
                        break;
                    }
                    ConditionalTerm left = container;
                    if (container.ConditionalsCount == 1)
                    {
                        left = container.Conditionals.First();
                    }
                    container = new ConditionalContainer(new Conditional(left, oper, conditional));
                }
                else
                {
                    container.Or(conditional);
                }
                if (enumerator.Current == null || (containerInfo != null && enumerator.Current.Text[0] == containerInfo.Stop))
                {
                    return(container);
                }
                else if (enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.And || enumerator.Current.Token == (int)Query.Parser.SqlTokenID.kAnd)
                {
                    logical = Text.InterpreterExpression.TokenID.And;
                }
                else if (enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Or || enumerator.Current.Token == (int)Query.Parser.SqlTokenID.kOr)
                {
                    logical = Text.InterpreterExpression.TokenID.Or;
                }
                else if (enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Plus || enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Minus || enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Star || enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Slash)
                {
                    logical = (Text.InterpreterExpression.TokenID)enumerator.Current.Token;
                }
                if (!enumerator.MoveNext())
                {
                    break;
                }
            }
            return(container);
        }
        /// <summary>
        /// Recupera o vetor de valores.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <param name="containerStop"></param>
        /// <param name="firstConstants"></param>
        /// <returns></returns>
        private static ConditionalTerm GetValuesArray(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration, char?containerStop, params ConditionalTerm[] firstConstants)
        {
            var values          = new List <ConditionalTerm>(firstConstants);
            var commaExpression = _conditionalLexer.TokenParser.GetTerm((int)Colosoft.Text.InterpreterExpression.TokenID.Comma);

            while (containerStop == null || enumerator.Current.Text[0] != containerStop)
            {
                if (enumerator.Current.Text != commaExpression)
                {
                    throw new ConditionalParserException(string.Format("Expected comma after '{0}'", values.Last().ToString()));
                }
                if (!enumerator.MoveNext())
                {
                    break;
                }
                var term = GetTerm(ref enumerator, configuration);
                values.Add(term);
                if (!enumerator.MoveNext())
                {
                    break;
                }
            }
            return(new ValuesArray()
            {
                Values = values.ToArray()
            });
        }
        /// <summary>
        /// Recupera uma condição.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <param name="containerStop">Caracter que identifica o fim da condicional.</param>
        /// <returns></returns>
        private static ConditionalTerm GetConditional(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration, char?containerStop)
        {
            var left           = GetTerm(ref enumerator, configuration);
            var isLeftNotToken = enumerator.Current != null && (enumerator.Current.Token == (int)Parser.SqlTokenID.kNot);
            int leftToken      = enumerator.Current != null ? (int)enumerator.Current.Token : -1;

            if (!enumerator.MoveNext() || enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.And || enumerator.Current.Token == (int)Parser.SqlTokenID.kAnd || enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.Or || enumerator.Current.Token == (int)Parser.SqlTokenID.kOr)
            {
                return(left);
            }
            if (containerStop != null && enumerator.Current.Text[0] == containerStop)
            {
                return(left);
            }
            var oper = enumerator.Current;

            if (Formula.IsArithmeticOperator(oper))
            {
                left = GetFormula(ref enumerator, configuration, left);
                oper = enumerator.Current;
                if (enumerator.Current == null || (containerStop != null && enumerator.Current.Text[0] == containerStop))
                {
                    return(left);
                }
            }
            var operToken = _conditionalLexer.TokenParser.Parse(oper.Text);

            if (left is Constant && operToken == (int)Text.InterpreterExpression.TokenID.LParen && (leftToken == (int)Parser.SqlTokenID.kDay || leftToken == (int)Parser.SqlTokenID.kMonth || leftToken == (int)Parser.SqlTokenID.kYear || leftToken == (int)Parser.SqlTokenID.kHour || leftToken == (int)Parser.SqlTokenID.kMinute || leftToken == (int)Parser.SqlTokenID.kSecond))
            {
                left = new Column(((Constant)left).Text);
            }
            if (isLeftNotToken && operToken == (int)Parser.SqlTokenID.kExists)
            {
                enumerator.MoveNext();
                left = GetFunctionCall(ref enumerator, configuration, containerStop, new Column("NOT EXISTS"));
                return(new Conditional(left, new Operator("NOT EXISTS"), null));
            }
            else if ((operToken == (int)Text.InterpreterExpression.TokenID.LParen) && (left is Column))
            {
                left = GetFunctionCall(ref enumerator, configuration, containerStop, (Column)left);
                var function = (FunctionCall)left;
                if (StringComparer.InvariantCultureIgnoreCase.Equals(((Column)function.Call).Name, "exists"))
                {
                    function.Call = new Constant("EXISTS");
                    return(new Conditional(left, new Operator("EXISTS"), null));
                }
                oper = enumerator.Current;
                if (oper == null)
                {
                    return(function);
                }
                operToken = _conditionalLexer.TokenParser.Parse(oper.Text);
            }
            else if (operToken == (int)Colosoft.Text.InterpreterExpression.TokenID.Comma)
            {
                return(GetValuesArray(ref enumerator, configuration, containerStop, left));
            }
            if (!enumerator.MoveNext())
            {
                if (left is FunctionCall)
                {
                    return(left);
                }
                throw new ConditionalParserException("Right conditional not found");
            }
            if (left is FunctionCall && enumerator.Current.Token == (int)Colosoft.Query.Parser.SqlTokenID.kEnd)
            {
                return(left);
            }
            var  optOper      = enumerator.Current;
            var  optOperToken = _conditionalLexer.TokenParser.Parse(optOper.Text);
            bool isOptOper    = (optOperToken == (int)Colosoft.Query.Parser.SqlTokenID.kNot) || (operToken == (int)Colosoft.Query.Parser.SqlTokenID.kNot);

            if (isOptOper)
            {
                if (!enumerator.MoveNext())
                {
                    throw new ConditionalParserException("Right conditional not found");
                }
            }
            var right = GetTerm(ref enumerator, configuration);

            enumerator.MoveNext();
            if ((enumerator.Current != null) && (enumerator.Current.Text[0] == '(') && (right is Column))
            {
                right = GetFunctionCall(ref enumerator, configuration, containerStop, (Column)right);
            }
            else if (operToken == (int)Colosoft.Query.Parser.SqlTokenID.kIn || optOperToken == (int)Colosoft.Query.Parser.SqlTokenID.kIn)
            {
                if (!(right is ValuesArray))
                {
                    var rcc = right as ConditionalContainer;
                    if (rcc != null)
                    {
                        right = new ValuesArray()
                        {
                            Values = rcc.Conditionals.ToArray()
                        };
                    }
                    else
                    {
                        right = new ValuesArray {
                            Values = new ConditionalTerm[] {
                                right
                            }
                        }
                    };
                }
            }
            return(new Conditional(left, new Operator((isOptOper) ? oper.Text + ' ' + optOper.Text : oper.Text), right));
        }
        /// <summary>
        /// Recupera o termo da condicional CASE.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        private static CaseConditional GetCaseConditional(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration)
        {
            if (!enumerator.MoveNext())
            {
                throw new ConditionalParserException("Expected input expression or WHEN keyword after CASE");
            }
            ConditionalTerm inputExpression = null;
            var             whenExpressions = new List <CaseWhenExpression>();

            if (enumerator.Current.Token != (int)Parser.SqlTokenID.kWhen)
            {
                inputExpression = GetTerm(ref enumerator, configuration);
                if (!enumerator.MoveNext())
                {
                    throw new ConditionalParserException("Expected expression WHEN");
                }
            }
            if (enumerator.Current.Token == (int)Parser.SqlTokenID.kWhen)
            {
                while (enumerator.Current.Token == (int)Parser.SqlTokenID.kWhen)
                {
                    if (!enumerator.MoveNext())
                    {
                        throw new ConditionalParserException("Expected expression after WHEN");
                    }
                    IEnumerator <Text.InterpreterExpression.Expression> enumerator2 = new StopControlEnumerator(enumerator, new StopControlEnumerator.ContainerStartStop[] {
                        new StopControlEnumerator.ContainerStartStop("CASE", "END")
                    }, new string[] {
                        "THEN"
                    }, StringComparer.InvariantCultureIgnoreCase);
                    var expression = GetTerm(ref enumerator2, configuration);
                    if (!enumerator.MoveNext() || enumerator.Current.Token != (int)Parser.SqlTokenID.kThen)
                    {
                        throw new ConditionalParserException(string.Format("Expected THEN after {0}", expression.ToString()));
                    }
                    if (!enumerator2.MoveNext())
                    {
                        throw new ConditionalParserException("Expected expression after THEN");
                    }
                    enumerator2 = new StopControlEnumerator(enumerator, new StopControlEnumerator.ContainerStartStop[] {
                        new StopControlEnumerator.ContainerStartStop("CASE", "END")
                    }, new string[] {
                        "THEN",
                        "ELSE",
                        "END"
                    }, StringComparer.InvariantCultureIgnoreCase);
                    var resultExpression = GetTerm(ref enumerator2, configuration);
                    whenExpressions.Add(new CaseWhenExpression(expression, resultExpression));
                    if (!enumerator2.MoveNext())
                    {
                        break;
                    }
                }
            }
            else
            {
                throw new ConditionalParserException("Expected expression WHEN");
            }
            ConditionalTerm elseExpression = null;

            if (enumerator.Current.Token == (int)Parser.SqlTokenID.kElse)
            {
                if (enumerator.MoveNext())
                {
                    IEnumerator <Text.InterpreterExpression.Expression> enumerator2 = new StopControlEnumerator(enumerator, new StopControlEnumerator.ContainerStartStop[] {
                        new StopControlEnumerator.ContainerStartStop("CASE", "END")
                    }, new string[] {
                        "END"
                    }, StringComparer.InvariantCultureIgnoreCase);
                    elseExpression = GetTerm(ref enumerator2, configuration);
                }
                if (elseExpression is FunctionCall && enumerator.Current.Token == (int)Parser.SqlTokenID.kEnd)
                {
                }
                else if (!enumerator.MoveNext())
                {
                    throw new ConditionalParserException("Expected expression END");
                }
            }
            if (enumerator.Current.Token == (int)Parser.SqlTokenID.kEnd)
            {
                return(new CaseConditional(inputExpression, whenExpressions, elseExpression));
            }
            throw new ConditionalParserException("Expected expression END");
        }
        /// <summary>
        /// Recupera a formula contida nas expressões.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <param name="firstPart"></param>
        /// <returns></returns>
        private static Formula GetFormula(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration, ConditionalTerm firstPart)
        {
            var formula = new Formula();

            formula.Parts.Add(firstPart);
            formula.Operators.Add(Formula.GetMathematicalOperator(enumerator.Current));
            while (true)
            {
                if (!enumerator.MoveNext())
                {
                    throw new ConditionalParserException(string.Format("Expected expression after mathematical operator {0}", formula.Operators.First().ToString()));
                }
                var part = GetTerm(ref enumerator, configuration);
                if (!enumerator.MoveNext())
                {
                    formula.Parts.Add(part);
                    break;
                }
                if (part is Column && (enumerator.Current.Token == (int)Text.InterpreterExpression.TokenID.LParen))
                {
                    part = GetFunctionCall(ref enumerator, configuration, ')', (Column)part);
                }
                formula.Parts.Add(part);
                if (Formula.IsArithmeticOperator(enumerator.Current))
                {
                    formula.Operators.Add(Formula.GetMathematicalOperator(enumerator.Current));
                }
                else
                {
                    break;
                }
            }
            return(formula);
        }
        /// <summary>
        /// Recupera o valor do termo condicional.
        /// </summary>
        /// <param name="enumerator"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        private static ConditionalTerm GetTerm(ref IEnumerator <Text.InterpreterExpression.Expression> enumerator, Text.InterpreterExpression.ILexerConfiguration configuration)
        {
            var item = enumerator.Current;

            if (item.Token == (int)Text.InterpreterExpression.TokenID.Minus)
            {
                if (!enumerator.MoveNext())
                {
                    throw new ConditionalParserException("Expected expression after minus");
                }
                return(new MinusTerm(GetTerm(ref enumerator, configuration)));
            }
            else if (item.Token == (int)Parser.SqlTokenID.kCase)
            {
                return(GetCaseConditional(ref enumerator, configuration));
            }
            var container = item.Text.Length > 0 && item.Token != (int)Colosoft.Text.InterpreterExpression.TokenID.StringLiteral ? Array.Find(configuration.Containers, f => f.Start == item.Text[0]) : null;

            if (container != null)
            {
                enumerator.MoveNext();
                return(GetContainer(ref enumerator, configuration, container));
            }
            var expression = new Parser.SqlExpression(item);

            if (expression.Type == Parser.SqlExpressionType.Column)
            {
                return(new Column(expression));
            }
            else if (expression.Type == Parser.SqlExpressionType.Variable)
            {
                return(new Variable(expression.Value.Text));
            }
            return(new Constant(item.ToString()));
        }