Package com.odiago.flumebase.parser

Source Code of com.odiago.flumebase.parser.FnCallExpr

/**
* Licensed to Odiago, Inc. under one or more contributor license
* agreements.  See the NOTICE.txt file distributed with this work for
* additional information regarding copyright ownership.  Odiago, Inc.
* licenses this file to you 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 com.odiago.flumebase.parser;

import java.io.IOException;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.odiago.flumebase.exec.Bucket;
import com.odiago.flumebase.exec.EventWrapper;
import com.odiago.flumebase.exec.FnSymbol;
import com.odiago.flumebase.exec.Symbol;
import com.odiago.flumebase.exec.SymbolTable;

import com.odiago.flumebase.lang.AggregateFunc;
import com.odiago.flumebase.lang.EvalException;
import com.odiago.flumebase.lang.Function;
import com.odiago.flumebase.lang.ListType;
import com.odiago.flumebase.lang.ScalarFunc;
import com.odiago.flumebase.lang.Type;
import com.odiago.flumebase.lang.TypeCheckException;
import com.odiago.flumebase.lang.UniversalConstraintExtractor;
import com.odiago.flumebase.lang.UniversalType;

/**
* Function call expression.
* fn(e1, e2, e3...)
*/
public class FnCallExpr extends Expr {
  private static final Logger LOG = LoggerFactory.getLogger(FnCallExpr.class.getName());

  private String mFunctionName;
  private List<Expr> mArgExprs; // List of expressions to evaluate for arguments.
  private List<Type> mExprTypes; // List of types returned by the arg expressions.

  // The types of the arguments themselves, to coerce results to.
  private Type[] mArgTypes;

  private Type mReturnType; // The type of the value we return.

  /** The symbol as resolved from a symbol table defining the executable function. */
  private FnSymbol mFnSymbol;
  private Function mExecFunc; // The function instance to execute.
  private boolean mAutoPromote; // true if we auto-promote argument return types.
  private Object[] mPartialResults; // reusable array where argument results are stored.

  public FnCallExpr(String fnName) {
    mFunctionName = fnName;
    mArgExprs = new ArrayList<Expr>();
    mExprTypes = new ArrayList<Type>();
    mArgTypes = null;
  }

  public String getFunctionName() {
    return mFunctionName;
  }

  public List<Expr> getArgExpressions() {
    return mArgExprs;
  }

  public void setArgExpressions(List<Expr> exprs) {
    mArgExprs = exprs;
  }

  /**
   * Adds the specified expression to the argument list.
   */
  public void addArg(Expr e) {
    mArgExprs.add(e);
  }

  @Override
  public void format(StringBuilder sb, int depth) {
    pad(sb, depth);
    sb.append("FnCallExpr mFunctionName=");
    sb.append(mFunctionName);
    sb.append("\n");
    pad(sb, depth + 1);
    sb.append("arguments:\n");
    for (Expr e : mArgExprs) {
      e.format(sb, depth + 2);
    }
  }
 
  @Override
  public String toStringOneLine() {
    StringBuilder sb = new StringBuilder();
    sb.append(mFunctionName);
    sb.append("(");
    boolean first = true;
    for (Expr arg : mArgExprs) {
      if (!first) {
        sb.append(", ");
      }

      sb.append(arg.toStringOneLine());
      first = false;
    }

    sb.append(")");
    return sb.toString();
  }

  /** Look up the return type of the function and return it. */
  @Override
  public Type getType(SymbolTable symTab) {
    if (mReturnType == null) {
      try {
        resolveArgTypes(symTab);
      } catch (TypeCheckException tce) {
        LOG.error("TypeCheckException in getType: " + tce);
        return null;
      }
    }

    return getResolvedType();
  }
 
