/*******************************************************************************
* 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
* * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl
* * Paul Klint - Paul.Klint@cwi.nl - CWI
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.interpreter.result;
import static org.rascalmpl.interpreter.result.ResultFactory.makeResult;
import java.util.Map;
import java.util.Set;
import org.rascalmpl.ast.Expression;
import org.rascalmpl.ast.KeywordFormal;
import org.rascalmpl.ast.Name;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.env.ModuleEnvironment.GenericKeywordParameters;
import org.rascalmpl.interpreter.staticErrors.UndeclaredAnnotation;
import org.rascalmpl.interpreter.staticErrors.UndeclaredField;
import org.rascalmpl.interpreter.staticErrors.UndeclaredType;
import org.rascalmpl.interpreter.staticErrors.UnexpectedKeywordArgumentType;
import org.rascalmpl.interpreter.staticErrors.UnexpectedType;
import org.rascalmpl.interpreter.staticErrors.UnsupportedOperation;
import org.rascalmpl.interpreter.utils.Names;
import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory;
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.exceptions.UndeclaredAbstractDataTypeException;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeStore;
public class ConstructorResult extends NodeResult {
public ConstructorResult(Type type, IConstructor cons, IEvaluatorContext ctx) {
super(type, cons, ctx);
}
@Override
public IConstructor getValue() {
return (IConstructor) super.getValue();
}
@Override
public Result<IBool> is(Name name) {
return ResultFactory.bool(
getValue().getName().equals(Names.name(name)), ctx);
}
@Override
public Result<IBool> has(Name name) {
String sname = Names.name(name);
return ResultFactory
.bool(getValue().has(sname)
|| (ctx.getCurrentEnvt()
.getStore()
.getKeywordParameterType(
getValue().getConstructorType(), sname) != null)
|| (getValue().isAnnotatable() && getValue()
.asAnnotatable().getAnnotation(sname) != null),
ctx);
}
@Override
public Result<IBool> isDefined(Name name) {
String sname = Names.name(name);
return ResultFactory.bool(getValue().has(sname)
|| getValue().asWithKeywordParameters().hasParameter(sname),
ctx);
}
@Override
public Result<IValue> call(Type[] argTypes, IValue[] argValues,
Map<String, IValue> keyArgValues) {
throw new UnsupportedOperation("Can not call a constructed "
+ getType() + " node as a function", ctx.getCurrentAST());
}
@SuppressWarnings("unchecked")
@Override
public <U extends IValue> Result<U> fieldAccess(String name, TypeStore store) {
try {
Type consType = getValue().getConstructorType();
if (!getType().hasField(name, store)
&& !getType().hasKeywordField(name, store)) {
throw new UndeclaredField(name, getType(), ctx.getCurrentAST());
}
if (!consType.hasField(name)
&& !consType.hasKeywordField(name, store)) {
throw RuntimeExceptionFactory.noSuchField(name,
ctx.getCurrentAST(), null);
}
if (getValue().mayHaveKeywordParameters() && getType().hasKeywordField(name, store)) { // it's a keyword parameter
Type kwType = store.getKeywordParameterType(getValue().getConstructorType(), name);
if (kwType == null) {
kwType = store.getKeywordParameterType(getType(), name);
}
assert kwType != null;
IValue parameter = getValue().asWithKeywordParameters().getParameter(name);
if (parameter == null) {
ConstructorFunction cons = ctx.getCurrentEnvt().getConstructorFunction(consType);
if (cons != null) {
return (Result<U>) cons.computeDefaultKeywordParameter(name, getValue(), ctx.getCurrentEnvt());
}
else {
// The constructor is not in scope, but there might be a generic keyword parameter in scope nevertheless
return (Result<U>) computeGenericDefaultKeywordParameter(name);
}
} else {
return makeResult(kwType, parameter, ctx);
}
} else if (consType.hasField(name)){ // it is a normal parameter
int index = consType.getFieldIndex(name);
return makeResult(consType.getFieldType(index),
getValue().get(index), ctx);
}
throw new UndeclaredField(name, getType(), ctx.getCurrentAST());
} catch (UndeclaredAbstractDataTypeException e) {
throw new UndeclaredType(getType().toString(), ctx.getCurrentAST());
}
}
private Result<IValue> computeGenericDefaultKeywordParameter(String label) {
Set<GenericKeywordParameters> kwps = ctx.getCurrentEnvt().lookupGenericKeywordParameters(getType());
IWithKeywordParameters<? extends IConstructor> wkw = getValue().asWithKeywordParameters();
Environment old = ctx.getCurrentEnvt();
for (GenericKeywordParameters gkw : kwps) {
// 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(ctx.getEvaluator());
}
if (name.equals(label)) {
// we have the one we need, bail out quickly
return kwResult;
}
else {
// we may need these in case they are used in the next definition
env.declareVariable(kwResult.getType(), name);
env.storeVariable(name, kwResult);
}
}
}
finally {
ctx.setCurrentEnvt(old);
}
}
throw new UndeclaredField(label, getType(), ctx.getCurrentAST());
}
@Override
public <U extends IValue, V extends IValue> Result<U> fieldUpdate(
String name, Result<V> repl, TypeStore store) {
ConstructorFunction cons = ctx.getCurrentEnvt().getConstructorFunction(
getValue().getConstructorType());
Type kwTypes = cons.getKeywordArgumentTypes(ctx.getCurrentEnvt());
if (!getType().hasField(name, store) && !kwTypes.hasField(name)) {
throw new UndeclaredField(name, getType(), ctx.getCurrentAST());
}
Type nodeType = getValue().getConstructorType();
if (!nodeType.hasField(name) && !kwTypes.hasField(name)) {
throw RuntimeExceptionFactory.noSuchField(name,
ctx.getCurrentAST(), null);
}
if (kwTypes.hasField(name)) {
Type fieldType = kwTypes.getFieldType(name);
if (!repl.getType().isSubtypeOf(fieldType)) {
throw new UnexpectedType(fieldType, repl.getType(),
ctx.getCurrentAST());
}
return makeResult(getType(), getValue().asWithKeywordParameters()
.setParameter(name, repl.getValue()), ctx);
} else {
// normal field
int index = nodeType.getFieldIndex(name);
Type fieldType = nodeType.getFieldType(index);
if (!repl.getType().isSubtypeOf(fieldType)) {
throw new UnexpectedType(fieldType, repl.getType(),
ctx.getCurrentAST());
}
return makeResult(getType(),
getValue().set(index, repl.getValue()), ctx);
}
}
@Override
public <U extends IValue> Result<U> getAnnotation(String annoName,
Environment env) {
Type annoType = env.getAnnotationType(getType(), annoName);
if (annoType == null) {
throw new UndeclaredAnnotation(annoName, getType(),
ctx.getCurrentAST());
}
IValue annoValue = getValue().asAnnotatable().getAnnotation(annoName);
if (annoValue == null) {
throw RuntimeExceptionFactory.noSuchAnnotation(annoName,
ctx.getCurrentAST(), null);
}
// TODO: applyRules?
return makeResult(annoType, annoValue, ctx);
}
}