/******************************************************************************* * 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 * * Emilie Balland - (CWI) * * Paul Klint - Paul.Klint@cwi.nl - CWI * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl * * Anastasia Izmaylova - A.Izmaylova@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.interpreter.result; import java.io.IOException; import java.io.Writer; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.Expression; import org.rascalmpl.ast.FunctionDeclaration; import org.rascalmpl.ast.KeywordFormal; import org.rascalmpl.ast.KeywordFormals; import org.rascalmpl.debug.IRascalMonitor; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.control_exceptions.MatchFailed; import org.rascalmpl.interpreter.env.Environment; import org.rascalmpl.interpreter.staticErrors.UnexpectedKeywordArgumentType; import org.rascalmpl.interpreter.staticErrors.UnexpectedType; import org.rascalmpl.interpreter.types.FunctionType; import org.rascalmpl.interpreter.types.RascalTypeFactory; import org.rascalmpl.interpreter.utils.LimitedResultWriter; import org.rascalmpl.interpreter.utils.LimitedResultWriter.IOLimitReachedException; import org.rascalmpl.interpreter.utils.Names; import org.rascalmpl.value.IAnnotatable; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IExternalValue; import org.rascalmpl.value.IListWriter; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.IWithKeywordParameters; import org.rascalmpl.value.exceptions.FactTypeUseException; import org.rascalmpl.value.exceptions.IllegalOperationException; import org.rascalmpl.value.io.StandardTextWriter; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.type.TypeStore; import org.rascalmpl.value.visitors.IValueVisitor; import org.rascalmpl.values.uptr.RascalValueFactory; abstract public class AbstractFunction extends Result<IValue> implements IExternalValue, ICallableValue { protected static final TypeFactory TF = TypeFactory.getInstance(); protected final Environment declarationEnvironment; protected final IEvaluator<Result<IValue>> eval; protected final FunctionType functionType; protected final boolean hasVarArgs; protected boolean hasKeyArgs; protected final Map<String, Expression> keywordParameterDefaults = new HashMap<>(); protected final static TypeStore hiddenStore = new TypeStore(); protected final AbstractAST ast; protected final IValueFactory vf; protected static int callNesting = 0; protected static boolean callTracing = false; // TODO: change arguments of these constructors to use EvaluatorContexts public AbstractFunction(AbstractAST ast, IEvaluator<Result<IValue>> eval, FunctionType functionType, List<KeywordFormal> initializers, boolean varargs, Environment env) { super(functionType, null, eval); this.ast = ast; this.functionType = functionType; this.eval = eval; this.hasVarArgs = varargs; this.hasKeyArgs = functionType.hasKeywordParameters(); this.declarationEnvironment = env; this.vf = eval.getValueFactory(); for (KeywordFormal init : initializers) { String label = Names.name(init.getName()); keywordParameterDefaults.put(label, init.getExpression()); } } @Override public IConstructor encodeAsConstructor() { return getValueFactory().constructor(RascalValueFactory.Function_Function, getAst().getLocation()); } protected static List<KeywordFormal> getFormals(FunctionDeclaration func) { KeywordFormals keywordFormals = func.getSignature().getParameters().getKeywordFormals(); return keywordFormals.hasKeywordFormalList() ? keywordFormals.getKeywordFormalList() : Collections.<KeywordFormal>emptyList(); } public boolean isTest() { return false; } @Override public Type getKeywordArgumentTypes(Environment env) { return functionType.getKeywordParameterTypes(); } public boolean hasKeywordParameter(String label) { return functionType.hasKeywordParameter(label); } @Override public int getArity() { return functionType.getArgumentTypes().getArity(); } public static void setCallTracing(boolean value){ callTracing = value; } public boolean isPatternDispatched() { return false; } public boolean isConcretePatternDispatched() { return false; } public Type getFormals() { return functionType.getArgumentTypes(); } public AbstractAST getAst() { return ast; } public boolean hasTag(String key) { return false; } public IValue getTag(String key) { return null; } public String getIndexedLabel() { return null; } public int getIndexedArgumentPosition() { return -1; } public IConstructor getIndexedProduction() { return null; } @Override public IValue getValue() { return this; } public Environment getEnv() { return declarationEnvironment; } public boolean match(Type actuals) { if (actuals.isSubtypeOf(getFormals())) { return true; } if (hasVarArgs) { return matchVarArgsFunction(actuals); } return false; } @Override public boolean hasVarArgs() { return hasVarArgs; } @Override public boolean hasKeywordArguments() { return hasKeyArgs; } public Map<String, Expression> getKeywordParameterDefaults(){ return keywordParameterDefaults; } @Override public Result<IValue> call(IRascalMonitor monitor, Type[] argTypes, IValue[] argValues, Map<String, IValue> keyArgValues) { IRascalMonitor old = ctx.getEvaluator().setMonitor(monitor); try { return call(argTypes,argValues, keyArgValues); } finally { ctx.getEvaluator().setMonitor(old); } } private boolean matchVarArgsFunction(Type actuals) { int arity = getFormals().getArity(); int i; for (i = 0; i < arity - 1; i++) { if (!actuals.getFieldType(i).isSubtypeOf(getFormals().getFieldType(i))) { return false; } } if (i > actuals.getArity()) { return false; } Type elementType = getFormals().getFieldType(i).getElementType(); for (; i < actuals.getArity(); i++) { if (!actuals.getFieldType(i).isSubtypeOf(elementType)) { return false; } } return true; } public abstract boolean isDefault(); private void printNesting(StringBuilder b) { for (int i = 0; i < callNesting; i++) { b.append('>'); } } private String strval(IValue value) { Writer w = new LimitedResultWriter(50); try { new StandardTextWriter(true, 2).write(value, w); return w.toString(); } catch (IOLimitReachedException e) { return w.toString(); } catch (IOException e) { return "..."; } } protected void printHeader(StringBuilder b, IValue[] actuals) { b.append(moduleName()); b.append("::"); b.append(getName()); b.append('('); Type formals = getFormals(); int n = Math.min(formals.getArity(), actuals.length); for (int i = 0; i < n; i++) { b.append(strval(actuals[i])); if (i < formals.getArity() - 1) { b.append(", "); } } b.append(')'); } protected void printStartTrace(IValue[] actuals) { StringBuilder b = new StringBuilder(); b.append("call >"); printNesting(b); printHeader(b, actuals); eval.getStdOut().println(b.toString()); eval.getStdOut().flush(); callNesting++; } private String moduleName() { String name = getEnv().getName(); int ind = name.lastIndexOf("::"); if (ind != -1) { name = name.substring(ind + 2); } return name; } protected void printExcept(Throwable e) { if (callTracing) { StringBuilder b = new StringBuilder(); b.append("except>"); printNesting(b); b.append(moduleName()); b.append("::"); b.append(getName()); b.append(": "); String msg = e.getMessage(); b.append(msg == null ? e.getClass().getSimpleName() : msg); eval.getStdOut().println(b.toString()); eval.getStdOut().flush(); } } protected void printEndTrace(IValue result) { if (callTracing) { StringBuilder b = new StringBuilder(); b.append("return>"); printNesting(b); b.append(moduleName()); b.append("::"); b.append(getName()); if (result != null) { b.append(":"); b.append(strval(result)); } eval.getStdOut().println(b); eval.getStdOut().flush(); } } protected void bindTypeParameters(Type actualTypes, Type formals, Environment env) { try { Map<Type, Type> bindings = new HashMap<Type, Type>(); if (!formals.match(actualTypes, bindings)) { throw new MatchFailed(); } env.storeTypeBindings(bindings); } catch (FactTypeUseException e) { throw new UnexpectedType(formals, actualTypes, ast); } } protected IValue[] computeVarArgsActuals(IValue[] actuals, Type formals) { int arity = formals.getArity(); IValue[] newActuals = new IValue[arity]; int i; if (formals.getArity() == actuals.length && actuals[actuals.length - 1].getType().isSubtypeOf(formals.getFieldType(formals.getArity() - 1))) { // variable length argument is provided as a list return actuals; } for (i = 0; i < arity - 1; i++) { newActuals[i] = actuals[i]; } Type lub = TF.voidType(); for (int j = i; j < actuals.length; j++) { lub = lub.lub(actuals[j].getType()); } IListWriter list = vf.listWriter(); list.insertAt(0, actuals, i, actuals.length - arity + 1); newActuals[i] = list.done(); return newActuals; } protected Type computeVarArgsActualTypes(Type actualTypes, Type formals) { if (actualTypes.isSubtypeOf(formals)) { // the argument is already provided as a list return actualTypes; } int arity = formals.getArity(); Type[] types = new Type[arity]; java.lang.String[] labels = new java.lang.String[arity]; int i; for (i = 0; i < arity - 1; i++) { types[i] = actualTypes.getFieldType(i); labels[i] = formals.getFieldName(i); } Type lub = TF.voidType(); for (int j = i; j < actualTypes.getArity(); j++) { lub = lub.lub(actualTypes.getFieldType(j)); } types[i] = TF.listType(lub); labels[i] = formals.getFieldName(i); return TF.tupleType(types, labels); } protected Type computeVarArgsActualTypes(Type[] actualTypes, Type formals) { Type actualTuple = TF.tupleType(actualTypes); if (actualTuple.isSubtypeOf(formals)) { // the argument is already provided as a list return actualTuple; } int arity = formals.getArity(); Type[] types = new Type[arity]; int i; for (i = 0; i < arity - 1; i++) { types[i] = actualTypes[i]; } Type lub = TF.voidType(); for (int j = i; j < actualTypes.length; j++) { lub = lub.lub(actualTypes[j]); } types[i] = TF.listType(lub); return TF.tupleType(types); } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E { return v.visitExternal(this); } @Override public boolean isEqual(IValue other) throws FactTypeUseException { return other == this; } public boolean isIdentical(IValue other) throws FactTypeUseException { return other == this; } @Override public <V extends IValue> LessThanOrEqualResult lessThanOrEqual(Result<V> that) { return super.lessThanOrEqual(that); } @Override public <U extends IValue, V extends IValue> Result<U> add(Result<V> that) { return that.addFunctionNonDeterministic(this); } @Override public OverloadedFunction addFunctionNonDeterministic(AbstractFunction that) { return (new OverloadedFunction(this)).add(that); } @Override public OverloadedFunction addFunctionNonDeterministic(OverloadedFunction that) { return (new OverloadedFunction(this)).join(that); } @Override public ComposedFunctionResult addFunctionNonDeterministic(ComposedFunctionResult that) { return new ComposedFunctionResult.NonDeterministic(that, this, ctx); } @Override public <U extends IValue, V extends IValue> Result<U> compose(Result<V> right) { return right.composeFunction(this); } @Override public ComposedFunctionResult composeFunction(AbstractFunction that) { return new ComposedFunctionResult(that, this, ctx); } @Override public ComposedFunctionResult composeFunction(OverloadedFunction that) { return new ComposedFunctionResult(that, this, ctx); } @Override public ComposedFunctionResult composeFunction(ComposedFunctionResult that) { return new ComposedFunctionResult(that, this, ctx); } @Override public String toString() { return getHeader() + ";"; } public String getHeader(){ String sep = ""; String strFormals = ""; for(Type tp : getFormals()){ strFormals = strFormals + sep + tp; sep = ", "; } String name = getName(); if (name == null) { name = ""; } String kwFormals = ""; return getReturnType() + " " + name + "(" + strFormals + kwFormals + ")"; } public FunctionType getFunctionType() { return (FunctionType) getType(); } /* test if a function is of type T(T) for a given T */ public boolean isTypePreserving() { Type t = getReturnType(); return getFunctionType().equivalent(RascalTypeFactory.getInstance().functionType(t,t, TF.voidType())); } public String getName() { return ""; } public Type getReturnType() { return functionType.getReturnType(); } @Override public IEvaluator<Result<IValue>> getEval() { return eval; } @Override public int hashCode() { return 7 + (ast != null ? ast.hashCode() * 23 : 23); } @Override public boolean equals(Object obj) { if(obj == null) return false; if (obj.getClass() == getClass()) { AbstractFunction other = (AbstractFunction) obj; return other.declarationEnvironment == declarationEnvironment && other.ast.equals(ast); } return false; } public String getResourceScheme() { return null; } public boolean hasResourceScheme() { return false; } public String getResolverScheme() { return null; } public boolean hasResolverScheme() { return false; } @Override public boolean isAnnotatable() { return false; } @Override public IAnnotatable<? extends IValue> asAnnotatable() { throw new IllegalOperationException( "Cannot be viewed as annotatable.", getType()); } @Override public boolean mayHaveKeywordParameters() { return false; // this is not a data value } @Override public IWithKeywordParameters<? extends IValue> asWithKeywordParameters() { // this is not a data value throw new IllegalOperationException( "Facade cannot be viewed as with keyword parameters.", getType()); } protected void bindKeywordArgs(Map<String, IValue> keyArgValues) { Environment env = ctx.getCurrentEnvt(); if (functionType.hasKeywordParameters()) { for (String kwparam : functionType.getKeywordParameterTypes().getFieldNames()){ Type kwType = functionType.getKeywordParameterType(kwparam); String isSetName = makeIsSetKeywordParameterName(kwparam); env.declareVariable(TF.boolType(), isSetName); if (keyArgValues.containsKey(kwparam)){ IValue r = keyArgValues.get(kwparam); if(!r.getType().isSubtypeOf(kwType)) { throw new UnexpectedKeywordArgumentType(kwparam, kwType, r.getType(), ctx.getCurrentAST()); } env.declareVariable(kwType, kwparam); env.storeVariable(kwparam, ResultFactory.makeResult(kwType, r, ctx)); env.storeVariable(isSetName, ResultFactory.makeResult(TF.boolType(), getValueFactory().bool(true), ctx)); } else { env.declareVariable(kwType, kwparam); Expression def = getKeywordParameterDefaults().get(kwparam); Result<IValue> kwResult = def.interpret(eval); env.storeVariable(kwparam, kwResult); env.storeVariable(isSetName, ResultFactory.makeResult(TF.boolType(), getValueFactory().bool(false), ctx)); } } } // TODO: what if the caller provides more arguments then are declared? They are // silently lost here. } public static String makeIsSetKeywordParameterName(String kwparam) { return "$" + kwparam + "isSet"; } }