/// <summary> /// The default method calls CheckForErrors on all AstNode children of this node. /// To do specific error checking, override this method and call the base method at the end. /// </summary> /// <param name="ast"></param> /// <param name="errors"></param> public virtual void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { foreach (var child in Children) { child.Value.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); } }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { // check the variable if (Variable != null) { Variable.CheckForErrors(ast, errorFunc, deferredFunctionSearches); } // check the expression base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); }
/// <summary> /// Evaluates the given expression in at the provided line number and returns the values /// that the expression can evaluate to. /// </summary> /// <param name="exprText">The expression to determine the result of.</param> /// <param name="index">The 0-based absolute index into the file where the expression should be evaluated within the module.</param> public override IAnalysisResult GetValueByIndex(string exprText, int index, IFunctionInformationProvider functionProvider, IDatabaseInformationProvider databaseProvider, IProgramFileProvider programFileProvider, bool isFunctionCallOrDefinition, out bool isDeferred, out IGeneroProject definingProject, out IProjectEntry projectEntry, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch) { _functionProvider = functionProvider; _databaseProvider = databaseProvider; _programFileProvider = programFileProvider; return(GetValueByIndex(exprText, index, this, out definingProject, out projectEntry, out isDeferred, searchInFunctionProvider, isFunctionCallOrDefinition)); }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { if (_arguments != null) { // 1) check to make sure arguments have corresponding defined variables foreach (var undefinedArg in _arguments.Where(x => x.Value == null)) { errorFunc(string.Format("No definition found for parameter {0}", undefinedArg.Key.Token.Value.ToString()), undefinedArg.Key.Span.Start, undefinedArg.Key.Span.End); } } // TODO: 2) Check to make sure the types in return statements match up across multiple return statements. if (_internalReturns != null && _internalReturns.Count > 1) { } base.CheckForErrors(ast, errorFunc, deferredFunctionSearches); }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { // 1) Check to make sure the function call is valid if (Function != null) { Function.CheckForErrors(ast, errorFunc, deferredFunctionSearches); } // 2) Check the return values if (Returns != null) { foreach (var ret in Returns) { ret.CheckForErrors(ast, errorFunc, deferredFunctionSearches); if (ret.ResolvedResult != null && !(ret.ResolvedResult is VariableDef || ret.ResolvedResult is ProgramRegister)) { errorFunc(string.Format("Return item {0} is not a variable", ret.Name), ret.StartIndex, ret.EndIndex); } } if (Function != null && Function.Function != null && Function.Function.ResolvedResult != null && Function.Function.ResolvedResult is IFunctionResult) { var numReturns = (Function.Function.ResolvedResult as IFunctionResult).Returns.Length; if (Returns.Count != numReturns) { errorFunc(string.Format("Unexpected number of return variables ({0}) found, expected {1} variables.", Returns.Count, numReturns), StartIndex, EndIndex); } } } base.CheckForErrors(ast, errorFunc, deferredFunctionSearches); }
public abstract IAnalysisResult GetValueByIndex(string exprText, int index, IFunctionInformationProvider functionProvider, IDatabaseInformationProvider databaseProvider, IProgramFileProvider programFileProvider, bool isFunctionCallOrDefinition, out bool isDeferred, out IGeneroProject definingProject, out IProjectEntry projectEntry, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch);
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { // This will do error checking on the type reference base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); if (VariableDefinitions != null) { if (Children.Count == 1 && Children[Children.Keys[0]] is TypeReference) { var resolvedType = (Children[Children.Keys[0]] as TypeReference).ResolvedType; if (resolvedType != null) { foreach (var vardef in VariableDefinitions) { // apply the resolved type definition to the variable // TODO: //vardef.ResolvedType = resolvedType; } } } } }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { // Check to see if the _firstPiece exists IGeneroProject proj; IProjectEntry projEntry; string searchStr = _firstPiece; if (searchInFunctionProvider != FunctionProviderSearchMode.NoSearch || isFunctionCallOrDefinition) { StringBuilder sb = new StringBuilder(searchStr); foreach (var child in Children.Values) { if (child is ArrayIndexFglNameExpressionPiece) { sb.Append(child.ToString()); } else if (child is MemberAccessNameExpressionPiece) { if ((child as MemberAccessNameExpressionPiece).Text != ".*") { sb.Append(child.ToString()); } else { break; } } } searchStr = sb.ToString(); } bool isDeferred; // TODO: need to defer database lookups too var res = ast.GetValueByIndex(searchStr, StartIndex, ast._functionProvider, ast._databaseProvider, ast._programFileProvider, isFunctionCallOrDefinition, out isDeferred, out proj, out projEntry, searchInFunctionProvider); if (res == null) { if (isDeferred) { if (deferredFunctionSearches.ContainsKey(searchStr)) { deferredFunctionSearches[searchStr].Add(StartIndex); } else { deferredFunctionSearches.Add(searchStr, new List <int> { StartIndex }); } } else { errorFunc(string.Format("No definition found for {0}", searchStr), StartIndex, StartIndex + searchStr.Length); } } else { if (Name.EndsWith(".*") && res is VariableDef && (res as VariableDef).ResolvedType == null) { // need to make sure that the res has a resolved type (res as VariableDef).Type.CheckForErrors(ast, errorFunc, deferredFunctionSearches); (res as VariableDef).ResolvedType = (res as VariableDef).Type.ResolvedType; } // TODO: need to check array element type ResolvedResult = res; } base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { if (MemberDictionary.Count > 0) { foreach (var recChild in MemberDictionary.Values) { recChild.Type.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); } } else { // TODO: check the database and table (deferred?) } // we don't store any children off, so this does nothing base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); }
public static IAnalysisResult GetValueByIndex(string exprText, int index, Genero4glAst ast, out IGeneroProject definingProject, out IProjectEntry projectEntry, out bool isDeferredFunction, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false, bool getTypes = true, bool getVariables = true, bool getConstants = true) { isDeferredFunction = false; definingProject = null; projectEntry = null; //_functionProvider = functionProvider; //_databaseProvider = databaseProvider; //_programFileProvider = programFileProvider; AstNode containingNode = null; if (ast != null) { containingNode = GetContainingNode(ast.Body, index); ast.Body.SetNamespace(null); } IAnalysisResult res = null; int tmpIndex = 0; int bracketDepth = 0; bool doSearch = false; bool resetStartIndex = false; int startIndex = 0, endIndex = 0; bool noEndIndexSet = false; if (exprText == null) { return(null); } while (tmpIndex < exprText.Length) { if (resetStartIndex) { startIndex = tmpIndex; resetStartIndex = false; if (startIndex + 1 > exprText.Length) { break; } } doSearch = false; switch (exprText[tmpIndex]) { case '.': { if (bracketDepth == 0) { endIndex = tmpIndex - 1; if (endIndex >= startIndex) { // we have our 'piece' doSearch = true; } if (exprText[startIndex] == '.') { startIndex++; } } tmpIndex++; } break; case '[': if (noEndIndexSet) { noEndIndexSet = false; } else { if (bracketDepth == 0) { endIndex = tmpIndex - 1; } } bracketDepth++; tmpIndex++; break; case ']': { bracketDepth--; if (bracketDepth == 0) { if (exprText.Length <= tmpIndex + 1 || exprText[tmpIndex + 1] != '[') { // we have our first 'piece' doSearch = true; } else { noEndIndexSet = true; } } tmpIndex++; } break; default: { if (bracketDepth == 0 && (tmpIndex + 1 == exprText.Length)) { endIndex = tmpIndex; doSearch = true; } tmpIndex++; } break; } if (!doSearch) { continue; } // we can do our search var dotPiece = exprText.Substring(startIndex, (endIndex - startIndex) + 1); if (dotPiece.Contains('(')) { // remove the params piece int remIndex = dotPiece.IndexOf('('); dotPiece = dotPiece.Substring(0, remIndex); } bool lookForFunctions = isFunctionCallOrDefinition && (endIndex + 1 == exprText.Length); resetStartIndex = true; if (res != null) { if (ast != null) { var gmi = new GetMemberInput { Name = dotPiece, AST = ast, IsFunction = lookForFunctions }; IAnalysisResult tempRes = res.GetMember(gmi); if (gmi.DefiningProject != null && definingProject != gmi.DefiningProject) { definingProject = gmi.DefiningProject; } if (gmi.ProjectEntry != null && projectEntry != gmi.ProjectEntry) { projectEntry = gmi.ProjectEntry; } res = tempRes; if (tempRes == null) { res = null; break; } } else { res = null; break; } } else { IFunctionResult funcRes; if (!lookForFunctions) { if (SystemVariables.TryGetValue(dotPiece, out res) || SystemConstants.TryGetValue(dotPiece, out res) || SystemMacros.TryGetValue(dotPiece, out res)) { continue; } } else { if (SystemFunctions.TryGetValue(dotPiece, out funcRes)) { res = funcRes; continue; } if (ast != null && ast._functionProvider != null && ast._functionProvider.Name.Equals(dotPiece, StringComparison.OrdinalIgnoreCase)) { res = ast._functionProvider; continue; } } if (containingNode != null && containingNode is IFunctionResult) { IFunctionResult func = containingNode as IFunctionResult; if ((getVariables && func.Variables.TryGetValue(dotPiece, out res))) { continue; } if (!lookForFunctions) { // Check for local vars, types, and constants if ((getTypes && func.Types.TryGetValue(dotPiece, out res)) || (getConstants && func.Constants.TryGetValue(dotPiece, out res))) { continue; } List <Tuple <IAnalysisResult, IndexSpan> > limitedScopeVars; if ((getVariables && func.LimitedScopeVariables.TryGetValue(dotPiece, out limitedScopeVars))) { foreach (var item in limitedScopeVars) { if (item.Item2.IsInSpan(index)) { res = item.Item1; break; } } if (res != null) { continue; } } } } if (ast != null && ast.Body is IModuleResult) { IModuleResult mod = ast.Body as IModuleResult; if (!lookForFunctions) { // check for module vars, types, and constants (and globals defined in this module) if ((getVariables && (mod.Variables.TryGetValue(dotPiece, out res) || mod.GlobalVariables.TryGetValue(dotPiece, out res))) || (getTypes && (mod.Types.TryGetValue(dotPiece, out res) || mod.GlobalTypes.TryGetValue(dotPiece, out res))) || (getConstants && (mod.Constants.TryGetValue(dotPiece, out res) || mod.GlobalConstants.TryGetValue(dotPiece, out res)))) { continue; } // check for cursors in this module if (mod.Cursors.TryGetValue(dotPiece, out res)) { continue; } } else { // check for module functions if (mod.Functions.TryGetValue(dotPiece, out funcRes)) { // check for any function info collected at the project entry level, and update the function's documentation with that. if (ast._projEntry != null && ast._projEntry is IGeneroProjectEntry) { var commentInfo = (ast._projEntry as IGeneroProjectEntry).GetFunctionInfo(funcRes.Name); if (commentInfo != null) { funcRes.SetCommentDocumentation(commentInfo); } } res = funcRes; continue; } } } // TODO: this could probably be done more efficiently by having each GeneroAst load globals and functions into // dictionaries stored on the IGeneroProject, instead of in each project entry. // However, this does required more upkeep when changes occur. Will look into it... if (ast != null && ast.ProjectEntry != null && ast.ProjectEntry is IGeneroProjectEntry) { IGeneroProjectEntry genProj = ast.ProjectEntry as IGeneroProjectEntry; if (genProj.ParentProject != null && !genProj.FilePath.ToLower().EndsWith(".inc")) { bool found = false; foreach (var projEntry in genProj.ParentProject.ProjectEntries.Where(x => x.Value != genProj)) { if (projEntry.Value.Analysis != null && projEntry.Value.Analysis.Body != null) { projEntry.Value.Analysis.Body.SetNamespace(null); IModuleResult modRes = projEntry.Value.Analysis.Body as IModuleResult; if (modRes != null) { if (!lookForFunctions) { // check global vars, types, and constants // TODO: need option to enable/disable legacy linking if ((getVariables && (modRes.Variables.TryGetValue(dotPiece, out res) || modRes.GlobalVariables.TryGetValue(dotPiece, out res))) || (getTypes && (modRes.Types.TryGetValue(dotPiece, out res) || modRes.GlobalTypes.TryGetValue(dotPiece, out res))) || (getConstants && (modRes.Constants.TryGetValue(dotPiece, out res) || modRes.GlobalConstants.TryGetValue(dotPiece, out res)))) { found = true; break; } // check for cursors in this module if (modRes.Cursors.TryGetValue(dotPiece, out res)) { found = true; break; } } else { // check project functions if (modRes.Functions.TryGetValue(dotPiece, out funcRes)) { if (funcRes.AccessModifier == AccessModifier.Public) { res = funcRes; found = true; break; } } } } } } if (found) { continue; } } } // check for classes GeneroPackage package; if (Packages.TryGetValue(dotPiece, out package)) { res = package; continue; } /* TODO: * Need to check for: * 1) Temp tables */ // Nothing found yet... // If our containing node is at the function or globals level, we need to go deeper if (containingNode != null && (containingNode is GlobalsNode || containingNode is FunctionBlockNode || containingNode is ReportBlockNode)) { containingNode = GetContainingNode(containingNode, index); } // check for record field if (containingNode != null && (containingNode is DefineNode || containingNode is TypeDefNode)) { containingNode = GetContainingNode(containingNode, index); if (containingNode != null && (containingNode is VariableDefinitionNode || containingNode is TypeDefinitionNode) && containingNode.Children.Count == 1 && containingNode.Children[containingNode.Children.Keys[0]] is TypeReference) { var typeRef = containingNode.Children[containingNode.Children.Keys[0]] as TypeReference; while (typeRef != null && typeRef.Children.Count == 1) { if (typeRef.Children[typeRef.Children.Keys[0]] is RecordDefinitionNode) { var recNode = typeRef.Children[typeRef.Children.Keys[0]] as RecordDefinitionNode; VariableDef recField; if (recNode.MemberDictionary.TryGetValue(exprText, out recField)) { res = recField; break; } else { recField = recNode.MemberDictionary.Where(x => x.Value.LocationIndex < index) .OrderByDescending(x => x.Value.LocationIndex) .Select(x => x.Value) .FirstOrDefault(); if (recField != null) { typeRef = recField.Type; } else { break; } } } else if (typeRef.Children[typeRef.Children.Keys[0]] is TypeReference) { typeRef = typeRef.Children[typeRef.Children.Keys[0]] as TypeReference; } else { break; } } } } // try an imported module if (ast != null && ast.Body is IModuleResult && ast.ProjectEntry != null && ast.ProjectEntry != null) { if ((ast.Body as IModuleResult).FglImports.Contains(dotPiece)) { // need to get the ast for the other project entry var refProjKVP = (ast.ProjectEntry as IGeneroProjectEntry).ParentProject.ReferencedProjects.Values.FirstOrDefault( x => { var fn = Path.GetFileNameWithoutExtension(x.Directory); return(fn?.Equals(dotPiece, StringComparison.OrdinalIgnoreCase) ?? false); }); if (refProjKVP is IAnalysisResult) { definingProject = refProjKVP; res = refProjKVP as IAnalysisResult; continue; } IAnalysisResult sysImportMod; // check the system imports if (SystemImportModules.TryGetValue(dotPiece, out sysImportMod)) { res = sysImportMod; continue; } } } if (!lookForFunctions) { // try include files var foundInclude = false; if (ast?.ProjectEntry != null) { foreach (var includeFile in ast.ProjectEntry.GetIncludedFiles()) { if (includeFile.Analysis?.Body is IModuleResult) { var mod = includeFile.Analysis.Body as IModuleResult; if ((getTypes && (mod.Types.TryGetValue(dotPiece, out res) || mod.GlobalTypes.TryGetValue(dotPiece, out res))) || (getConstants && (mod.Constants.TryGetValue(dotPiece, out res) || mod.GlobalConstants.TryGetValue(dotPiece, out res)))) { foundInclude = true; break; } } } } if (foundInclude) { continue; } if (ast?._databaseProvider != null) { res = ast._databaseProvider.GetTable(dotPiece); if (res != null) { continue; } } } // Only do a public function search if the dotPiece is the whole text we're searching for // I.e. no namespaces if (lookForFunctions && dotPiece == exprText) { if (searchInFunctionProvider == FunctionProviderSearchMode.Search) { if (res == null && ast?._functionProvider != null) { // check for the function name in the function provider var funcs = ast._functionProvider.GetFunction(dotPiece); if (funcs != null) { res = funcs.FirstOrDefault(); if (res != null) { continue; } } } } else if (searchInFunctionProvider == FunctionProviderSearchMode.Deferred) { isDeferredFunction = true; } } if (res == null) { break; } } } return(res); }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { if (Children.Count > 0) { base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); } else if (!string.IsNullOrWhiteSpace(DatabaseName) || !string.IsNullOrWhiteSpace(TableName) || !string.IsNullOrWhiteSpace(ColumnName)) { // TODO: do deferred checking of these } else if (_typeNameString != null && !_isConstrainedType) { // see if the _typeNameString is a base type var tok = Tokens.GetToken(_typeNameString); if (tok == null || !Genero4glAst.BuiltinTypes.Contains(tok.Kind)) { // if it's not a base type, look up the type IGeneroProject dummyProj; IProjectEntry dummyProjEntry; bool isDeferred; var res = Genero4glAst.GetValueByIndex(_typeNameString, StartIndex, ast as Genero4glAst, out dummyProj, out dummyProjEntry, out isDeferred, FunctionProviderSearchMode.NoSearch, false, true, false, false); if (res == null) { errorFunc(string.Format("Type {0} not found.", _typeNameString), StartIndex, EndIndex); } else { if (res is GeneroPackageClass || res is TypeDefinitionNode) { ResolvedType = res; } else { errorFunc(string.Format("Invalid type {0} found.", _typeNameString), StartIndex, EndIndex); } } } } }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { if (Children.Count > 0) { base.CheckForErrors(ast, errorFunc, deferredFunctionSearches, searchInFunctionProvider, isFunctionCallOrDefinition); } else if (!string.IsNullOrWhiteSpace(DatabaseName) || !string.IsNullOrWhiteSpace(TableName) || !string.IsNullOrWhiteSpace(ColumnName)) { // TODO: do deferred checking of these } else if (_typeNameString != null && !_isConstrainedType) { // see if the _typeNameString is a base type var tok = Tokens.GetToken(_typeNameString); if (tok == null || !Genero4glAst.BuiltinTypes.Contains(tok.Kind)) { string resolveErrMsg; var resolvedType = GetResolvedType(ast as Genero4glAst, out resolveErrMsg); if (resolvedType == null && resolveErrMsg != null) { errorFunc(resolveErrMsg, StartIndex, EndIndex); } else { ResolvedType = resolvedType; } } } }
public override void CheckForErrors(GeneroAst ast, Action <string, int, int> errorFunc, Dictionary <string, List <int> > deferredFunctionSearches, FunctionProviderSearchMode searchInFunctionProvider = FunctionProviderSearchMode.NoSearch, bool isFunctionCallOrDefinition = false) { // 1) Check parameters for errors // - undefined identifier // - record without .* if (_skipValidationFunctionNames.Contains(Function.Name)) { return; } // Check for the function name if (Function != null) { Function.CheckForErrors(ast, errorFunc, deferredFunctionSearches, FunctionProviderSearchMode.Deferred, true); } if (Parameters != null) { if (Function != null && Function.ResolvedResult != null && (Function.ResolvedResult is IFunctionResult)) { // TODO: if a parameter is a record, then the number of required input arguments increases by the number of fields in that record. int totalRequiredParams = 0; IGeneroProject proj; IProjectEntry projEntry; bool isDeferred; foreach (var param in (Function.ResolvedResult as IFunctionResult).Parameters) { TypeReference typeRef = null; var typeName = param.Type; int index = typeName.IndexOf("record", StringComparison.OrdinalIgnoreCase); // TODO: need to look into how importable projects populate their parameter result types if (index == 0 && (typeName.Length == 6 || char.IsWhiteSpace(typeName[6]))) { if (Function.ResolvedResult is AstNode4gl && (Function.ResolvedResult as AstNode4gl).StartIndex >= 0) { var useAst = ast as Genero4glAst; if ((Function.ResolvedResult as AstNode4gl).SyntaxTree != null && (Function.ResolvedResult as AstNode4gl).SyntaxTree != ast) { useAst = (Function.ResolvedResult as AstNode4gl).SyntaxTree as Genero4glAst; } // need to retrieve the variable and then get its type var resVar = Genero4glAst.GetValueByIndex(param.Name, (Function.ResolvedResult as AstNode4gl).StartIndex, useAst, out proj, out projEntry, out isDeferred); if (resVar != null && resVar is VariableDef) { typeRef = ((resVar as VariableDef).ResolvedType as TypeReference) ?? (resVar as VariableDef).Type; } } } else { // TODO: eventually, this needs to handle system types var resType = Genero4glAst.GetValueByIndex(typeName, StartIndex, ast as Genero4glAst, out proj, out projEntry, out isDeferred); if (resType != null && resType is TypeDefinitionNode && (resType as TypeDefinitionNode).TypeRef != null) { typeRef = (resType as TypeDefinitionNode).TypeRef; } } if (typeRef != null && typeRef.IsRecord && (typeRef.Children[typeRef.Children.Keys[0]] is RecordDefinitionNode)) { int recFieldCount = (typeRef.Children[typeRef.Children.Keys[0]] as RecordDefinitionNode).GetMembers(ast as Genero4glAst, Analysis.MemberType.All, false).Count(); if (recFieldCount > 0) { totalRequiredParams += recFieldCount; } else { totalRequiredParams++; } } else { totalRequiredParams++; } } int totalParameters = 0; foreach (var param in Parameters) { // The genero compiler does not do error checking of types, so I guess anything goes... // That makes things easier... if (param is FglNameExpression) { var nameParam = param as FglNameExpression; nameParam.CheckForErrors(ast, errorFunc, deferredFunctionSearches, FunctionProviderSearchMode.Deferred); if (nameParam.ResolvedResult != null) { if (nameParam.ResolvedResult is TypeDefinitionNode) // Check for any invalid parameters TODO: others { errorFunc("Invalid parameter found.", param.StartIndex, param.EndIndex); } else { TypeReference typeRef = null; if (nameParam.ResolvedResult is VariableDef) { if ((nameParam.ResolvedResult as VariableDef).ResolvedType != null && (nameParam.ResolvedResult as VariableDef).ResolvedType is TypeDefinitionNode) { var typeDef = (nameParam.ResolvedResult as VariableDef).ResolvedType as TypeDefinitionNode; if (typeDef?.TypeRef != null) { typeRef = typeDef.TypeRef; } } else { typeRef = (nameParam.ResolvedResult as VariableDef).Type; } } if (typeRef != null && typeRef.IsRecord && (typeRef.Children[typeRef.Children.Keys[0]] is RecordDefinitionNode)) { if ((nameParam.ResolvedResult as VariableDef).Type.IsArray) { if ((param as FglNameExpression).Name.EndsWith(".*")) { // need to get the number of fields in the record, as they count toward our passed parameter total int recFieldCount = ((RecordDefinitionNode)typeRef.Children[typeRef.Children.Keys[0]]).GetMembers(ast as Genero4glAst, Analysis.MemberType.All, false).Count(); if (recFieldCount > 0) { totalParameters += (recFieldCount - 1); // minus 1 so we can do the increment below } } } else { if (!(param as FglNameExpression).Name.EndsWith(".*") && !_allowedNonStarRecordParamFunctions.Contains(Function.Name)) { errorFunc("Records must be specified with a '.*' ending when passed as a function parameter.", param.StartIndex, param.EndIndex); } else { if (!_allowedNonStarRecordParamFunctions.Contains(Function.Name)) { // need to get the number of fields in the record, as they count toward our passed parameter total int recFieldCount = ((RecordDefinitionNode)typeRef.Children[typeRef.Children.Keys[0]]).GetMembers(ast as Genero4glAst, Analysis.MemberType.All, false).Count(); if (recFieldCount > 0) { totalParameters += (recFieldCount - 1); // minus 1 so we can do the increment below } } } } } } } } param.CheckForErrors(ast, errorFunc, deferredFunctionSearches); totalParameters++; } // need to determine if any of the passed in parameters are records, and if so, adjust the passed count if (totalParameters != totalRequiredParams) { errorFunc(string.Format("Unexpected number of parameters ({0}) found, expected {1} variables.", totalParameters, totalRequiredParams), StartIndex, EndIndex); } } } // TODO: should we do something with the anything parameters base.CheckForErrors(ast, errorFunc, deferredFunctionSearches); }