/*******************************************************************************
* Copyright (c) 2009-2015 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
*/
package org.rascalmpl.parser.uptr.action;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.control_exceptions.Failure;
import org.rascalmpl.interpreter.control_exceptions.Filtered;
import org.rascalmpl.interpreter.control_exceptions.MatchFailed;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.result.ICallableValue;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.staticErrors.ArgumentMismatch;
import org.rascalmpl.interpreter.types.NonTerminalType;
import org.rascalmpl.interpreter.types.RascalTypeFactory;
import org.rascalmpl.parser.gtd.result.action.IActionExecutor;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.SymbolAdapter;
import org.rascalmpl.values.uptr.TreeAdapter;
/**
* This is the way of executing actions for Rascal syntax definitions. Each function
* that returns a non-terminal type and is named one of the constructor names of one
* of the alternatives and has the same argument types as the syntax production will
* be called when a certain production is constructed, e.g:
*
* Stat if(Exp e, Stat thenPart, Stat elsePart);
*
* Also, on ambiguity clusters functions named 'amb' are called with a set[&T] argument
* for the alternatives, e.g. Stat amb(set[Stat] alternatives);
*
* Also, on entering a production the 'enter' function is called with a reifed type argument
* for the production type that is entered: void enter(type[Stat.If] prod);
*
* Also on exiting a production the 'exit' function is called, similarly:
* void exit(type[Stat.If] prod);
*
* Note that RascalFunctionActionExecutors use functions visible from the call site of the parse
* function.
*/
public class RascalFunctionActionExecutor implements IActionExecutor<ITree> {
private final static TypeFactory TF = TypeFactory.getInstance();
private final IEvaluatorContext ctx;
public RascalFunctionActionExecutor(IEvaluatorContext ctx) {
this.ctx = ctx;
}
public void completed(Object environment, boolean filtered) {
}
public Object createRootEnvironment() {
return ctx.getCurrentEnvt();
}
public Object enteringListNode(Object production, int index, Object environment) {
return environment;
}
public Object enteringListProduction(Object production, Object env) {
return env;
}
public Object enteringNode(Object production, int index, Object environment) {
return environment;
}
public Object enteringProduction(Object production, Object env) {
return env;
}
public void exitedListProduction(Object production, boolean filtered, Object environment) {
}
public void exitedProduction(Object production, boolean filtered, Object environment) {
}
public ITree filterAmbiguity(ITree ambCluster, Object environment) {
ISet alts = (ISet) ambCluster.get("alternatives");
if (alts.size() == 0) {
return null;
}
Environment env = (Environment) environment;
Result<IValue> var = env.getFrameVariable("amb");
if (var != null && var instanceof ICallableValue) {
Type type = RascalTypeFactory.getInstance().nonTerminalType(ambCluster);
ICallableValue func = (ICallableValue) var;
try {
Result<IValue> result = func.call(
new Type[] {TF.setType(type)}, new IValue[] {alts}, null
);
if (result.getType().isBottom()) {
return ambCluster;
}
ITree r = (ITree) result.getValue();
if (TreeAdapter.isAmb(r)) {
ISet returnedAlts = TreeAdapter.getAlternatives(r);
if (returnedAlts.size() == 1) {
return (ITree) returnedAlts.iterator().next();
}
else if (returnedAlts.size() == 0) {
return null;
}
else {
return r;
}
}
return (ITree) result.getValue();
}
catch (ArgumentMismatch e) {
return ambCluster;
}
}
return ambCluster;
}
@Override
public ITree filterCycle(ITree cycle, Object environment) {
return cycle;
}
@Override
public ITree filterListAmbiguity(ITree ambCluster, Object environment) {
return filterAmbiguity(ambCluster, environment);
}
@Override
public ITree filterListCycle(ITree cycle, Object environment) {
return cycle;
}
@Override
public ITree filterListProduction(ITree tree, Object environment) {
return tree;
}
@Override
public ITree filterProduction(ITree tree,
Object environment) {
String cons = TreeAdapter.getConstructorName(tree);
if (cons != null) {
Environment env = (Environment) environment;
Result<IValue> var = env.getFrameVariable(cons);
if (var != null && var instanceof ICallableValue) {
ICallableValue function = (ICallableValue) var;
try{
Result<IValue> result = null;
if(TreeAdapter.isContextFree(tree)){
// For context free trees, try it without layout and literal arguments first.
result = call(function, TreeAdapter.getASTArgs(tree));
}
if (result == null){
result = call(function, TreeAdapter.getArgs(tree));
}
if (result == null) {
return tree;
}
if (result.getType().isBottom()) {
return tree;
}
if (!(result.getType() instanceof NonTerminalType
&& SymbolAdapter.isEqual(((NonTerminalType) result.getType()).getSymbol(), TreeAdapter.getType(tree)))) {
// do not call the function if it does not return the right type
return tree;
}
return (ITree) result.getValue();
} catch(Filtered f){
return null;
}
}
}
return tree;
}
private static Result<IValue> call(ICallableValue function, IList args) {
try{
int nrOfArgs = args.length();
Type[] types = new Type[nrOfArgs];
IValue[] actuals = new IValue[nrOfArgs];
for(int i = nrOfArgs - 1; i >= 0; --i){
IValue arg = args.get(i);
types[i] = RascalTypeFactory.getInstance().nonTerminalType((IConstructor) arg);
actuals[i] = arg;
}
return function.call(types, actuals, null);
}catch(MatchFailed e){
return null;
}catch(Failure f){
return null;
}
}
public boolean isImpure(Object rhs) {
return true;
}
}