/// <summary> /// Clones the stack object and also clones the highest item on the context stack (only!) /// </summary> public ResolverContextStack Clone() { var rc = new ResolverContext(); rc.ApplyFrom(CurrentContext); return(new ResolverContextStack(ParseCache, rc)); }
public void ApplyFrom(ResolverContext other) { if (other == null) return; ScopedBlock = other.ScopedBlock; ScopedStatement = other.ScopedStatement; }
public void ApplyFrom(ResolverContext other) { if (other == null) { return; } ScopedBlock = other.ScopedBlock; ScopedStatement = other.ScopedStatement; }
public ResolverContext PushNewScope(IBlockNode scope) { var ctxtOverride = new ResolverContext(); ctxtOverride.ScopedBlock = scope; ctxtOverride.ScopedStatement = null; stack.Push(ctxtOverride); return(ctxtOverride); }
public void Push(ResolverContext c) { stack.Push(c); }
public static ArgumentsResolutionResult ResolveArgumentContext( string code, int caretOffset, CodeLocation caretLocation, DMethod MethodScope, IEnumerable<IAbstractSyntaxTree> parseCache, IEnumerable<IAbstractSyntaxTree> ImportCache) { var ctxt = new ResolverContext { ScopedBlock = MethodScope, ParseCache = parseCache, ImportCache=ImportCache }; #region Parse the code between the last block opener and the caret var curMethodBody = MethodScope.GetSubBlockAt(caretLocation); if (curMethodBody == null && MethodScope.Parent is DMethod) { MethodScope = MethodScope.Parent as DMethod; curMethodBody = MethodScope.GetSubBlockAt(caretLocation); } if (curMethodBody == null) return null; var blockOpenerLocation = curMethodBody.StartLocation; var blockOpenerOffset = blockOpenerLocation.Line <= 0 ? blockOpenerLocation.Column : DocumentHelper.LocationToOffset(code, blockOpenerLocation); if (blockOpenerOffset >= 0 && caretOffset - blockOpenerOffset > 0) { var codeToParse = code.Substring(blockOpenerOffset, caretOffset - blockOpenerOffset); curMethodBody = DParser.ParseBlockStatement(codeToParse, blockOpenerLocation, MethodScope); if (curMethodBody != null) ctxt.ScopedStatement = curMethodBody.SearchStatementDeeply(caretLocation); } if (curMethodBody == null || ctxt.ScopedStatement == null) return null; #endregion // Scan statement for method calls or template instantiations var e = DResolver.SearchForMethodCallsOrTemplateInstances(ctxt.ScopedStatement, caretLocation); /* * 1) foo( -- normal arguments only * 2) foo!(...)( -- normal arguments + template args * 3) foo!( -- template args only * 4) new myclass( -- ctor call * 5) new myclass!( -- ditto * 6) new myclass!(...)( * 7) mystruct( -- opCall call */ var res = new ArgumentsResolutionResult() { ParsedExpression = e }; ITypeDeclaration methodIdentifier = null; // 1), 2) if (e is PostfixExpression_MethodCall) { res.IsMethodArguments = true; var call = e as PostfixExpression_MethodCall; if (call.Arguments != null) { int i = 0; foreach (var arg in call.Arguments) { if (caretLocation >= arg.Location && caretLocation <= arg.EndLocation) { res.CurrentlyTypedArgumentIndex = i; break; } i++; } } methodIdentifier = call.PostfixForeExpression.ExpressionTypeRepresentation; } // 3) else if (e is TemplateInstanceExpression) { var templ = e as TemplateInstanceExpression; res.IsTemplateInstanceArguments = true; if (templ.Arguments != null) { int i = 0; foreach (var arg in templ.Arguments) { if (caretLocation >= arg.Location && caretLocation <= arg.EndLocation) { res.CurrentlyTypedArgumentIndex = i; break; } i++; } } methodIdentifier = new IdentifierDeclaration(templ.TemplateIdentifier.Value) { InnerDeclaration=templ.InnerDeclaration }; } else if (e is NewExpression) { var ne = e as NewExpression; if (ne.Arguments != null) { int i = 0; foreach (var arg in ne.Arguments) { if (caretLocation >= arg.Location && caretLocation <= arg.EndLocation) { res.CurrentlyTypedArgumentIndex = i; break; } i++; } } methodIdentifier = ne.ExpressionTypeRepresentation; } if (methodIdentifier == null) return null; // Resolve all types, methods etc. which belong to the methodIdentifier res.ResolvedTypesOrMethods = ResolveType(methodIdentifier, ctxt); if (res.ResolvedTypesOrMethods == null) return res; // 4),5),6) if (e is NewExpression) { var substitutionList = new List<ResolveResult>(); foreach (var rr in res.ResolvedTypesOrMethods) if (rr is TypeResult) { var classDef = (rr as TypeResult).ResolvedTypeDefinition as DClassLike; if (classDef == null) continue; //TODO: Regard protection attributes for ctor members foreach (var i in classDef) if (i is DMethod && (i as DMethod).SpecialType == DMethod.MethodType.Constructor) substitutionList.Add(HandleNodeMatch(i, ctxt, resultBase: rr)); } if (substitutionList.Count > 0) res.ResolvedTypesOrMethods = substitutionList.ToArray(); } // 7) else if (e is PostfixExpression_MethodCall) { var substitutionList = new List<ResolveResult>(); var nonAliases=TryRemoveAliasesFromResult(res.ResolvedTypesOrMethods); foreach (var rr in nonAliases) if (rr is TypeResult) { var classDef = (rr as TypeResult).ResolvedTypeDefinition as DClassLike; if (classDef == null) continue; //TODO: Regard protection attributes for opCall members foreach (var i in classDef) if (i is DMethod && i.Name == "opCall") substitutionList.Add(HandleNodeMatch(i, ctxt, resultBase: rr)); } if (substitutionList.Count > 0) nonAliases = substitutionList.ToArray(); res.ResolvedTypesOrMethods = nonAliases; } return res; }
static ResolveResult[] HandleIdDeclaration(IdentifierDeclaration typeId, ResolverContext lastResCtxt, IAbstractSyntaxTree SyntaxTree, CodeScanResult csr, Dictionary<string, ResolveResult> compDict) { var typeString = typeId.ToString(); bool WasAlreadyResolved = false; ResolveResult[] allCurrentResults = null; ResolveResult rr = null; /* * string,wstring,dstring are highlighted by the editor's syntax definition automatically.. * Anyway, allow to resolve e.g. "object.string" */ if (typeString == "" || typeString == "string" || typeString == "wstring" || typeString == "dstring") return null; lastResCtxt.ScopedBlock = DResolver.SearchBlockAt(SyntaxTree, typeId.Location, out lastResCtxt.ScopedStatement); if (typeString == "th" && typeId.Location.Line == 114) { } if (!(WasAlreadyResolved = compDict.TryGetValue(typeString, out rr))) { allCurrentResults = DResolver.ResolveType(typeId, lastResCtxt); if (allCurrentResults != null && allCurrentResults.Length > 0) rr = allCurrentResults[0]; } if (rr == null) { if (typeId is IdentifierDeclaration) csr.UnresolvedIdentifiers.Add(typeId as IdentifierDeclaration); } else { /* * Note: It is of course possible to highlight more than one type in one type declaration! * So, we scan down the result hierarchy for TypeResults and highlight all of them later. */ var curRes = rr; /* * Note: Since we want to use results multiple times, * we at least have to 'update' their type declarations * to ensure that the second, third, fourth etc. occurence of this result * are also highlit (and won't(!) cause an Already-Added-Exception of our finalDict-Array) */ var curTypeDeclBase = typeId as ITypeDeclaration; while (curRes != null) { if (curRes is MemberResult) { var mr = curRes as MemberResult; // If curRes is an alias or a template parameter, highlight it if (mr.ResolvedMember is TemplateParameterNode || (mr.ResolvedMember is DVariable && (mr.ResolvedMember as DVariable).IsAlias)) { try { csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); } catch { } // See performance reasons //if (curRes != rr && !WasAlreadyResolved && !) compDict.Add(curTypeDeclBase.ToString(), curRes); } } else if (curRes is TypeResult) { // Yeah, in quite all cases we do identify a class via its name ;-) if (curTypeDeclBase is IdentifierDeclaration && !(curTypeDeclBase is DTokenDeclaration) && !csr.ResolvedIdentifiers.ContainsKey(curTypeDeclBase as IdentifierDeclaration)) { csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); // See performance reasons //if (curRes != rr && !WasAlreadyResolved) compDict.Add(curTypeDeclBase.ToString(), curRes); } } else if (curRes is ModuleResult) { if (curTypeDeclBase is IdentifierDeclaration && !csr.ResolvedIdentifiers.ContainsKey(curTypeDeclBase as IdentifierDeclaration)) csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); } curRes = curRes.ResultBase; curTypeDeclBase = curTypeDeclBase.InnerDeclaration; } } return allCurrentResults; }
/// <summary> /// Scans the syntax tree for all kinds of identifier declarations, /// tries to resolve them, /// adds them to a dictionary. If not found, /// they will be added to a second, special array. /// /// Note: For performance reasons, it's recommended to disable 'ResolveAliases' in the ResolverContext parameter /// </summary> /// <param name="lastResCtxt"></param> /// <param name="SyntaxTree"></param> /// <returns></returns> public static CodeScanResult ScanSymbols(ResolverContext lastResCtxt,IAbstractSyntaxTree SyntaxTree) { var csr = new CodeScanResult(); var compDict = new Dictionary<string, ResolveResult>(); // Step 1: Enum all existing type id's that shall become resolved'n displayed var typeObjects = CodeScanner.ScanForTypeIdentifiers(SyntaxTree); foreach (var o in typeObjects) { if (o == null) continue; #region Identifier Declarations if (o is IdentifierDeclaration) { HandleIdDeclaration(o as IdentifierDeclaration, lastResCtxt, SyntaxTree, csr, compDict); } #endregion /* #region Method call check else if (o is PostfixExpression_MethodCall) { var mc=o as PostfixExpression_MethodCall; int argc = mc.ArgumentCount; var methodOverloads=HandleIdDeclaration(mc.ExpressionTypeRepresentation as IdentifierDeclaration, lastResCtxt, SyntaxTree, csr, compDict); // Important: Also check template parameters and arguments! IExpression[] TemplateArguments=null; if (mc.PostfixForeExpression is TemplateInstanceExpression) TemplateArguments = (mc.PostfixForeExpression as TemplateInstanceExpression).Arguments; int TemplateArgCount = TemplateArguments == null ? 0 : TemplateArguments.Length; // Note: If no method members were found, we already show the semantic error as a generically missing symbol if (methodOverloads != null) { var l1 = new List<DMethod>(); var l2 = new List<DMethod>(); #region First add all available method overloads foreach (var rr_ in methodOverloads) { // Like every time, remove unnecessary aliasing var rr = DResolver.TryRemoveAliasesFromResult(rr_); // Can be either 1) a normal method OR 2) a type related opCall // 1) if (rr is MemberResult) { var mr = rr as MemberResult; //FIXME: What about delegate aliases'n stuff? if (!(mr.ResolvedMember is DMethod)) continue; var dm = mr.ResolvedMember as DMethod; // Even before checking argument types etc, pre-select possibly chosen overloads // by comparing the method's parameter count with the call's argument count if (dm.Parameters.Count == argc && (dm.TemplateParameters==null?true: dm.TemplateParameters.Length==TemplateArgCount)) l1.Add(dm); } // 2) else if (rr is TypeResult) { var tr = rr as TypeResult; // Scan the type for opCall members var opCalls=DResolver.ScanNodeForIdentifier(tr.ResolvedTypeDefinition, "opCall", lastResCtxt); if (opCalls != null) foreach (var n in opCalls) { var dm = n as DMethod; // Also pre-filter opCall members by param count comparison if (dm!=null && dm.Parameters.Count == argc && (dm.TemplateParameters == null ? true : dm.TemplateParameters.Length == TemplateArgCount)) l1.Add(dm); } } } #endregion // Must be a semantic error - no method fits in any way if(l1.Count<1) { csr.ParameterActions.Add(o as IExpression, null); continue; } // Compare template arguments first } } #endregion */ } return csr; }
static ResolveResult[] HandleIdDeclaration(IdentifierDeclaration typeId, ResolverContext lastResCtxt, IAbstractSyntaxTree SyntaxTree, CodeScanResult csr, Dictionary <string, ResolveResult> compDict) { var typeString = typeId.ToString(); bool WasAlreadyResolved = false; ResolveResult[] allCurrentResults = null; ResolveResult rr = null; /* * string,wstring,dstring are highlighted by the editor's syntax definition automatically.. * Anyway, allow to resolve e.g. "object.string" */ if (typeString == "" || typeString == "string" || typeString == "wstring" || typeString == "dstring") { return(null); } lastResCtxt.ScopedBlock = DResolver.SearchBlockAt(SyntaxTree, typeId.Location, out lastResCtxt.ScopedStatement); if (typeString == "th" && typeId.Location.Line == 114) { } if (!(WasAlreadyResolved = compDict.TryGetValue(typeString, out rr))) { allCurrentResults = DResolver.ResolveType(typeId, lastResCtxt); if (allCurrentResults != null && allCurrentResults.Length > 0) { rr = allCurrentResults[0]; } } if (rr == null) { if (typeId is IdentifierDeclaration) { csr.UnresolvedIdentifiers.Add(typeId as IdentifierDeclaration); } } else { /* * Note: It is of course possible to highlight more than one type in one type declaration! * So, we scan down the result hierarchy for TypeResults and highlight all of them later. */ var curRes = rr; /* * Note: Since we want to use results multiple times, * we at least have to 'update' their type declarations * to ensure that the second, third, fourth etc. occurence of this result * are also highlit (and won't(!) cause an Already-Added-Exception of our finalDict-Array) */ var curTypeDeclBase = typeId as ITypeDeclaration; while (curRes != null) { if (curRes is MemberResult) { var mr = curRes as MemberResult; // If curRes is an alias or a template parameter, highlight it if (mr.ResolvedMember is TemplateParameterNode || (mr.ResolvedMember is DVariable && (mr.ResolvedMember as DVariable).IsAlias)) { try { csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); } catch { } // See performance reasons //if (curRes != rr && !WasAlreadyResolved && !) compDict.Add(curTypeDeclBase.ToString(), curRes); } } else if (curRes is TypeResult) { // Yeah, in quite all cases we do identify a class via its name ;-) if (curTypeDeclBase is IdentifierDeclaration && !(curTypeDeclBase is DTokenDeclaration) && !csr.ResolvedIdentifiers.ContainsKey(curTypeDeclBase as IdentifierDeclaration)) { csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); // See performance reasons //if (curRes != rr && !WasAlreadyResolved) compDict.Add(curTypeDeclBase.ToString(), curRes); } } else if (curRes is ModuleResult) { if (curTypeDeclBase is IdentifierDeclaration && !csr.ResolvedIdentifiers.ContainsKey(curTypeDeclBase as IdentifierDeclaration)) { csr.ResolvedIdentifiers.Add(curTypeDeclBase as IdentifierDeclaration, curRes); } } curRes = curRes.ResultBase; curTypeDeclBase = curTypeDeclBase.InnerDeclaration; } } return(allCurrentResults); }
/// <summary> /// Scans the syntax tree for all kinds of identifier declarations, /// tries to resolve them, /// adds them to a dictionary. If not found, /// they will be added to a second, special array. /// /// Note: For performance reasons, it's recommended to disable 'ResolveAliases' in the ResolverContext parameter /// </summary> /// <param name="lastResCtxt"></param> /// <param name="SyntaxTree"></param> /// <returns></returns> public static CodeScanResult ScanSymbols(ResolverContext lastResCtxt, IAbstractSyntaxTree SyntaxTree) { var csr = new CodeScanResult(); var compDict = new Dictionary <string, ResolveResult>(); // Step 1: Enum all existing type id's that shall become resolved'n displayed var typeObjects = CodeScanner.ScanForTypeIdentifiers(SyntaxTree); foreach (var o in typeObjects) { if (o == null) { continue; } #region Identifier Declarations if (o is IdentifierDeclaration) { HandleIdDeclaration(o as IdentifierDeclaration, lastResCtxt, SyntaxTree, csr, compDict); } #endregion /* #region Method call check * else if (o is PostfixExpression_MethodCall) * { * var mc=o as PostfixExpression_MethodCall; * * int argc = mc.ArgumentCount; * * var methodOverloads=HandleIdDeclaration(mc.ExpressionTypeRepresentation as IdentifierDeclaration, * lastResCtxt, * SyntaxTree, * csr, * compDict); * * // Important: Also check template parameters and arguments! * IExpression[] TemplateArguments=null; * * if (mc.PostfixForeExpression is TemplateInstanceExpression) * TemplateArguments = (mc.PostfixForeExpression as TemplateInstanceExpression).Arguments; * * int TemplateArgCount = TemplateArguments == null ? 0 : TemplateArguments.Length; * * // Note: If no method members were found, we already show the semantic error as a generically missing symbol * if (methodOverloads != null) * { * var l1 = new List<DMethod>(); * var l2 = new List<DMethod>(); * #region First add all available method overloads * foreach (var rr_ in methodOverloads) * { * // Like every time, remove unnecessary aliasing * var rr = DResolver.TryRemoveAliasesFromResult(rr_); * * // Can be either 1) a normal method OR 2) a type related opCall * * // 1) * if (rr is MemberResult) * { * var mr = rr as MemberResult; * * //FIXME: What about delegate aliases'n stuff? * if (!(mr.ResolvedMember is DMethod)) * continue; * * var dm = mr.ResolvedMember as DMethod; * * // Even before checking argument types etc, pre-select possibly chosen overloads * // by comparing the method's parameter count with the call's argument count * if (dm.Parameters.Count == argc && (dm.TemplateParameters==null?true: * dm.TemplateParameters.Length==TemplateArgCount)) * l1.Add(dm); * } * * // 2) * else if (rr is TypeResult) * { * var tr = rr as TypeResult; * * // Scan the type for opCall members * var opCalls=DResolver.ScanNodeForIdentifier(tr.ResolvedTypeDefinition, "opCall", lastResCtxt); * * if (opCalls != null) * foreach (var n in opCalls) * { * var dm = n as DMethod; * * // Also pre-filter opCall members by param count comparison * if (dm!=null && * dm.Parameters.Count == argc && (dm.TemplateParameters == null ? true : * dm.TemplateParameters.Length == TemplateArgCount)) * l1.Add(dm); * } * } * } #endregion * * // Must be a semantic error - no method fits in any way * if(l1.Count<1) * { * csr.ParameterActions.Add(o as IExpression, null); * continue; * } * * // Compare template arguments first * } * } #endregion */ } return(csr); }
public static ResolveResult[] ResolveType(IEditorData editor, ResolverContext ctxt, bool alsoParseBeyondCaret = false, bool onlyAssumeIdentifierList = false) { var code = editor.ModuleCode; // First check if caret is inside a comment/string etc. int lastNonNormalStart = 0; int lastNonNormalEnd = 0; var caretContext = CommentSearching.GetTokenContext(code, editor.CaretOffset, out lastNonNormalStart,out lastNonNormalEnd); // Return if comment etc. found if (caretContext != CommentSearching.TokenContext.None) return null; var start = ReverseParsing.SearchExpressionStart(code,editor.CaretOffset-1, /*FIXME: Only backward-parse code until the last comment - * this can be provoking errors e.g. * on MyType /* Some comment * / .myProp * But: In quite all cases this shouldn't occur - so it's still acceptable */ (lastNonNormalEnd>0 && lastNonNormalEnd<editor.CaretOffset)?lastNonNormalEnd:0); if (start < 0 || editor.CaretOffset<=start) return null; var expressionCode = code.Substring(start, alsoParseBeyondCaret ? code.Length - start : editor.CaretOffset - start); var parser = DParser.Create(new StringReader(expressionCode)); parser.Lexer.SetInitialLocation(DocumentHelper.OffsetToLocation(editor.ModuleCode,start)); parser.Step(); if (onlyAssumeIdentifierList && parser.Lexer.LookAhead.Kind == DTokens.Identifier) return ResolveType(parser.IdentifierList(), ctxt); else if (parser.IsAssignExpression()) { var expr = parser.AssignExpression(); if (expr != null) { expr = ExpressionHelper.SearchExpressionDeeply(expr, editor.CaretLocation); var ret = ResolveType(expr.ExpressionTypeRepresentation, ctxt); if (ret == null && expr != null && !(expr is TokenExpression)) ret = new[] { new ExpressionResult() { Expression = expr } }; return ret; } } else return ResolveType(parser.Type(), ctxt); return null; }
public static ResolveResult[] ResolveType(ITypeDeclaration declaration, ResolverContext ctxt, IBlockNode currentScopeOverride = null) { if (ctxt == null) return null; var ctxtOverride=ctxt; if(currentScopeOverride!=null && currentScopeOverride!=ctxt.ScopedBlock){ ctxtOverride=new ResolverContext(); ctxtOverride.ApplyFrom(ctxt); ctxtOverride.ScopedBlock = currentScopeOverride; ctxtOverride.ScopedStatement = null; } if(ctxtOverride.ScopedBlock!=null &&( ctxtOverride.ImportCache==null || ctxtOverride.ScopedBlock.NodeRoot!=ctxt.ScopedBlock.NodeRoot)) { ctxtOverride.ImportCache=ResolveImports(ctxtOverride.ScopedBlock.NodeRoot as DModule,ctxt.ParseCache); } if (currentScopeOverride == null) currentScopeOverride = ctxt.ScopedBlock; if (ctxt == null || declaration == null) return null; ResolveResult[] preRes = null; object scopeObj = null; if (ctxtOverride.ScopedStatement != null) { var curStmtLevel=ctxtOverride.ScopedStatement; while (curStmtLevel != null && !(curStmtLevel is BlockStatement)) curStmtLevel = curStmtLevel.Parent; if(curStmtLevel is BlockStatement) scopeObj = curStmtLevel; } if (scopeObj == null) scopeObj = ctxtOverride.ScopedBlock; // Check if already resolved once if (ctxtOverride.TryGetAlreadyResolvedType(declaration.ToString(), out preRes, scopeObj)) return preRes; var returnedResults = new List<ResolveResult>(); // Walk down recursively to resolve everything from the very first to declaration's base type declaration. ResolveResult[] rbases = null; if (declaration.InnerDeclaration != null) rbases = ResolveType(declaration.InnerDeclaration, ctxtOverride); // If it's a template, resolve the template id first if (declaration is TemplateInstanceExpression) declaration = (declaration as TemplateInstanceExpression).TemplateIdentifier; /* * If there is no parent resolve context (what usually means we are searching the type named like the first identifier in the entire declaration), * search the very first type declaration by walking along the current block scope hierarchy. * If there wasn't any item found in that procedure, search in the global parse cache */ #region Search initial member/type/module/whatever if (rbases == null) { #region IdentifierDeclaration if (declaration is IdentifierDeclaration) { string searchIdentifier = (declaration as IdentifierDeclaration).Value as string; if (string.IsNullOrEmpty(searchIdentifier)) return null; // Try to convert the identifier into a token int searchToken = string.IsNullOrEmpty(searchIdentifier) ? 0 : DTokens.GetTokenID(searchIdentifier); // References current class scope if (searchToken == DTokens.This) { var classDef = ctxt.ScopedBlock; while (!(classDef is DClassLike) && classDef != null) classDef = classDef.Parent as IBlockNode; if (classDef is DClassLike) { var res = HandleNodeMatch(classDef, ctxtOverride, typeBase: declaration); if (res != null) returnedResults.Add(res); } } // References super type of currently scoped class declaration else if (searchToken == DTokens.Super) { var classDef = currentScopeOverride; while (!(classDef is DClassLike) && classDef != null) classDef = classDef.Parent as IBlockNode; if (classDef != null) { var baseClassDefs = ResolveBaseClass(classDef as DClassLike, ctxtOverride); if (baseClassDefs != null) { // Important: Overwrite type decl base with 'super' token foreach (var bc in baseClassDefs) bc.TypeDeclarationBase = declaration; returnedResults.AddRange(baseClassDefs); } } } // If we found a base type, return a static-type-result else if (searchToken > 0) { if (DTokens.BasicTypes[searchToken]) returnedResults.Add(new StaticTypeResult() { BaseTypeToken = searchToken, TypeDeclarationBase = declaration }); // anything else is just a key word, not a type } // (As usual) Go on searching in the local&global scope(s) else { var matches = new List<INode>(); // Search in current statement's declarations (if possible) var decls = BlockStatement.GetItemHierarchy(ctxt.ScopedStatement, declaration.Location); if(decls!=null) foreach (var decl in decls) if (decl != null && decl.Name == searchIdentifier) matches.Add(decl); // First search along the hierarchy in the current module var curScope = ctxtOverride.ScopedBlock; while (curScope != null) { /* * If anonymous enum, skip that one, because in the following ScanForNodeIdentifier call, * its children already become added to the match list */ if (curScope is DEnum && curScope.Name == "") curScope = curScope.Parent as IBlockNode; if (curScope is DMethod) { var dm = curScope as DMethod; // If the method is a nested method, // this method won't be 'linked' to the parent statement tree directly - // so, we've to gather the parent method and add its locals to the return list if (dm.Parent is DMethod) { var parDM = dm.Parent as DMethod; var nestedBlock = parDM.GetSubBlockAt(declaration.Location); if (nestedBlock != null) { // Search for the deepest statement scope and test all declarations done in the entire scope hierarchy decls = BlockStatement.GetItemHierarchy(nestedBlock.SearchStatementDeeply(declaration.Location), declaration.Location); foreach (var decl in decls) // ... Add them if match was found if (decl != null && decl.Name == searchIdentifier) matches.Add(decl); } } // Do not check further method's children but its (template) parameters foreach (var p in dm.Parameters) if (p.Name == searchIdentifier) matches.Add(p); if (dm.TemplateParameters != null) foreach (var tp in dm.TemplateParameterNodes) if (tp.Name == searchIdentifier) matches.Add(tp); } else { var m = ScanNodeForIdentifier(curScope, searchIdentifier, ctxtOverride); if (m != null) matches.AddRange(m); var mod = curScope as IAbstractSyntaxTree; if (mod != null) { var modNameParts = mod.ModuleName.Split('.'); if (!string.IsNullOrEmpty(mod.ModuleName) && modNameParts[0] == searchIdentifier) matches.Add(curScope); } } curScope = curScope.Parent as IBlockNode; } // Then go on searching in the global scope var ThisModule = currentScopeOverride is IAbstractSyntaxTree ? currentScopeOverride as IAbstractSyntaxTree : currentScopeOverride.NodeRoot as IAbstractSyntaxTree; if (ctxt.ParseCache != null) foreach (var mod in ctxt.ParseCache) { if (mod == ThisModule) continue; var modNameParts = mod.ModuleName.Split('.'); if (modNameParts[0] == searchIdentifier) matches.Add(mod); } if (ctxtOverride.ImportCache != null) foreach (var mod in ctxtOverride.ImportCache) { var m = ScanNodeForIdentifier(mod, searchIdentifier, null); if (m != null) matches.AddRange(m); } var results = HandleNodeMatches(matches, ctxtOverride, TypeDeclaration: declaration); if (results != null) returnedResults.AddRange(results); } } #endregion #region TypeOfDeclaration else if(declaration is TypeOfDeclaration) { var typeOf=declaration as TypeOfDeclaration; // typeof(return) if(typeOf.InstanceId is TokenExpression && (typeOf.InstanceId as TokenExpression).Token==DTokens.Return) { var m= HandleNodeMatch(currentScopeOverride,ctxt,currentScopeOverride,null,declaration); if(m!=null) returnedResults.Add(m); } // typeOf(myInt) === int else if(typeOf.InstanceId!=null) { var wantedTypes=ResolveType(typeOf.InstanceId.ExpressionTypeRepresentation,ctxt,currentScopeOverride); // Scan down for variable's base types var c1=new List<ResolveResult>(wantedTypes); var c2=new List<ResolveResult>(); while(c1.Count>0) { foreach(var t in c1) { if(t is MemberResult) c2.AddRange((t as MemberResult).MemberBaseTypes); else returnedResults.Add(t); } c1.Clear(); c1.AddRange(c2); c2.Clear(); } } } #endregion else returnedResults.Add(new StaticTypeResult() { TypeDeclarationBase = declaration }); } #endregion #region Search in further, deeper levels else foreach (var rbase in rbases) { #region Identifier if (declaration is IdentifierDeclaration) { string searchIdentifier = (declaration as IdentifierDeclaration).Value as string; // Scan for static properties var staticProp = StaticPropertyResolver.TryResolveStaticProperties( rbase, declaration as IdentifierDeclaration, ctxtOverride); if (staticProp != null) { returnedResults.Add(staticProp); continue; } var scanResults = new List<ResolveResult>(); scanResults.Add(rbase); var nextResults = new List<ResolveResult>(); while (scanResults.Count > 0) { foreach (var scanResult in scanResults) { // First filter out all alias and member results..so that there will be only (Static-)Type or Module results left.. if (scanResult is MemberResult) { var _m = (scanResult as MemberResult).MemberBaseTypes; if (_m != null) nextResults.AddRange(_m); } else if (scanResult is TypeResult) { var tr=scanResult as TypeResult; var nodeMatches=ScanNodeForIdentifier(tr.ResolvedTypeDefinition, searchIdentifier, ctxtOverride); var results = HandleNodeMatches( nodeMatches, ctxtOverride, tr.ResolvedTypeDefinition, resultBase: rbase, TypeDeclaration: declaration); if (results != null) returnedResults.AddRange(results); } else if (scanResult is ModuleResult) { var modRes = (scanResult as ModuleResult); if (modRes.IsOnlyModuleNamePartTyped()) { var modNameParts = modRes.ResolvedModule.ModuleName.Split('.'); if (modNameParts[modRes.AlreadyTypedModuleNameParts] == searchIdentifier) { returnedResults.Add(new ModuleResult() { ResolvedModule = modRes.ResolvedModule, AlreadyTypedModuleNameParts = modRes.AlreadyTypedModuleNameParts + 1, ResultBase = modRes, TypeDeclarationBase = declaration }); } } else { var results = HandleNodeMatches( ScanNodeForIdentifier((scanResult as ModuleResult).ResolvedModule, searchIdentifier, ctxtOverride), ctxtOverride, currentScopeOverride, rbase, TypeDeclaration: declaration); if (results != null) returnedResults.AddRange(results); } } else if (scanResult is StaticTypeResult) { } } scanResults = nextResults; nextResults = new List<ResolveResult>(); } } #endregion else if (declaration is ArrayDecl || declaration is PointerDecl) { returnedResults.Add(new StaticTypeResult() { TypeDeclarationBase = declaration, ResultBase = rbase }); } else if (declaration is DExpressionDecl) { var expr = (declaration as DExpressionDecl).Expression; /* * Note: Assume e.g. foo.bar.myArray in foo.bar.myArray[0] has been resolved! * So, we just have to take the last postfix expression */ /* * After we've done this, we reduce the stack.. * Target of this action is to retrieve the value type: * * int[string][] myArray; // Is an array that holds an associative array, whereas the value type is 'int', and key type is 'string' * * auto mySubArray=myArray[0]; // returns a reference to an int[string] array * * auto myElement=mySubArray["abcd"]; // returns the most basic value type: 'int' */ if (rbase is StaticTypeResult) { var str = rbase as StaticTypeResult; if (str.TypeDeclarationBase is ArrayDecl && expr is PostfixExpression_Index) { returnedResults.Add(new StaticTypeResult() { TypeDeclarationBase = (str.TypeDeclarationBase as ArrayDecl).ValueType }); } } else if (rbase is MemberResult) { var mr = rbase as MemberResult; if (mr.MemberBaseTypes != null && mr.MemberBaseTypes.Length > 0) foreach (var memberType in TryRemoveAliasesFromResult(mr.MemberBaseTypes)) { if (expr is PostfixExpression_Index) { var str = (memberType as StaticTypeResult); /* * If the member's type is an array, and if our expression contains an index-expression (e.g. myArray[0]), * take the value type of the */ // For array and pointer declarations, the StaticTypeResult object contains the array's value type / pointer base type. if (str != null && (str.TypeDeclarationBase is ArrayDecl || str.TypeDeclarationBase is PointerDecl)) returnedResults.AddRange(TryRemoveAliasesFromResult(str.ResultBase)); } else returnedResults.Add(memberType); } } } } #endregion if (returnedResults.Count > 0) { ctxt.TryAddResults(declaration.ToString(), returnedResults.ToArray(), ctxtOverride.ScopedBlock); return returnedResults.ToArray(); } return null; }
/// <summary> /// Scans through the node. Also checks if n is a DClassLike or an other kind of type node and checks their specific child and/or base class nodes. /// </summary> /// <param name="n"></param> /// <param name="name"></param> /// <param name="parseCache">Needed when trying to search base classes</param> /// <returns></returns> public static INode[] ScanNodeForIdentifier(IBlockNode curScope, string name, ResolverContext ctxt) { var matches = new List<INode>(); if (curScope.Count > 0) foreach (var n in curScope) { // Scan anonymous enums if (n is DEnum && n.Name == "") { foreach (var k in n as DEnum) if (k.Name == name) matches.Add(k); } if (n.Name == name) matches.Add(n); } // If our current Level node is a class-like, also attempt to search in its baseclass! if (curScope is DClassLike) { var baseClasses = ResolveBaseClass(curScope as DClassLike, ctxt); if (baseClasses != null) foreach (var i in baseClasses) { var baseClass = i as TypeResult; if (baseClass == null) continue; // Search for items called name in the base class(es) var r = ScanNodeForIdentifier(baseClass.ResolvedTypeDefinition, name, ctxt); if (r != null) matches.AddRange(r); } } // Check parameters if (curScope is DMethod) { var dm = curScope as DMethod; foreach (var ch in dm.Parameters) { if (name == ch.Name) matches.Add(ch); } } // and template parameters if (curScope is DNode && (curScope as DNode).TemplateParameters != null) foreach (var ch in (curScope as DNode).TemplateParameters) { if (name == ch.Name) matches.Add(new TemplateParameterNode(ch)); } return matches.Count > 0 ? matches.ToArray() : null; }
public static ResolveResult[] HandleNodeMatches(IEnumerable<INode> matches, ResolverContext ctxt, IBlockNode currentlyScopedNode = null, ResolveResult resultBase = null, ITypeDeclaration TypeDeclaration = null) { var rl = new List<ResolveResult>(); if (matches != null) foreach (var m in matches) { if (m == null) continue; var res = HandleNodeMatch(m, ctxt, currentlyScopedNode, resultBase, typeBase: TypeDeclaration); if (res != null) rl.Add(res); } return rl.ToArray(); }
/// <summary> /// The variable's or method's base type will be resolved (if auto type, the intializer's type will be taken). /// A class' base class will be searched. /// etc.. /// </summary> /// <returns></returns> public static ResolveResult HandleNodeMatch( INode m, ResolverContext ctxt, IBlockNode currentlyScopedNode = null, ResolveResult resultBase = null, ITypeDeclaration typeBase = null) { if (currentlyScopedNode == null) currentlyScopedNode = ctxt.ScopedBlock; stackNum_HandleNodeMatch++; //HACK: Really dirty stack overflow prevention via manually counting call depth var DoResolveBaseType = stackNum_HandleNodeMatch > 5 ? false : ctxt.ResolveBaseTypes; // Prevent infinite recursion if the type accidently equals the node's name if (m.Type != null && m.Type.ToString(false) == m.Name) DoResolveBaseType = false; if (m is DVariable) { var v = m as DVariable; var memberbaseTypes = DoResolveBaseType ? ResolveType(v.Type, ctxt, currentlyScopedNode) : null; // For auto variables, use the initializer to get its type if (memberbaseTypes == null && DoResolveBaseType && v.ContainsAttribute(DTokens.Auto) && v.Initializer != null) { var init = v.Initializer; memberbaseTypes = ResolveType(init.ExpressionTypeRepresentation, ctxt, currentlyScopedNode); } // Resolve aliases if wished if (ctxt.ResolveAliases && memberbaseTypes != null) { /* * To ensure that absolutely all kinds of alias definitions became resolved (includes aliased alias definitions!), * loop through the resolution process again, after at least one aliased type has been found. */ while (memberbaseTypes.Length > 0) { bool hadAliasResolution = false; var memberBaseTypes_Override = new List<ResolveResult>(); foreach (var type in memberbaseTypes) { var mr = type as MemberResult; if (mr != null && mr.ResolvedMember is DVariable) { var dv = mr.ResolvedMember as DVariable; // Note: Normally, a variable's base type mustn't be an other variable but an alias defintion... if (dv.IsAlias) { var newRes = ResolveType(dv.Type, ctxt, currentlyScopedNode); if (newRes != null) memberBaseTypes_Override.AddRange(newRes); hadAliasResolution = true; continue; } } // If no alias found, re-add it to our override list again memberBaseTypes_Override.Add(type); } memberbaseTypes = memberBaseTypes_Override.ToArray(); if (!hadAliasResolution) break; } } // Note: Also works for aliases! In this case, we simply try to resolve the aliased type, otherwise the variable's base type stackNum_HandleNodeMatch--; return new MemberResult() { ResolvedMember = m, MemberBaseTypes = memberbaseTypes, ResultBase = resultBase, TypeDeclarationBase = typeBase }; } else if (m is DMethod) { var method = m as DMethod; var methodType = method.Type; /* * If a method's type equals null, assume that it's an 'auto' function.. * 1) Search for a return statement * 2) Resolve the returned expression * 3) Use that one as the method's type */ //TODO: What about handling 'null'-returns? if (methodType == null && method.Body != null) { ReturnStatement returnStmt = null; var list = new List<IStatement> { method.Body }; var list2 = new List<IStatement>(); while (returnStmt == null && list.Count > 0) { foreach (var stmt in list) { if (stmt is ReturnStatement) { returnStmt = stmt as ReturnStatement; break; } if (stmt is StatementContainingStatement) list2.AddRange((stmt as StatementContainingStatement).SubStatements); } list = list2; list2 = new List<IStatement>(); } if (returnStmt != null && returnStmt.ReturnExpression != null) { currentlyScopedNode = method; methodType = returnStmt.ReturnExpression.ExpressionTypeRepresentation; } } var ret = new MemberResult() { ResolvedMember = m, MemberBaseTypes = DoResolveBaseType ? ResolveType(methodType, ctxt, currentlyScopedNode) : null, ResultBase = resultBase, TypeDeclarationBase = typeBase }; stackNum_HandleNodeMatch--; return ret; } else if (m is DClassLike) { var Class = m as DClassLike; var bc = DoResolveBaseType ? ResolveBaseClass(Class, ctxt) : null; stackNum_HandleNodeMatch--; return new TypeResult() { ResolvedTypeDefinition = Class, BaseClass = bc, ResultBase = resultBase, TypeDeclarationBase = typeBase }; } else if (m is IAbstractSyntaxTree) { stackNum_HandleNodeMatch--; return new ModuleResult() { ResolvedModule = m as IAbstractSyntaxTree, AlreadyTypedModuleNameParts = 1, ResultBase = resultBase, TypeDeclarationBase = typeBase }; } else if (m is DEnum) { stackNum_HandleNodeMatch--; return new TypeResult() { ResolvedTypeDefinition = m as IBlockNode, ResultBase = resultBase, TypeDeclarationBase = typeBase }; } else if (m is TemplateParameterNode) { stackNum_HandleNodeMatch--; return new MemberResult() { ResolvedMember = m, TypeDeclarationBase = typeBase, ResultBase = resultBase }; } stackNum_HandleNodeMatch--; // This never should happen.. return null; }
public void ApplyFrom(ResolverContext other) { if (other == null) return; ScopedBlock = other.ScopedBlock; ScopedStatement = other.ScopedStatement; ParseCache = other.ParseCache; ImportCache = other.ImportCache; ResolveBaseTypes = other.ResolveBaseTypes; ResolveAliases = other.ResolveAliases; resolvedTypes = other.resolvedTypes; }
/// <summary> /// Tries to resolve a static property's name. /// Returns a result describing the theoretical member (".init"-%gt;MemberResult; ".typeof"->TypeResult etc). /// Returns null if nothing was found. /// </summary> /// <param name="InitialResult"></param> /// <returns></returns> public static ResolveResult TryResolveStaticProperties(ResolveResult InitialResult, IdentifierDeclaration Identifier, ResolverContext ctxt = null) { if (InitialResult == null || Identifier == null || InitialResult is ModuleResult) { return null; } var propertyName = Identifier.Value as string; if (propertyName == null) return null; INode relatedNode = null; if (InitialResult is MemberResult) relatedNode = (InitialResult as MemberResult).ResolvedMember; else if (InitialResult is TypeResult) relatedNode = (InitialResult as TypeResult).ResolvedTypeDefinition; #region init if (propertyName == "init") { var prop_Init = new DVariable { Name = "init", Description = "Initializer" }; if (relatedNode != null) { if (!(relatedNode is DVariable)) { prop_Init.Parent = relatedNode.Parent; prop_Init.Type = new IdentifierDeclaration(relatedNode.Name); } else { prop_Init.Parent = relatedNode; prop_Init.Initializer = (relatedNode as DVariable).Initializer; prop_Init.Type = relatedNode.Type; } } return new MemberResult { ResultBase = InitialResult, MemberBaseTypes = new[] { InitialResult }, TypeDeclarationBase = Identifier, ResolvedMember = prop_Init }; } #endregion #region sizeof if (propertyName == "sizeof") return new MemberResult { ResultBase = InitialResult, TypeDeclarationBase = Identifier, ResolvedMember = new DVariable { Name = "sizeof", Type = new DTokenDeclaration(DTokens.Int), Initializer = new IdentifierExpression(4), Description = "Size in bytes (equivalent to C's sizeof(type))" } }; #endregion #region alignof if (propertyName == "alignof") return new MemberResult { ResultBase = InitialResult, TypeDeclarationBase = Identifier, ResolvedMember = new DVariable { Name = "alignof", Type = new DTokenDeclaration(DTokens.Int), Description = "Alignment size" } }; #endregion #region mangleof if (propertyName == "mangleof") return new MemberResult { ResultBase = InitialResult, TypeDeclarationBase = Identifier, ResolvedMember = new DVariable { Name = "mangleof", Type = new IdentifierDeclaration("string"), Description = "String representing the ‘mangled’ representation of the type" }, MemberBaseTypes = ResolveType(new IdentifierDeclaration("string"), ctxt) }; #endregion #region stringof if (propertyName == "stringof") return new MemberResult { ResultBase = InitialResult, TypeDeclarationBase = Identifier, ResolvedMember = new DVariable { Name = "stringof", Type = new IdentifierDeclaration("string"), Description = "String representing the source representation of the type" }, MemberBaseTypes = ResolveType(new IdentifierDeclaration("string"), ctxt) }; #endregion bool isArray = false, isAssocArray = false, isInt = false, isFloat = false; #region See AbsractCompletionSupport.StaticPropertyAddition if (InitialResult is StaticTypeResult) { var srr = InitialResult as StaticTypeResult; var type = srr.TypeDeclarationBase; // on things like immutable(char), pass by the surrounding attribute.. while (type is MemberFunctionAttributeDecl) type = (type as MemberFunctionAttributeDecl).InnerType; if (type is ArrayDecl) { var ad = type as ArrayDecl; // Normal array if (ad.KeyType is DTokenDeclaration && DTokens.BasicTypes_Integral[(ad.KeyType as DTokenDeclaration).Token]) isArray = true; // Associative array else isAssocArray = true; } else if (!(type is PointerDecl)) { int TypeToken = srr.BaseTypeToken; if (TypeToken <= 0 && type is DTokenDeclaration) TypeToken = (type as DTokenDeclaration).Token; if (TypeToken > 0) { // Determine whether float by the var's base type isInt = DTokens.BasicTypes_Integral[srr.BaseTypeToken]; isFloat = DTokens.BasicTypes_FloatingPoint[srr.BaseTypeToken]; } } } else if (InitialResult is ExpressionResult) { var err = InitialResult as ExpressionResult; var expr = err.Expression; // 'Skip' surrounding parentheses while (expr is SurroundingParenthesesExpression) expr = (expr as SurroundingParenthesesExpression).Expression; var idExpr = expr as IdentifierExpression; if (idExpr != null) { // Char literals, Integrals types & Floats if ((idExpr.Format & LiteralFormat.Scalar) == LiteralFormat.Scalar || idExpr.Format == LiteralFormat.CharLiteral) { // Floats also imply integral properties isInt = true; isFloat = (idExpr.Format & LiteralFormat.FloatingPoint) == LiteralFormat.FloatingPoint; } // String literals else if (idExpr.Format == LiteralFormat.StringLiteral || idExpr.Format == LiteralFormat.VerbatimStringLiteral) { isArray = true; } } // Pointer conversions (e.g. (myInt*).sizeof) } #endregion //TODO: Resolve static [assoc] array props if (isArray || isAssocArray) { } return null; }
public ResolverContextStack(ParseCacheList ParseCache, ResolverContext initialContext) { this.ParseCache = ParseCache; stack.Push(initialContext); }
public static TypeResult[] ResolveBaseClass(DClassLike ActualClass, ResolverContext ctxt) { if (bcStack > 8) { bcStack--; return null; } if (ActualClass == null || ((ActualClass.BaseClasses == null || ActualClass.BaseClasses.Count < 1) && ActualClass.Name != null && ActualClass.Name.ToLower() == "object")) return null; var ret = new List<TypeResult>(); // Implicitly set the object class to the inherited class if no explicit one was done var type = (ActualClass.BaseClasses == null || ActualClass.BaseClasses.Count < 1) ? new IdentifierDeclaration("Object") : ActualClass.BaseClasses[0]; // A class cannot inherit itself if (type == null || type.ToString(false) == ActualClass.Name || ActualClass.NodeRoot == ActualClass) return null; bcStack++; /* * If the ActualClass is defined in an other module (so not in where the type resolution has been started), * we have to enable access to the ActualClass's module's imports! * * module modA: * import modB; * * class A:B{ * * void bar() * { * fooC(); // Note that modC wasn't imported publically! Anyway, we're still able to access this method! * // So, the resolver must know that there is a class C. * } * } * * ----------------- * module modB: * import modC; * * // --> When being about to resolve B's base class C, we have to use the imports of modB(!), not modA * class B:C{} * ----------------- * module modC: * * class C{ * * void fooC(); * * } */ ResolveResult[] results = null; if (ctxt != null) { var ctxtOverride = new ResolverContext(); // Take ctxt's parse cache etc. ctxtOverride.ApplyFrom(ctxt); // First override the scoped block ctxtOverride.ScopedBlock = ActualClass.Parent as IBlockNode; // Then override the import cache with imports of the ActualClass's module if (ctxt.ScopedBlock != null && ctxt.ScopedBlock.NodeRoot != ActualClass.NodeRoot) ctxtOverride.ImportCache = ResolveImports(ActualClass.NodeRoot as DModule, ctxt.ParseCache); results = ResolveType(type, ctxtOverride); } else { results = ResolveType(type, null, ActualClass.Parent as IBlockNode); } if (results != null) foreach (var i in results) if (i is TypeResult) ret.Add(i as TypeResult); bcStack--; return ret.Count > 0 ? ret.ToArray() : null; }