Code review comment: I think is better to rewrite array_last(lambda,array)--->element_at(array_filter,-1) 

Code before review:
public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
FunctionName fnName = getFnName();
FunctionParams fnParams = getFnParams();
if (!LAMBDA_FUNCTION_SET.contains(fnName.getFunction().toLowerCase())) {
throw new AnalysisException(
"Function {} maybe not in the LAMBDA_FUNCTION_SET, should check the implement" + fnName
.getFunction());
}
int childSize = this.children.size();
Type[] argTypes = new Type[childSize];
for (int i = 0; i < childSize; ++i) {
this.children.get(i).analyze(analyzer);
argTypes[i] = this.children.get(i).getType();
}
if (fnName.getFunction().equalsIgnoreCase("array_map")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least two params");
}
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
Type lastType = argTypes[childSize - 1];
Expr lastChild = getChild(childSize - 1);
for (int i = childSize - 1; i > 0; --i) {
argTypes[i] = getChild(i - 1).getType();
this.setChild(i, getChild(i - 1));
}
argTypes[0] = lastType;
this.setChild(0, lastChild);
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
Expr lambda = this.children.get(0);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(ArrayType.create(lambda.getChild(0).getType(), true));
} else if (fnName.getFunction().equalsIgnoreCase("array_exists")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 1) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least one param");
}
Type[] newArgTypes = new Type[1];
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr castExpr = arrayMapFunc.castTo(ArrayType.create(Type.BOOLEAN, true));
this.clearChildren();
this.addChild(castExpr);
newArgTypes[0] = castExpr.getType();
}
if (!(getChild(0) instanceof CastExpr)) {
Expr castExpr = getChild(0).castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(0, castExpr);
newArgTypes[0] = castExpr.getType();
}
fn = getBuiltinFunction(fnName.getFunction(), newArgTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if (fnName.getFunction().equalsIgnoreCase("array_filter")) {
if (fnParams.exprs() == null || fnParams.exprs().size() != 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have two params");
}
/*
* array_filter(x->x>3, [1,2,3,6,34,3,11]) --->
* array_filter([1,2,3,6,34,3,11],x->x>3)
* ---> array_filter([1,2,3,6,34,3,11], array_map(x->x>3, [1,2,3,6,34,3,11]))
*/
if (getChild(1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
params.add(getChild(1));
params.add(getChild(0));
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr castExpr = arrayMapFunc.castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(1, castExpr);
argTypes[1] = castExpr.getType();
}
if (!(getChild(1) instanceof CastExpr)) {
Expr castExpr = getChild(1).castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(1, castExpr);
argTypes[1] = castExpr.getType();
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if (fnName.getFunction().equalsIgnoreCase("array_sortby")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least two params");
}
/*
* array_sortby((x,y)->(x+y), [1,-2,3], [10,11,12]) --->
* array_sortby([1,-2,3],[10,11,12], (x,y)->(x+y))
* ---> array_sortby([1,-2,3], array_map((x,y)->(x+y), [1,-2,3], [10,11,12]))
*/
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr firstExpr = getChild(0);
this.clearChildren();
this.addChild(firstExpr);
this.addChild(arrayMapFunc);
argTypes = new Type[2];
argTypes[0] = getChild(0).getType();
argTypes[1] = getChild(1).getType();
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if ( fnName.getFunction().equalsIgnoreCase("array_last") ) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr filterFunc = new LambdaFunctionCallExpr("array_filter", params);
IntLiteral indexParam = new IntLiteral(-1, Type.INT);
params = Lists.newArrayList(filterFunc, indexParam) ;
Type[] argTypesForCallExpr = new Type[2];
argTypesForCallExpr[0] = getChild(0).getType();
argTypesForCallExpr[1] = getChild(1).getType();
FunctionCallExpr callExpr = new FunctionCallExpr("element_at", params) ;
callExpr.analyzeImpl(analyzer);
this.setFnName(arrayCloneExpr) ;
this.children.clear() ;
this.children.add(callExpr);
argTypes = new Type[1] ;
argTypes[0] = getChild(0).getType();
fn = getBuiltinFunction(arrayCloneExpr.getFunction(), argTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn element_at not exists");
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if ( fnName.getFunction().equalsIgnoreCase("array_clone_expr") ) {
fn = getBuiltinFunction(fnName.getFunction(), argTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn element_at not exists");
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
}
LOG.info("fn string: " + fn.signatureString() + ". return type: " + fn.getReturnType());
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
this.type = fn.getReturnType();
}

Line under review: } else if ( fnName.getFunction().equalsIgnoreCase("array_clone_expr") ) {

Code after addressing the review:
public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
FunctionName fnName = getFnName();
FunctionParams fnParams = getFnParams();
if (!LAMBDA_FUNCTION_SET.contains(fnName.getFunction().toLowerCase())) {
throw new AnalysisException(
"Function {} maybe not in the LAMBDA_FUNCTION_SET, should check the implement" + fnName
.getFunction());
}
int childSize = this.children.size();
Type[] argTypes = new Type[childSize];
for (int i = 0; i < childSize; ++i) {
this.children.get(i).analyze(analyzer);
argTypes[i] = this.children.get(i).getType();
}
if (fnName.getFunction().equalsIgnoreCase("array_map")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least two params");
}
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
Type lastType = argTypes[childSize - 1];
Expr lastChild = getChild(childSize - 1);
for (int i = childSize - 1; i > 0; --i) {
argTypes[i] = getChild(i - 1).getType();
this.setChild(i, getChild(i - 1));
}
argTypes[0] = lastType;
this.setChild(0, lastChild);
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
Expr lambda = this.children.get(0);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(ArrayType.create(lambda.getChild(0).getType(), true));
} else if (fnName.getFunction().equalsIgnoreCase("array_exists")
|| fnName.getFunction().equalsIgnoreCase("array_first_index")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 1) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least one param");
}
Type[] newArgTypes = new Type[1];
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr castExpr = arrayMapFunc.castTo(ArrayType.create(Type.BOOLEAN, true));
this.clearChildren();
this.addChild(castExpr);
newArgTypes[0] = castExpr.getType();
}
if (!(getChild(0) instanceof CastExpr)) {
Expr castExpr = getChild(0).castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(0, castExpr);
newArgTypes[0] = castExpr.getType();
}
fn = getBuiltinFunction(fnName.getFunction(), newArgTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
} else if (fnName.getFunction().equalsIgnoreCase("array_filter")) {
if (fnParams.exprs() == null || fnParams.exprs().size() != 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have two params");
}
/*
* array_filter(x->x>3, [1,2,3,6,34,3,11]) --->
* array_filter([1,2,3,6,34,3,11],x->x>3)
* ---> array_filter([1,2,3,6,34,3,11], array_map(x->x>3, [1,2,3,6,34,3,11]))
*/
if (getChild(1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
params.add(getChild(1));
params.add(getChild(0));
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr castExpr = arrayMapFunc.castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(1, castExpr);
argTypes[1] = castExpr.getType();
}
if (!(getChild(1) instanceof CastExpr)) {
Expr castExpr = getChild(1).castTo(ArrayType.create(Type.BOOLEAN, true));
this.setChild(1, castExpr);
argTypes[1] = castExpr.getType();
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if (fnName.getFunction().equalsIgnoreCase("array_sortby")) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 2) {
throw new AnalysisException("The " + fnName.getFunction() + " function must have at least two params");
}
/*
* array_sortby((x,y)->(x+y), [1,-2,3], [10,11,12]) --->
* array_sortby([1,-2,3],[10,11,12], (x,y)->(x+y))
* ---> array_sortby([1,-2,3], array_map((x,y)->(x+y), [1,-2,3], [10,11,12]))
*/
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr arrayMapFunc = new LambdaFunctionCallExpr("array_map",
params);
arrayMapFunc.analyzeImpl(analyzer);
Expr firstExpr = getChild(0);
this.clearChildren();
this.addChild(firstExpr);
this.addChild(arrayMapFunc);
argTypes = new Type[2];
argTypes[0] = getChild(0).getType();
argTypes[1] = getChild(1).getType();
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(getChild(0).getType());
} else if (fnName.getFunction().equalsIgnoreCase("array_last")) {
if (getChild(childSize - 1) instanceof LambdaFunctionExpr) {
List<Expr> params = new ArrayList<>();
for (int i = 0; i <= childSize - 1; ++i) {
params.add(getChild(i));
}
LambdaFunctionCallExpr arrayFilterFunc = new LambdaFunctionCallExpr("array_filter", params);
arrayFilterFunc.analyzeImpl(analyzer);
IntLiteral indexParam = new IntLiteral(-1, Type.INT);
argTypes = new Type[2];
argTypes[0] = getChild(0).getType();
argTypes[1] = indexParam.getType();
this.children.clear();
this.children.add(arrayFilterFunc);
this.children.add(indexParam);
}
fnName = new FunctionName(null, "element_at");
fn = getBuiltinFunction(fnName.getFunction(), argTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
LOG.warn("fn element_at not exists");
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
fn.setReturnType(((ArrayType) argTypes[0]).getItemType());
}
LOG.info("fn string: " + fn.signatureString() + ". return type: " + fn.getReturnType());
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
this.type = fn.getReturnType();
}