context: /* ====================================================================
      Copyright (C) 2004-2006  fyiReporting Software, LLC
  
      This file is part of the fyiReporting RDL project.
  	
      This library is free software; you can redistribute it and/or modify
      it under the terms of the GNU Lesser General Public License as published by
      the Free Software Foundation; either version 2.1 of the License, or
      (at your option) any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU Lesser General Public License for more details.
  
      You should have received a copy of the GNU Lesser General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
  
      For additional information, email info@fyireporting.com or visit
      the website www.fyiReporting.com.
  */
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Globalization;
  using System.Xml;
  using System.Reflection;
  using fyiReporting.RDL;
  
  
  namespace fyiReporting.RDL
  {
  	/// <summary>
  	/// <p>Language parser.   Recursive descent parser.  Precedence of operators
  	/// is handled by starting with lowest precedence and calling down recursively
  	/// to the highest.</p>
  	/// AND/OR
  	/// NOT
  	/// relational operators, eq, ne, lt, lte, gt, gte
  	/// +, -
  	/// *, /, %
  	/// ^ (exponentiation)
  	/// unary +, -
  	/// parenthesis (...)
  	/// <p>
  	/// In BNF the language grammar is:</p>
  	///	<code>
  	/// Expr: Term ExprRhs
  	/// ExprRhs: PlusMinusOperator Term ExprRhs
  	/// Term: Factor TermRhs
  	/// TermRhs: MultDivOperator Factor TermRhs
  	/// Factor: ( Expr ) | BaseType | - BaseType | - ( Expr )
  	/// BaseType: FuncIdent | NUMBER | QUOTE   
  	/// FuncIDent: IDENTIFIER ( [Expr] [, Expr]*) | IDENTIFIER
  	/// PlusMinusOperator: + | -
  	/// MultDivOperator: * | /
  	///	</code>
  	///	
  	/// </summary>
  	internal class Parser
  	{
  		static internal long Counter;			// counter used for unique expression count
  		private TokenList tokens;
  		private Stack operandStack = new Stack();
  		private Stack operatorStack = new Stack();
  		private Token curToken=null;
  		private NameLookup idLookup=null;
          private List<ICacheData> _DataCache;
  		private bool _InAggregate;
          private List<FunctionField> _FieldResolve;
  		private bool _NoAggregate=false;
  
  		/// <summary>
  		/// Parse an expression.
  		/// </summary>
          internal Parser(List<ICacheData> c) 
  		{
  			_DataCache = c;
  		}
  
  		/// <summary>
  		/// Returns a parsed Expression
  		/// </summary>
  		/// <param name="lu">The NameLookUp class used to resolve names.</param>
  		/// <param name="expr">The expression to be parsed.</param>
  		/// <returns>An expression that can be run after validation and binding.</returns>
  		internal IExpr Parse(NameLookup lu, string expr)
  		{
  			_InAggregate = false;
  
  			if (expr.Substring(0,1) != "=")		// if 1st char not '='
  				return new Constant(expr);		//   this is a constant value
  
  			idLookup = lu;	
  			IExpr e = this.ParseExpr(new StringReader(expr));
  			
  			if (e == null)					// Didn't get an expression?
  				e = new Constant(expr);		//  then provide a constant
  
  			return e;
  		}
  
  		internal bool NoAggregateFunctions
  		{	// marked true when in an aggregate function 
  			get {return _NoAggregate;}
  			set {_NoAggregate = value;}
  		}
  
  		/// <summary>
  		/// Returns a parsed DPL instance.
  		/// </summary>
  		/// <param name="reader">The TextReader value to be parsed.</param>
  		/// <returns>A parsed Program instance.</returns>
  		private IExpr ParseExpr(TextReader reader)
  		{
  			IExpr result=null;
  			Lexer lexer = new Lexer(reader);
  			tokens = lexer.Lex();
  
  			if (tokens.Peek().Type == TokenTypes.EQUAL)
  			{
  				tokens.Extract();		// skip over the equal
  				curToken = tokens.Extract();	// set up the first token
  				MatchExprAndOr(out result);	// start with lowest precedence and work up
  			}
  
  			if (curToken.Type != TokenTypes.EOF)
  				throw new ParserException("End of expression expected." + "  At column " + Convert.ToString(curToken.StartCol));
  
  			return result;
  		}
  
  		// ExprAndOr: 
  		private void MatchExprAndOr(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  
  			IExpr lhs;
  			MatchExprNot(out lhs);
  			result = lhs;			// in case we get no matches
  			while ((t = curToken.Type) == TokenTypes.AND || t == TokenTypes.OR)
  			{
  				curToken = tokens.Extract();
  				IExpr rhs;
  				MatchExprNot(out rhs);
  				bool bBool = (rhs.GetTypeCode() == TypeCode.Boolean &&
  					lhs.GetTypeCode() == TypeCode.Boolean);
  				if (!bBool)
  					throw new ParserException("AND/OR operations require both sides to be boolean expressions." + "  At column " + Convert.ToString(curToken.StartCol));
  
  				switch(t)
  				{
  					case TokenTypes.AND:
  						result = new FunctionAnd(lhs, rhs);
  						break;
  					case TokenTypes.OR:
  						result = new FunctionOr(lhs, rhs);
  						break;
  				}
  				lhs = result;		// in case we have more AND/OR s
  			}
  		}
  		
  		private void MatchExprNot(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  			t = curToken.Type;
  			if (t == TokenTypes.NOT)
  			{
  				curToken = tokens.Extract();
  			}
  			MatchExprRelop(out result);
  			if (t == TokenTypes.NOT)
  			{
  				if (result.GetTypeCode() != TypeCode.Boolean)
  					throw new ParserException("NOT requires boolean expression." + "  At column " + Convert.ToString(curToken.StartCol));
  				result = new FunctionNot(result);
  			}
  		}
  
  		// ExprRelop: 
  		private void MatchExprRelop(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  
  			IExpr lhs;
  			MatchExprAddSub(out lhs);
  			result = lhs;			// in case we get no matches
  			while ((t = curToken.Type) == TokenTypes.EQUAL ||
  				t == TokenTypes.NOTEQUAL ||
  				t == TokenTypes.GREATERTHAN ||
  				t == TokenTypes.GREATERTHANOREQUAL ||
  				t == TokenTypes.LESSTHAN ||
  				t == TokenTypes.LESSTHANOREQUAL)
  			{
  				curToken = tokens.Extract();
  				IExpr rhs;
  				MatchExprAddSub(out rhs);
  
  				switch(t)
  				{
  					case TokenTypes.EQUAL:
  						result = new FunctionRelopEQ(lhs, rhs);
  						break;
  					case TokenTypes.NOTEQUAL:
  						result = new FunctionRelopNE(lhs, rhs);
  						break;
  					case TokenTypes.GREATERTHAN:
  						result = new FunctionRelopGT(lhs, rhs);
  						break;
  					case TokenTypes.GREATERTHANOREQUAL:
  						result = new FunctionRelopGTE(lhs, rhs);
  						break;
  					case TokenTypes.LESSTHAN:
  						result = new FunctionRelopLT(lhs, rhs);
  						break;
  					case TokenTypes.LESSTHANOREQUAL:
  						result = new FunctionRelopLTE(lhs, rhs);
  						break;
  				}
  				lhs = result;		// in case we continue the loop
  			}
  		}
  
  		// ExprAddSub: PlusMinusOperator Term ExprRhs
  		private void MatchExprAddSub(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  
  			IExpr lhs;
  			MatchExprMultDiv(out lhs);
  			result = lhs;			// in case we get no matches
  			while ((t = curToken.Type) == TokenTypes.PLUS || t == TokenTypes.PLUSSTRING || t == TokenTypes.MINUS)
  			{
  				curToken = tokens.Extract();
  				IExpr rhs;
  				MatchExprMultDiv(out rhs);
  				TypeCode lt = lhs.GetTypeCode();
  				TypeCode rt = rhs.GetTypeCode();
  				bool bDecimal = (rt == TypeCode.Decimal &&
  					lt == TypeCode.Decimal);
  				bool bString = (rt == TypeCode.String ||
  					lt == TypeCode.String);
  
  				switch(t)
  				{
  					case TokenTypes.PLUSSTRING:
  						result = new FunctionPlusString(lhs, rhs);
  						break;
  					case TokenTypes.PLUS:
  						if (bDecimal)
  							result = new FunctionPlusDecimal(lhs, rhs);
  						else if (bString)
  							result = new FunctionPlusString(lhs, rhs);
  						else
  							result = new FunctionPlus(lhs, rhs);
  						break;
  					case TokenTypes.MINUS:
  						if (bDecimal)
  							result = new FunctionMinusDecimal(lhs, rhs);
  						else if (bString)
  							throw new ParserException("'-' operator works only on numbers." + "  At column " + Convert.ToString(curToken.StartCol));
  						else
  							result = new FunctionMinus(lhs, rhs);
  						break;
  				}
  				lhs = result;		// in case continue in the loop
  			}
  		}
  
  		// TermRhs: MultDivOperator Factor TermRhs
  		private void MatchExprMultDiv(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  			IExpr lhs;
  			MatchExprExp(out lhs);
  			result = lhs;			// in case we get no matches
  			while ((t = curToken.Type) == TokenTypes.FORWARDSLASH ||
  				t == TokenTypes.STAR ||
  				t == TokenTypes.MODULUS)
  			{
  				curToken = tokens.Extract();
  				IExpr rhs;
  				MatchExprExp(out rhs);
  				bool bDecimal = (rhs.GetTypeCode() == TypeCode.Decimal &&
  					lhs.GetTypeCode() == TypeCode.Decimal);
  				switch (t)
  				{
  					case TokenTypes.FORWARDSLASH:
  						if (bDecimal)
  							result = new FunctionDivDecimal(lhs, rhs);	
  						else
  							result = new FunctionDiv(lhs, rhs);	
  						break;
  					case TokenTypes.STAR:
  						if (bDecimal)
  							result = new FunctionMultDecimal(lhs, rhs);
  						else
  							result = new FunctionMult(lhs, rhs);
  						break;
  					case TokenTypes.MODULUS:
  						result = new FunctionModulus(lhs, rhs);
  						break;
  				}
  				lhs = result;		// in case continue in the loop
  			}
  		}
  
  		// TermRhs: ExpOperator Factor TermRhs
  		private void MatchExprExp(out IExpr result)
  		{
  			IExpr lhs;
  			MatchExprUnary(out lhs);
  			if (curToken.Type == TokenTypes.EXP)
  			{
  				curToken = tokens.Extract();
  				IExpr rhs;
  				MatchExprUnary(out rhs);
  				result = new FunctionExp(lhs, rhs);	
  			}
  			else
  				result = lhs;
  		}
  		
  		private void MatchExprUnary(out IExpr result)
  		{
  			TokenTypes t;			// remember the type
  			t = curToken.Type;
  			if (t == TokenTypes.PLUS || t == TokenTypes.MINUS)
  			{
  				curToken = tokens.Extract();
  			}
  			MatchExprParen(out result);
  			if (t == TokenTypes.MINUS)
  			{
  				if (result.GetTypeCode() == TypeCode.Decimal)
  					result = new FunctionUnaryMinusDecimal(result);
  				else if (result.GetTypeCode() == TypeCode.Int32)
  					result = new FunctionUnaryMinusInteger(result);
  				else
  					result = new FunctionUnaryMinus(result);
  			}
  		}
  		
  		// Factor: ( Expr ) | BaseType | - BaseType | - ( Expr )
  		private void MatchExprParen(out IExpr result)
  		{
  			// Match- ( Expr )
  			if (curToken.Type == TokenTypes.LPAREN)
  			{	// trying to match ( Expr )
  				curToken = tokens.Extract();
  				MatchExprAndOr(out result);
  				if (curToken.Type != TokenTypes.RPAREN)
  					throw new ParserException("')' expected but not found.  Found '" + curToken.Value + "'  At column " + Convert.ToString(curToken.StartCol));
  				curToken = tokens.Extract();
  			}
  			else
  				MatchBaseType(out result);
  		}
  
  		// BaseType: FuncIdent | NUMBER | QUOTE   - note certain types are restricted in expressions
  		private void MatchBaseType(out IExpr result)
  		{
  			if (MatchFuncIDent(out result))
  				return;
  
  			switch (curToken.Type)
  			{
  				case TokenTypes.NUMBER:
  					result = new ConstantDecimal(curToken.Value);
  					break;
  				case TokenTypes.DATETIME:
  					result = new ConstantDateTime(curToken.Value);
  					break;
  				case TokenTypes.DOUBLE:
  					result = new ConstantDouble(curToken.Value);
  					break;
  				case TokenTypes.INTEGER:
  					result = new ConstantInteger(curToken.Value);
  					break;
  				case TokenTypes.QUOTE:
  					result = new ConstantString(curToken.Value);
  					break;
  				default:
  					throw new ParserException("Constant or Identifier expected but not found.  Found '" + curToken.Value + "'  At column " + Convert.ToString(curToken.StartCol));
  			}
  			curToken = tokens.Extract();
  
  			return;
  		}
  
  		// FuncIDent: IDENTIFIER ( [Expr] [, Expr]*) | IDENTIFIER
  		private bool MatchFuncIDent(out IExpr result)
  		{
  			IExpr e;
  			string fullname;			// will hold the full name
  			string method;				// will hold method name or second part of name
  			string firstPart;			// will hold the collection name
  			string thirdPart;			// will hold third part of name
  			bool bOnePart;				// simple name: no ! or . in name
  
  			result = null;
  
  			if (curToken.Type != TokenTypes.IDENTIFIER)
  				return false;
  
  			// Disentangle method calls from collection references
  			method = fullname = curToken.Value;
  			curToken = tokens.Extract();
  
  			// Break the name into parts
  			char[] breakChars = new char[] {'!', '.'};
  
  			int posBreak = method.IndexOfAny(breakChars);
  			if (posBreak > 0)
  			{
  				bOnePart = false;
  				firstPart = method.Substring(0, posBreak);
  				method = method.Substring(posBreak+1);		// rest of expression
  			}
  			else
  			{
  				bOnePart = true;
  				firstPart = method;
  			}
  
  			posBreak = method.IndexOf('.');
  			if (posBreak > 0)
  			{
  				thirdPart = method.Substring(posBreak+1);	// rest of expression
  				method = method.Substring(0, posBreak);
  			}
  			else
  				thirdPart = null;
  
  			if (curToken.Type != TokenTypes.LPAREN) switch (firstPart)
  			{
  				case "Fields":
  					Field f = idLookup.LookupField(method);
  					if (f == null && !this._InAggregate)
  						throw new ParserException("Field '" + method + "'  not found.");
  					if (thirdPart == null || thirdPart == "Value")
  					{
  						if (f == null)
  						{
                              FunctionField ff;
                              result = ff = new FunctionField(method);
  							this._FieldResolve.Add(ff);
  						}
  						else
  							result = new FunctionField(f);	
  					}
  					else if (thirdPart == "IsMissing")
  					{
  						if (f == null)
  						{
                              FunctionField ff;
  							result = ff = new FunctionFieldIsMissing(method);
  							this._FieldResolve.Add(ff);
  						}
  						else
  							result = new FunctionFieldIsMissing(f);
  					}
  					else
  						throw new ParserException("Field '" + method + "'  only supports 'Value' and 'IsMissing' properties.");
  					return true;
                  case "Parameters":  // see ResolveParametersMethod for resolution of MultiValue parameter function reference
  					ReportParameter p = idLookup.LookupParameter(method);
  					if (p == null)
  						throw new ParserException("Report parameter '" + method + "'  not found.");
                      int ci = thirdPart == null? -1: thirdPart.IndexOf(".Count");
                      if (ci > 0)
                          thirdPart = thirdPart.Substring(0, ci);
                      FunctionReportParameter r;                    
  					if (thirdPart == null || thirdPart == "Value")
  						r = new FunctionReportParameter(p);
  					else if (thirdPart == "Label")
  						r = new FunctionReportParameterLabel(p);
  					else
  						throw new ParserException("Parameter '" + method + "'  only supports 'Value' and 'Label' properties.");
                      if (ci > 0)
                          r.SetParameterMethod("Count", null);
                      
                      result = r;
                      return true;
  				case "ReportItems":
  					Textbox t = idLookup.LookupReportItem(method);
  					if (t == null)
  						throw new ParserException("ReportItem '" + method + "'  not found.");
  					if (thirdPart != null && thirdPart != "Value")
  						throw new ParserException("ReportItem '" + method + "'  only supports 'Value' property.");
  					result = new FunctionTextbox(t, idLookup.ExpressionName);	
  					return true;
  				case "Globals":
  					e = idLookup.LookupGlobal(method);
  					if (e == null)
  						throw new ParserException("Globals '" + method + "'  not found.");
  					result = e;
  					return true;
  				case "User":
  					e = idLookup.LookupUser(method);
  					if (e == null)
  						throw new ParserException("User variable '" + method + "'  not found.");
  					result = e;
  					return true;
  				case "Recursive":	// Only valid for some aggregate functions
  					result = new IdentifierKey(IdentifierKeyEnum.Recursive);
  					return true;
  				case "Simple":		// Only valid for some aggregate functions
  					result = new IdentifierKey(IdentifierKeyEnum.Simple);
  					return true;
  				default:
  					if (!bOnePart)
  						throw new ParserException(string.Format("'{0}' is an unknown identifer.", fullname));
  
  					switch (method.ToLower())		// lexer should probably mark these
  					{
  						case "true":
  						case "false":
  							result = new ConstantBoolean(method.ToLower());
  							break;
  						default:
  							// usually this is enum that will be used in an aggregate 
  							result = new Identifier(method);
  							break;
  					}
  					return true;
  			}
  
  			// We've got an function reference
  			curToken = tokens.Extract();		// get rid of '('
  
  			// Got a function now obtain the arguments
  			int argCount=0;
  			
  			bool isAggregate = IsAggregate(method, bOnePart);
  			if (_NoAggregate && isAggregate)
  				throw new ParserException("Aggregate function '" + method + "' cannot be used within a Grouping expression.");
  			if (_InAggregate && isAggregate)
  				throw new ParserException("Aggregate function '" + method + "' cannot be nested in another aggregate function.");
  			_InAggregate = isAggregate;
  			if (_InAggregate)
  				_FieldResolve = new List<FunctionField>();
  
              List<IExpr> largs = new List<IExpr>();
  			while(true)
  			{
  
gt: 				if (curToken.Type == TokenTypes.RPAREN)