/******************************************************************************* * 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 * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Paul Klint - Paul.Klint@cwi.nl (CWI) *******************************************************************************/ package org.rascalmpl.interpreter.result; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.List; import java.util.Map; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.FunctionDeclaration; import org.rascalmpl.ast.FunctionModifier; import org.rascalmpl.ast.KeywordFormal; import org.rascalmpl.ast.NullASTVisitor; import org.rascalmpl.ast.Signature; import org.rascalmpl.ast.Tag; import org.rascalmpl.ast.TagString; import org.rascalmpl.ast.Tags; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.asserts.ImplementationError; import org.rascalmpl.interpreter.control_exceptions.MatchFailed; import org.rascalmpl.interpreter.env.Environment; import org.rascalmpl.interpreter.result.util.MemoizationCache; import org.rascalmpl.interpreter.types.FunctionType; import org.rascalmpl.interpreter.utils.Names; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IValue; import org.rascalmpl.value.type.Type; abstract public class NamedFunction extends AbstractFunction { private static final String RESOURCE_TAG = "resource"; private static final String RESOLVER_TAG = "resolver"; protected final String name; protected final boolean isDefault; protected final boolean isTest; protected final boolean isStatic; protected final String resourceScheme; protected final String resolverScheme; protected final Map<String, IValue> tags; private SoftReference<MemoizationCache<Result<IValue>>> memoization; protected final boolean hasMemoization; public NamedFunction(AbstractAST ast, IEvaluator<Result<IValue>> eval, FunctionType functionType, List<KeywordFormal> initializers, String name, boolean varargs, boolean isDefault, boolean isTest, Environment env) { super(ast, eval, functionType, initializers, varargs, env); this.name = name; this.isDefault = isDefault; this.isTest = isTest; this.isStatic = env.isRootScope() && eval.__getRootScope() != env; if (ast instanceof FunctionDeclaration) { tags = parseTags((FunctionDeclaration) ast); this.resourceScheme = getResourceScheme((FunctionDeclaration) ast); this.resolverScheme = getResolverScheme((FunctionDeclaration) ast); this.hasMemoization = checkMemoization((FunctionDeclaration) ast); } else { tags = new HashMap<String, IValue>(); this.resourceScheme = null; this.resolverScheme = null; this.hasMemoization = false; } } protected static boolean hasTestMod(Signature sig) { for (FunctionModifier m : sig.getModifiers().getModifiers()) { if (m.isTest()) { return true; } } return false; } @Override public String getName() { return name; } protected Result<IValue> getMemoizedResult(IValue[] argValues, Map<String, IValue> keyArgValues) { if (hasMemoization()) { MemoizationCache<Result<IValue>> memoizationActual = getMemoizationCache(false); if (memoizationActual == null) { return null; } return memoizationActual.getStoredResult(argValues, keyArgValues); } return null; } private MemoizationCache<Result<IValue>> getMemoizationCache(boolean returnFresh) { MemoizationCache<Result<IValue>> result = null; if (memoization == null) { result = new MemoizationCache<Result<IValue>>(); memoization = new SoftReference<>(result); return returnFresh ? result : null; } result = memoization.get(); if (result == null ) { result = new MemoizationCache<Result<IValue>>(); memoization = new SoftReference<>(result); return returnFresh ? result : null; } return result; } protected Result<IValue> storeMemoizedResult(IValue[] argValues, Map<String, IValue> keyArgValues, Result<IValue> result) { if (hasMemoization()) { getMemoizationCache(true).storeResult(argValues, keyArgValues, result); } return result; } @Override public Result<IValue> call(Type[] argTypes, IValue[] argValues, Map<String, IValue> keyArgValues) throws MatchFailed { Result<IValue> result = getMemoizedResult(argValues, keyArgValues); if (result == null) { result = super.call(argTypes, argValues, keyArgValues); storeMemoizedResult(argValues, keyArgValues, result); } return result; } protected static String getResourceScheme(FunctionDeclaration declaration) { return getScheme(RESOURCE_TAG, declaration); } protected static String getResolverScheme(FunctionDeclaration declaration) { return getScheme(RESOLVER_TAG, declaration); } protected boolean checkMemoization(FunctionDeclaration func) { for (Tag tag : func.getTags().getTags()) { if (Names.name(tag.getName()).equalsIgnoreCase("memo")) { return true; } } return false; } protected int computeIndexedPosition(AbstractAST node) { if (ast instanceof FunctionDeclaration) { FunctionDeclaration ast = (FunctionDeclaration) node; for (Tag tag : ast.getTags().getTags()) { if (Names.name(tag.getName()).equalsIgnoreCase("index") && tag.hasExpression()) { IInteger anno = (IInteger) tag.getExpression().interpret(getEval()).getValue(); int pos = anno.intValue(); if (pos >= 0 && pos < 10) { return pos; } else { throw new ImplementationError("indexing only supported for argument 0 to 9"); } } } } // fall back to first if no annotation is provided return 0; } protected boolean hasMemoization() { return hasMemoization; } protected Map<String, IValue> parseTags(FunctionDeclaration declaration) { final Map<String, IValue> result = new HashMap<String, IValue>(); Tags tags = declaration.getTags(); if (tags.hasTags()) { for (Tag tag : tags.getTags()) { final String key = Names.name(tag.getName()); result.put(key, tag.accept(new NullASTVisitor<IValue>() { @Override public IValue visitTagDefault(Tag.Default x) { String value = ((TagString.Lexical) x.getContents()).getString(); value = value.substring(1, value.length() - 1); return vf.string(value); } @Override public IValue visitTagEmpty(Tag.Empty x) { return vf.string(""); } @Override public IValue visitTagExpression(Tag.Expression x) { return x.getExpression().interpret(eval).getValue(); } })); } } return result; } @Override public IValue getTag(String key) { return tags.get(key); } @Override public boolean hasTag(String key) { return tags.containsKey(key); } private static String getScheme(String schemeTag, FunctionDeclaration declaration) { Tags tags = declaration.getTags(); if (tags.hasTags()) { for (Tag tag : tags.getTags()) { if (Names.name(tag.getName()).equals(schemeTag)) { String contents = ((TagString.Lexical) tag.getContents()).getString(); if (contents.length() > 2 && contents.startsWith("{")) { contents = contents.substring(1, contents.length() - 1); } return contents; } } } return null; } protected static boolean isDefault(FunctionDeclaration func) { List<FunctionModifier> mods = func.getSignature().getModifiers().getModifiers(); for (FunctionModifier m : mods) { if (m.isDefault()) { return true; } } return false; } @Override public boolean isTest() { return isTest; } 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 = ""; if(keywordParameterDefaults != null){ sep = (strFormals.length() > 0) ? ", " : ""; for(String kw : keywordParameterDefaults.keySet()){ kwFormals += sep + functionType.getKeywordParameterType(kw) + " " + kw + "= ..."; sep = ", "; } } return getReturnType() + " " + name + "(" + strFormals + kwFormals + ")"; } @Override public boolean isDefault() { return isDefault; } @Override public String getResourceScheme() { return this.resourceScheme; } @Override public boolean hasResourceScheme() { return this.resourceScheme != null; } @Override public boolean hasResolverScheme() { return this.resolverScheme != null; } @Override public String getResolverScheme() { return this.resolverScheme; } }