/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.interpreter.types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISetWriter;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.exceptions.IllegalOperationException;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeFactory.TypeReifier;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.uptr.RascalValueFactory;
public class OverloadedFunctionType extends RascalType {
private final Set<FunctionType> alternatives;
private final Type returnType;
private static final TypeFactory TF = TypeFactory.getInstance();
private static final RascalTypeFactory RTF = RascalTypeFactory.getInstance();
/*package*/ OverloadedFunctionType(Set<FunctionType> alternatives) {
this.alternatives = alternatives;
this.returnType = alternatives.iterator().next().getReturnType();
}
public static class Reifier implements TypeReifier {
@Override
public Type getSymbolConstructorType() {
throw new UnsupportedOperationException();
}
@Override
public Set<Type> getSymbolConstructorTypes() {
return Arrays.stream(new Type[] {
normalSymbolType(),
deprecatedSymbolType(), // TODO: can be removed after bootstrap
}).collect(Collectors.toSet());
}
private Type normalSymbolType() {
return symbols().typeSymbolConstructor("overloaded", TF.setType(symbols().symbolADT()), "alternatives");
}
private Type deprecatedSymbolType() {
return symbols().typeSymbolConstructor("overloaded", TF.setType(symbols().symbolADT()), "overloads", TF.setType(symbols().symbolADT()), "defaults");
}
@Override
public Type fromSymbol(IConstructor symbol, TypeStore store, Function<IConstructor, Set<IConstructor>> grammar) {
Set<FunctionType> newAlts = new HashSet<>();
if (symbol.getConstructorType() == deprecatedSymbolType()) {
// TODO remove after bootstrap
for (IValue alt : ((ISet) symbol.get("overloads"))) {
Type fromSymbol = symbols().fromSymbol((IConstructor) alt, store, grammar);
newAlts.add((FunctionType) fromSymbol);
}
for (IValue alt : ((ISet) symbol.get("defaults"))) {
Type fromSymbol = symbols().fromSymbol((IConstructor) alt, store, grammar);
if (fromSymbol.isConstructor()) {
newAlts.add((FunctionType) RTF.functionType(fromSymbol.getAbstractDataType(), fromSymbol.getFieldTypes(), TF.voidType()));
} else {
newAlts.add((FunctionType) fromSymbol);
}
}
}
else {
for (IValue alt : ((ISet) symbol.get("alternatives"))) {
newAlts.add((FunctionType) symbols().fromSymbol((IConstructor) alt, store, grammar));
}
}
return RTF.overloadedFunctionType(newAlts);
}
@Override
public void asProductions(Type type, IValueFactory vf, TypeStore store, ISetWriter grammar,
Set<IConstructor> done) {
for (Type alt : ((OverloadedFunctionType) type).getAlternatives()) {
alt.asProductions(vf, store, grammar, done);
}
}
@Override
public IConstructor toSymbol(Type type, IValueFactory vf, TypeStore store, ISetWriter grammar, Set<IConstructor> done) {
ISetWriter w = vf.setWriter();
for (Type alt : ((OverloadedFunctionType) type).getAlternatives()) {
w.insert(alt.asSymbol(vf, store, grammar, done));
}
return vf.constructor(normalSymbolType(), w.done());
}
@Override
public boolean isRecursive() {
return true;
}
@Override
public Type randomInstance(Supplier<Type> next, TypeStore store, Random rnd) {
int size = rnd.nextInt(5) + 2;
Set<FunctionType> alts = new HashSet<>();
Type returnType = next.get();
int arity = rnd.nextInt(4);
while (size-- > 0) {
alts.add((FunctionType) RascalTypeFactory.getInstance().functionType(returnType, randomTuple(next, store, rnd, arity), null));
}
return RascalTypeFactory.getInstance().overloadedFunctionType(alts);
}
}
@Override
public TypeReifier getTypeReifier() {
return new Reifier();
}
@Override
public boolean isOverloadedFunction() {
return true;
}
@Override
public Type asAbstractDataType() {
return RascalValueFactory.Production;
}
public Type getKeywordParameterTypes() {
// TODO: what does this union mean in case of overlapping names?
ArrayList<String> labels = new ArrayList<>();
ArrayList<Type> types = new ArrayList<>();
for (FunctionType f : alternatives) {
for (String label : f.getKeywordParameterTypes().getFieldNames()) {
if (!labels.contains(label)) {
labels.add(label);
types.add(f.getKeywordParameterType(label));
}
// TODO: a clash, but we silently ignore it here?
}
}
return TF.tupleType(types.toArray(new Type[types.size()]), labels.toArray(new String[labels.size()]));
}
public int size() {
return alternatives.size();
}
public Type getReturnType() {
return returnType;
}
@Override
public <T, E extends Throwable> T accept(IRascalTypeVisitor<T, E> visitor) throws E {
return visitor.visitOverloadedFunction(this);
}
@Override
protected boolean isSupertypeOf(RascalType type) {
return type.isSubtypeOfOverloadedFunction(this);
}
@Override
protected Type lub(RascalType type) {
return type.lubWithOverloadedFunction(this);
}
@Override
protected Type glb(RascalType type) {
return type.glbWithOverloadedFunction(this);
}
public Set<FunctionType> getAlternatives() {
return Collections.unmodifiableSet(alternatives);
}
@Override
public int getArity() {
int arity = alternatives.stream().findFirst().get().getArity();
assert !alternatives.stream().filter(t -> t.getArity() != arity).findAny().isPresent();
return arity;
}
@Override
protected boolean isSubtypeOfOverloadedFunction(RascalType type) {
OverloadedFunctionType of = (OverloadedFunctionType) type;
// if this has at least one alternative that is a sub-type of the other,
// then yes, this function can act as the other and should be a sub-type
// TODO: this is broken because of defaults. We should distinguish!
for(FunctionType f : getAlternatives()) {
if(f.isSubtypeOf(of)) {
return true;
}
}
for(FunctionType f : of.getAlternatives()) {
if(!this.isSubtypeOf(f)) {
return false;
}
}
return true;
}
@Override
protected boolean isSubtypeOfFunction(RascalType type) {
// TODO: this is broken because of defaults. We should distinguish!
for (FunctionType a : alternatives) {
if (a.isSubtypeOf(type)) {
return true;
}
}
return false;
}
@Override
protected Type lubWithOverloadedFunction(RascalType type) {
if(this == type) {
return this;
}
OverloadedFunctionType of = (OverloadedFunctionType) type;
Set<FunctionType> newAlternatives = new HashSet<FunctionType>();
for(FunctionType f : getAlternatives()) {
for(FunctionType g : of.getAlternatives()) {
Type lub = f.lubWithFunction(g);
if(lub instanceof FunctionType)
newAlternatives.add((FunctionType)lub);
}
}
if(!newAlternatives.isEmpty())
return RTF.overloadedFunctionType(newAlternatives);
return TF.valueType();
}
@Override
protected Type lubWithFunction(RascalType type) {
FunctionType f = (FunctionType) type;
Set<FunctionType> newAlternatives = new HashSet<FunctionType>();
newAlternatives.add(f);
return this.lubWithOverloadedFunction((RascalType)RTF.overloadedFunctionType(newAlternatives));
}
@Override
protected Type glbWithOverloadedFunction(RascalType type) {
if(this == type) {
return this;
}
OverloadedFunctionType of = (OverloadedFunctionType) type;
Set<FunctionType> newAlternatives = new HashSet<FunctionType>();
if(getReturnType() == of.getReturnType()) {
newAlternatives.addAll(getAlternatives());
newAlternatives.addAll(of.getAlternatives());
return RTF.overloadedFunctionType(newAlternatives);
}
Type returnType = getReturnType().glb(of.getReturnType());
for(FunctionType f : getAlternatives()) {
newAlternatives.add((FunctionType)RTF.functionType(returnType, f.getArgumentTypes(), f.getKeywordParameterTypes()));
}
for(FunctionType f : of.getAlternatives()) {
newAlternatives.add((FunctionType)RTF.functionType(returnType, f.getArgumentTypes(), f.getKeywordParameterTypes()));
}
return RTF.overloadedFunctionType(newAlternatives);
}
@Override
protected Type glbWithFunction(RascalType type) {
FunctionType f = (FunctionType) type;
Set<FunctionType> newAlternatives = new HashSet<FunctionType>();
newAlternatives.add(f);
return this.glbWithOverloadedFunction((RascalType)RTF.overloadedFunctionType(newAlternatives));
}
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if (obj.getClass().equals(getClass())) {
OverloadedFunctionType f = (OverloadedFunctionType) obj;
return alternatives.equals(f.alternatives);
}
return false;
}
@Override
public int hashCode() {
// TODO: better hashCode?
return 31 + alternatives.hashCode();
}
@Override
public String toString() {
StringBuffer b = new StringBuffer();
b.append(getReturnType() + "(");
int i = 0;
for (FunctionType t : alternatives) {
assert t.getReturnType() == getReturnType();
if (i++ != 0) {
b.append(" + ");
}
b.append(t.getArgumentTypes().toString() + " ");
if (t.getKeywordParameterTypes() != null) {
b.append(t.getKeywordParameterTypes().toString() + " ");
}
}
b.append(")");
return b.toString();
}
@Override
public Type compose(Type right) {
if (right.isBottom()) {
return right;
}
Set<FunctionType> newAlternatives = new HashSet<FunctionType>();
if(right instanceof FunctionType) {
for(FunctionType ftype : this.alternatives) {
if(TF.tupleType(((FunctionType) right).getReturnType()).isSubtypeOf(ftype.getArgumentTypes())) {
newAlternatives.add((FunctionType) RTF.functionType(ftype.getReturnType(), ((FunctionType) right).getArgumentTypes(), ((FunctionType) right).getKeywordParameterTypes()));
}
}
} else if(right instanceof OverloadedFunctionType) {
for(FunctionType ftype : ((OverloadedFunctionType) right).getAlternatives()) {
for(FunctionType gtype : this.alternatives) {
if(TF.tupleType(ftype.getReturnType()).isSubtypeOf(gtype.getArgumentTypes())) {
newAlternatives.add((FunctionType) RTF.functionType(gtype.getReturnType(), ftype.getArgumentTypes(), ftype.getKeywordParameterTypes()));
}
}
}
} else {
throw new IllegalOperationException("compose", this, right);
}
if(!newAlternatives.isEmpty())
return RTF.overloadedFunctionType(newAlternatives);
return TF.voidType();
}
}