blob: c01f352d976e724965cb13f3dc37f1eb0d4a8b31 [file] [log] [blame]
/*******************************************************************************
* 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.LinkedList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.photran.internal.core.analysis.types.Type;
import org.eclipse.photran.internal.core.lexer.Token;
import org.eclipse.photran.internal.core.parser.ASTExecutableProgramNode;
import org.eclipse.photran.internal.core.parser.ASTModuleNode;
import org.eclipse.photran.internal.core.parser.ASTOnlyNode;
import org.eclipse.photran.internal.core.parser.ASTRenameNode;
import org.eclipse.photran.internal.core.parser.ASTUseStmtNode;
import org.eclipse.photran.internal.core.parser.IASTListNode;
import org.eclipse.photran.internal.core.parser.IProgramUnit;
import org.eclipse.photran.internal.core.properties.SearchPathProperties;
import org.eclipse.photran.internal.core.vpg.PhotranTokenRef;
import org.eclipse.photran.internal.core.vpg.PhotranVPG;
import org.eclipse.photran.internal.core.vpg.PhotranVPGBuilder;
/**
* Phase 5 of name-binding analysis.
* <p>
* Visits USE statements in an AST, marking a dependency in the VPG,
* locating the used module, and importing declarations from it.
* <p>
* The user may provide module paths (via the Eclipse
* project properties), which are applied when locating the module.
*
* @author Jeff Overbey
* @see Binder
*/
public class ModuleLoader extends VisibilityCollector
{
// Visit USE statements and Access-Spec statements first to make sure
// all definitions are imported;
// then annotate the "module:whatever" virtual file in the VPG with
// the full module symbol table
@Override
public void visitASTModuleNode(ASTModuleNode node)
{
traverseChildren(node);
// TODO: Apply module paths
Token moduleName = node.getModuleStmt().getModuleName().getModuleName();
List<Definition> moduleSymtab = node.getAllPublicDefinitions();
//System.out.println(moduleName.getText() + ": " + moduleSymtab);
vpg.setModuleSymbolTable(moduleName, moduleSymtab);
}
// # R1107
// <UseStmt> ::=
// | <LblDef> T_USE <Name> T_EOS
// | <LblDef> T_USE <Name> T_COMMA <RenameList> T_EOS
// | <LblDef> T_USE <Name> T_COMMA T_ONLY T_COLON ( <OnlyList> )? T_EOS
//
// <RenameList> ::=
// | <Rename>
// | @:<RenameList> T_COMMA <Rename>
//
// <OnlyList> ::=
// | <Only>
// | @:<OnlyList> T_COMMA <Only>
//
// # R1108
// <Rename> ::= T_IDENT T_EQGREATERTHAN <UseName>
//
// # R1109
// <Only> ::=
// | <GenericSpec>
// | T_IDENT T_EQGREATERTHAN <UseName>
// | <UseName>
private boolean shouldImportModules;
private IFile fileContainingUseStmt;
private IProgressMonitor progressMonitor;
private ASTUseStmtNode useStmt;
private Token moduleNameToken;
private String moduleName;
public ModuleLoader(IFile fileContainingUseStmt /*, IProgressMonitor progressMonitor*/)
{
this.vpg = (PhotranVPGBuilder)PhotranVPG.getInstance();
this.shouldImportModules = true;
this.fileContainingUseStmt = fileContainingUseStmt;
this.progressMonitor = new NullProgressMonitor(); //progressMonitor;
}
// # R1107
// <UseStmt> ::=
// <LblDef> T_USE <Name> T_EOS
// | <LblDef> T_USE <Name> T_COMMA <RenameList> T_EOS
// | <LblDef> T_USE <Name> T_COMMA T_ONLY T_COLON ( <OnlyList> )? T_EOS
@Override public void visitASTUseStmtNode(ASTUseStmtNode node)
{
super.traverseChildren(node);
try
{
vpg.markFileAsImportingModule(fileContainingUseStmt, node.getName().getText());
if (this.shouldImportModules)
loadModule(node);
}
catch (Exception e)
{
throw new Error(e);
}
}
private void loadModule(ASTUseStmtNode node) throws Exception
{
this.useStmt = node;
this.moduleNameToken = useStmt.getName();
this.moduleName = PhotranVPG.canonicalizeIdentifier(moduleNameToken.getText());
progressMonitor.subTask(Messages.bind(Messages.ModuleLoader_LoadingModule, moduleName));
if (moduleExistsInFileContainingUseStmt())
bindToSymbolsIn(fileContainingUseStmt);
else
findModuleInModulePaths();
}
private boolean moduleExistsInFileContainingUseStmt() throws Exception
{
return findModuleIn(fileContainingUseStmt) != null;
}
private ASTModuleNode findModuleIn(IFile file) throws Exception
{
ASTExecutableProgramNode fileAST = vpg.acquireTransientAST(file).getRoot();
for (IProgramUnit pu : fileAST.getProgramUnitList())
if (pu instanceof ASTModuleNode && isNamed(moduleName, (ASTModuleNode)pu))
return (ASTModuleNode)pu;
return null;
}
private boolean isNamed(String targetName, ASTModuleNode node)
{
String nameOfThisModule = PhotranVPG.canonicalizeIdentifier(node.getModuleStmt().getModuleName().getModuleName().getText());
return nameOfThisModule.equals(targetName);
}
private void findModuleInModulePaths() throws Exception
{
List<IFile> files = vpg.findFilesThatExportModule(moduleName);
if (files.isEmpty())
{
if (!isIntrinsicModule())
vpg.log.logError(
Messages.bind(Messages.ModuleLoader_NoFilesExportAModuleNamed, moduleName),
useStmt.getName().getTokenRef());
return;
}
files = applyModulePaths(files);
if (files.isEmpty())
{
vpg.log.logError(
Messages.bind(
Messages.ModuleLoader_ModuleNotFoundInModulePathsButFoundElsewhere,
moduleName),
useStmt.getName().getTokenRef());
return;
}
for (IFile file : files)
bindToSymbolsIn(file);
}
/**
* @return true iff {@link #moduleName} is the name of an intrinsic module
*/
private boolean isIntrinsicModule()
{
// Fortran 2003
return moduleName.equals("iso_c_binding"); //$NON-NLS-1$
}
private List<IFile> applyModulePaths(List<IFile> files)
{
String[] paths = new SearchPathProperties().getListProperty(fileContainingUseStmt,
SearchPathProperties.MODULE_PATHS_PROPERTY_NAME);
if (paths.length == 0) return files; // Do not apply if property not set
List<IFile> result = new LinkedList<IFile>();
// Check in the directory with the file containing the USE statement first
if (findModuleIn(PhotranVPG.getFilenameForIResource(fileContainingUseStmt.getParent()), files, result))
return result;
// Then check in the user-specified module paths
for (String path : paths)
if (findModuleIn(path, files, result))
return result;
return result; // May be empty
}
private boolean findModuleIn(String path, List<IFile> files, List<IFile> result)
{
for (IFile file : files)
{
if (PhotranVPG.getFilenameForIResource(file.getParent()).startsWith(path))
{
result.add(file);
if (!result.isEmpty()) return true;
}
}
return false;
}
// private void bindToSymbolsIn(IFile file) throws Exception
// {
// ASTModuleNode moduleNode = findModuleIn(file);
// if (moduleNode == null) return; // Shouldn't happen if VPG is up to date
//
// bind(useStmt.getName(), moduleNode.getRepresentativeToken());
//
// ScopingNode newScope = useStmt.getUseToken().getEnclosingScope();
//
// for (Definition def : moduleNode.getAllPublicDefinitions())
// if (shouldImportDefinition(def))
// importDefinition(def, newScope);
//
// bindIdentifiersInRenameList(useStmt.getRenameList(), moduleNode);
// bindIdentifiersInOnlyList(useStmt.getOnlyList(), moduleNode);
// }
private void bindToSymbolsIn(IFile file) throws Exception
{
PhotranTokenRef moduleToken = vpg.getModuleTokenRef(moduleName);
if (moduleToken == null) return; // Shouldn't happen if VPG is up to date
bind(useStmt.getName(), moduleToken);
ScopingNode newScope = useStmt.getUseToken().getEnclosingScope();
List<Definition> moduleSymtab = vpg.getModuleSymbolTable(moduleName);
if (moduleSymtab == null) // Just in case
{
vpg.log.logError(Messages.bind(Messages.ModuleLoader_ModuleNotFoundInFile, moduleName, file.getFullPath().toOSString()));
}
else
{
for (Definition def : moduleSymtab)
if (shouldImportDefinition(def))
importDefinition(def, newScope);
bindIdentifiersInRenameList(useStmt.getRenameList(), moduleSymtab);
bindIdentifiersInOnlyList(useStmt.getOnlyList(), moduleSymtab);
}
}
private boolean shouldImportDefinition(Definition def)
{
IASTListNode<ASTRenameNode> renameList = useStmt.getRenameList();
IASTListNode<ASTOnlyNode> onlyList = useStmt.getOnlyList();
if (renameList == null && onlyList == null)
{
return true;
}
else if (renameList != null)
{
for (int i = 0; i < renameList.size(); i++)
{
String entityBeingRenamed = PhotranVPG.canonicalizeIdentifier(renameList.get(i).getName().getText());
if (def.matches(entityBeingRenamed))
return false;
}
return true;
}
else // (onlyList != null)
{
for (int i = 0; i < onlyList.size(); i++)
{
Token useName = onlyList.get(i).getName();
String entityToImport = useName == null ? null : PhotranVPG.canonicalizeIdentifier(useName.getText());
boolean isRenamed = onlyList.get(i).isRenamed();
if (def.matches(entityToImport) && !isRenamed) return true;
}
return false;
}
}
private void bindIdentifiersInRenameList(IASTListNode<ASTRenameNode> renameList, List<Definition> moduleSymtab) throws Exception
{
if (renameList == null) return;
for (int i = 0; i < renameList.size(); i++)
{
if (!renameList.get(i).isOperator()) // TODO: User-defined operators
{
Token newName = renameList.get(i).getNewName();
Token oldName = renameList.get(i).getName();
bindPossiblyRenamedIdentifier(newName, oldName, moduleSymtab);
}
}
}
private void bindIdentifiersInOnlyList(IASTListNode<ASTOnlyNode> onlyList, List<Definition> moduleSymtab) throws Exception
{
if (onlyList == null) return;
for (int i = 0; i < onlyList.size(); i++)
{
if (!onlyList.get(i).isOperator()) // TODO: User-defined operators
{
Token newName = onlyList.get(i).getNewName();
Token oldName = onlyList.get(i).getName();
if (oldName != null) bindPossiblyRenamedIdentifier(newName, oldName, moduleSymtab);
}
}
}
private void bindPossiblyRenamedIdentifier(Token newName, Token oldName, List<Definition> moduleSymtab) throws Exception
{
List<PhotranTokenRef> definitionsInModule = new LinkedList<PhotranTokenRef>();
String canonicalizedOldName = PhotranVPG.canonicalizeIdentifier(oldName.getText());
for (Definition def : moduleSymtab)
if (def != null && def.matches(canonicalizedOldName))
definitionsInModule.add(def.getTokenRef());
for (PhotranTokenRef def : definitionsInModule)
{
bindRenamedEntity(newName, def);
bind(oldName, def);
}
Type type = definitionsInModule.size() == 1 ? vpg.getDefinitionFor(definitionsInModule.get(0)).getType() : Type.UNKNOWN;
addDefinition(newName, Definition.Classification.RENAMED_MODULE_ENTITY, type);
}
// private void bindPossiblyRenamedIdentifier(Token newName, Token oldName, ASTModuleNode moduleNode) throws Exception
// {
// List<PhotranTokenRef> definitionsInModule = moduleNode.manuallyResolve(oldName);
//
// for (PhotranTokenRef def : definitionsInModule)
// {
// bindRenamedEntity(newName, def);
// bind(oldName, def);
// }
//
// Type type = definitionsInModule.size() == 1 ? vpg.getDefinitionFor(definitionsInModule.get(0)).getType() : Type.UNKNOWN;
// addDefinition(newName, Definition.Classification.RENAMED_MODULE_ENTITY, type);
// }
}