/**
* (C) Copyright IBM Corp. 2010, 2015
*
* 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 com.ibm.bi.dml.parser;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.ibm.bi.dml.hops.Hop.FileFormatTypes;
public abstract class Expression
{
public enum Kind {
UnaryOp, BinaryOp, BooleanOp, BuiltinFunctionOp, ParameterizedBuiltinFunctionOp, DataOp, Data, Literal, RelationalOp, ExtBuiltinFunctionOp, FunctionCallOp
};
public enum BinaryOp {
PLUS, MINUS, MULT, DIV, MODULUS, INTDIV, MATMULT, POW, INVALID
};
public enum RelationalOp {
LESSEQUAL, LESS, GREATEREQUAL, GREATER, EQUAL, NOTEQUAL, INVALID
};
public enum BooleanOp {
CONDITIONALAND, CONDITIONALOR, LOGICALAND, LOGICALOR, NOT, INVALID
};
public enum BuiltinFunctionOp {
ABS,
ACOS,
ASIN,
ATAN,
AVG,
CAST_AS_MATRIX,
CAST_AS_SCALAR,
CAST_AS_DOUBLE,
CAST_AS_INT,
CAST_AS_BOOLEAN,
COLMEAN,
COLMAX,
COLMIN,
COLSUM,
COS,
COV,
CUMMAX,
CUMMIN,
CUMPROD,
CUMSUM,
DIAG,
EXP,
INTERQUANTILE,
IQM,
LENGTH,
LOG,
MAX,
MEAN,
MIN,
MOMENT,
NCOL,
NROW,
OUTER,
PPRED,
PROD,
QUANTILE,
RANGE,
ROUND,
ROWINDEXMAX,
ROWMAX,
ROWMEAN,
ROWMIN,
ROWINDEXMIN,
ROWSUM,
SEQ,
SIN,
SQRT,
SUM,
TABLE,
TAN,
TRACE,
TRANS,
QR,
LU,
EIGEN,
SOLVE,
CEIL,
FLOOR,
CBIND, //previously APPEND
RBIND,
MEDIAN,
INVERSE,
SAMPLE
};
public enum ParameterizedBuiltinFunctionOp {
GROUPEDAGG, RMEMPTY, REPLACE, ORDER,
// Distribution Functions
CDF, INVCDF, PNORM, QNORM, PT, QT, PF, QF, PCHISQ, QCHISQ, PEXP, QEXP,
TRANSFORM,
INVALID
};
public enum DataOp {
READ, WRITE, RAND, MATRIX, INVALID
}
public enum FunctCallOp {
INTERNAL, EXTERNAL
};
public enum ExtBuiltinFunctionOp {
EIGEN, CHOLESKY
};
public enum AggOp {
SUM, MIN, MAX, INVALID
};
public enum ReorgOp {
TRANSPOSE, DIAG
};
//public enum DataOp {
// READ, WRITE
//};
public enum DataType {
MATRIX, SCALAR, FRAME, OBJECT, UNKNOWN;
public boolean isMatrix() {
return (this == MATRIX);
}
public boolean isScalar() {
return (this == SCALAR);
}
};
public enum ValueType {
INT, DOUBLE, STRING, BOOLEAN, OBJECT, UNKNOWN
};
public enum FormatType {
TEXT, BINARY, MM, CSV, UNKNOWN
};
protected static final Log LOG = LogFactory.getLog(Expression.class.getName());
public abstract Expression rewriteExpression(String prefix) throws LanguageException;
protected Kind _kind;
protected Identifier[] _outputs;
private static int _tempId;
public Expression() {
_outputs = null;
}
public void setOutput(Identifier output) {
if ( _outputs == null) {
_outputs = new Identifier[1];
}
_outputs[0] = output;
}
public Kind getKind() {
return _kind;
}
public Identifier getOutput() {
if (_outputs != null && _outputs.length > 0)
return _outputs[0];
else
return null;
}
public Identifier[] getOutputs() {
return _outputs;
}
public void validateExpression(HashMap<String, DataIdentifier> ids, HashMap<String, ConstIdentifier> currConstVars, boolean conditional)
throws LanguageException
{
raiseValidateError("Should never be invoked in Baseclass 'Expression'", false);
}
public void validateExpression(MultiAssignmentStatement mas, HashMap<String, DataIdentifier> ids, HashMap<String, ConstIdentifier> currConstVars, boolean conditional)
throws LanguageException
{
raiseValidateError("Should never be invoked in Baseclass 'Expression'", false);
}
public static BinaryOp getBinaryOp(String val) {
if (val.equalsIgnoreCase("+"))
return BinaryOp.PLUS;
else if (val.equalsIgnoreCase("-"))
return BinaryOp.MINUS;
else if (val.equalsIgnoreCase("*"))
return BinaryOp.MULT;
else if (val.equalsIgnoreCase("/"))
return BinaryOp.DIV;
else if (val.equalsIgnoreCase("%%"))
return BinaryOp.MODULUS;
else if (val.equalsIgnoreCase("%/%"))
return BinaryOp.INTDIV;
else if (val.equalsIgnoreCase("^"))
return BinaryOp.POW;
else if (val.equalsIgnoreCase("%*%"))
return BinaryOp.MATMULT;
return BinaryOp.INVALID;
}
public static RelationalOp getRelationalOp(String val) {
if (val == null)
return null;
else if (val.equalsIgnoreCase("<"))
return RelationalOp.LESS;
else if (val.equalsIgnoreCase("<="))
return RelationalOp.LESSEQUAL;
else if (val.equalsIgnoreCase(">"))
return RelationalOp.GREATER;
else if (val.equalsIgnoreCase(">="))
return RelationalOp.GREATEREQUAL;
else if (val.equalsIgnoreCase("=="))
return RelationalOp.EQUAL;
else if (val.equalsIgnoreCase("!="))
return RelationalOp.NOTEQUAL;
return RelationalOp.INVALID;
}
public static BooleanOp getBooleanOp(String val) {
if (val.equalsIgnoreCase("&&"))
return BooleanOp.CONDITIONALAND;
else if (val.equalsIgnoreCase("&"))
return BooleanOp.LOGICALAND;
else if (val.equalsIgnoreCase("||"))
return BooleanOp.CONDITIONALOR;
else if (val.equalsIgnoreCase("|"))
return BooleanOp.LOGICALOR;
else if (val.equalsIgnoreCase("!"))
return BooleanOp.NOT;
return BooleanOp.INVALID;
}
/**
* Convert format types from parser to Hops enum : default is text
*/
public static FileFormatTypes convertFormatType(String fn) {
if (fn == null)
return FileFormatTypes.TEXT;
if (fn.equalsIgnoreCase(DataExpression.FORMAT_TYPE_VALUE_TEXT)) {
return FileFormatTypes.TEXT;
}
if (fn.equalsIgnoreCase(DataExpression.FORMAT_TYPE_VALUE_BINARY)) {
return FileFormatTypes.BINARY;
}
if (fn.equalsIgnoreCase(DataExpression.FORMAT_TYPE_VALUE_MATRIXMARKET)) {
return FileFormatTypes.MM;
}
if (fn.equalsIgnoreCase(DataExpression.FORMAT_TYPE_VALUE_CSV)) {
return FileFormatTypes.CSV;
}
// ToDo : throw parse exception for invalid / unsupported format type
return FileFormatTypes.TEXT;
}
/**
* Construct Hops from parse tree : Create temporary views in expressions
*/
public static String getTempName() {
return "parsertemp" + _tempId++;
}
public abstract VariableSet variablesRead();
public abstract VariableSet variablesUpdated();
public static DataType computeDataType(Expression e1, Expression e2, boolean cast) throws LanguageException {
return computeDataType(e1.getOutput(), e2.getOutput(), cast);
}
public static DataType computeDataType(Identifier id1, Identifier id2, boolean cast) throws LanguageException {
DataType d1 = id1.getDataType();
DataType d2 = id2.getDataType();
if (d1 == d2)
return d1;
if (cast) {
if (d1 == DataType.MATRIX && d2 == DataType.SCALAR)
return DataType.MATRIX;
if (d1 == DataType.SCALAR && d2 == DataType.MATRIX)
return DataType.MATRIX;
}
LOG.error(id1.printErrorLocation() + "Invalid Datatypes for operation");
throw new LanguageException(id1.printErrorLocation() + "Invalid Datatypes for operation",
LanguageException.LanguageErrorCodes.INVALID_PARAMETERS);
}
public static ValueType computeValueType(Expression e1, Expression e2, boolean cast) throws LanguageException {
return computeValueType(e1.getOutput(), e2.getOutput(), cast);
}
public static ValueType computeValueType(Identifier id1, Identifier id2, boolean cast) throws LanguageException {
ValueType v1 = id1.getValueType();
ValueType v2 = id2.getValueType();
if (v1 == v2)
return v1;
if (cast) {
if (v1 == ValueType.DOUBLE && v2 == ValueType.INT)
return ValueType.DOUBLE;
if (v2 == ValueType.DOUBLE && v1 == ValueType.INT)
return ValueType.DOUBLE;
// String value type will override others
// Primary operation involving strings is concatenation (+)
if ( v1 == ValueType.STRING || v2 == ValueType.STRING )
return ValueType.STRING;
}
LOG.error(id1.printErrorLocation() + "Invalid Valuetypes for operation");
throw new LanguageException(id1.printErrorLocation() + "Invalid Valuetypes for operation",
LanguageException.LanguageErrorCodes.INVALID_PARAMETERS);
}
@Override
public boolean equals(Object that)
{
//empty check for robustness
if( that == null || !(that instanceof Expression) )
return false;
Expression thatExpr = (Expression) that;
//approach is to compare string representation of expression
String thisStr = this.toString();
String thatStr = thatExpr.toString();
return thisStr.equalsIgnoreCase(thatStr);
}
@Override
public int hashCode()
{
//use identity hash code
return super.hashCode();
}
///////////////////////////////////////////////////////////////
// validate error handling (consistent for all expressions)
/**
*
* @param msg
* @param conditional
* @throws LanguageException
*/
public void raiseValidateError( String msg, boolean conditional )
throws LanguageException
{
raiseValidateError(msg, conditional, null);
}
/**
*
* @param msg
* @param conditional
* @param code
* @throws LanguageException
*/
public void raiseValidateError( String msg, boolean conditional, String errorCode )
throws LanguageException
{
if( conditional ) //warning if conditional
{
String fullMsg = this.printWarningLocation() + msg;
LOG.warn( fullMsg );
}
else //error and exception if unconditional
{
String fullMsg = this.printErrorLocation() + msg;
LOG.error( fullMsg );
if( errorCode != null )
throw new LanguageException( fullMsg, errorCode );
else
throw new LanguageException( fullMsg );
}
}
/**
* Returns the matrix characteristics for scalar-scalar, scalar-matrix, matrix-scalar, matrix-matrix
* operations. This method is aware of potentially unknowns and matrix-vector (col/row) operations.
*
* Format: rlen, clen, brlen, bclen.
*
* @param left
* @param right
* @return
*/
public static long[] getBinaryMatrixCharacteristics( Expression left, Expression right )
{
long[] ret = new long[]{ -1, -1, -1, -1 };
Identifier idleft = left.getOutput();
Identifier idright = right.getOutput();
if( idleft.getDataType()==DataType.SCALAR && idright.getDataType()==DataType.SCALAR ) {
ret[0] = 0;
ret[1] = 0;
ret[2] = 0;
ret[3] = 0;
}
else if( idleft.getDataType()==DataType.SCALAR && idright.getDataType()==DataType.MATRIX ) {
ret[0] = idright.getDim1();
ret[1] = idright.getDim2();
ret[2] = idright.getRowsInBlock();
ret[3] = idright.getColumnsInBlock();
}
else if( idleft.getDataType()==DataType.MATRIX && idright.getDataType()==DataType.SCALAR ) {
ret[0] = idleft.getDim1();
ret[1] = idleft.getDim2();
ret[2] = idleft.getRowsInBlock();
ret[3] = idleft.getColumnsInBlock();
}
else if( idleft.getDataType()==DataType.MATRIX && idright.getDataType()==DataType.MATRIX ) {
ret[0] = idleft.getDim1();
ret[1] = idleft.getDim2();
ret[2] = idleft.getRowsInBlock();
ret[3] = idleft.getColumnsInBlock();
if( ret[0] < 0 && idright.getDim1() > 1 ) //robustness for row vectors
ret[0] = idright.getDim1();
if( ret[1] < 0 && idright.getDim2() > 1 ) //robustness for row vectors
ret[1] = idright.getDim2();
}
return ret;
}
///////////////////////////////////////////////////////////////////////////
// store exception info + position information for expressions
///////////////////////////////////////////////////////////////////////////
private String _filename;
private int _beginLine, _beginColumn;
private int _endLine, _endColumn;
private ArrayList<String> _parseExceptionList = new ArrayList<String>();
public void setFilename(String passed) { _filename = passed; }
public void setBeginLine(int passed) { _beginLine = passed; }
public void setBeginColumn(int passed) { _beginColumn = passed; }
public void setEndLine(int passed) { _endLine = passed; }
public void setEndColumn(int passed) { _endColumn = passed; }
public void setParseExceptionList(ArrayList<String> passed) { _parseExceptionList = passed;}
public void setAllPositions(String filename, int blp, int bcp, int elp, int ecp){
_filename = filename;
_beginLine = blp;
_beginColumn = bcp;
_endLine = elp;
_endColumn = ecp;
}
public String getFilename() { return _filename; }
public int getBeginLine() { return _beginLine; }
public int getBeginColumn() { return _beginColumn; }
public int getEndLine() { return _endLine; }
public int getEndColumn() { return _endColumn; }
public ArrayList<String> getParseExceptionList() { return _parseExceptionList; }
public String printErrorLocation(){
return "ERROR: " + _filename + " -- line " + _beginLine + ", column " + _beginColumn + " -- ";
}
public String printErrorLocation(int beginLine, int beginColumn){
return "ERROR: " + _filename + " -- line " + beginLine + ", column " + beginColumn + " -- ";
}
public String printWarningLocation(){
return "WARNING: " + _filename + " -- line " + _beginLine + ", column " + _beginColumn + " -- ";
}
public String printInfoLocation(){
return "INFO: " + _filename + " -- line " + _beginLine + ", column " + _beginColumn + " -- ";
}
}