  /**
   * Called by the type checker to compare the types of the function's arguments
   * against the types returned by the argument expressions.
   * Sets mFnSymbol, mExprTypes, mArgTypes, mReturnType.
   * @throws TypeCheckException if an expression type cannot be promoted to
   * an argument type.
   */
  public void resolveArgTypes(SymbolTable symTab) throws TypeCheckException {
    if (null != mReturnType) {
      // Already resolved this expression instance.
      return;
    }

    // Get a handle to the symbol defining the function.
    Symbol fnSymbol = symTab.resolve(mFunctionName);
    if (null == fnSymbol) {
      throw new TypeCheckException("No such function: " + mFunctionName);
    }

    fnSymbol = fnSymbol.resolveAliases();

    if (!(fnSymbol instanceof FnSymbol)) {
      // This symbol isn't a function call?
      throw new TypeCheckException("Symbol " + mFunctionName + " is not a function");
    }

    mFnSymbol = (FnSymbol) fnSymbol; // memoize this for later.

    // Get a list of argument types from the function symbol. These may include
    // universal types we need to concretize.
    List<Type> abstractArgTypes = new ArrayList<Type>(mFnSymbol.getArgumentTypes());

    // Argument types for varargs, after the fixed args.
    List<Type> abstractVarArgTypes = mFnSymbol.getVarArgTypes();

    // Check that arity matches.
    int argsRemaining = mArgExprs.size();
    argsRemaining -= abstractArgTypes.size();
    if (argsRemaining < 0 || (argsRemaining > 0 && abstractVarArgTypes.size() == 0)) {
      // Too few actual args, or too many args (and this is not a varargs fn).
      throw new TypeCheckException("Function " + mFunctionName + " requires "
          + abstractArgTypes.size() + " arguments, but received " + mArgExprs.size());
    }

    if (argsRemaining > 0 && abstractVarArgTypes.size() > 0) {
      // varargs may need to come in pairs, etc. Check that we have a correct multiple
      // of the number of varargs available.
      int argRemainder = argsRemaining % abstractVarArgTypes.size();
      if (0 != argRemainder) {
        throw new TypeCheckException("Function " + mFunctionName + " requires varargs "
            + "in sets of " + abstractVarArgTypes.size() + ", but this call has "
            + argRemainder + " too few.");
      }
    }

    // For each actual vararg, add its type to the abstractArgTypes list.
    if (abstractVarArgTypes.size() > 0) {
      int numVarArgSets = (mArgExprs.size() - abstractArgTypes.size())
          / abstractVarArgTypes.size();
      for (int i = 0; i < numVarArgSets; i++) {
        abstractArgTypes.addAll(abstractVarArgTypes);
      }
    }

    assert mArgExprs.size() == abstractArgTypes.size();

    // Check that each expression type can promote to the argument type.
    for (int i = 0; i < mArgExprs.size(); i++) {
      Type exprType = mArgExprs.get(i).getType(symTab);
      mExprTypes.add(exprType);
      if (!exprType.promotesTo(abstractArgTypes.get(i))) {
        throw new TypeCheckException("Invalid argument to function " + mFunctionName
            + ": argument " + i + " has type " + exprType + "; requires type "
            + abstractArgTypes.get(i));
      }
    }

    mArgTypes = new Type[mArgExprs.size()];

    // Now identify all the UniversalType instances in here, and the
    // actual constraints on each of these.
    Map<UniversalType, List<Type>> unifications = new HashMap<UniversalType, List<Type>>();
    for (int i = 0; i < abstractArgTypes.size(); i++) {
      Type abstractType = abstractArgTypes.get(i);
      Type actualType = mExprTypes.get(i);
      UniversalConstraintExtractor constraintExtractor = new UniversalConstraintExtractor();
      if (constraintExtractor.extractConstraint(abstractType, actualType)) {
        // Found a UniversalType. Make sure it's mapped to a list of actual constraints.
        UniversalType univType = constraintExtractor.getUniversalType();
        List<Type> actualConstraints = unifications.get(univType);
        if (null == actualConstraints) {
          actualConstraints = new ArrayList<Type>();
          unifications.put(univType, actualConstraints);
        }

        // Add the actual constraint of the expression being applied as this argument.
        actualConstraints.add(constraintExtractor.getConstraintType());
      }
    }

    // Perform unifications on all the UniversalType expressions.
    Map<Type, Type> unificationOut = new HashMap<Type, Type>();
    for (Map.Entry<UniversalType, List<Type>> unification : unifications.entrySet()) {
      UniversalType univType = unification.getKey();
      List<Type> actualConstraints = unification.getValue();
      Type out = univType.getRuntimeType(actualConstraints);
      unificationOut.put(univType, out);
    }
   
    // Finally, generate a list of concrete argument types for coercion purposes.
    for (int i = 0; i < abstractArgTypes.size(); i++ ) {
      Type abstractType = abstractArgTypes.get(i);
      mArgTypes[i] = abstractType.replaceUniversal(unificationOut);
      assert mArgTypes[i] != null;
    }

    // Also set mReturnType; if this referenced a UniversalType, use the resolved
    // version. Otherwise, use the version from the function directly.
    Type fnRetType = mFnSymbol.getReturnType();
    try {
      mReturnType = fnRetType.replaceUniversal(unificationOut);
    } catch (TypeCheckException tce) {
      // We can only resolve against our arguments, not our caller's type.
      if (fnRetType instanceof ListType) {
        // If the unresolved typevar is an argument to a list type, we can
        // return this -- it's going to be an empty list, so we can return
        // LIST<NULL>
        // TODO(aaron): This allows to_list() to produce an empty list, but
        // putting this check here feels a bit like a hack to me.
        mReturnType = new ListType(Type.getNullable(Type.TypeName.NULL));
      } else {
        // This fails for being too abstract.
        throw new TypeCheckException("Output type of function " + mFunctionName
            + " is an unresolved UniversalType: " + fnRetType, tce);
      }
    }

    assert null != mReturnType;

    mExecFunc = mFnSymbol.getFuncInstance();
    mAutoPromote = mExecFunc.autoPromoteArguments();
    mPartialResults = new Object[mExprTypes.size()];
  }

