// 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) { if (curToken.Type == TokenTypes.RPAREN) { // We've got our function curToken = tokens.Extract(); break; } if (argCount == 0) { // don't need to do anything } else if (curToken.Type == TokenTypes.COMMA) { curToken = tokens.Extract(); } else throw new ParserException("Invalid function arguments. Found '" + curToken.Value + "' At column " + Convert.ToString(curToken.StartCol)); MatchExprAndOr(out e); if (e == null) throw new ParserException("Expecting ',' or ')'. Found '" + curToken.Value + "' At column " + Convert.ToString(curToken.StartCol)); largs.Add(e); argCount++; } if (_InAggregate) { ResolveFields(method, this._FieldResolve, largs); _FieldResolve = null; _InAggregate = false; } IExpr[] args = largs.ToArray(); object scope; bool bSimple; if (!bOnePart) { result = (firstPart == "Parameters")? ResolveParametersMethod(method, thirdPart, args): ResolveMethodCall(fullname, args); // throw exception when fails } else switch(method.ToLower()) { case "iif": if (args.Length != 3) throw new ParserException("iff function requires 3 arguments." + " At column " + Convert.ToString(curToken.StartCol)); // We allow any type for the first argument; it will get converted to boolean at runtime // if (args[0].GetTypeCode() != TypeCode.Boolean) // throw new ParserException("First argument to iif function must be boolean." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionIif(args[0], args[1], args[2]); break; case "choose": if (args.Length <= 2) throw new ParserException("Choose function requires at least 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); switch (args[0].GetTypeCode()) { case TypeCode.Double: case TypeCode.Single: case TypeCode.Int32: case TypeCode.Decimal: case TypeCode.Int16: case TypeCode.Int64: break; default: throw new ParserException("First argument to Choose function must be numeric." + " At column " + Convert.ToString(curToken.StartCol)); } result = new FunctionChoose(args); break; case "switch": if (args.Length <= 2) throw new ParserException("Switch function requires at least 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); if (args.Length % 2 != 0) throw new ParserException("Switch function must have an even number of arguments." + " At column " + Convert.ToString(curToken.StartCol)); for (int i=0; i < args.Length; i = i+2) { if (args[i].GetTypeCode() != TypeCode.Boolean) throw new ParserException("Switch function must have a boolean expression every other argument." + " At column " + Convert.ToString(curToken.StartCol)); } result = new FunctionSwitch(args); break; case "format": if (args.Length > 2 || args.Length < 1) throw new ParserException("Format function requires 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); if (args.Length == 1) { result = new FunctionFormat(args[0], new ConstantString("")); } else { if (args[1].GetTypeCode() != TypeCode.String) throw new ParserException("Second argument to Format function must be a string." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionFormat(args[0], args[1]); } break; case "fields": if (args.Length != 1) throw new ParserException("Fields collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionFieldCollection(idLookup.Fields, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." TODO: generalize this code curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "parameters": if (args.Length != 1) throw new ParserException("Parameters collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionParameterCollection(idLookup.Parameters, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "reportitems": if (args.Length != 1) throw new ParserException("ReportItems collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionReportItemCollection(idLookup.ReportItems, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "globals": if (args.Length != 1) throw new ParserException("Globals collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionGlobalCollection(idLookup.Globals, args[0]); break; case "user": if (args.Length != 1) throw new ParserException("User collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionUserCollection(idLookup.User, args[0]); break; case "sum": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrSum aggrFS = new FunctionAggrSum(_DataCache, args[0], scope); aggrFS.LevelCheck = bSimple; result = aggrFS; break; case "avg": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrAvg aggrFA = new FunctionAggrAvg(_DataCache, args[0], scope); aggrFA.LevelCheck = bSimple; result = aggrFA; break; case "min": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMin aggrFMin = new FunctionAggrMin(_DataCache, args[0], scope); aggrFMin.LevelCheck = bSimple; result = aggrFMin; break; case "max": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMax aggrFMax = new FunctionAggrMax(_DataCache, args[0], scope); aggrFMax.LevelCheck = bSimple; result = aggrFMax; break; case "first": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrFirst(_DataCache, args[0], scope); break; case "last": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrLast(_DataCache, args[0], scope); break; case "next": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrNext(_DataCache, args[0], scope); break; case "previous": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrPrevious(_DataCache, args[0], scope); break; case "level": scope = ResolveAggrScope(args, 1, out bSimple); result = new FunctionAggrLevel(scope); break; case "aggregate": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrArray aggr = new FunctionAggrArray(_DataCache, args[0], scope); aggr.LevelCheck = bSimple; result = aggr; break; case "count": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCount aggrFC = new FunctionAggrCount(_DataCache, args[0], scope); aggrFC.LevelCheck = bSimple; result = aggrFC; break; case "countrows": scope = ResolveAggrScope(args, 1, out bSimple); FunctionAggrCountRows aggrFCR = new FunctionAggrCountRows(scope); aggrFCR.LevelCheck = bSimple; result = aggrFCR; break; case "countdistinct": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCountDistinct aggrFCD = new FunctionAggrCountDistinct(_DataCache, args[0], scope); aggrFCD.LevelCheck = bSimple; result = aggrFCD; break; case "rownumber": scope = ResolveAggrScope(args, 1, out bSimple); IExpr texpr = new ConstantDouble("0"); result = new FunctionAggrRvCount(_DataCache, texpr, scope); break; case "runningvalue": if (args.Length < 2 || args.Length > 3) throw new ParserException("RunningValue takes 2 or 3 arguments." + " At column " + Convert.ToString(curToken.StartCol)); string aggrFunc = args[1].EvaluateString(null, null); if (aggrFunc == null) throw new ParserException("RunningValue 'Function' argument is invalid." + " At column " + Convert.ToString(curToken.StartCol)); scope = ResolveAggrScope(args, 3, out bSimple); switch(aggrFunc.ToLower()) { case "sum": result = new FunctionAggrRvSum(_DataCache, args[0], scope); break; case "avg": result = new FunctionAggrRvAvg(_DataCache, args[0], scope); break; case "count": result = new FunctionAggrRvCount(_DataCache, args[0], scope); break; case "max": result = new FunctionAggrRvMax(_DataCache, args[0], scope); break; case "min": result = new FunctionAggrRvMin(_DataCache, args[0], scope); break; case "stdev": result = new FunctionAggrRvStdev(_DataCache, args[0], scope); break; case "stdevp": result = new FunctionAggrRvStdevp(_DataCache, args[0], scope); break; case "var": result = new FunctionAggrRvVar(_DataCache, args[0], scope); break; case "varp": result = new FunctionAggrRvVarp(_DataCache, args[0], scope); break; default: throw new ParserException("RunningValue function '" + aggrFunc + "' is not supported. At column " + Convert.ToString(curToken.StartCol)); } break; case "stdev": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdev aggrSDev = new FunctionAggrStdev(_DataCache, args[0], scope); aggrSDev.LevelCheck = bSimple; result = aggrSDev; break; case "stdevp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdevp aggrSDevP = new FunctionAggrStdevp(_DataCache, args[0], scope); aggrSDevP.LevelCheck = bSimple; result = aggrSDevP; break; case "var": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVar aggrVar = new FunctionAggrVar(_DataCache, args[0], scope); aggrVar.LevelCheck = bSimple; result = aggrVar; break; case "varp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVarp aggrVarP = new FunctionAggrVarp(_DataCache, args[0], scope); aggrVarP.LevelCheck = bSimple; result = aggrVarP; break; default: result = ResolveMethodCall(fullname, args); // through exception when fails break; } return true; }
// 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 = null; if (inAggregateDataSet != null) { f = inAggregateDataSet.Fields == null ? null : inAggregateDataSet.Fields[method]; if (f == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_FieldNotInDataSet, method, inAggregateDataSet.Name.Nm)); } else { f = idLookup.LookupField(method); if (f == null) { throw new ParserException(string.Format(Strings.Parser_ErrorP_FieldNotFound, method)); } } if (thirdPart == null || thirdPart == "Value") { result = new FunctionField(f); } else if (thirdPart == "IsMissing") { result = new FunctionFieldIsMissing(f); } else throw new ParserException(string.Format(Strings.Parser_ErrorP_FieldSupportsValueAndIsMissing, method)); return true; case "Parameters": // see ResolveParametersMethod for resolution of MultiValue parameter function reference ReportParameter p = idLookup.LookupParameter(method); if (p == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_ParameterNotFound, method)); 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(string.Format(Strings.Parser_ErrorP_ParameterSupportsValueAndLabel, method)); if (ci > 0) r.SetParameterMethod("Count", null); result = r; return true; case "ReportItems": Textbox t = idLookup.LookupReportItem(method); if (t == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_ItemNotFound, method)); if (thirdPart != null && thirdPart != "Value") throw new ParserException(string.Format(Strings.Parser_ErrorP_ItemSupportsValue, method)); result = new FunctionTextbox(t, idLookup.ExpressionName); return true; case "Globals": e = idLookup.LookupGlobal(method); if (e == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_GlobalsNotFound, method)); result = e; return true; case "User": e = idLookup.LookupUser(method); if (e == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_UserVarNotFound, method)); 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(Strings.Parser_ErrorP_UnknownIdentifer, 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(string.Format(Strings.Parser_ErrorP_AggregateCannotUsedWithinGrouping, method)); if (_InAggregate && isAggregate) throw new ParserException(string.Format(Strings.Parser_ErrorP_AggregateCannotNestedInAnotherAggregate, method)); _InAggregate = isAggregate; if (_InAggregate) { int level = 0; bool nextScope = false; Token scopeToken = null; foreach(Token tok in tokens) { if(nextScope) { scopeToken = tok; break; } if(level == 0 && tok.Type == TokenTypes.COMMA) { nextScope = true; continue; } if (tok.Type == TokenTypes.RPAREN) level--; if (tok.Type == TokenTypes.LPAREN) level++; } if (scopeToken != null) { if (scopeToken.Type != TokenTypes.QUOTE) throw new ParserException(string.Format(Strings.Parser_ErrorP_ScopeMustConstant, scopeToken.Value)); inAggregateDataSet = this.idLookup.ScopeDataSet(scopeToken.Value); if (inAggregateDataSet == null) throw new ParserException(string.Format(Strings.Parser_ErrorP_ScopeNotKnownDataSet, scopeToken.Value)); } } List<IExpr> largs = new List<IExpr>(); while(true) { if (curToken.Type == TokenTypes.RPAREN) { // We've got our function curToken = tokens.Extract(); break; } if (argCount == 0) { // don't need to do anything } else if (curToken.Type == TokenTypes.COMMA) { curToken = tokens.Extract(); } else throw new ParserException(Strings.Parser_ErrorP_Invalid_function_arguments + GetLocationInfoWithValue(curToken)); MatchExprAndOr(out e); if (e == null) throw new ParserException(Strings.Parser_ErrorP_ExpectingComma + GetLocationInfoWithValue(curToken)); largs.Add(e); argCount++; } if (_InAggregate) { inAggregateDataSet = null; _InAggregate = false; } IExpr[] args = largs.ToArray(); object scope; bool bSimple; if (!bOnePart) { result = (firstPart == "Parameters")? ResolveParametersMethod(method, thirdPart, args): ResolveMethodCall(fullname, args); // throw exception when fails } else switch(method.ToLower()) { case "iif": if (args.Length != 3) throw new ParserException(Strings.Parser_ErrorP_iff_function_requires_3_arguments + GetLocationInfo(curToken)); // We allow any type for the first argument; it will get converted to boolean at runtime // if (args[0].GetTypeCode() != TypeCode.Boolean) // throw new ParserException("First argument to iif function must be boolean." + GetLocationInfo(curToken)); result = new FunctionIif(args[0], args[1], args[2]); break; case "choose": if (args.Length <= 2) throw new ParserException(Strings.Parser_ErrorP_ChooseRequires2Arguments + GetLocationInfo(curToken)); switch (args[0].GetTypeCode()) { case TypeCode.Double: case TypeCode.Single: case TypeCode.Int32: case TypeCode.Decimal: case TypeCode.Int16: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: break; default: throw new ParserException(Strings.Parser_ErrorP_ChooseFirstArgumentMustNumeric + GetLocationInfo(curToken)); } result = new FunctionChoose(args); break; case "switch": if (args.Length <= 2) throw new ParserException(Strings.Parser_ErrorP_SwitchRequires2Arguments + GetLocationInfo(curToken)); if (args.Length % 2 != 0) throw new ParserException(Strings.Parser_ErrorP_SwitchMustEvenArguments + GetLocationInfo(curToken)); for (int i=0; i < args.Length; i = i+2) { if (args[i].GetTypeCode() != TypeCode.Boolean) throw new ParserException(Strings.Parser_ErrorP_SwitchMustBoolean + GetLocationInfo(curToken)); } result = new FunctionSwitch(args); break; case "format": if (args.Length > 2 || args.Length < 1) throw new ParserException(Strings.Parser_ErrorP_FormatRequires2Arguments + GetLocationInfo(curToken)); if (args.Length == 1) { result = new FunctionFormat(args[0], new ConstantString("")); } else { if (args[1].GetTypeCode() != TypeCode.String) throw new ParserException(Strings.Parser_ErrorP_SecondMustString + GetLocationInfo(curToken)); result = new FunctionFormat(args[0], args[1]); } break; case "fields": if (args.Length != 1) throw new ParserException(Strings.Parser_ErrorP_FieldsRequires1Argument + GetLocationInfo(curToken)); result = new FunctionFieldCollection(idLookup.Fields, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." TODO: generalize this code curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(string.Format(Strings.Parser_ErrorP_UnknownProperty, curToken.Value, "Fields") + GetLocationInfo(curToken)); } break; case "parameters": if (args.Length != 1) throw new ParserException(Strings.Parser_ErrorP_ParametersRequires1Argument + GetLocationInfo(curToken)); result = new FunctionParameterCollection(idLookup.Parameters, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException((string.Format(Strings.Parser_ErrorP_UnknownProperty, curToken.Value, "Parameters") + GetLocationInfo(curToken))); } break; case "reportitems": if (args.Length != 1) throw new ParserException(Strings.Parser_ErrorP_ReportItemsRequires1Argument + GetLocationInfo(curToken)); result = new FunctionReportItemCollection(idLookup.ReportItems, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException((string.Format(Strings.Parser_ErrorP_UnknownProperty, curToken.Value, "ReportItems") + GetLocationInfo(curToken))); } break; case "globals": if (args.Length != 1) throw new ParserException(Strings.Parser_ErrorP_GlobalsRequires1Argument + GetLocationInfo(curToken)); result = new FunctionGlobalCollection(idLookup.Globals, args[0]); break; case "user": if (args.Length != 1) throw new ParserException(Strings.Parser_ErrorP_UserRequires1Argument + GetLocationInfo(curToken)); result = new FunctionUserCollection(idLookup.User, args[0]); break; case "sum": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrSum aggrFS = new FunctionAggrSum(_DataCache, args[0], scope); aggrFS.LevelCheck = bSimple; result = aggrFS; break; case "avg": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrAvg aggrFA = new FunctionAggrAvg(_DataCache, args[0], scope); aggrFA.LevelCheck = bSimple; result = aggrFA; break; case "min": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMin aggrFMin = new FunctionAggrMin(_DataCache, args[0], scope); aggrFMin.LevelCheck = bSimple; result = aggrFMin; break; case "max": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMax aggrFMax = new FunctionAggrMax(_DataCache, args[0], scope); aggrFMax.LevelCheck = bSimple; result = aggrFMax; break; case "first": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrFirst(_DataCache, args[0], scope); break; case "last": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrLast(_DataCache, args[0], scope); break; case "next": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrNext(_DataCache, args[0], scope); break; case "previous": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrPrevious(_DataCache, args[0], scope); break; case "level": scope = ResolveAggrScope(args, 1, out bSimple); result = new FunctionAggrLevel(scope); break; case "aggregate": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrArray aggr = new FunctionAggrArray(_DataCache, args[0], scope); aggr.LevelCheck = bSimple; result = aggr; break; case "count": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCount aggrFC = new FunctionAggrCount(_DataCache, args[0], scope); aggrFC.LevelCheck = bSimple; result = aggrFC; break; case "countrows": scope = ResolveAggrScope(args, 1, out bSimple); FunctionAggrCountRows aggrFCR = new FunctionAggrCountRows(scope); aggrFCR.LevelCheck = bSimple; result = aggrFCR; break; case "countdistinct": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCountDistinct aggrFCD = new FunctionAggrCountDistinct(_DataCache, args[0], scope); aggrFCD.LevelCheck = bSimple; result = aggrFCD; break; case "rownumber": scope = ResolveAggrScope(args, 1, out bSimple); IExpr texpr = new ConstantDouble("0"); result = new FunctionAggrRvCount(_DataCache, texpr, scope); break; case "runningvalue": if (args.Length < 2 || args.Length > 3) throw new ParserException(Strings.Parser_ErrorP_RunningValue_takes_2_or_3_arguments + GetLocationInfo(curToken)); string aggrFunc = args[1].EvaluateString(null, null); if (aggrFunc == null) throw new ParserException(Strings.Parser_ErrorP_RunningValueArgumentInvalid + GetLocationInfo(curToken)); scope = ResolveAggrScope(args, 3, out bSimple); switch(aggrFunc.ToLower()) { case "sum": result = new FunctionAggrRvSum(_DataCache, args[0], scope); break; case "avg": result = new FunctionAggrRvAvg(_DataCache, args[0], scope); break; case "count": result = new FunctionAggrRvCount(_DataCache, args[0], scope); break; case "max": result = new FunctionAggrRvMax(_DataCache, args[0], scope); break; case "min": result = new FunctionAggrRvMin(_DataCache, args[0], scope); break; case "stdev": result = new FunctionAggrRvStdev(_DataCache, args[0], scope); break; case "stdevp": result = new FunctionAggrRvStdevp(_DataCache, args[0], scope); break; case "var": result = new FunctionAggrRvVar(_DataCache, args[0], scope); break; case "varp": result = new FunctionAggrRvVarp(_DataCache, args[0], scope); break; default: throw new ParserException(string.Format(Strings.Parser_ErrorP_RunningValueNotSupported, aggrFunc) + GetLocationInfo(curToken)); } break; case "stdev": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdev aggrSDev = new FunctionAggrStdev(_DataCache, args[0], scope); aggrSDev.LevelCheck = bSimple; result = aggrSDev; break; case "stdevp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdevp aggrSDevP = new FunctionAggrStdevp(_DataCache, args[0], scope); aggrSDevP.LevelCheck = bSimple; result = aggrSDevP; break; case "var": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVar aggrVar = new FunctionAggrVar(_DataCache, args[0], scope); aggrVar.LevelCheck = bSimple; result = aggrVar; break; case "varp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVarp aggrVarP = new FunctionAggrVarp(_DataCache, args[0], scope); aggrVarP.LevelCheck = bSimple; result = aggrVarP; break; default: result = ResolveMethodCall(fullname, args); // through exception when fails break; } return true; }