package org.overture.codegen.analysis.vdm;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.AAssignmentDefinition;
import org.overture.ast.definitions.AClassClassDefinition;
import org.overture.ast.definitions.AExplicitOperationDefinition;
import org.overture.ast.definitions.AInheritedDefinition;
import org.overture.ast.definitions.AInstanceVariableDefinition;
import org.overture.ast.definitions.AStateDefinition;
import org.overture.ast.definitions.ASystemClassDefinition;
import org.overture.ast.definitions.AThreadDefinition;
import org.overture.ast.definitions.AValueDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.modules.AModuleModules;
import org.overture.ast.node.INode;
import org.overture.ast.statements.ABlockSimpleBlockStm;
import org.overture.ast.statements.AIdentifierStateDesignator;
import org.overture.ast.statements.PStm;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
/**
* Computes the definitions of identifier state designators
*
* @author pvj
*/
public class IdStateDesignatorDefCollector extends VdmAnalysis
{
private List<PDefinition> defsInScope;
private Map<AIdentifierStateDesignator, PDefinition> idDefs;
private Set<INode> visited;
private ITypeCheckerAssistantFactory af;
private Logger log = Logger.getLogger(this.getClass().getName());
public IdStateDesignatorDefCollector(INode topNode,
List<? extends INode> classes, ITypeCheckerAssistantFactory af)
{
super(topNode);
this.af = af;
loadGlobals(classes);
this.idDefs = new HashMap<>();
this.visited = new HashSet<>();
}
private void loadGlobals(List<? extends INode> classes)
{
this.defsInScope = new LinkedList<>();
for (INode n : classes)
{
if (n != topNode)
{
if (n instanceof SClassDefinition)
{
loadClassGlobals((SClassDefinition) n);
} else if (n instanceof AModuleModules)
{
loadModuleGlobals((AModuleModules) n);
} else
{
log.error("Expected class or module. Got: " + n);
}
}
}
}
public static Map<AIdentifierStateDesignator, PDefinition> getIdDefs(
List<? extends INode> classes, ITypeCheckerAssistantFactory af)
throws AnalysisException
{
Map<AIdentifierStateDesignator, PDefinition> allDefs = new HashMap<>();
for (INode node : classes)
{
IdStateDesignatorDefCollector collector = new IdStateDesignatorDefCollector(node, classes, af);
node.apply(collector);
allDefs.putAll(collector.idDefs);
}
return allDefs;
}
@Override
public void caseAClassClassDefinition(AClassClassDefinition node)
throws AnalysisException
{
handleClass(node);
}
@Override
public void caseASystemClassDefinition(ASystemClassDefinition node)
throws AnalysisException
{
handleClass(node);
}
private void handleClass(SClassDefinition node) throws AnalysisException
{
if (!proceed(node))
{
return;
}
loadClassGlobals(node);
for (PDefinition def : node.getDefinitions())
{
// Check only explicit operations or threads within the enclosing class
if (def instanceof AExplicitOperationDefinition
|| def instanceof AThreadDefinition)
{
def.apply(this);
}
}
}
private void loadClassGlobals(SClassDefinition node)
{
LinkedList<PDefinition> allDefs = new LinkedList<PDefinition>(node.getAllInheritedDefinitions());
allDefs.addAll(node.getDefinitions());
// Instance variables and values are visible to all operations
for (int i = 0; i < allDefs.size(); i++)
{
PDefinition def = allDefs.get(i);
while (def instanceof AInheritedDefinition)
{
def = ((AInheritedDefinition) def).getSuperdef();
}
if (def instanceof AInstanceVariableDefinition
|| def instanceof AValueDefinition)
{
defsInScope.addAll(af.createPDefinitionAssistant().getDefinitions(def));
}
}
}
@Override
public void caseAModuleModules(AModuleModules node) throws AnalysisException
{
if (!proceed(node))
{
return;
}
loadModuleGlobals(node);
for (PDefinition def : node.getDefs())
{
// Check only explicit operations
if (def instanceof AExplicitOperationDefinition)
{
def.apply(this);
}
}
}
private void loadModuleGlobals(AModuleModules node)
{
for (PDefinition def : node.getDefs())
{
if (def instanceof AStateDefinition
|| def instanceof AValueDefinition)
{
defsInScope.addAll(af.createPDefinitionAssistant().getDefinitions(def));
}
}
}
@Override
public void caseABlockSimpleBlockStm(ABlockSimpleBlockStm node)
throws AnalysisException
{
if (!proceed(node))
{
return;
}
int adds = 0;
for (AAssignmentDefinition d : node.getAssignmentDefs())
{
defsInScope.add(d);
adds++;
}
for (PStm stm : node.getStatements())
{
stm.apply(this);
}
for (int i = 0; i < adds; i++)
{
defsInScope.remove(defsInScope.size() - 1);
}
}
@Override
public void caseAIdentifierStateDesignator(AIdentifierStateDesignator node)
throws AnalysisException
{
if (!proceed(node))
{
return;
}
for (int i = defsInScope.size() - 1; i >= 0; i--)
{
PDefinition nextDef = defsInScope.get(i);
ILexNameToken defname = nextDef.getName();
if (defname == null)
{
log.error("Definition name was null");
} else if (node.getName().getName().equals(nextDef.getName().getName()))
{
this.idDefs.put(node, nextDef);
break;
}
}
}
@Override
protected boolean proceed(INode node)
{
if (visited.contains(node))
{
return false;
} else
{
visited.add(node);
return super.proceed(node);
}
}
}