/*******************************************************************************
* PSHDL is a library and (trans-)compiler for PSHDL input. It generates
* output suitable for implementation or simulation of it.
*
* Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This License does not grant permission to use the trade names, trademarks,
* service marks, or product names of the Licensor, except as required for
* reasonable and customary use in describing the origin of the Work.
*
* Contributors:
* Karsten Becker - initial API and implementation
******************************************************************************/
package org.pshdl.model.utils;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.pshdl.model.HDLArithOp;
import org.pshdl.model.HDLArithOp.HDLArithOpType;
import org.pshdl.model.HDLAssignment;
import org.pshdl.model.HDLClass;
import org.pshdl.model.HDLDeclaration;
import org.pshdl.model.HDLEnumDeclaration;
import org.pshdl.model.HDLEnumRef;
import org.pshdl.model.HDLExpression;
import org.pshdl.model.HDLForLoop;
import org.pshdl.model.HDLFunction;
import org.pshdl.model.HDLFunctionCall;
import org.pshdl.model.HDLInterfaceDeclaration;
import org.pshdl.model.HDLInterfaceInstantiation;
import org.pshdl.model.HDLInterfaceRef;
import org.pshdl.model.HDLLiteral;
import org.pshdl.model.HDLManip;
import org.pshdl.model.HDLManip.HDLManipType;
import org.pshdl.model.HDLOpExpression;
import org.pshdl.model.HDLPackage;
import org.pshdl.model.HDLRange;
import org.pshdl.model.HDLReference;
import org.pshdl.model.HDLStatement;
import org.pshdl.model.HDLType;
import org.pshdl.model.HDLUnit;
import org.pshdl.model.HDLVariable;
import org.pshdl.model.HDLVariableDeclaration;
import org.pshdl.model.HDLVariableDeclaration.HDLDirection;
import org.pshdl.model.HDLVariableRef;
import org.pshdl.model.IHDLObject;
import org.pshdl.model.evaluation.ConstantEvaluate;
import org.pshdl.model.extensions.FullNameExtension;
import org.pshdl.model.extensions.TypeExtension;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
public class Refactoring {
public static ISideEffectResolver SUBSTITUTE_RESOLVER = new SubstituteExpressionResolver();
public static class SubstituteExpressionResolver implements ISideEffectResolver {
@Override
public void resolveSideEffectExpression(HDLExpression exp, HDLReference ref, ModificationSet ms) {
resolveExpression(exp, ref, ms);
}
private void resolveExpression(HDLExpression exp, HDLExpression ref, ModificationSet ms) {
final EnumSet<HDLClass> classSet = exp.getClassSet();
if (classSet.contains(HDLClass.HDLOpExpression)) {
final HDLOpExpression op = (HDLOpExpression) exp;
if (op.getLeft() == ref) {
ms.replace(op, op.getRight());
return;
}
if (op.getRight() == ref) {
ms.replace(op, op.getLeft());
return;
}
}
if (classSet.contains(HDLClass.HDLManip)) {
final HDLManip manip = (HDLManip) exp;
// It doesn't make sense to simply replace or remove an HDLManip
final IHDLObject container = exp.getContainer();
if (container instanceof HDLExpression) {
resolveExpression((HDLExpression) container, manip, ms);
} else {
resolveStatement((HDLStatement) container, manip, ms);
}
return;
}
replaceWithZeroOrConstant(ref, ms);
}
public static void replaceWithZeroOrConstant(HDLExpression ref, ModificationSet ms) {
HDLLiteral value = HDLLiteral.get(0);
final Optional<BigInteger> constValue = ConstantEvaluate.valueOf(ref);
if (constValue.isPresent()) {
value = HDLLiteral.get(constValue.get());
}
final Optional<? extends HDLType> typeOf = TypeExtension.typeOf(ref);
if (!typeOf.isPresent())
throw new IllegalArgumentException("Should be able to resolve type, but failed for expression:" + ref);
ms.replace(ref, new HDLManip().setType(HDLManipType.CAST).setCastTo(typeOf.get()).setTarget(value));
}
@Override
public void resolveSideEffectStatement(HDLStatement stmnt, HDLReference ref, ModificationSet ms) {
resolveStatement(stmnt, ref, ms);
}
private void resolveStatement(HDLStatement stmnt, HDLExpression ref, ModificationSet ms) {
switch (stmnt.getClassType()) {
case HDLAssignment: {
final HDLAssignment ass = (HDLAssignment) stmnt;
if (ass.getLeft() == ref) {
ms.remove(ass);
}
if (ass.getRight() == ref) {
replaceWithZeroOrConstant(ref, ms);
}
return;
}
case HDLIfStatement:
case HDLSwitchStatement:
case HDLSwitchCaseStatement:
replaceWithZeroOrConstant(ref, ms);
return;
default:
throw new IllegalArgumentException("Did not expect expression in statement:" + stmnt.getClassType());
}
}
@Override
public void resolveSideEffectOther(IHDLObject var, HDLReference ref, ModificationSet ms) {
replaceWithZeroOrConstant(ref, ms);
}
}
public static interface ISideEffectResolver {
void resolveSideEffectExpression(HDLExpression exp, HDLReference ref, ModificationSet ms);
void resolveSideEffectStatement(HDLStatement ass, HDLReference ref, ModificationSet ms);
void resolveSideEffectOther(IHDLObject var, HDLReference ref, ModificationSet ms);
}
public static HDLUnit removeVariable(HDLUnit obj, HDLVariable hdlVariable, ISideEffectResolver resolver) {
HDLUnit res = obj;
int counter = 0;
do {
counter++;
if (counter > 50)
throw new IllegalArgumentException("Something went wrong when removing variable references");
obj = res;
final ModificationSet ms = new ModificationSet();
final Iterable<? extends HDLReference> refs = getAllVarRefs(obj, hdlVariable, ms, counter == 1);
for (final HDLReference ref : refs) {
final IHDLObject container = ref.getContainer();
if (container instanceof HDLExpression) {
final HDLExpression exp = (HDLExpression) container;
resolver.resolveSideEffectExpression(exp, ref, ms);
continue;
}
if (container instanceof HDLStatement) {
final HDLStatement stmnt = (HDLStatement) container;
resolver.resolveSideEffectStatement(stmnt, ref, ms);
continue;
}
resolver.resolveSideEffectOther(container, ref, ms);
}
res = ms.apply(obj);
} while (res != obj);
return res;
}
private static Iterable<? extends HDLReference> getAllVarRefs(HDLUnit obj, HDLVariable hdlVariable, final ModificationSet ms, boolean init) {
final IHDLObject varContainer = hdlVariable.getContainer();
final HDLClass classType = varContainer.getClassType();
Iterable<? extends HDLReference> refs;
switch (classType) {
case HDLEnum:
refs = HDLQuery.getEnumRefs(obj, hdlVariable);
break;
case HDLVariableDeclaration:
final HDLVariableDeclaration hvd = (HDLVariableDeclaration) varContainer;
final ArrayList<HDLVariable> vars = hvd.getVariables();
if (init) {
if (vars.size() == 1) {
ms.remove(varContainer);
} else {
vars.remove(hdlVariable);
ms.replace(hvd, hvd.setVariables(vars));
}
}
refs = HDLQuery.getVarRefs(obj, hdlVariable);
break;
case HDLFunctionParameter:
if (init) {
ms.remove(varContainer);
}
refs = HDLQuery.getVarRefs(obj, hdlVariable);
break;
case HDLForLoop:
refs = HDLQuery.getVarRefs(obj, hdlVariable);
break;
case HDLDirectGeneration:
case HDLInterfaceInstantiation:
if (init) {
ms.remove(varContainer);
}
refs = HDLQuery.getInterfaceRefs(obj, hdlVariable);
break;
default:
throw new RuntimeException("Did not expect a container of type:" + classType);
}
return refs;
}
/**
* Rename a variable
*
* @param var
* the variable to rename
* @param to
* a new name
* @param obj
* the obj in which to search for the variable
* @return the obj with the variable renamed
*/
public static <T extends IHDLObject> T renameVariable(HDLVariable var, String to, T obj) {
final ModificationSet ms = new ModificationSet();
renameVariable(var, var.asRef().skipLast(1).append(to), obj, ms);
return ms.apply(obj);
}
public static <T extends IHDLObject> void renameVariable(HDLVariable hdlVariable, HDLQualifiedName newName, T obj, ModificationSet ms) {
ms.replace(hdlVariable, hdlVariable.setName(newName.getLastSegment()));
final HDLClass classType = hdlVariable.getContainer().getClassType();
switch (classType) {
case HDLEnum:
final Collection<HDLEnumRef> enumRefs = HDLQuery.getEnumRefs(obj, hdlVariable);
for (final HDLEnumRef ref : enumRefs) {
ms.replace(ref, ref.setVar(newName));
}
break;
case HDLVariableDeclaration:
case HDLFunctionParameter:
case HDLForLoop:
final Collection<HDLVariableRef> varRefs = HDLQuery.getVarRefs(obj, hdlVariable);
for (final HDLVariableRef ref : varRefs) {
ms.replace(ref, ref.setVar(newName));
}
break;
case HDLDirectGeneration:
case HDLInterfaceInstantiation:
final Collection<HDLInterfaceRef> ifRefs = HDLQuery.getInterfaceRefs(obj, hdlVariable);
for (final HDLInterfaceRef hir : ifRefs) {
ms.replace(hir, hir.setHIf(newName));
}
break;
default:
throw new RuntimeException("Did not expect a container of type:" + classType);
}
}
public static HDLPackage inlineUnit(HDLUnit container, HDLInterfaceInstantiation hi, HDLUnit subUnit, char separator) {
final HDLVariable hiVar = hi.getVar();
final String prefix = hiVar.getName();
final ModificationSet ms = new ModificationSet();
HDLPackage pkg = createHDLPackage(subUnit, ms);
subUnit = ms.apply(subUnit);
subUnit = prefixVariables(subUnit, prefix, separator);
subUnit = extractDefaultValue(subUnit);
final ArrayList<HDLExpression> outerDims = hiVar.getDimensions();
final List<HDLQualifiedName> iterNames = Lists.newArrayListWithCapacity(outerDims.size());
for (int i = 0; i < outerDims.size(); i++) {
iterNames.add(new HDLQualifiedName(Insulin.getTempName("inline", "idx")));
}
subUnit = addArrayReference(subUnit, outerDims, iterNames);
subUnit = addNewDimensions(subUnit, outerDims);
subUnit = changeDirection(subUnit);
subUnit = dereferenceRefs(subUnit);
subUnit = Insulin.generateClkAndReset(subUnit);
final ModificationSet res = new ModificationSet();
final Collection<HDLInterfaceRef> ifRefs = HDLQuery.getInterfaceRefs(container, hiVar);
for (final HDLInterfaceRef hir : ifRefs) {
final HDLQualifiedName newName = HDLQualifiedName.create(prefix + separator + hir.getVarRefName().getLastSegment());
final ArrayList<HDLExpression> ifArray = hir.getIfArray();
ifArray.addAll(hir.getArray());
final HDLVariableRef newRef = new HDLVariableRef().setVar(newName).setBits(hir.getBits()).setArray(ifArray);
res.replace(hir, newRef);
}
final List<HDLStatement> allStatements = subUnit.getInits();
allStatements.addAll(subUnit.getStatements());
for (final Iterator<HDLStatement> iterator = allStatements.iterator(); iterator.hasNext();) {
final HDLStatement hdlStatement = iterator.next();
if (hdlStatement instanceof HDLVariableDeclaration) {
final HDLVariableDeclaration hvd = (HDLVariableDeclaration) hdlStatement;
if (hvd.getDirection() == HDLDirection.PARAMETER) {
iterator.remove();
}
}
}
res.replace(hi, allStatements.toArray(new HDLStatement[allStatements.size()]));
HDLUnit apply = res.apply(container);
for (int i = 0; i < iterNames.size(); i++) {
final HDLQualifiedName iterName = iterNames.get(i);
final HDLVariable loopName = new HDLVariable().setName(iterName.toString());
final HDLExpression loopTarget = outerDims.get(i);
final HDLRange range = new HDLRange().setFrom(HDLLiteral.get(0)).setTo(new HDLArithOp().setLeft(loopTarget).setType(HDLArithOpType.MINUS).setRight(HDLLiteral.get(1)));
final HDLUnit derefed = dereferenceRefs(apply);
final List<HDLStatement> combined = derefed.getInits();
combined.addAll(derefed.getStatements());
final List<HDLStatement> newStmnts = Lists.newArrayList();
for (final Iterator<HDLStatement> iterator = combined.iterator(); iterator.hasNext();) {
final HDLStatement hdlStatement = iterator.next();
if (hdlStatement instanceof HDLVariableDeclaration) {
final HDLVariableDeclaration hvd = (HDLVariableDeclaration) hdlStatement;
if ((hvd.getDirection() == HDLDirection.CONSTANT) || (hvd.getDirection() == HDLDirection.PARAMETER)) {
iterator.remove();
newStmnts.add(hvd);
}
}
}
final HDLForLoop loop = new HDLForLoop().setParam(loopName).addRange(range).setDos(combined);
newStmnts.add(loop);
apply = apply.setInits(null).setStatements(newStmnts).copyDeepFrozen(container.getContainer());
}
pkg = pkg.addUnits(apply);
try {
apply.validateAllFields(container.getContainer(), true);
} catch (final RuntimeException e) {
try {
Files.write(apply.toString().getBytes(Charsets.UTF_8), new File("CrashedUnit.pshdl"));
} catch (final IOException e1) {
e1.printStackTrace();
}
throw e;
}
return pkg;
}
private static HDLPackage createHDLPackage(HDLUnit subUnit, final ModificationSet ms) {
HDLPackage pkg = new HDLPackage();
final HDLDeclaration[] decls = subUnit.getAllObjectsOf(HDLDeclaration.class, true);
for (final HDLDeclaration decl : decls) {
if (decl instanceof HDLVariableDeclaration) {
continue;
}
if (decl instanceof HDLEnumDeclaration) {
final HDLEnumDeclaration hde = (HDLEnumDeclaration) decl;
final HDLQualifiedName fqn = FullNameExtension.fullNameOf(hde.getHEnum());
final String newName = fqn.toString();
pkg = pkg.addDeclarations(hde.setHEnum(hde.getHEnum().setName(newName)));
final HDLQualifiedName sqfn = HDLQualifiedName.create(fqn.getLastSegment());
final Collection<HDLEnumRef> allRefs = HDLQuery.select(HDLEnumRef.class).from(subUnit).where(HDLEnumRef.fHEnum).isEqualTo(sqfn).getAll();
for (final HDLEnumRef ref : allRefs) {
ms.replace(ref, ref.setHEnum(fqn));
}
final Collection<HDLVariableDeclaration> allVars = HDLQuery.select(HDLVariableDeclaration.class).from(subUnit).where(HDLVariableDeclaration.fType).isEqualTo(sqfn)
.getAll();
for (final HDLVariableDeclaration hvd : allVars) {
ms.replace(hvd, hvd.setType(fqn));
}
}
if (decl instanceof HDLFunction) {
final HDLFunction hdf = (HDLFunction) decl;
final HDLQualifiedName fqn = FullNameExtension.fullNameOf(hdf);
pkg = pkg.addDeclarations(hdf.setName(fqn.toString()));
final HDLQualifiedName sqfn = HDLQualifiedName.create(fqn.getLastSegment());
final Collection<HDLFunctionCall> allRefs = HDLQuery.select(HDLFunctionCall.class).from(subUnit).where(HDLFunctionCall.fFunction).isEqualTo(sqfn).getAll();
for (final HDLFunctionCall ref : allRefs) {
ms.replace(ref, ref.setFunction(fqn));
}
}
if (decl instanceof HDLInterfaceDeclaration) {
final HDLInterfaceDeclaration hid = (HDLInterfaceDeclaration) decl;
final HDLQualifiedName fqn = FullNameExtension.fullNameOf(hid.getHIf());
final String newName = fqn.toString();
pkg = pkg.addDeclarations(hid.setHIf(hid.getHIf().setName(newName)));
final HDLQualifiedName sfqn = HDLQualifiedName.create(fqn.getLastSegment());
final Collection<HDLInterfaceInstantiation> allRefs = HDLQuery.select(HDLInterfaceInstantiation.class).from(subUnit).where(HDLInterfaceInstantiation.fHIf)
.isEqualTo(sfqn).getAll();
for (final HDLInterfaceInstantiation hii : allRefs) {
ms.replace(hii, hii.setHIf(fqn));
}
}
ms.remove(decl);
}
return pkg;
}
private static HDLUnit dereferenceRefs(HDLUnit subUnit) {
final String subUnitName = FullNameExtension.fullNameOf(subUnit).toString();
final ModificationSet ms = new ModificationSet();
final HDLVariableRef[] ref = subUnit.getAllObjectsOf(HDLVariableRef.class, true);
for (final HDLVariableRef varRef : ref) {
final String string = varRef.getVarRefName().toString();
if (string.startsWith(subUnitName)) {
ms.replace(varRef, varRef.setVar(new HDLQualifiedName(varRef.getVarRefName().getLastSegment())));
}
}
return ms.apply(subUnit);
}
private static HDLUnit extractDefaultValue(HDLUnit subUnit) {
final ModificationSet subMS = new ModificationSet();
final HDLVariable[] vars = subUnit.getAllObjectsOf(HDLVariable.class, true);
for (final HDLVariable hdlVariable : vars) {
final HDLDirection dir = hdlVariable.getDirection();
if ((dir == HDLDirection.CONSTANT) || (dir == HDLDirection.PARAMETER)) {
continue;
}
final HDLExpression defaultValue = hdlVariable.getDefaultValue();
if (defaultValue != null) {
final HDLAssignment ass = new HDLAssignment().setLeft(hdlVariable.asHDLRef()).setRight(defaultValue);
subMS.insertAfter(hdlVariable.getContainer(HDLStatement.class), ass);
}
subMS.replace(hdlVariable, hdlVariable.setDefaultValue(null));
}
return subMS.apply(subUnit);
}
private static HDLUnit changeDirection(HDLUnit subUnit) {
final ModificationSet subMS = new ModificationSet();
final HDLVariableDeclaration[] hvds = subUnit.getAllObjectsOf(HDLVariableDeclaration.class, true);
for (final HDLVariableDeclaration hvd : hvds) {
switch (hvd.getDirection()) {
case IN:
case OUT:
case INOUT:
subMS.replace(hvd, hvd.setDirection(HDLDirection.INTERNAL));
break;
default:
break;
}
}
subUnit = subMS.apply(subUnit);
return subUnit;
}
private static HDLUnit addNewDimensions(HDLUnit subUnit, ArrayList<HDLExpression> outerDims) {
final ModificationSet subMS = new ModificationSet();
final HDLVariable[] vars = subUnit.getAllObjectsOf(HDLVariable.class, true);
for (final HDLVariable hdlVariable : vars) {
final ArrayList<HDLExpression> dims = hdlVariable.getDimensions();
dims.addAll(0, outerDims);
subMS.replace(hdlVariable, hdlVariable.setDimensions(dims));
}
subUnit = subMS.apply(subUnit);
return subUnit;
}
private static HDLUnit addArrayReference(HDLUnit subUnit, ArrayList<HDLExpression> outerDims, List<HDLQualifiedName> iteratorNames) {
final ModificationSet subMS = new ModificationSet();
final HDLVariableRef[] refs = subUnit.getAllObjectsOf(HDLVariableRef.class, true);
for (final HDLVariableRef ref : refs) {
final LinkedList<HDLExpression> array = Lists.newLinkedList();
for (int i = 0; i < outerDims.size(); i++) {
array.add(new HDLVariableRef().setVar(iteratorNames.get(i)));
}
array.addAll(ref.getArray());
subMS.replace(ref, ref.setArray(array));
}
subUnit = subMS.apply(subUnit);
return subUnit;
}
private static HDLUnit prefixVariables(HDLUnit subUnit, String prefix, char separator) {
final ModificationSet subMS = new ModificationSet();
final HDLVariable[] vars = subUnit.getAllObjectsOf(HDLVariable.class, true);
for (final HDLVariable hdlVariable : vars) {
final String newName;
if (hdlVariable.getDirection() == HDLDirection.PARAMETER) {
newName = prefix + '_' + hdlVariable.getName();
} else {
newName = prefix + separator + hdlVariable.getName();
}
Refactoring.renameVariable(hdlVariable, HDLQualifiedName.create(newName), subUnit, subMS);
}
subUnit = subMS.apply(subUnit);
return subUnit;
}
}