/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.visit;
import java.util.*;
import polyglot.ast.*;
import polyglot.types.LocalDef;
import polyglot.types.Name;
import polyglot.util.InternalCompilerError;
import polyglot.util.UniqueID;
import polyglot.util.CollectionUtil;
import x10.ast.ForLoop;
import x10.ast.StmtSeq;
import x10.util.CollectionFactory;
/**
* The <code>AlphaRenamer</code> runs over the AST and alpha-renames any local
* variable declarations that it encounters.
**/
public class AlphaRenamer extends NodeVisitor {
// Each set in this stack tracks the set of local decls in a block that
// we're traversing.
protected Stack<Set<Name>> setStack;
protected Map<Name, Name> renamingMap;
protected Map<LocalDef, Name> oldNamesMap;
// Tracks the set of variables known to be fresh.
protected Set<Name> freshVars;
protected Map<Name, Name> labelMap;
protected boolean clearMaps;
/**
* Creates a visitor for alpha-renaming locals.
*/
public AlphaRenamer() {
this(true);
}
public AlphaRenamer(boolean clearOutOfScopeMaps) {
this.setStack = new Stack<Set<Name>>();
this.setStack.push(CollectionFactory.<Name> newHashSet());
this.oldNamesMap = CollectionFactory.newHashMap();
this.renamingMap = CollectionFactory.newHashMap();
this.labelMap = CollectionFactory.newHashMap();
this.freshVars = CollectionFactory.newHashSet();
this.clearMaps = clearOutOfScopeMaps;
}
/** Map from local def to old names. */
public Map<LocalDef, Name> getMap() {
return oldNamesMap;
}
public static final String LABEL_PREFIX = "label ";
public NodeVisitor enter(Node n) {
if (isNewScope(n)) {
// Push a new, empty set onto the stack.
setStack.push(CollectionFactory.<Name> newHashSet());
}
if (n instanceof LocalDecl) {
LocalDecl l = (LocalDecl) n;
Name name = l.name().id();
if (!freshVars.contains(name)) {
// Add a new entry to the current renaming map.
Name name_ = Name.makeFresh(alphaPrefixHeuristic(name));
freshVars.add(name_);
setStack.peek().add(name);
renamingMap.put(name, name_);
}
}
if (n instanceof Formal) {
Formal f = (Formal) n;
Name name = f.name().id();
if (!freshVars.contains(name)) {
// Add a new entry to the current renaming map.
Name name_ = Name.makeFresh(alphaPrefixHeuristic(name));
freshVars.add(name_);
setStack.peek().add(name);
renamingMap.put(name, name_);
}
}
if (n instanceof Labeled) {
Labeled l = (Labeled) n;
Name name = l.labelNode().id();
Name key = Name.make(LABEL_PREFIX + name.toString());
if (!freshVars.contains(key)) {
Name name_ = Name.makeFresh(alphaPrefixHeuristic(name));
Name key_ = Name.make(LABEL_PREFIX + name_.toString());
freshVars.add(key_);
setStack.peek().add(key);
labelMap.put(key, name_);
}
}
return this;
}
// Try to avoid really ugly variable names by hueristically ignoring trailing
// digits in names that are often (but not always) a symptom of the Name
// having previously been generated by makeFresh.
private static String alphaPrefixHeuristic(Name n) {
String prefix = n.toString();
boolean chopped = false;
int tail = prefix.length();
while (tail > 0 && Character.isDigit(prefix.charAt(tail-1))) {
tail = tail-1;
chopped = true;
}
return tail == prefix.length() ? prefix : prefix.substring(0, tail);
}
public Node leave(Node old, Node n, NodeVisitor v) {
if (isNewScope(n)) {
// Pop the current name set off the stack and remove the
// corresponding entries from the renaming map.
Set<Name> s = setStack.pop();
if (clearMaps) {
renamingMap.keySet().removeAll(s);
labelMap.keySet().removeAll(s);
}
return n;
}
if (n instanceof Local) {
// Rename the local if its name is in the renaming map.
Local l = (Local) n;
Name name = l.name().id();
if (!renamingMap.containsKey(name)) {
return n;
}
// Update the local instance as necessary.
Name newName = renamingMap.get(name);
// LocalType li = l.localInstance();
// if (li != null) li.setName(newName);
return l.name(l.name().id(newName));
}
if (n instanceof LocalDecl) {
// Rename the local decl.
LocalDecl l = (LocalDecl) n;
Name name = l.name().id();
if (freshVars.contains(name)) {
return n;
}
if (!renamingMap.containsKey(name)) {
throw new InternalCompilerError("Unexpected error encountered while alpha-renaming.");
}
// Update the local instance as necessary.
Name newName = renamingMap.get(name);
LocalDef li = l.localDef();
if (li != null) {
oldNamesMap.put(li, li.name());
li.setName(newName);
}
return l.name(l.name().id(newName));
}
if (n instanceof Formal) {
// Rename the local decl.
Formal f = (Formal) n;
Name name = f.name().id();
if (freshVars.contains(name)) {
return n;
}
if (!renamingMap.containsKey(name)) {
throw new InternalCompilerError("Unexpected error encountered while alpha-renaming.");
}
// Update the local instance as necessary.
Name newName = renamingMap.get(name);
LocalDef li = f.localDef();
if (li != null) {
oldNamesMap.put(li, li.name());
li.setName(newName);
}
return f.name(f.name().id(newName));
}
if (n instanceof Branch) {
// Rename the label if its name is in the renaming map.
Branch b = (Branch) n;
if (b.labelNode() == null) {
return n;
}
Name name = b.labelNode().id();
Name key = Name.make(LABEL_PREFIX + name.toString());
if (!labelMap.containsKey(key)) {
return n;
}
Name newName = labelMap.get(key);
return b.labelNode(b.labelNode().id(newName));
}
if (n instanceof Labeled) {
Labeled l = (Labeled) n;
Name name = l.labelNode().id();
Name key = Name.make(LABEL_PREFIX + name.toString());
if (freshVars.contains(key)) {
return n;
}
if (!labelMap.containsKey(key)) {
throw new InternalCompilerError("Unexpected error encountered while alpha-renaming.");
}
Name newName = labelMap.get(key);
return l.labelNode(l.labelNode().id(newName));
}
return n;
}
/**
* Does this node define a new scope with its own locals?
*/
protected static boolean isNewScope(Node n) {
return (n instanceof Block && !(n instanceof StmtSeq)) || n instanceof For || n instanceof ForLoop;
}
}