context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text.RegularExpressions;
  using Antlr.Runtime;
  using Antlr.Runtime.Tree;
  using System.Globalization;
  
  namespace FxGqlLib
  {
  	partial class GqlParser
  	{
  		IExpression ParseExpression (IProvider provider, ITree tree)
  		{
  			IExpression expression;
  			switch (tree.Text.ToUpperInvariant ()) {
  			case "T_NUMBER":
  				expression = ParseExpressionNumber (tree);
  				break;
  			case "T_STRING":
  				expression = ParseExpressionString (tree);
  				break;
  			case "T_SYSTEMVAR":
  				expression = ParseExpressionSystemVar (tree);
  				break;
  			case "T_FUNCTIONCALL":
  				expression = ParseExpressionFunctionCall (provider, tree);
  				break;
  			case "T_CONVERT":
  				expression = ParseExpressionConvert (provider, tree);
  				break;
  			case "T_OP_UNARY":
  				expression = ParseExpressionOperatorUnary (provider, tree);
  				break;
  			case "T_OP_BINARY":
  				expression = ParseExpressionOperatorBinary (provider, tree);
  				break;
  			case "T_EXISTS":
  				expression = ParseExpressionExists (tree);
  				break;
  			case "T_COLUMN":
  				expression = ParseExpressionColumn (provider, tree);
  				break;
  			case "T_CASE":
  				expression = ParseExpressionCase (provider, tree);
  				break;
  			case "T_VARIABLE":
  				expression = ParseExpressionVariable (tree);
  				break;
  			case "T_SUBQUERY":
  				expression = ParseExpressionSubquery (provider, tree);
  				break;
  			case "T_DATEPART":
  				expression = ParseExpressionDatePart (tree);
  				break;
  			default:
  				throw new UnexpectedTokenAntlrException (tree);
  			}
  			
  			return expression;
  		}
  
  		IExpression ParseExpressionNumber (ITree expressionNumberTree)
  		{
  			string text;
  			if (expressionNumberTree.ChildCount == 1)
  				text = expressionNumberTree.GetChild (0).Text;
  			else
  				text = expressionNumberTree.GetChild (0).Text + expressionNumberTree.GetChild (1).Text;
  
  			long multiplier = 1;
  			if (text.Length > 0) {
  				char suffix = text [text.Length - 1];
  				if (!char.IsDigit (suffix)) {
  					switch (suffix) {
  					case 'k':
  						multiplier = 1000L;
  						break;
  					case 'M':
  						multiplier = 1000L * 1000;
  						break;
  					case 'G':
  						multiplier = 1000L * 1000 * 1000;
  						break;
  					case 'T':
  						multiplier = 1000L * 1000 * 1000 * 1000;
  						break;
  					case 'P':
  						multiplier = 1000L * 1000 * 1000 * 1000 * 1000;
  						break;
  					case 'E':
  						multiplier = 1000L * 1000 * 1000 * 1000 * 1000 * 1000;
  						break;
  					default:
  						throw new Exception (string.Format ("Unknown number suffix '{0}'", suffix));
  					}
  					text = text.Substring (0, text.Length - 1);
  				}
  			}
  
  			/* Number Literals are 'invariant' (with '.' as decimal point indicator) */
  			/* Numbers converted from strings (string literals or string input from file) follow the current culture, 
  			as specified by SET CULTURE */
  			if (text.Contains ('.'))
  				return new ConstExpression<DataFloat> (double.Parse (text, CultureInfo.InvariantCulture.NumberFormat) * multiplier);
  			else
  				return new ConstExpression<DataInteger> (long.Parse (text, CultureInfo.InvariantCulture.NumberFormat) * multiplier);
  		}
  
  		class Token<T> : IExpression
  		{
  			public T Value { get; set; }
  
  			public Token (T value)
  			{
  				Value = value;
  			}
  
  			#region IExpression implementation
  
  			public IData EvaluateAsData (GqlQueryState gqlQueryState)
  			{
  				throw new InvalidOperationException ();
  			}
  
  			public Type GetResultType ()
  			{
  				throw new InvalidOperationException ();
  			}
  
  			public bool IsAggregated ()
  			{
  				return false;
  			}
  
              public bool HasState()
              {
                  return false;
              }
  
              public bool IsConstant ()
  			{
  				return true;
  			}
  
  			public void Process (StateBin state, GqlQueryState gqlQueryState)
  			{
  				throw new InvalidOperationException ();
  			}
  
  			public IData ProcessCalculate (StateBin state)
  			{
  				throw new InvalidOperationException ();
  			}
  
  			#endregion
  
  		}
  
  		IExpression ParseExpressionDatePart (ITree datePartTree)
  		{
  			ITree tree = GetSingleChild (datePartTree);
  			
  			DatePartType datePart;
  			try {
  				datePart = DatePartHelper.Parse (tree.Text);
  			} catch (Exception x) {
  				throw new ParserException ("Invalid DatePart type", tree, x);
  			}
  			
  			return new Token<DatePartType> (datePart);
  		}
  
  		Expression<DataString> ParseExpressionString (ITree expressionStringTree)
  		{
  			ITree tree = GetSingleChild (expressionStringTree);
  			
  			string text = ParseString (tree);
  			return new ConstExpression<DataString> (text);
  		}
  
  		IExpression ParseExpressionSystemVar (ITree expressionSystemVarTree)
  		{
  			ITree tree = GetSingleChild (expressionSystemVarTree);
  			
  			IExpression expression;
  			switch (tree.Text.ToUpperInvariant ()) {
  			case "$LINE":
  				expression = new LineSystemVar ();
  				break;
  			case "$TOTALLINENO":
  				expression = new TotalLineNoSystemVar ();
  				break;
  			case "$FILENAME":
  				expression = new FileNameSystemVar (false);
  				break;
  			case "$FULLFILENAME":
  				expression = new FileNameSystemVar (true);
  				break;
  			case "$LINENO":
  				expression = new LineNoSystemVar ();
  				break;
  			default:
  				throw new ParserException (
  					string.Format ("Unknown system variable '{0}'.", tree.Text),
  					tree
  				);
  			}
  			
  			return expression;
  		}
  
  		IExpression ParseExpressionFunctionCall (IProvider provider, ITree functionCallTree)
  		{
  			AssertAntlrToken (functionCallTree, "T_FUNCTIONCALL", 1, -1);
  			
  			string functionName = functionCallTree.GetChild (0).Text;
  			
  			IExpression result;
  			int argCount = functionCallTree.ChildCount - 1;
  			switch (argCount) {
  			case 0:
  				result = ParseExpressionFunctionCall_0 (
  					provider,
  					functionCallTree,
  					functionName
  				);
  				break;
  			case 1:
  				result = ParseExpressionFunctionCall_1 (
  					provider,
  					functionCallTree,
  					functionName
  				);
  				break;
  			case 2:
  				result = ParseExpressionFunctionCall_2 (
  					provider,
  					functionCallTree,
  					functionName
  				);
  				break;
  			case 3:
  				result = ParseExpressionFunctionCall_3 (
  					provider,
  					functionCallTree,
  					functionName
  				);
  				break;
  			case 4:
  				result = ParseExpressionFunctionCall_4 (
  					provider,
  					functionCallTree,
  					functionName
  				);
  				break;
  			default:
  				throw new ParserException (
  					string.Format (
  						"Function call with '{0}' arguments not supported.",
  						argCount
  					),
  					functionCallTree
  				);
  			}
  			
  			return result;
  		}
  
  		IExpression ParseExpressionFunctionCall_0 (IProvider provider, ITree functionCallTree, string functionName)
  		{
  			IExpression result;
  			
  			switch (functionName.ToUpperInvariant ()) {
  			case "GETCURDIR":
  				result = new GetCurDirFunction ();
  				break;
  			case "GETDATE":
  				result = new NullaryExpression<DataDateTime> (() => DateTime.Now);
  				break;
  			case "GETUTCDATE":
  				result = new NullaryExpression<DataDateTime> (() => DateTime.UtcNow);
  				break;
  			default:
  				throw new ParserException (string.Format (
  					"Function call to {0} with 0 parameters not supported.",
  					functionName
  				), 
  					functionCallTree);
  			}
  			
  			return result;
  		}
  
  		IExpression ParseExpressionFunctionCall_1 (IProvider provider, ITree functionCallTree, string functionName)
  		{
  			IExpression arg;
  			
  			string functionNameUpper = functionName.ToUpperInvariant ();
  			if ((functionNameUpper == "T_COUNT" || functionNameUpper == "T_DISTINCTCOUNT") && functionCallTree.GetChild (1).Text == "T_ALLCOLUMNS") {
  				ITree allColumnsTree = functionCallTree.GetChild (1);
  				string providerAlias;
  				if (allColumnsTree.ChildCount == 1)
  					providerAlias = ParseProviderAlias (allColumnsTree.GetChild (0));
  				else
  					providerAlias = null;
  				
  				if (providerAlias != null) {
  					string[] providerAliases = provider.GetAliases ();
  					if (!providerAliases.Any (p => StringComparer.InvariantCultureIgnoreCase.Compare (p, providerAlias) == 0)) {
  						throw new InvalidOperationException (string.Format ("Invalid provider alias '{0}'", providerAlias));
  					}
  				}
  				arg = new ConstExpression<DataInteger> (1);
  			} else {
  				arg = ParseExpression (provider, functionCallTree.GetChild (1));
  			}
  			
  			IExpression result;
  			
  			switch (functionNameUpper) {
  			case "ESCAPEREGEX":
  				result = UnaryExpression<DataString, DataString>.CreateAutoConvert ((a) => Regex.Escape (a), arg, cultureInfo);
  				break;
  			case "LTRIM":
  				result = UnaryExpression<DataString, DataString>.CreateAutoConvert ((a) => a.Value.TrimStart (), arg, cultureInfo);
  				break;
  			case "RTRIM":
  				result = UnaryExpression<DataString, DataString>.CreateAutoConvert ((a) => a.Value.TrimEnd (), arg, cultureInfo);
  				break;
  			case "TRIM":
  				result = UnaryExpression<DataString, DataString>.CreateAutoConvert ((a) => a.Value.Trim (), arg, cultureInfo);
  				break;
  			case "LEN":
  				result = UnaryExpression<DataString, DataInteger>.CreateAutoConvert ((a) => a.Value.Length, arg, cultureInfo);
  				break;
  			case "ABS":
  				if (arg.GetResultType () == typeof(DataFloat))
  					result = UnaryExpression<DataFloat, DataFloat>.CreateAutoConvert ((a) => Math.Abs (a.Value), arg, cultureInfo);
  				else
  					result = UnaryExpression<DataInteger, DataInteger>.CreateAutoConvert ((a) => Math.Abs (a.Value), arg, cultureInfo);
  				break;
              case "LAG":
                  result = LagExpressionFactory.Create(arg);
                  break;
  			//case "COUNT":
  			case "T_COUNT":
  				result = new AggregationExpression<IData, DataInteger, DataInteger> ((a) => 1, 
  					(s, a) => s + 1, 
  					(s) => s, 
  					ConvertExpression.CreateData (arg));
  				break;
  			case "T_DISTINCTCOUNT":
  				result = new AggregationExpression<IData, SortedSet<ColumnsComparerKey>, DataInteger> (
  					(a) => new SortedSet<ColumnsComparerKey> (),
  					delegate(SortedSet<ColumnsComparerKey> s, IData a) {
  						ColumnsComparerKey columnsComparerKey = new ColumnsComparerKey (new IData[] { a });
  						if (!s.Contains (columnsComparerKey))
  							s.Add (columnsComparerKey);
  						return s;
  					},
  					(s) => s.Count, 
  					ConvertExpression.CreateData (arg),
  					true);
  				break;
  			case "SUM":
  				if (arg is Expression<DataInteger>)
  					result = new AggregationExpression<DataInteger, DataInteger, DataInteger> (
  						(a) => a, 
  						(s, a) => s + a, 
  						(s) => s, 
  						arg as Expression<DataInteger>);
  				else if (arg is Expression<DataFloat>)
  					result = new AggregationExpression<DataFloat, DataFloat, DataFloat> (
  						(a) => a, 
  						(s, a) => s + a, 
  						(s) => s, 
  						arg as Expression<DataFloat>);
  				else {
  					throw new ParserException (
  						string.Format ("SUM aggregation function cannot be used on datatype '{0}'",
  							arg.GetResultType ().ToString ()),
  						functionCallTree);
  				}
  				break;
  			case "MIN":
  				if (arg.GetResultType () == typeof(DataString))
  					result = new AggregationExpression<DataString, DataString, DataString> (
  						(a) => a, 
  						(s, a) => string.Compare (a, s, dataComparer.StringComparison) < 0 ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataString (arg, cultureInfo));
  				else if (arg.GetResultType () == typeof(DataInteger))
  					result = new AggregationExpression<DataInteger, DataInteger, DataInteger> (
  						(a) => a, 
  						(s, a) => a < s ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataInteger (arg, cultureInfo));
  				else if (arg.GetResultType () == typeof(DataFloat))
  					result = new AggregationExpression<DataFloat, DataFloat, DataFloat> (
  						(a) => a, 
  						(s, a) => a < s ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataFloat (arg, cultureInfo));
  				else {
  					throw new ParserException (
  						string.Format ("MIN aggregation function cannot be used on datatype '{0}'",
  							arg.GetResultType ().ToString ()),
  						functionCallTree);
  				}
  				break;
  			case "MAX":
  				if (arg.GetResultType () == typeof(DataString))
  					result = new AggregationExpression<DataString, DataString, DataString> (
  						(a) => a, 
  						(s, a) => string.Compare (a, s, dataComparer.StringComparison) > 0 ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataString (arg, cultureInfo));
  				else if (arg.GetResultType () == typeof(DataInteger))
  					result = new AggregationExpression<DataInteger, DataInteger, DataInteger> (
  						(a) => a, 
  						(s, a) => a > s ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataInteger (arg, cultureInfo));
  				else if (arg.GetResultType () == typeof(DataFloat))
  					result = new AggregationExpression<DataFloat, DataFloat, DataFloat> (
  						(a) => a, 
  						(s, a) => a > s ? a : s, 
  						(s) => s, 
  						ConvertExpression.CreateDataFloat (arg, cultureInfo));
  				else {
  					throw new ParserException (
  						string.Format ("MAX aggregation function cannot be used on datatype '{0}'",
  							arg.GetResultType ().ToString ()),
  						functionCallTree);
  				}
  				break;
  			case "FIRST":
  				result = new AggregationExpression<IData, IData, IData> (
  					(a) => a, 
  					(s, a) => s, 
  					(s) => s, 
  					ConvertExpression.CreateData (arg));
  				break;
  			case "LAST":
  				result = new AggregationExpression<IData, IData, IData> (
  					(a) => a, 
  					(s, a) => a, 
  					(s) => s, 
  					ConvertExpression.CreateData (arg));
  				break;
  			case "AVG":
  				if (arg is Expression<DataInteger>) {
  					Expression<DataInteger> resultSum = new AggregationExpression<DataInteger, DataInteger, DataInteger> (
  						                                    (a) => a, 
  						                                    (s, a) => s + a, 
  						                                    (s) => s, 
  						                                    arg as Expression<DataInteger>);
  					Expression<DataInteger> resultCount = new AggregationExpression<DataInteger, DataInteger, DataInteger> (
  						                                      (a) => 1, 
  						                                      (s, a) => s + 1, 
  						                                      (s) => s, 
  						                                      arg as Expression<DataInteger>);
  					result = new BinaryExpression<DataInteger, DataInteger, DataInteger> (
  						(a, b) => a / b, resultSum, resultCount);
  				} else if (arg is Expression<DataFloat>) {
  					Expression<DataFloat> resultSum = new AggregationExpression<DataFloat, DataFloat, DataFloat> (
  						                                  (a) => a, 
  						                                  (s, a) => s + a, 
  						                                  (s) => s, 
  						                                  arg as Expression<DataFloat>);
  					Expression<DataInteger> resultCount = new AggregationExpression<DataFloat, DataInteger, DataInteger> (
  						                                      (a) => 1, 
  						                                      (s, a) => s + 1, 
  						                                      (s) => s, 
  						                                      arg as Expression<DataFloat>);
  					result = new BinaryExpression<DataFloat, DataInteger, DataFloat> (
  
gt: 						(a, b) => a / b, resultSum, resultCount);