  /** @return true if this fn call is an aggregate function. */
  public boolean isAggregate() {
    return mExecFunc instanceof AggregateFunc;
  }

  /** @return true if this fn call is a scalar function. */
  public boolean isScalar() {
    return mExecFunc instanceof ScalarFunc;
  }

  @Override
  public List<TypedField> getRequiredFields(SymbolTable symTab) {
    List<TypedField> out = new ArrayList<TypedField>();
    for (Expr e : mArgExprs) {
      out.addAll(e.getRequiredFields(symTab));
    }

    return out;
  }

  /**
   * Evaluate all the arguments to the function in preparation for
   * calling the function on them. Stores its output in the
   * mPartialResults array.
   */
  private void evaluateArguments(EventWrapper e) throws IOException {
    // Ensure that we have actual and specified types for all actual expression arguments.
    // We may have some extras, if virtual arguments were used to finish type unification.
    assert mArgExprs.size() <= mArgTypes.length;
    assert mArgExprs.size() <= mExprTypes.size();

    // Evaluate arguments left-to-right.
    for (int i = 0; i < mArgExprs.size(); i++) {
      Object result = mArgExprs.get(i).eval(e);
      if (mAutoPromote) {
        mPartialResults[i] = coerce(result, mExprTypes.get(i), mArgTypes[i]);
      } else {
        mPartialResults[i] = result;
      }
    }
  }

  /** {@inheritDoc} */
  @Override
  public Object eval(EventWrapper e) throws IOException {
    assert mExecFunc instanceof ScalarFunc;
    evaluateArguments(e);

    try {
      return ((ScalarFunc) mExecFunc).eval(e, mPartialResults);
    } catch (EvalException ee) {
      throw new IOException(ee);
    }
  }

  /**
   * For a function call representing an aggregation function, call the
   * bucket-insertion method of the AggregationFunc on the (already
   * evaluated) arguments of this function.
   */
  public <T> void insertAggregate(EventWrapper e, Bucket<T> bucket) throws IOException {
    assert mExecFunc instanceof AggregateFunc;
    evaluateArguments(e);
   
    try {
      ((AggregateFunc<T>) mExecFunc).addToBucket(mPartialResults[0], bucket, mReturnType);
    } catch (EvalException ee) {
      throw new IOException(ee);
    }
  }

  /**
   * For a function call representing an aggregation function, call the
   * finishWindow method of the AggregationFunc on the set of buckets
   * comprising the time window we are aggregating over.
   */
  public <T> Object finishWindow(Iterable<Bucket<T>> buckets) throws IOException {
    assert mExecFunc instanceof AggregateFunc;

    try {
      return ((AggregateFunc<T>) mExecFunc).finishWindow(buckets, mReturnType);
    } catch (EvalException ee) {
      throw new IOException(ee);
    }
  }

  @Override
  public Type getResolvedType() {
    return mReturnType;
  }

  @Override
  public boolean isConstant() {
    return false;
  }
}
TOP

Related Classes of com.odiago.flumebase.parser.FnCallExpr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.