/**
* (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.runtime.controlprogram;
import java.util.ArrayList;
import com.ibm.bi.dml.api.DMLScript;
import com.ibm.bi.dml.hops.Hop;
import com.ibm.bi.dml.parser.ForStatementBlock;
import com.ibm.bi.dml.parser.Expression.ValueType;
import com.ibm.bi.dml.runtime.DMLRuntimeException;
import com.ibm.bi.dml.runtime.DMLScriptException;
import com.ibm.bi.dml.runtime.DMLUnsupportedOperationException;
import com.ibm.bi.dml.runtime.controlprogram.context.ExecutionContext;
import com.ibm.bi.dml.runtime.instructions.Instruction;
import com.ibm.bi.dml.runtime.instructions.cp.Data;
import com.ibm.bi.dml.runtime.instructions.cp.IntObject;
import com.ibm.bi.dml.runtime.instructions.cp.ScalarObject;
import com.ibm.bi.dml.runtime.util.UtilFunctions;
import com.ibm.bi.dml.yarn.DMLAppMasterUtils;
public class ForProgramBlock extends ProgramBlock
{
protected ArrayList<Instruction> _fromInstructions;
protected ArrayList<Instruction> _toInstructions;
protected ArrayList<Instruction> _incrementInstructions;
protected ArrayList <Instruction> _exitInstructions ;
protected ArrayList<ProgramBlock> _childBlocks;
protected String[] _iterablePredicateVars; //from,to,where constants/internal vars not captured via instructions
public void printMe()
{
LOG.debug("***** current for block predicate inst: *****");
LOG.debug("FROM:");
for (Instruction cp : _fromInstructions){
cp.printMe();
}
LOG.debug("TO:");
for (Instruction cp : _toInstructions){
cp.printMe();
}
LOG.debug("INCREMENT:");
for (Instruction cp : _incrementInstructions){
cp.printMe();
}
LOG.debug("***** children block inst: *****");
for (ProgramBlock pb : this._childBlocks){
pb.printMe();
}
LOG.debug("***** current block inst exit: *****");
for (Instruction i : this._exitInstructions) {
i.printMe();
}
}
public ForProgramBlock(Program prog, String[] iterPredVars) throws DMLRuntimeException
{
super(prog);
_exitInstructions = new ArrayList<Instruction>();
_childBlocks = new ArrayList<ProgramBlock>();
_iterablePredicateVars = iterPredVars;
}
public ArrayList<Instruction> getFromInstructions()
{
return _fromInstructions;
}
public void setFromInstructions(ArrayList<Instruction> instructions)
{
_fromInstructions = instructions;
}
public ArrayList<Instruction> getToInstructions()
{
return _toInstructions;
}
public void setToInstructions(ArrayList<Instruction> instructions)
{
_toInstructions = instructions;
}
public ArrayList<Instruction> getIncrementInstructions()
{
return _incrementInstructions;
}
public void setIncrementInstructions(ArrayList<Instruction> instructions)
{
_incrementInstructions = instructions;
}
public void addExitInstruction(Instruction inst){
_exitInstructions.add(inst);
}
public ArrayList<Instruction> getExitInstructions(){
return _exitInstructions;
}
public void setExitInstructions(ArrayList<Instruction> inst){
_exitInstructions = inst;
}
public void addProgramBlock(ProgramBlock childBlock) {
_childBlocks.add(childBlock);
}
public ArrayList<ProgramBlock> getChildBlocks()
{
return _childBlocks;
}
public void setChildBlocks(ArrayList<ProgramBlock> pbs)
{
_childBlocks = pbs;
}
public String[] getIterablePredicateVars()
{
return _iterablePredicateVars;
}
public void setIterablePredicateVars(String[] iterPredVars)
{
_iterablePredicateVars = iterPredVars;
}
@Override
public void execute(ExecutionContext ec)
throws DMLRuntimeException, DMLUnsupportedOperationException
{
// add the iterable predicate variable to the variable set
String iterVarName = _iterablePredicateVars[0];
// evaluate from, to, incr only once (assumption: known at for entry)
IntObject from = executePredicateInstructions( 1, _fromInstructions, ec );
IntObject to = executePredicateInstructions( 2, _toInstructions, ec );
IntObject incr = executePredicateInstructions( 3, _incrementInstructions, ec );
if ( incr.getLongValue() <= 0 ) //would produce infinite loop
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Expression for increment of variable '" + iterVarName + "' must evaluate to a positive value.");
// initialize iter var to from value
IntObject iterVar = new IntObject(iterVarName, from.getLongValue() );
// execute for loop
try
{
// run for loop body as long as predicate is true
// (for supporting dynamic TO, move expression execution to end of while loop)
while( iterVar.getLongValue() <= to.getLongValue() )
{
ec.setVariable(iterVarName, iterVar);
//for all child blocks
for (int i=0 ; i < this._childBlocks.size() ; i++) {
ec.updateDebugState( i );
_childBlocks.get(i).execute(ec);
}
// update the iterable predicate variable
if(ec.getVariable(iterVarName) == null || !(ec.getVariable(iterVarName) instanceof IntObject))
throw new DMLRuntimeException("Iterable predicate variable " + iterVarName + " must remain of type scalar int.");
//increment of iterVar (changes in loop body get discarded)
iterVar = new IntObject( iterVarName, iterVar.getLongValue()+incr.getLongValue() );
}
}
catch (DMLScriptException e)
{
throw e;
}
catch (Exception e)
{
throw new DMLRuntimeException(printBlockErrorLocation() + "Error evaluating for program block", e);
}
//execute exit instructions
try {
executeInstructions(_exitInstructions, ec);
}
catch (Exception e){
throw new DMLRuntimeException(printBlockErrorLocation() + "Error evaluating for exit instructions", e);
}
}
/**
*
* @param pos
* @param instructions
* @param ec
* @return
* @throws DMLRuntimeException
*/
protected IntObject executePredicateInstructions( int pos, ArrayList<Instruction> instructions, ExecutionContext ec )
throws DMLRuntimeException
{
ScalarObject tmp = null;
IntObject ret = null;
try
{
if( _iterablePredicateVars[pos] != null )
{
//probe for scalar variables
Data ldat = ec.getVariable( _iterablePredicateVars[pos] );
if( ldat != null && ldat instanceof ScalarObject )
tmp = (ScalarObject)ldat;
else //handle literals
tmp = new IntObject( UtilFunctions.parseToLong(_iterablePredicateVars[pos]) );
}
else
{
if( _sb!=null )
{
if( DMLScript.isActiveAM() ) //set program block specific remote memory
DMLAppMasterUtils.setupProgramBlockRemoteMaxMemory(this);
ForStatementBlock fsb = (ForStatementBlock)_sb;
Hop predHops = null;
boolean recompile = false;
if (pos == 1){
predHops = fsb.getFromHops();
recompile = fsb.requiresFromRecompilation();
}
else if (pos == 2) {
predHops = fsb.getToHops();
recompile = fsb.requiresToRecompilation();
}
else if (pos == 3){
predHops = fsb.getIncrementHops();
recompile = fsb.requiresIncrementRecompilation();
}
tmp = (IntObject) executePredicate(instructions, predHops, recompile, ValueType.INT, ec);
}
else
tmp = (IntObject) executePredicate(instructions, null, false, ValueType.INT, ec);
}
}
catch(Exception ex)
{
String predNameStr = null;
if (pos == 1) predNameStr = "from";
else if (pos == 2) predNameStr = "to";
else if (pos == 3) predNameStr = "increment";
throw new DMLRuntimeException(this.printBlockErrorLocation() +"Error evaluating '" + predNameStr + "' predicate", ex);
}
//final check of resulting int object (guaranteed to be non-null, see executePredicate)
if( tmp instanceof IntObject )
ret = (IntObject)tmp;
else //downcast to int if necessary
ret = new IntObject(tmp.getName(),tmp.getLongValue());
return ret;
}
public String printBlockErrorLocation(){
return "ERROR: Runtime error in for program block generated from for statement block between lines " + _beginLine + " and " + _endLine + " -- ";
}
}