package edu.ucsd.arcum.interpreter.ast;
import static edu.ucsd.arcum.interpreter.ast.ASTUtil.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.exceptions.SourceLocation;
import edu.ucsd.arcum.interpreter.ast.ASTUtil.NameAccessor;
import edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable;
public class Option extends TopLevelConstruct
{
private final String optionInterfaceName;
private final SourceLocation position;
private final FreeStandingRequirements freeStandingRequirements;
private OptionInterface optionInterface;
private TraitSignature constructor;
private Option(String name, String optionInterfaceName, String importsString,
SourceLocation position)
{
super(name, importsString);
this.optionInterfaceName = optionInterfaceName;
this.optionInterface = null;
this.constructor = null;
this.position = position;
this.freeStandingRequirements = new FreeStandingRequirements();
}
public static Option newOption(final String name, final String trait,
final String importsString, ArcumDeclarationTable table,
final SourceLocation position)
{
return table.conditionalCreate(name, new ConstructorThunk<Option>() {
public Option create() {
return new Option(name, trait, importsString, position);
}
});
}
@Override public String toString() {
StringBuilder buff = new StringBuilder();
buff.append("option ");
buff.append(getName());
buff.append(" implements ");
buff.append(optionInterfaceName);
buff.append(String.format("%n{"));
if (constructor != null) {
buff.append(String.format("%n "));
buff.append(constructor.toString());
buff.append(String.format("%n"));
}
for (RealizationStatement stmt : getRealizationStatements()) {
buff.append(String.format("%n "));
buff.append(stmt.toString());
buff.append(String.format(";%n"));
}
buff.append(String.format("}%n"));
return buff.toString();
}
// non-mandatory constructor that an option can have
// TODO: Support for this is probably not fully implemented
public void addConstructor(TraitSignature constructor) {
this.constructor = constructor;
}
@Override public void doTypeCheck(ArcumDeclarationTable table) {
this.optionInterface = table.lookup(optionInterfaceName, OptionInterface.class);
if (optionInterface == null) {
ArcumError.fatalUserError(position, "The option %s's implementing"
+ " interface \"%s\" cannot be resolved:" + " possible typo", getName(),
optionInterfaceName);
}
checkCorrectRealizations(optionInterface);
checkSingletonNamesAreUnique();
checkRealizationConsistency(optionInterface);
List<TraitSignature> traitSignatures;
traitSignatures = Lists.newArrayList(optionInterface.getTraitSignatures());
List<RealizationStatement> stmts = getRealizationStatements();
for (int i = 0; i < stmts.size(); ++i) {
RealizationStatement stmt = stmts.get(i);
List<TraitSignature> tuplesRealized = stmt.getTuplesRealized();
if (tuplesRealized.size() == 1) {
TraitSignature traitSignature = tuplesRealized.get(0);
String traitName = traitSignature.getName();
if (!optionInterface.declaresAsAbstract(traitName) && !stmt.isStatic()) {
stmts.set(i, StaticRealizationStatement.makeLocal(stmt,
traitSignature));
}
}
}
for (RealizationStatement stmt : stmts) {
List<TraitSignature> tuplesRealized = stmt.getTuplesRealized();
traitSignatures.addAll(tuplesRealized);
}
for (RealizationStatement stmt : getRealizationStatements()) {
checkExpressionPredicates(stmt, traitSignatures);
}
checkRequireClausePredicates(traitSignatures);
Set<String> varsInScope = TraitSignature.getGlobals(traitSignatures);
freeStandingRequirements.checkUserDefinedPredicates(traitSignatures, varsInScope);
// TODO: also type check the program fragment types, argument types, etc
// * the constructor singleton cannot be realized or defined
}
// Check that each abstract tuple set in the trait is realized exactly once
private void checkCorrectRealizations(OptionInterface optionInterface) {
List<String> allTuplesRealized = new ArrayList<String>();
for (RealizationStatement stmt : getRealizationStatements()) {
List<TraitSignature> tuplesRealized = stmt.getTuplesRealized();
extractNames(allTuplesRealized, tuplesRealized,
new NameAccessor<TraitSignature>() {
public String getName(TraitSignature element) {
return element.getName();
}
});
}
checkNames(allTuplesRealized, IDENTITY_ACCESSOR, "The tuple set %s cannot"
+ " be realized more than once");
List<TraitSignature> traitSignatures = optionInterface.getTraitSignatures();
List<String> traitsToRealize = new ArrayList<String>();
for (TraitSignature tupleSet : traitSignatures) {
if (tupleSet.isAbstract()) {
String traitName = tupleSet.getName();
traitsToRealize.add(traitName);
}
}
for (String toRealize : traitsToRealize) {
if (!allTuplesRealized.contains(toRealize)) {
ArcumError.fatalUserError(position,
"The option %s must realize the \'%s\' tuple set", this.getName(),
toRealize);
}
}
}
// each name introduced by a singleton is unique
private void checkSingletonNamesAreUnique() {
List<String> singletonElements = new ArrayList<String>();
for (RealizationStatement stmt : getRealizationStatements()) {
List<TraitSignature> tuplesRealized = stmt.getTuplesRealized();
for (TraitSignature tupleRealized : tuplesRealized) {
if (tupleRealized.isSingleton()) {
singletonElements.add(tupleRealized.getName());
}
}
}
checkNames(singletonElements, IDENTITY_ACCESSOR, "Name clash: %s");
}
// assumes a doTypeCheck has already been performed
public List<FormalParameter> getSingletonParameters(ArcumDeclarationTable table) {
OptionInterface optionInterface;
optionInterface = table.lookup(optionInterfaceName, OptionInterface.class);
List<FormalParameter> result = optionInterface.getSingletonParameters();
if (constructor != null) {
List<FormalParameter> optionFormals = constructor.getFormals();
List<String> allNames = new ArrayList<String>();
extractNames(allNames, optionFormals, PARAMETER_NAME);
extractNames(allNames, result, PARAMETER_NAME);
checkNames(allNames, IDENTITY_ACCESSOR);
result.addAll(optionFormals);
}
return result;
}
public RealizationStatement getStatementForReplacement(String name) {
for (RealizationStatement stmt : getRealizationStatements()) {
List<TraitSignature> tuplesRealized = stmt.getTuplesRealized();
if (tuplesRealized.size() == 1) {
TraitSignature signature = tuplesRealized.get(0);
if (signature.getName().equals(name)) {
return stmt;
}
}
else if (tuplesRealized.size() != 0) {
boolean allSingletons = true;
for (TraitSignature tupleRealized : tuplesRealized) {
if (!tupleRealized.isSingleton()) {
allSingletons = false;
break;
}
}
if (!allSingletons) {
ArcumError
.fatalError("Cannot realize more than one abstract trait at a time");
}
}
}
return null;
}
// TASK -- All locals, not just singletons, and the static defines they may
// depend upon
// TASK -- Ultimately the concept of singleton versus local needs to be removed
// completely. In terms of language syntax and implementation.
public List<RealizationStatement> getRealizationsOfLocals() {
List<RealizationStatement> result = new ArrayList<RealizationStatement>();
List<RealizationStatement> stmts = getRealizationStatements();
for (RealizationStatement stmt : stmts) {
if (stmt.isSingletonRealization() || stmt.isLocal() || stmt.isStatic()) {
result.add(stmt);
}
}
return result;
}
public OptionInterface getOptionInterface() {
return optionInterface;
}
public FreeStandingRequirements getFreeStandingRequirements() {
return freeStandingRequirements;
}
}