All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jamesii.mlrules.parser.nodes.FunctionCallNode Maven / Gradle / Ivy

/*
 *   Copyright 2015 University of Rostock
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package org.jamesii.mlrules.parser.nodes;

import org.jamesii.core.math.parsetree.INode;
import org.jamesii.core.math.parsetree.Node;
import org.jamesii.core.math.parsetree.ValueNode;
import org.jamesii.core.math.parsetree.variables.IEnvironment;
import org.jamesii.mlrules.parser.functions.Function;
import org.jamesii.mlrules.parser.functions.FunctionDefinition;
import org.jamesii.mlrules.parser.types.Tuple;
import org.jamesii.mlrules.util.Assignment;
import org.jamesii.mlrules.util.LazyInitialization;
import org.jamesii.mlrules.util.MLEnvironment;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The function call node takes a set of arguments and a function node and
 * calculates the values of the arguments and decides which arguments match
 * which function alternative. Function alternatives are processed top-down.
 * 
 * @author Tobias Helms
 *
 */
public class FunctionCallNode extends Node {

  private static final long serialVersionUID = 1L;

  private Node functionNode;

  private final List arguments;

  private FunctionDefinition matched(Function usedFunction, List values, Map vars) {
    for (FunctionDefinition definition : usedFunction.getDefinitions()) {
      boolean valid = true;
      for (int i = 0; i < definition.getParameter().size(); ++i) {
        if (!definition.getParameter().get(i).match(values.get(i), vars)) {
          valid = false;
          vars.clear();
          break;
        }
      }
      if (valid) {
        return definition;
      }
    }
    throw new IllegalArgumentException(String.format("None of the definitions of %s matched the arguments %s.",
        usedFunction.getName(), values.toString()));
  }

  private Function getFunction(MLEnvironment env) {
    Function function = (Function) ((ValueNode) functionNode.calc(env)).getValue();
    if (function.getDefinitions().isEmpty()) {
      function = (Function) env.getValue(function.getName());
    }
    return function;
  }

  private void computeArguments(List results, Function function, MLEnvironment env) {
    for (Node argument : arguments) {
      Node result = argument.calc(env);
      if (result instanceof ValueNode && ((ValueNode) result).getValue() != null) {
        if (result instanceof TupleNode) {
          TupleNode tuple = (TupleNode) result;
          List subResults = new ArrayList<>();
          for (INode sub : tuple.getChildren()) {
            Node subResult = sub.calc(env);
            if (subResult instanceof ValueNode && ((ValueNode) subResult).getValue() != null) {
              subResults.add(((ValueNode) subResult).getValue());
            } else {
              throw new IllegalArgumentException(
                  String.format("The value of the argument %s could not be computed.", sub));
            }
          }
          results.add(new Tuple(subResults));
        } else {
          results.add(((ValueNode) result).getValue());
        }
      } else {
        throw new IllegalArgumentException(String
            .format("The value of the argument %s of function %s could not be computed.", result, function.getName()));
      }
    }
  }

  private MLEnvironment updateEnv(Function function, Map vars, MLEnvironment env) {
    MLEnvironment newEnv = env.newLevel();
    vars.forEach((k, v) -> newEnv.setValue(k, v));
    return newEnv;
  }

  private  N calculate(FunctionDefinition def, MLEnvironment env) {
    return def.getFunction().calc(env);
  }

  public FunctionCallNode(Node function, List arguments) {
    this.functionNode = function;
    this.arguments = arguments;
  }

  @Override
  public List getChildren() {
    List result = new ArrayList<>();
    result.add(functionNode);
    result.addAll(arguments);
    return result;
  }

  @Override
  public  N calc(IEnvironment cEnv) {
    if (cEnv instanceof MLEnvironment) {
      MLEnvironment env = (MLEnvironment) cEnv;
      Function function = getFunction(env);

      List results = new ArrayList<>();
      computeArguments(results, function, env);
      Map vars = new HashMap<>();
      FunctionDefinition def = matched(function, results, vars);
      MLEnvironment newEnv = updateEnv(function, vars, env);

      for (Assignment assign : def.getAssignments()) {
        assign.getNames().forEach(n -> newEnv.setValue(n, new LazyInitialization(assign, newEnv)));
      }

      N result = calculate(def, newEnv);
      if (!(result instanceof ValueNode) || ((ValueNode) result).getValue() == null) {
        throw new IllegalArgumentException(
            String.format("Could not compute value of function %s.", function.getName()));
      }
      return result;
    }
    throw new IllegalArgumentException(
        String.format("The given environment to calculate the value of the function %s is not an MLEnvironment.",
            functionNode.toString()));
  }

  @Override
  public String toString() {
    ValueNode tmp = (ValueNode) this.functionNode;
    Function f = (Function) tmp.getValue();
    List argumentStrs = new ArrayList<>();
    for (Node n : this.arguments) {
      argumentStrs.add(n.toString());
    }
    return String.format("%s(%s)", f.getName(), String.join(",", argumentStrs));
  }

}