| /******************************************************************************* |
| * Copyright (c) 2007 University of Illinois at Urbana-Champaign and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * UIUC - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.photran.internal.core.analysis.binding; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.photran.core.IFortranAST; |
| import org.eclipse.photran.internal.core.analysis.binding.Definition.Visibility; |
| import org.eclipse.photran.internal.core.analysis.types.Type; |
| import org.eclipse.photran.internal.core.lexer.Terminal; |
| import org.eclipse.photran.internal.core.lexer.Token; |
| import org.eclipse.photran.internal.core.lexer.Token.FakeToken; |
| import org.eclipse.photran.internal.core.parser.ASTBlockConstructNode; |
| import org.eclipse.photran.internal.core.parser.ASTBlockDataNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTBlockDataStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTBlockDataSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTBlockStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTCallStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTDerivedTypeDefNode; |
| import org.eclipse.photran.internal.core.parser.ASTDerivedTypeStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndBlockDataStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndBlockStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndFunctionStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndInterfaceStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndModuleStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndProgramStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndSubmoduleStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndSubroutineStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTEndTypeStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTExecutableProgramNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTGenericNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTGenericSpecNode; |
| import org.eclipse.photran.internal.core.parser.ASTInterfaceBlockNode; |
| import org.eclipse.photran.internal.core.parser.ASTInterfaceStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTListNode; |
| import org.eclipse.photran.internal.core.parser.ASTMainProgramNode; |
| import org.eclipse.photran.internal.core.parser.ASTModuleNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTModuleNode; |
| import org.eclipse.photran.internal.core.parser.ASTModuleStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTNode; |
| import org.eclipse.photran.internal.core.parser.ASTProgramNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTProgramStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubmoduleNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubmoduleStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTTypeNameNode; |
| import org.eclipse.photran.internal.core.parser.ASTUseStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTVarOrFnRefNode; |
| import org.eclipse.photran.internal.core.parser.ASTVisitor; |
| import org.eclipse.photran.internal.core.parser.IASTListNode; |
| import org.eclipse.photran.internal.core.parser.IASTNode; |
| import org.eclipse.photran.internal.core.parser.IBlockDataBodyConstruct; |
| import org.eclipse.photran.internal.core.parser.IBodyConstruct; |
| import org.eclipse.photran.internal.core.parser.IDerivedTypeBodyConstruct; |
| import org.eclipse.photran.internal.core.parser.IInterfaceSpecification; |
| import org.eclipse.photran.internal.core.parser.IInternalSubprogram; |
| import org.eclipse.photran.internal.core.parser.IModuleBodyConstruct; |
| import org.eclipse.photran.internal.core.util.Notification; |
| import org.eclipse.photran.internal.core.vpg.PhotranTokenRef; |
| import org.eclipse.photran.internal.core.vpg.PhotranVPG; |
| import org.eclipse.photran.internal.core.vpg.PhotranVPGBuilder; |
| |
| /** |
| * An AST node representing a scope. |
| * <p> |
| * (View the type hierarchy to see which nodes are scoping nodes.) |
| * |
| * @author Jeff Overbey |
| */ |
| public abstract class ScopingNode extends ASTNode |
| { |
| public static ScopingNode getEnclosingScope(IASTNode node) |
| { |
| for (IASTNode candidate = node.getParent(); candidate != null; candidate = candidate.getParent()) |
| { |
| if (isScopingNode(candidate)) |
| { |
| ScopingNode scope = (ScopingNode)candidate; |
| |
| //if (node == scope.getRepresentativeToken().findToken()) |
| if (shouldBeBoundToOuterScope(node)) |
| return getEnclosingScope(scope); |
| else |
| return scope; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static ScopingNode getLocalScope(IASTNode node) |
| { |
| for (IASTNode candidate = node.getParent(); candidate != null; candidate = candidate.getParent()) |
| if (isScopingNode(candidate)) |
| return (ScopingNode)candidate; |
| |
| return null; |
| } |
| |
| /** |
| * In cases such as |
| * <pre> |
| * SUBROUTINE S |
| * INTEGER :: S |
| * END SUBROUTINE S |
| * </pre> |
| * the <code>S</code>'s in the Subroutine and End Subroutine statements are under the |
| * ASTSubroutineSubprogram node in the AST, but they should actually be bound in the |
| * <i>outer</i> scope. |
| * <p> |
| * In general, this is true for |
| * derived type names, program names, module names, block data names, |
| * interface names, subroutine names, function names, submodule names, and block |
| * construct names in the beginning and ending |
| * statements for their respective scoping nodes. |
| */ |
| private static boolean shouldBeBoundToOuterScope(IASTNode node) |
| { |
| IASTNode parent = node.getParent(); |
| if (parent == null) return false; |
| IASTNode grandparent = parent.getParent(); |
| if (grandparent == null) return false; |
| |
| if (isDeclStmtForScope(parent)) |
| { |
| if (parent instanceof ASTFunctionStmtNode && node == ((ASTFunctionStmtNode)parent).getName()) // result clause |
| return false; |
| else |
| return true; |
| } |
| else if (parent instanceof ASTProgramNameNode |
| || parent instanceof ASTFunctionNameNode |
| || parent instanceof ASTSubroutineNameNode |
| || parent instanceof ASTModuleNameNode |
| || parent instanceof ASTBlockDataNameNode |
| || parent instanceof ASTTypeNameNode // <- These are used in other contexts |
| || parent instanceof ASTGenericNameNode // | as well, so we must test the |
| || parent instanceof ASTGenericSpecNode // | grandparent node to make a |
| || parent instanceof ASTEndNameNode) // <- decision |
| { |
| if (inAnonymousInterface(grandparent)) |
| return false; |
| else |
| return isDeclStmtForScope(grandparent); |
| } |
| else return false; |
| } |
| |
| private static boolean isDeclStmtForScope(IASTNode node) |
| { |
| return node instanceof ASTProgramStmtNode |
| || node instanceof ASTFunctionStmtNode |
| || node instanceof ASTSubroutineStmtNode |
| || node instanceof ASTModuleStmtNode |
| || node instanceof ASTBlockDataStmtNode |
| || node instanceof ASTDerivedTypeStmtNode |
| || node instanceof ASTInterfaceStmtNode |
| || node instanceof ASTSubmoduleStmtNode |
| || node instanceof ASTBlockStmtNode |
| || node instanceof ASTEndProgramStmtNode |
| || node instanceof ASTEndFunctionStmtNode |
| || node instanceof ASTEndSubroutineStmtNode |
| || node instanceof ASTEndModuleStmtNode |
| || node instanceof ASTEndBlockDataStmtNode |
| || node instanceof ASTEndTypeStmtNode |
| || node instanceof ASTEndInterfaceStmtNode |
| || node instanceof ASTEndSubmoduleStmtNode |
| || node instanceof ASTEndBlockStmtNode; |
| } |
| |
| private static boolean inAnonymousInterface(IASTNode n) |
| { |
| for (IASTNode node = n.getParent(); node != null; node = node.getParent()) |
| if (node instanceof ASTInterfaceBlockNode && isAnonymousInterface((ASTInterfaceBlockNode)node)) |
| return true; |
| return false; |
| } |
| |
| public static boolean isScopingNode(IASTNode node) |
| { |
| return node instanceof ASTExecutableProgramNode |
| || node instanceof ASTMainProgramNode |
| || node instanceof ASTFunctionSubprogramNode |
| || node instanceof ASTSubroutineSubprogramNode |
| || node instanceof ASTModuleNode |
| || node instanceof ASTBlockDataSubprogramNode |
| || node instanceof ASTDerivedTypeDefNode |
| || (node instanceof ASTInterfaceBlockNode && !isAnonymousInterface((ASTInterfaceBlockNode)node)) |
| || node instanceof ASTSubmoduleNode |
| || node instanceof ASTBlockConstructNode; |
| } |
| |
| private static boolean isAnonymousInterface(ASTInterfaceBlockNode node) |
| { |
| return node.getInterfaceStmt().getGenericName() != null |
| && node.getInterfaceStmt().getGenericSpec() != null; |
| } |
| |
| public ScopingNode getEnclosingScope() |
| { |
| return getEnclosingScope(this); |
| } |
| |
| public ScopingNode getGlobalScope() |
| { |
| IASTNode result = this; |
| |
| // Find root of AST (i.e., topmost ASTExecutableProgramNode) |
| while (result.getParent() != null) result = result.getParent(); |
| |
| return (ScopingNode)result; |
| } |
| |
| /* |
| * According to a profile obtained (6/24/09) by running |
| * org.eclipse.photran.cmdline/vpgstats-profiled |
| * on FMLIB (one of the projects in UIUC's SVN repo), |
| * caching the representative token for a scope reduced |
| * the Binder's maximal times as follows: |
| * BEFORE============ AFTER============ |
| * DefinitionCollector: 8771 ms (FM.f90) 3475 ms (FM.f90) |
| * ImplicitSpecCollector: 1816 ms (FM.f90) 404 ms (FM.f90) |
| * ReferenceCollector: 122225 ms (FM.f90) 35226 ms (FM.f90) |
| */ |
| private PhotranTokenRef cachedRepresentataiveToken = null; |
| |
| public PhotranTokenRef getRepresentativeToken() |
| { |
| return getRepresentativeToken(false); |
| } |
| |
| public PhotranTokenRef getRepresentativeToken(boolean force) |
| { |
| if (force || cachedRepresentataiveToken == null) |
| { |
| Token result = internalGetRepresentativeToken(); |
| if (result == null) |
| { |
| Token firstToken = findFirstToken(); |
| if (firstToken == null) throw new Error(Messages.ScopingNode_EmptyFile); |
| cachedRepresentataiveToken = new PhotranTokenRef(firstToken.getLogicalFile(), -1, 0); |
| } |
| else |
| cachedRepresentataiveToken = result.getTokenRef(); |
| } |
| |
| return cachedRepresentataiveToken; |
| } |
| |
| private Token internalGetRepresentativeToken() |
| { |
| // TODO: GET RID OF THIS MESS AFTER INDIVIDUAL NODES CAN BE CUSTOMIZED |
| // AND DYNAMICALLY DISPATCHED TO! |
| |
| if (this instanceof ASTExecutableProgramNode) |
| { |
| return null; |
| } |
| else if (this instanceof ASTMainProgramNode) |
| { |
| ASTProgramStmtNode m = ((ASTMainProgramNode)this).getProgramStmt(); |
| if (m != null) |
| { |
| if (m.getProgramName() != null) |
| return m.getProgramName().getProgramName(); |
| else |
| return m.getProgramToken(); |
| } |
| else |
| { |
| ASTEndProgramStmtNode s = ((ASTMainProgramNode)this).getEndProgramStmt(); |
| return s.getEndToken(); |
| } |
| } |
| else if (this instanceof ASTFunctionSubprogramNode) |
| { |
| return ((ASTFunctionSubprogramNode)this).getFunctionStmt().getFunctionName().getFunctionName(); |
| } |
| else if (this instanceof ASTSubroutineSubprogramNode) |
| { |
| return ((ASTSubroutineSubprogramNode)this).getSubroutineStmt().getSubroutineName().getSubroutineName(); |
| } |
| else if (this instanceof ASTModuleNode) |
| { |
| return ((ASTModuleNode)this).getModuleStmt().getModuleName().getModuleName(); |
| } |
| else if (this instanceof ASTBlockDataSubprogramNode) |
| { |
| ASTBlockDataStmtNode s = ((ASTBlockDataSubprogramNode)this).getBlockDataStmt(); |
| if (s.getBlockDataName() != null) |
| return s.getBlockDataName().getBlockDataName(); |
| else |
| return s.getBlockDataToken(); |
| } |
| else if (this instanceof ASTDerivedTypeDefNode) |
| { |
| return ((ASTDerivedTypeDefNode)this).getDerivedTypeStmt().getTypeName(); |
| } |
| else if (this instanceof ASTInterfaceBlockNode) |
| { |
| ASTInterfaceStmtNode s = ((ASTInterfaceBlockNode)this).getInterfaceStmt(); |
| if (s.getGenericName() != null) |
| return s.getGenericName().getGenericName(); |
| else if (s.getGenericSpec() != null && s.getGenericSpec().getEqualsToken() != null) |
| return s.getGenericSpec().getEqualsToken(); |
| else |
| return s.getInterfaceToken(); |
| } |
| else if (this instanceof ASTSubmoduleNode) |
| { |
| return ((ASTSubmoduleNode)this).getSubmoduleStmt().getSubmoduleName().getModuleName(); |
| } |
| else if (this instanceof ASTBlockConstructNode) |
| { |
| return ((ASTBlockConstructNode)this).findFirstToken(); |
| } |
| else |
| { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| public void clearAllCachedRepresentativeTokens() |
| { |
| this.accept(new ASTVisitor() |
| { |
| @Override public void visitASTNode(IASTNode node) |
| { |
| if (isScopingNode(node)) |
| ((ScopingNode)node).cachedRepresentataiveToken = null; |
| super.visitASTNode(node); |
| } |
| }); |
| } |
| |
| public ASTNode getHeaderStmt() |
| { |
| // TODO: GET RID OF THIS MESS AFTER INDIVIDUAL NODES CAN BE CUSTOMIZED |
| // AND DYNAMICALLY DISPATCHED TO! |
| |
| if (this instanceof ASTExecutableProgramNode) |
| return null; |
| else if (this instanceof ASTMainProgramNode) |
| return ((ASTMainProgramNode)this).getProgramStmt(); |
| else if (this instanceof ASTFunctionSubprogramNode) |
| return ((ASTFunctionSubprogramNode)this).getFunctionStmt(); |
| else if (this instanceof ASTSubroutineSubprogramNode) |
| return ((ASTSubroutineSubprogramNode)this).getSubroutineStmt(); |
| else if (this instanceof ASTModuleNode) |
| return ((ASTModuleNode)this).getModuleStmt(); |
| else if (this instanceof ASTBlockDataSubprogramNode) |
| return ((ASTBlockDataSubprogramNode)this).getBlockDataStmt(); |
| else if (this instanceof ASTDerivedTypeDefNode) |
| return ((ASTDerivedTypeDefNode)this).getDerivedTypeStmt(); |
| else if (this instanceof ASTInterfaceBlockNode) |
| return ((ASTInterfaceBlockNode)this).getInterfaceStmt(); |
| else if (this instanceof ASTSubmoduleNode) |
| return ((ASTSubmoduleNode)this).getSubmoduleStmt(); |
| else if (this instanceof ASTBlockConstructNode) |
| return ((ASTBlockConstructNode)this).getBlockStmt(); |
| else |
| throw new UnsupportedOperationException(); |
| } |
| |
| public IASTListNode<? extends IASTNode /*IBodyConstruct*/> getBody() |
| { |
| // TODO: GET RID OF THIS MESS AFTER INDIVIDUAL NODES CAN BE CUSTOMIZED |
| // AND DYNAMICALLY DISPATCHED TO! |
| |
| if (this instanceof ASTExecutableProgramNode) |
| return null; |
| else if (this instanceof ASTMainProgramNode) |
| return ((ASTMainProgramNode)this).getBody(); |
| else if (this instanceof ASTFunctionSubprogramNode) |
| return ((ASTFunctionSubprogramNode)this).getBody(); |
| else if (this instanceof ASTSubroutineSubprogramNode) |
| return ((ASTSubroutineSubprogramNode)this).getBody(); |
| else if (this instanceof ASTModuleNode) |
| return ((ASTModuleNode)this).getModuleBody(); |
| else if (this instanceof ASTBlockDataSubprogramNode) |
| return ((ASTBlockDataSubprogramNode)this).getBlockDataBody(); |
| else if (this instanceof ASTDerivedTypeDefNode) |
| return ((ASTDerivedTypeDefNode)this).getDerivedTypeBody(); |
| else if (this instanceof ASTInterfaceBlockNode) |
| return ((ASTInterfaceBlockNode)this).getInterfaceBlockBody(); |
| else if (this instanceof ASTSubmoduleNode) |
| return ((ASTSubmoduleNode)this).getModuleBody(); |
| else if (this instanceof ASTBlockConstructNode) |
| return ((ASTBlockConstructNode)this).getBody(); |
| else |
| throw new UnsupportedOperationException(); |
| } |
| |
| public IASTListNode<? extends IASTNode /*IBodyConstruct*/> getOrCreateBody() |
| { |
| if (getBody() == null) |
| { |
| // TODO: GET RID OF THIS MESS AFTER INDIVIDUAL NODES CAN BE CUSTOMIZED |
| // AND DYNAMICALLY DISPATCHED TO! |
| |
| if (this instanceof ASTMainProgramNode) |
| ((ASTMainProgramNode)this).setBody(new ASTListNode<IBodyConstruct>()); |
| else if (this instanceof ASTFunctionSubprogramNode) |
| ((ASTFunctionSubprogramNode)this).setBody(new ASTListNode<IBodyConstruct>()); |
| else if (this instanceof ASTSubroutineSubprogramNode) |
| ((ASTSubroutineSubprogramNode)this).setBody(new ASTListNode<IBodyConstruct>()); |
| else if (this instanceof ASTModuleNode) |
| ((ASTModuleNode)this).setModuleBody(new ASTListNode<IModuleBodyConstruct>()); |
| else if (this instanceof ASTBlockDataSubprogramNode) |
| ((ASTBlockDataSubprogramNode)this).setBlockDataBody(new ASTListNode<IBlockDataBodyConstruct>()); |
| else if (this instanceof ASTDerivedTypeDefNode) |
| ((ASTDerivedTypeDefNode)this).setDerivedTypeBody(new ASTListNode<IDerivedTypeBodyConstruct>()); |
| else if (this instanceof ASTInterfaceBlockNode) |
| ((ASTInterfaceBlockNode)this).setInterfaceBlockBody(new ASTListNode<IInterfaceSpecification>()); |
| else if (this instanceof ASTSubmoduleNode) |
| ((ASTSubmoduleNode)this).setModuleBody(new ASTListNode<IModuleBodyConstruct>()); |
| else if (this instanceof ASTBlockConstructNode) |
| ((ASTBlockConstructNode)this).setBody(new ASTListNode<IBodyConstruct>()); |
| else |
| throw new UnsupportedOperationException(); |
| } |
| |
| return getBody(); |
| } |
| |
| public boolean isSubprogram() |
| { |
| return this instanceof ASTFunctionSubprogramNode || this instanceof ASTSubroutineSubprogramNode; |
| } |
| |
| public boolean isMainProgram() |
| { |
| return this instanceof ASTMainProgramNode; |
| } |
| |
| public boolean isModule() |
| { |
| return this instanceof ASTModuleNode; |
| } |
| |
| public boolean isInternal() |
| { |
| return getParent() instanceof IInternalSubprogram; |
| } |
| |
| public ImplicitSpec getImplicitSpec() |
| { |
| return (ImplicitSpec)PhotranVPG.getDatabase().getAnnotation(getRepresentativeToken(), PhotranVPG.SCOPE_IMPLICIT_SPEC_ANNOTATION_TYPE); |
| } |
| |
| public boolean isImplicitNone() |
| { |
| return getImplicitSpec() == null; |
| } |
| |
| public boolean isDefaultVisibilityPrivate() |
| { |
| return PhotranVPG.getDatabase().getAnnotation(getRepresentativeToken(), PhotranVPG.SCOPE_DEFAULT_VISIBILITY_IS_PRIVATE_ANNOTATION_TYPE) != null; |
| } |
| |
| public boolean isParentScopeOf(ScopingNode scope) |
| { |
| for (IASTNode node = scope.getParent(); node != null; node = node.getParent()) |
| if (node == this) |
| return true; |
| |
| return false; |
| } |
| |
| /** @return this scope and all scopes nested within it */ |
| public List<ScopingNode> getAllContainedScopes() |
| { |
| final List<ScopingNode> scopes = new LinkedList<ScopingNode>(); |
| |
| this.accept(new ASTVisitor() |
| { |
| @Override public void visitASTNode(IASTNode node) |
| { |
| if (isScopingNode(node)) |
| scopes.add((ScopingNode)node); |
| super.visitASTNode(node); |
| } |
| }); |
| |
| return scopes; |
| } |
| |
| private static interface BindingResolutionCallback |
| { |
| void foundDefinition(PhotranTokenRef definition, ScopingNode scope); |
| } |
| |
| public ScopingNode findScopeDeclaringOrImporting(Token identifier) |
| { |
| try |
| { |
| manuallyResolve(identifier, new BindingResolutionCallback() |
| { |
| public void foundDefinition(PhotranTokenRef definition, ScopingNode scope) |
| { |
| throw new Notification(scope); |
| } |
| }); |
| |
| return null; |
| } |
| catch (Notification n) |
| { |
| return (ScopingNode)n.getResult(); |
| } |
| } |
| |
| public Iterable<ScopingNode> findImportingScopes() |
| { |
| if (this instanceof ASTModuleNode) |
| { |
| return new Iterable<ScopingNode>() |
| { |
| public Iterator<ScopingNode> iterator() |
| { |
| String moduleName = getName(); |
| return new ModuleIterator(moduleName); |
| } |
| }; |
| } |
| else |
| { |
| return Collections.emptyList(); |
| } |
| } |
| |
| private static class ModuleIterator implements Iterator<ScopingNode> |
| { |
| private String moduleName; |
| private Iterator<IFile> files; |
| private Iterator<ScopingNode> scopesInFile; |
| |
| public ModuleIterator(String moduleName) |
| { |
| this.moduleName = moduleName; |
| this.files = PhotranVPG.getInstance().findFilesThatImportModule(moduleName).iterator(); |
| this.scopesInFile = Collections.<ScopingNode>emptyList().iterator(); |
| } |
| |
| public boolean hasNext() |
| { |
| if (scopesInFile.hasNext()) |
| return true; |
| else |
| return this.files.hasNext(); |
| } |
| |
| public ScopingNode next() |
| { |
| if (scopesInFile.hasNext()) |
| return scopesInFile.next(); |
| else |
| return firstScopeInNextFile(); |
| } |
| |
| private ScopingNode firstScopeInNextFile() |
| { |
| if (!files.hasNext()) return null; |
| |
| IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(files.next()); |
| if (ast == null) return firstScopeInNextFile(); |
| |
| scopesInFile = collectImportingScopingNodes(ast).iterator(); |
| if (!scopesInFile.hasNext()) return firstScopeInNextFile(); |
| |
| return scopesInFile.next(); |
| } |
| |
| private Set<ScopingNode> collectImportingScopingNodes(IFortranAST ast) |
| { |
| final Set<ScopingNode> importingScopes = new HashSet<ScopingNode>(); |
| ast.accept(new ASTVisitor() |
| { |
| @Override public void visitASTUseStmtNode(ASTUseStmtNode node) |
| { |
| if (node.getName().getText().equalsIgnoreCase(moduleName)) |
| importingScopes.add(node.findFirstToken().getEnclosingScope()); |
| } |
| }); |
| return importingScopes; |
| } |
| |
| public void remove() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| public static ScopingNode findScopingNodeForRepresentativeToken(PhotranTokenRef tr) |
| { |
| PhotranVPG vpg = PhotranVPG.getInstance(); |
| |
| if (tr.getOffset() < 0) |
| return vpg.acquireTransientAST(tr.getFilename()).getRoot(); |
| else |
| return vpg.findToken(tr).findNearestAncestor(ScopingNode.class); |
| } |
| |
| private HashMap<String, List<PhotranTokenRef>> definitionCache = new HashMap<String, List<PhotranTokenRef>>(); |
| |
| public List<PhotranTokenRef> manuallyResolve(Token identifier) |
| { |
| String canonicalizedIdentifier = null; |
| if (PhotranVPG.getInstance().isDefinitionCachingEnabled()) |
| { |
| canonicalizedIdentifier = PhotranVPG.canonicalizeIdentifier(identifier.getText()); |
| if (definitionCache.containsKey(canonicalizedIdentifier)) |
| return definitionCache.get(canonicalizedIdentifier); |
| } |
| |
| final List<PhotranTokenRef> bindings = new LinkedList<PhotranTokenRef>(); |
| |
| manuallyResolve(identifier, new BindingResolutionCallback() |
| { |
| public void foundDefinition(PhotranTokenRef definition, ScopingNode scope) |
| { |
| bindings.add(definition); |
| } |
| }); |
| |
| if (PhotranVPG.getInstance().isDefinitionCachingEnabled()) |
| definitionCache.put(canonicalizedIdentifier, bindings); |
| |
| return bindings; |
| } |
| |
| public List<PhotranTokenRef> manuallyResolveInLocalScope(Token identifier) |
| { |
| final List<PhotranTokenRef> bindings = new LinkedList<PhotranTokenRef>(); |
| |
| manuallyResolveInLocalScope(identifier, new BindingResolutionCallback() |
| { |
| public void foundDefinition(PhotranTokenRef definition, ScopingNode scope) |
| { |
| bindings.add(definition); |
| } |
| }); |
| |
| return bindings; |
| } |
| |
| private void manuallyResolve(Token identifier, BindingResolutionCallback result) |
| { |
| if (!manuallyResolveInLocalScope(identifier, result)) |
| if (!manuallyResolveInParentScopes(identifier, result)) |
| if (!manuallyResolveIntrinsic(identifier, result)) |
| attemptToDeclareImplicit(identifier, result); |
| } |
| |
| public List<PhotranTokenRef> manuallyResolveNoImplicits(Token identifier) |
| { |
| final List<PhotranTokenRef> bindings = new LinkedList<PhotranTokenRef>(); |
| |
| manuallyResolveNoImplicits(identifier, new BindingResolutionCallback() |
| { |
| public void foundDefinition(PhotranTokenRef definition, ScopingNode scope) |
| { |
| bindings.add(definition); |
| } |
| }); |
| |
| return bindings; |
| } |
| |
| private void manuallyResolveNoImplicits(Token identifier, BindingResolutionCallback result) |
| { |
| if (!manuallyResolveInLocalScope(identifier, result)) |
| if (!manuallyResolveInParentScopes(identifier, result)) |
| manuallyResolveIntrinsic(identifier, result); |
| } |
| |
| private boolean manuallyResolveInLocalScope(Token identifier, BindingResolutionCallback bindings) |
| { |
| String name = PhotranVPG.canonicalizeIdentifier(identifier.getText()); |
| boolean wasSuccessful = false; |
| |
| for (Definition def : getAllDefinitions()) |
| { |
| if (def != null && def.matches(name)) // TODO: Why are we getting null here? |
| { |
| bindings.foundDefinition(def.getTokenRef(), this); |
| wasSuccessful = true; |
| } |
| } |
| |
| return wasSuccessful; |
| } |
| |
| private boolean manuallyResolveInParentScopes(Token identifier, BindingResolutionCallback bindings) |
| { |
| for (ScopingNode scope = getEnclosingScope(); scope != null; scope = scope.getEnclosingScope()) |
| if (scope.manuallyResolveInLocalScope(identifier, bindings)) |
| return true; |
| |
| return false; |
| } |
| |
| private boolean manuallyResolveIntrinsic(Token identifier, BindingResolutionCallback bindings) |
| { |
| Definition def = Intrinsic.resolve(identifier); |
| if (def == null) return false; |
| |
| ((PhotranVPGBuilder)PhotranVPG.getInstance()).setDefinitionFor(identifier.getTokenRef(), def); |
| bindings.foundDefinition(def.getTokenRef(), getGlobalScope()); |
| return true; |
| } |
| |
| private void attemptToDeclareImplicit(Token identifier, BindingResolutionCallback bindings) |
| { |
| PhotranVPGBuilder vpg = (PhotranVPGBuilder)PhotranVPG.getInstance(); |
| ImplicitSpec implicitSpec = getImplicitSpec(); |
| |
| if (implicitSpec == null) return; // Implicit None |
| |
| if (identifier instanceof FakeToken) return; // Not a real token; used to test bindings only |
| |
| String name = PhotranVPG.canonicalizeIdentifier(identifier.getText()); |
| |
| |
| PhotranTokenRef tokenRef = identifier.getTokenRef(); |
| Type type = implicitSpec.getType(name.charAt(0)); |
| Definition.Classification classification = Definition.Classification.IMPLICIT_LOCAL_VARIABLE; |
| if (isSubroutineNameInCallStmt(identifier) || isFunctionNameInFunctionCall(identifier)) |
| classification = Definition.Classification.IMPLICIT_EXTERNAL_SUBPROGRAM; |
| else |
| classification = Definition.Classification.IMPLICIT_LOCAL_VARIABLE; |
| Definition def = new Definition(identifier.getText(), tokenRef, classification, /*Visibility.PUBLIC,*/ type); |
| |
| vpg.setDefinitionFor(tokenRef, def); |
| vpg.markScope(tokenRef, this); |
| vpg.markDefinitionVisibilityInScope(tokenRef, this, Visibility.PUBLIC); |
| bindings.foundDefinition(tokenRef, getGlobalScope()); |
| } |
| |
| private boolean isSubroutineNameInCallStmt(Token identifier) |
| { |
| ASTCallStmtNode call = identifier.findNearestAncestor(ASTCallStmtNode.class); |
| if (call != null && call.getSubroutineName() != null) |
| return matches(call.getSubroutineName(), identifier); |
| else |
| return false; |
| } |
| |
| private boolean isFunctionNameInFunctionCall(Token identifier) |
| { |
| ASTVarOrFnRefNode call = identifier.findNearestAncestor(ASTVarOrFnRefNode.class); |
| if (call != null && call.getName() != null) |
| return matches(call.getName().getName(), identifier) |
| && (call.getPrimarySectionSubscriptList() != null |
| || call.getFunctionArgList() != null); |
| else |
| return false; |
| } |
| |
| private boolean matches(Token identifier1, Token identifier2) |
| { |
| if (identifier1 == null || identifier2 == null) |
| return false; |
| else |
| return PhotranVPG.canonicalizeIdentifier(identifier1.getText()) |
| .equals(PhotranVPG.canonicalizeIdentifier(identifier2.getText())); |
| } |
| |
| public List<Definition> getAllDefinitions() |
| { |
| PhotranVPG vpg = PhotranVPG.getInstance(); |
| List<Definition> result = new LinkedList<Definition>(); |
| |
| for (PhotranTokenRef t : vpg.db.getIncomingEdgeSources(this.getRepresentativeToken(), PhotranVPG.DEFINED_IN_SCOPE_EDGE_TYPE)) |
| result.add(vpg.getDefinitionFor(t)); |
| |
| // for (PhotranTokenRef t : vpg.db.getIncomingEdgeSources(this.getRepresentativeToken(), PhotranVPG.IMPORTED_INTO_SCOPE_EDGE_TYPE)) |
| // result.add(vpg.getDefinitionFor(t)); |
| |
| return result; |
| } |
| |
| public List<Definition> getAllPublicDefinitions() |
| { |
| PhotranVPG vpg = PhotranVPG.getInstance(); |
| List<Definition> result = new LinkedList<Definition>(); |
| |
| for (Definition def : getAllDefinitions()) |
| if (def != null && vpg.getVisibilityFor(def, this).equals(Visibility.PUBLIC)) |
| result.add(def); |
| |
| return result; |
| } |
| |
| public IMarker createMarker() |
| { |
| try |
| { |
| Token firstToken = findFirstTokenIn(this); |
| Token lastToken = findLastTokenIn(this); |
| |
| if (firstToken == null |
| || lastToken == null |
| || firstToken.getPhysicalFile() == null |
| || firstToken.getPhysicalFile().getIFile() == null) |
| return null; |
| |
| int startOffset = firstToken.getFileOffset(); |
| startOffset -= firstToken.getWhiteBefore().length(); |
| |
| int endOffset = lastToken.getFileOffset()+lastToken.getLength(); |
| //endOffset += lastToken.getWhiteAfter().length(); |
| |
| IMarker marker = firstToken.getPhysicalFile().getIFile().createMarker(IMarker.TEXT); |
| marker.setAttribute(IMarker.CHAR_START, startOffset); |
| marker.setAttribute(IMarker.CHAR_END, endOffset); |
| return marker; |
| } |
| catch (CoreException e) |
| { |
| return null; |
| } |
| } |
| |
| // TODO: This was copied from FortranRefactoring.java |
| // Parse Tree Searching /////////////////////////////////////////////////// |
| |
| protected Token findFirstTokenIn(ASTNode node) |
| { |
| try |
| { |
| node.accept(new ASTVisitor() |
| { |
| @Override |
| public void visitToken(Token token) |
| { |
| throw new Notification(token); |
| } |
| |
| }); |
| } |
| catch (Notification n) |
| { |
| return (Token)n.getResult(); |
| } |
| return null; |
| } |
| |
| private final static class LastTokenVisitor extends ASTVisitor |
| { |
| private Token lastToken; |
| |
| @Override |
| public void visitToken(Token token) |
| { |
| lastToken = token; |
| } |
| |
| public Token getLastToken() { return lastToken; } |
| } |
| |
| protected Token findLastTokenIn(ASTNode node) |
| { |
| LastTokenVisitor lastTokenVisitor = new LastTokenVisitor(); |
| node.accept(lastTokenVisitor); |
| return lastTokenVisitor.getLastToken(); |
| } |
| |
| public boolean isNamed(String targetName) |
| { |
| String name = getName(); |
| if (name == null) return false; |
| |
| String actualName = PhotranVPG.canonicalizeIdentifier(name); |
| String expectedName = PhotranVPG.canonicalizeIdentifier(targetName); |
| return actualName.equals(expectedName); |
| } |
| |
| public String getName() |
| { |
| return getName(PhotranVPG.getDatabase().isInHypotheticalMode()); |
| } |
| |
| public String getName(boolean force) |
| { |
| Token nameToken = getNameToken(force); |
| return nameToken == null ? null : nameToken.getText(); |
| } |
| |
| public Token getNameToken() |
| { |
| return getNameToken(PhotranVPG.getDatabase().isInHypotheticalMode()); |
| } |
| |
| public Token getNameToken(boolean force) |
| { |
| Token repToken = force |
| ? internalGetRepresentativeToken() // does not use cached TokenRef |
| : getRepresentativeToken().findTokenOrReturnNull(); // might use cached TokenRef |
| if (repToken == null || repToken.getTerminal() != Terminal.T_IDENT) |
| return null; |
| else |
| return repToken; |
| } |
| |
| /** @return a human-readable description of this scope */ |
| public String describe() |
| { |
| Token nameToken = getNameToken(); |
| if (nameToken == null) |
| return Messages.ScopingNode_Anonymous; |
| else |
| return nameToken.getText(); |
| } |
| } |