/*******************************************************************************
* 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
* * Emilie Balland - (CWI)
* * Mark Hills - Mark.Hills@cwi.nl (CWI)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
* * Paul Klint - Paul.Klint@cwi.nl
*******************************************************************************/
package org.rascalmpl.interpreter.result;
import static org.rascalmpl.interpreter.result.ResultFactory.makeResult;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.rascalmpl.ast.AbstractAST;
import org.rascalmpl.ast.Expression;
import org.rascalmpl.ast.KeywordFormal;
import org.rascalmpl.interpreter.IEvaluator;
import org.rascalmpl.interpreter.TypeDeclarationEvaluator;
import org.rascalmpl.interpreter.control_exceptions.MatchFailed;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.env.ModuleEnvironment.GenericKeywordParameters;
import org.rascalmpl.interpreter.staticErrors.UndeclaredField;
import org.rascalmpl.interpreter.staticErrors.UnexpectedKeywordArgumentType;
import org.rascalmpl.interpreter.types.FunctionType;
import org.rascalmpl.interpreter.types.RascalTypeFactory;
import org.rascalmpl.interpreter.utils.Names;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.value.IBool;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IWithKeywordParameters;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.values.uptr.RascalValueFactory;
public class ConstructorFunction extends NamedFunction {
protected final Type constructorType;
private Type kwTypes = null; // cache
private final List<KeywordFormal> initializers;
public ConstructorFunction(AbstractAST ast, IEvaluator<Result<IValue>> eval, Environment env, Type constructorType, List<KeywordFormal> initializers) {
super(ast, eval, (FunctionType) RascalTypeFactory.getInstance().functionType(constructorType.getAbstractDataType(), constructorType.getFieldTypes(), TypeDeclarationEvaluator.computeKeywordParametersType(initializers, eval)), initializers, constructorType.getName(), false, true, false, env);
this.constructorType = constructorType;
this.initializers = initializers;
}
public Type getConstructorType() {
return constructorType;
}
@Override
public ConstructorFunction cloneInto(Environment env) {
ConstructorFunction c = new ConstructorFunction(getAst(), getEval(), env, constructorType, initializers);
c.setPublic(isPublic());
return c;
}
// TODO: refactor and make small. For now this does the job.
public Result<IValue> computeDefaultKeywordParameter(String label, IConstructor value, Environment callerEnvironment) {
Set<GenericKeywordParameters> kws = callerEnvironment.lookupGenericKeywordParameters(constructorType.getAbstractDataType());
IWithKeywordParameters<? extends IConstructor> wkw = value.asWithKeywordParameters();
Environment old = ctx.getCurrentEnvt();
Environment resultEnv = new Environment(declarationEnvironment, URIUtil.rootLocation("initializer"), "keyword parameter initializer");
// first we compute the keyword parameters for the abstract data-type:
for (GenericKeywordParameters gkw : kws) {
// for hygiene's sake, each list of generic params needs to be evaluated in its declaring environment
Environment env = new Environment(gkw.getEnv(), URIUtil.rootLocation("initializer"), "kwp initializer");
try {
ctx.setCurrentEnvt(env);
for (KeywordFormal kwparam : gkw.getFormals()) {
String name = Names.name(kwparam.getName());
Type kwType = gkw.getTypes().get(name);
if (kwType == null) {
continue;
}
Result<IValue> kwResult;
if (wkw.hasParameter(name)){
IValue r = wkw.getParameter(name);
if(!r.getType().isSubtypeOf(kwType)) {
throw new UnexpectedKeywordArgumentType(name, kwType, r.getType(), ctx.getCurrentAST());
}
kwResult = ResultFactory.makeResult(kwType, r, ctx);
}
else {
Expression def = kwparam.getExpression();
kwResult = def.interpret(eval);
}
if (name.equals(label)) {
// we have the one we need, bail out quickly
return kwResult;
}
else {
env.declareVariable(kwResult.getType(), name);
env.storeVariable(name, kwResult);
resultEnv.declareVariable(kwResult.getType(), name);
resultEnv.storeVariable(name, kwResult);
}
}
}
finally {
ctx.setCurrentEnvt(old);
}
}
Type formals = getFunctionType().getArgumentTypes();
try {
// we set up an environment to hold the positional parameter values
ctx.setCurrentEnvt(resultEnv);
for (int i = 0; i < formals.getArity(); i++) {
String fieldName = formals.getFieldName(i);
Type fieldType = formals.getFieldType(i);
resultEnv.declareVariable(fieldType, fieldName);
resultEnv.storeLocalVariable(fieldName, ResultFactory.makeResult(fieldType, value.get(fieldName), ctx));
}
for (String kwparam : functionType.getKeywordParameterTypes().getFieldNames()) {
Type kwType = functionType.getKeywordParameterType(kwparam);
Result<IValue> kwResult;
if (wkw.hasParameter(kwparam)){
IValue r = wkw.getParameter(kwparam);
if(!r.getType().isSubtypeOf(kwType)) {
throw new UnexpectedKeywordArgumentType(kwparam, kwType, r.getType(), ctx.getCurrentAST());
}
kwResult = ResultFactory.makeResult(kwType, r, ctx);
}
else {
Expression def = getKeywordParameterDefaults().get(kwparam);
kwResult = def.interpret(eval);
}
if (kwparam.equals(label)) {
return kwResult;
}
else {
resultEnv.declareVariable(kwType, kwparam);
resultEnv.storeVariable(kwparam, kwResult);
}
}
throw new UndeclaredField(label,constructorType, ctx.getCurrentAST());
}
finally {
ctx.setCurrentEnvt(old);
}
}
@Override
public Type getKeywordArgumentTypes(Environment env) {
if (kwTypes != null) {
return kwTypes;
}
Type kwTypes = functionType.getKeywordParameterTypes();
ArrayList<Type> types = new ArrayList<>();
ArrayList<String> labels = new ArrayList<>();
for (String label : kwTypes.getFieldNames()) {
types.add(kwTypes.getFieldType(label));
labels.add(label);
}
Set<GenericKeywordParameters> kws = env.lookupGenericKeywordParameters(constructorType.getAbstractDataType());
for (GenericKeywordParameters p : kws) {
Map<String, Type> m = p.getTypes();
for (String name : m.keySet()) {
labels.add(name);
types.add(m.get(name));
}
}
Type[] typeArray = types.toArray(new Type[0]);
String[] stringArray = labels.toArray(new String[0]);
kwTypes = TypeFactory.getInstance().tupleType(typeArray, stringArray);
return kwTypes;
}
@Override
public boolean isStatic() {
return true;
}
@Override
public Result<IValue> call(Type[] actualTypes, IValue[] actuals, Map<String, IValue> keyArgValues) {
// TODO: when characters get proper types we need to add them here.
if (constructorType == RascalValueFactory.Tree_Appl || constructorType == RascalValueFactory.Tree_Amb || constructorType == RascalValueFactory.Tree_Cycle) {
return new ConcreteConstructorFunction(ast, constructorType, eval, declarationEnvironment).call(actualTypes, actuals, keyArgValues);
}
Map<Type,Type> bindings = new HashMap<Type,Type>();
if (!constructorType.getFieldTypes().match(TF.tupleType(actualTypes), bindings)) {
throw new MatchFailed();
}
Type formalTypeParameters = constructorType.getAbstractDataType().getTypeParameters();
Type instantiated = constructorType;
if (!formalTypeParameters.isBottom()) {
for (Type field : formalTypeParameters) {
if (!bindings.containsKey(field)) {
bindings.put(field, TF.voidType());
}
}
instantiated = constructorType.instantiate(bindings);
}
return makeResult(instantiated.getAbstractDataType(), ctx.getValueFactory().constructor(constructorType, actuals, keyArgValues), ctx);
}
@Override
public int hashCode() {
return constructorType.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ConstructorFunction) {
return constructorType == ((ConstructorFunction) obj).constructorType;
}
return false;
}
@Override
public String toString() {
return constructorType.toString();
}
@Override
public <V extends IValue> Result<IBool> equals(Result<V> that) {
return that.equalToConstructorFunction(this);
}
@Override
public Result<IBool> equalToConstructorFunction(ConstructorFunction that) {
return ResultFactory.bool((constructorType == that.constructorType), ctx);
}
}