ShadowRemover.java
package dev.civl.abc.transform.common;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import dev.civl.abc.ast.IF.AST;
import dev.civl.abc.ast.IF.ASTFactory;
import dev.civl.abc.ast.entity.IF.Entity;
import dev.civl.abc.ast.entity.IF.OrdinaryEntity;
import dev.civl.abc.ast.entity.IF.Scope;
import dev.civl.abc.ast.node.IF.ASTNode;
import dev.civl.abc.ast.node.IF.ASTNode.NodeKind;
import dev.civl.abc.ast.node.IF.IdentifierNode;
import dev.civl.abc.ast.node.IF.SequenceNode;
import dev.civl.abc.ast.node.IF.statement.BlockItemNode;
import dev.civl.abc.token.IF.SyntaxException;
import dev.civl.abc.transform.IF.BaseTransformer;
/**
* Renames any variable not in file scope that has the same name as a variable
* in file scope.
*
* @author siegel
*
*/
public class ShadowRemover extends BaseTransformer {
public final static String CODE = "shadow";
public final static String LONG_NAME = "ShadowRemover";
public final static String SHORT_DESCRIPTION = "renames entities that shadow others";
private Map<Entity, String> nameMap = new HashMap<>();
private Map<String, Integer> countMap = new HashMap<>();
public ShadowRemover(ASTFactory factory) {
super(CODE, LONG_NAME, SHORT_DESCRIPTION, factory);
}
/**
* Iterate over ordinary entities in this scope. For each entity x, if x has
* the same name as another ordinary entity in a higher scope, choose a new
* name for x and add it to the map. If the old name is "X" the new name
* will be "_X_n", where n is an integer.
*
* Then invokes itself on children scopes.
*
* @param scope
*/
private void pass1(Scope scope) {
Iterable<OrdinaryEntity> entities = scope.getOrdinaryEntities();
for (OrdinaryEntity entity : entities) {
String name = entity.getName();
for (Scope scope2 = scope
.getParentScope(); scope2 != null; scope2 = scope2
.getParentScope()) {
Entity entity2 = scope2.getOrdinaryEntity(false, name);
if (entity2 != null) {
Integer count = countMap.get(name);
if (count == null)
count = 1;
else
count++;
String newName = "_" + name + "_" + count;
nameMap.put(entity, newName);
countMap.put(name, count);
break;
}
}
}
Iterator<Scope> childIter = scope.getChildrenScopes();
while (childIter.hasNext())
pass1(childIter.next());
}
/**
* Go over all nodes, updating names.
*/
private void pass2(AST ast) {
int numNodes = ast.getNumberOfNodes();
for (int i = 0; i < numNodes; i++) {
ASTNode node = ast.getNode(i);
NodeKind kind = node.nodeKind();
if (kind == NodeKind.IDENTIFIER) {
IdentifierNode identNode = (IdentifierNode) node;
Entity entity = identNode.getEntity();
if (entity != null) {
String newName = nameMap.get(entity);
if (newName != null)
identNode.setName(newName);
}
}
}
}
@Override
public AST transform(AST ast) throws SyntaxException {
SequenceNode<BlockItemNode> rootNode = ast.getRootNode();
Scope rootScope = rootNode.getScope();
Iterator<Scope> scopeIter = rootScope.getChildrenScopes();
while (scopeIter.hasNext())
pass1(scopeIter.next());
if (nameMap.isEmpty())
return ast;
pass2(ast);
ast.release();
AST result = astFactory.newAST(rootNode, ast.getSourceFiles(),
ast.isWholeProgram());
countMap = new HashMap<>();
nameMap = new HashMap<>();
return result;
}
}