/*******************************************************************************
* 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 static org.pshdl.model.extensions.FullNameExtension.fullNameOf;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.pshdl.model.HDLClass;
import org.pshdl.model.HDLEnum;
import org.pshdl.model.HDLEnumDeclaration;
import org.pshdl.model.HDLFunction;
import org.pshdl.model.HDLInterface;
import org.pshdl.model.HDLRegisterConfig;
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.IHDLObject;
import org.pshdl.model.extensions.ScopingExtension;
import com.google.common.base.Optional;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
public class HDLResolver {
private final boolean descent;
private Map<HDLQualifiedName, HDLEnum> enumCache;
private Map<HDLQualifiedName, HDLInterface> ifCache;
private Multimap<HDLQualifiedName, HDLFunction> funcCache;
private final IHDLObject resolveTo;
private Map<HDLQualifiedName, HDLType> typeCache;
private Map<HDLQualifiedName, HDLVariable> variableCache;
private IHDLObject resolveContainer;
private HDLQualifiedName resolveName;
public HDLResolver(IHDLObject resolveTo, boolean descent) {
super();
this.resolveTo = resolveTo;
this.resolveContainer = resolveTo.getContainer();
this.resolveName = fullNameOf(resolveTo);
this.descent = descent;
}
public HDLResolver(IHDLObject resolveTo, boolean descent, String libURI) {
super();
this.resolveTo = resolveTo;
this.descent = descent;
}
protected List<HDLType> doGetTypeDeclarations() {
final List<HDLType> types = new LinkedList<HDLType>();
for (final HDLEnumDeclaration hEnumDecl : ScopingExtension.INST.doGetEnumDeclarations(resolveTo)) {
types.add(hEnumDecl.getHEnum());
}
for (final HDLVariable varDecl : ScopingExtension.INST.doGetVariables(resolveTo)) {
final IHDLObject container = varDecl.getContainer();
if (container instanceof HDLVariableDeclaration) {
final HDLVariableDeclaration hvd = (HDLVariableDeclaration) container;
if (hvd.getPrimitive() != null) {
types.add(hvd.getPrimitive());
}
}
}
for (final HDLInterface ifDecl : ScopingExtension.INST.doGetInterfaceDeclarations(resolveTo)) {
types.add(ifDecl);
}
return types;
}
public Optional<HDLEnum> resolveEnum(HDLQualifiedName hEnum) {
if (enumCache == null) {
synchronized (this) {
final HDLEnumDeclaration[] enumDecl = resolveTo.getAllObjectsOf(HDLEnumDeclaration.class, false);
enumCache = Maps.newLinkedHashMap();
for (final HDLEnumDeclaration hdlEnumDeclaration : enumDecl) {
enumCache.put(fullNameOf(hdlEnumDeclaration.getHEnum()), hdlEnumDeclaration.getHEnum());
}
}
}
// XXX Check if the qualifier does either match the pkg name, or is not
// existant
final HDLEnum checkCache = checkCache(hEnum, enumCache);
if (checkCache != null)
return Optional.of(checkCache);
if ((resolveContainer == null) || !descent)
return Optional.absent();
return ScopingExtension.INST.resolveEnum(resolveContainer, hEnum);
}
public Optional<Iterable<HDLFunction>> resolveFunction(HDLQualifiedName hEnum) {
if (funcCache == null) {
synchronized (this) {
final HDLFunction[] funcDecl = resolveTo.getAllObjectsOf(HDLFunction.class, false);
funcCache = LinkedHashMultimap.create();
for (final HDLFunction hdlfuncDeclaration : funcDecl) {
funcCache.put(fullNameOf(hdlfuncDeclaration), hdlfuncDeclaration);
}
}
}
// XXX Check if the qualifier does either match the pkg name, or is not
// existant
final Iterable<HDLFunction> checkCache = checkCache(hEnum, funcCache);
if ((checkCache != null) && checkCache.iterator().hasNext())
return Optional.of(checkCache);
if ((resolveContainer == null) || !descent)
return Optional.absent();
return ScopingExtension.INST.resolveFunctionName(resolveContainer, hEnum);
}
public Optional<HDLInterface> resolveInterface(HDLQualifiedName hIf) {
if (ifCache == null) {
synchronized (this) {
final List<HDLInterface> ifDecl = ScopingExtension.INST.doGetInterfaceDeclarations(resolveTo);
ifCache = Maps.newLinkedHashMap();
for (final HDLInterface hdlIfDeclaration : ifDecl) {
final HDLQualifiedName fqn = fullNameOf(hdlIfDeclaration);
// Usually this should not happen, but when the interface
// can not be resolved, it might happen.
if (fqn != null) {
ifCache.put(fqn, hdlIfDeclaration);
}
}
}
}
// XXX Check if the qualifier does either match the pkg name, or is not
// existant
final HDLInterface checkCache = checkCache(hIf, ifCache);
if (checkCache != null)
return Optional.of(checkCache);
if ((resolveContainer == null) || !descent)
return Optional.absent();
return ScopingExtension.INST.resolveInterface(resolveContainer, hIf);
}
public Optional<? extends HDLType> resolveType(HDLQualifiedName var) {
if (typeCache == null) {
synchronized (this) {
final List<HDLType> typeDecl = doGetTypeDeclarations();
typeCache = Maps.newLinkedHashMap();
for (final HDLType hdlTypeDeclaration : typeDecl) {
if (hdlTypeDeclaration.getClassType() != HDLClass.HDLPrimitive) {
typeCache.put(fullNameOf(hdlTypeDeclaration), hdlTypeDeclaration);
}
}
}
}
final HDLType checkCache = checkCache(var, typeCache);
if (checkCache != null)
return Optional.of(checkCache);
if ((resolveContainer == null) || !descent) {
if (resolveTo instanceof HDLUnit) {
final HDLUnit unit = (HDLUnit) resolveTo;
final String uri = unit.getLibURI();
if (uri != null) {
final HDLLibrary library = HDLLibrary.getLibrary(uri);
if (library != null) {
final List<String> imports = unit.getImports();
imports.add(fullNameOf(unit).skipLast(1).append("*").toString());
return library.resolve(imports, var);
}
}
}
return Optional.absent();
}
return ScopingExtension.INST.resolveType(resolveContainer, var);
}
public Optional<HDLVariable> resolveVariable(HDLQualifiedName var) {
if (variableCache == null) {
synchronized (this) {
final List<HDLVariable> varDecl = ScopingExtension.INST.doGetVariables(resolveTo);
variableCache = Maps.newLinkedHashMap();
for (final HDLVariable declVars : varDecl) {
variableCache.put(fullNameOf(declVars), declVars);
}
}
}
final HDLVariable checkCache = checkCache(var, variableCache);
if (checkCache != null)
return Optional.of(checkCache);
if (var.length > 1) {
// Using lastSgement if $for0.I or ThisObject.I
if (var.getSegment(0).startsWith("$") || var.getTypePart().equals(resolveName.getTypePart())) {
final String string = var.getLastSegment();
for (final Entry<HDLQualifiedName, HDLVariable> entry : variableCache.entrySet())
if (entry.getKey().getLastSegment().equals(string))
return Optional.of(entry.getValue());
}
}
if (HDLRegisterConfig.DEF_CLK.equals(var.getLastSegment()))
return Optional.of(HDLRegisterConfig.defaultClk(true));
if (HDLRegisterConfig.DEF_RST.equals(var.getLastSegment()))
return Optional.of(HDLRegisterConfig.defaultRst(true));
final IHDLObject container = resolveContainer;
if ((container == null) || !descent)
return Optional.absent();
return ScopingExtension.INST.resolveVariable(container, var);
}
private <T> Iterable<T> checkCache(HDLQualifiedName var, Multimap<HDLQualifiedName, T> map) {
if (map.get(var) != null)
return map.get(var);
if (var.length == 1) {
final HDLQualifiedName fqn = resolveName.append(var);
if (map.get(fqn) != null)
return map.get(fqn);
}
return null;
}
private <T> T checkCache(HDLQualifiedName var, Map<HDLQualifiedName, T> map) {
if (map.get(var) != null)
return map.get(var);
if (var.length == 1) {
final HDLQualifiedName fqn = resolveName.append(var);
if (map.get(fqn) != null)
return map.get(fqn);
}
return null;
}
public static List<HDLEnumDeclaration> getallEnumDeclarations(List<HDLStatement> stmnts) {
final List<HDLEnumDeclaration> res = new LinkedList<HDLEnumDeclaration>();
for (final HDLStatement hdlStatement : stmnts) {
res.addAll(ScopingExtension.INST.doGetEnumDeclarations(hdlStatement));
}
return res;
}
public static List<HDLInterface> getallInterfaceDeclarations(List<HDLStatement> stmnts) {
final List<HDLInterface> res = new LinkedList<HDLInterface>();
for (final HDLStatement hdlStatement : stmnts) {
res.addAll(ScopingExtension.INST.doGetInterfaceDeclarations(hdlStatement));
}
return res;
}
public static List<HDLVariable> getallVariableDeclarations(List<HDLStatement> stmnts) {
final List<HDLVariable> res = new LinkedList<HDLVariable>();
for (final HDLStatement hdlStatement : stmnts) {
res.addAll(ScopingExtension.INST.doGetVariables(hdlStatement));
}
return res;
}
}