Skip to content

Commit

Permalink
[incubator-kie-issues#1350] Refactor ForExpressionNode to allow dynam…
Browse files Browse the repository at this point in the history
…ically-generated iteration contexts (apache#6041)

* [incubator-kie-issues#1350] Refactor ForExpressionNode to allow dynamically-generated iteration contexts

* [incubator-kie-issues#1350] Fix headers

* [incubator-kie-issues#1350] Minor fix

---------

Co-authored-by: Gabriele-Cardosi <[email protected]>
  • Loading branch information
2 people authored and rgdoliveira committed Aug 26, 2024
1 parent c550c53 commit 0077171
Show file tree
Hide file tree
Showing 9 changed files with 842 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.lang.ast.forexpressioniterators.ForIteration;
import org.kie.dmn.feel.lang.types.BuiltInType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -35,6 +37,7 @@
public class ForExpressionNode
extends BaseNode {

private static final Logger LOG = LoggerFactory.getLogger(ForExpressionNode.class);

private List<IterationContextNode> iterationContexts;
private BaseNode expression;
Expand Down Expand Up @@ -74,16 +77,11 @@ public void setExpression(BaseNode expression) {
public Object evaluate(EvaluationContext ctx) {
try {
ctx.enterFrame();
List results = new ArrayList();
ctx.setValue("partial", results);
ForIteration[] ictx = initializeContexts(ctx, iterationContexts);

while (nextIteration(ctx, ictx)) {
Object result = expression.evaluate(ctx);
results.add(result);
ctx.exitFrame(); // last i-th scope unrolled, see also ForExpressionNode.nextIteration(...)
}
return results;
List<Object> toReturn = new ArrayList<>();
ctx.setValue("partial", toReturn);
populateToReturn(0, ctx, toReturn);
LOG.trace("returning {}", toReturn);
return toReturn;
} catch (EndpointOfRangeNotValidTypeException | EndpointOfRangeOfDifferentTypeException e) {
// ast error already reported
return null;
Expand All @@ -92,61 +90,54 @@ public Object evaluate(EvaluationContext ctx) {
}
}

public static boolean nextIteration(EvaluationContext ctx, ForIteration[] ictx) {
int i = ictx.length - 1;
while (i >= 0 && i < ictx.length) {
if (ictx[i].hasNextValue()) {
ctx.enterFrame(); // on first iter, open last scope frame; or new ones when prev unrolled
setValueIntoContext(ctx, ictx[i]);
i++;
} else {
if (i > 0) {
// end of iter loop for this i-th scope; i-th scope is always unrolled as part of the
// for-loop cycle, so here must unroll the _prev_ scope;
// the if-guard for this code block makes sure NOT to unroll bottom one.
ctx.exitFrame();
}
i--;
private void populateToReturn(int k, EvaluationContext ctx, List<Object> toPopulate) {
LOG.trace("populateToReturn at index {}", k);
if (k > iterationContexts.size() - 1) {
LOG.trace("Index {} out of range, returning", k);
return;
}
IterationContextNode iterationContextNode = iterationContexts.get(k);
ForIteration forIteration = createForIteration(ctx, iterationContextNode);
while (forIteration.hasNextValue()) {
LOG.trace("{} has next value", forIteration);
ctx.enterFrame(); // open loop scope frame, for every iter ctx, except last one as guarded by if clause
// above
setValueIntoContext(ctx, forIteration.getName(), forIteration.getNextValue());
if (k == iterationContexts.size() - 1) {
LOG.trace("i == iterationContexts.size() -1: this is the last iteration context; evaluating {}",
expression);
Object result = expression.evaluate(ctx);
LOG.trace("add {} to toReturn", result);
toPopulate.add(result);
} else if (k < iterationContexts.size() - 1) {
populateToReturn(k + 1, ctx, toPopulate);
}
}
return i >= 0;
ctx.exitFrame();
}

public static void setValueIntoContext(EvaluationContext ctx, ForIteration forIteration) {
ctx.setValue(forIteration.getName(), forIteration.getNextValue());
static void setValueIntoContext(EvaluationContext ctx, String name, Object value) {
ctx.setValue(name, value);
}

@Override
public Type getResultType() {
return BuiltInType.LIST;
}

private ForIteration[] initializeContexts(EvaluationContext ctx, List<IterationContextNode> iterationContexts) {
ForIteration[] ictx = new ForIteration[iterationContexts.size()];
int i = 0;
for (IterationContextNode icn : iterationContexts) {
ictx[i] = createQuantifiedExpressionIterationContext(ctx, icn);
if (i < iterationContexts.size() - 1 && ictx[i].hasNextValue()) {
ctx.enterFrame(); // open loop scope frame, for every iter ctx, except last one as guarded by if clause above
setValueIntoContext(ctx, ictx[i]);
}
i++;
}
return ictx;
}

private ForIteration createQuantifiedExpressionIterationContext(EvaluationContext ctx, IterationContextNode icn) {
ForIteration fi;
String name = icn.evaluateName(ctx);
Object result = icn.evaluate(ctx);
Object rangeEnd = icn.evaluateRangeEnd(ctx);
private ForIteration createForIteration(EvaluationContext ctx, IterationContextNode iterationContextNode) {
LOG.trace("Creating ForIteration for {}", iterationContextNode);
ForIteration toReturn;
String name = iterationContextNode.evaluateName(ctx);
Object result = iterationContextNode.evaluate(ctx);
Object rangeEnd = iterationContextNode.evaluateRangeEnd(ctx);
if (rangeEnd == null) {
Iterable values = result instanceof Iterable ? (Iterable) result : Collections.singletonList(result);
fi = new ForIteration(name, values);
Iterable values = result instanceof Iterable iterable? iterable : Collections.singletonList(result);
toReturn = new ForIteration(name, values);
} else {
fi = getForIteration(ctx, name, result, rangeEnd);
toReturn = getForIteration(ctx, name, result, rangeEnd);
}
return fi;
return toReturn;
}

@Override
Expand All @@ -161,5 +152,4 @@ public ASTNode[] getChildrenNode() {
public <T> T accept(Visitor<T> v) {
return v.visit(this);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@

import org.antlr.v4.runtime.ParserRuleContext;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class IterationContextNode
extends BaseNode {

private static final Logger LOG = LoggerFactory.getLogger(IterationContextNode.class);

private NameDefNode name;
private BaseNode expression;
private BaseNode rangeEndExpr = null;
Expand Down Expand Up @@ -69,15 +73,18 @@ public void setExpression(BaseNode expression) {
}

public String evaluateName(EvaluationContext ctx) {
LOG.trace("evaluateName {}", name);
return this.name.evaluate(ctx);
}

@Override
public Object evaluate(EvaluationContext ctx) {
LOG.trace("evaluate {}", expression);
return expression != null ? expression.evaluate( ctx ) : null;
}

public Object evaluateRangeEnd(EvaluationContext ctx) {
LOG.trace("evaluateRangeEnd {}", rangeEndExpr);
return rangeEndExpr != null ? rangeEndExpr.evaluate(ctx) : null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ public Object getNextValue() {
public String getName() {
return name;
}

@Override
public String toString() {
return "ForIteration{" +
"values=" + values +
", name='" + name + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.FEELDialect;
import org.kie.dmn.feel.util.NumberEvalHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EvaluationContextImpl implements EvaluationContext {

private static final Logger LOG = LoggerFactory.getLogger(EvaluationContextImpl.class);

private final FEELEventListenersManager eventsManager;
private ArrayDeque<ExecutionFrame> stack;
private DMNRuntime dmnRuntime;
Expand Down Expand Up @@ -102,6 +106,7 @@ public Deque<ExecutionFrame> getStack() {

@Override
public void enterFrame() {
LOG.trace("Creating new head element in stack");
push( new ExecutionFrameImpl( peek() /*, symbols, scope*/ ) );
}

Expand All @@ -111,11 +116,13 @@ public void enterFrame(int size) {

@Override
public void exitFrame() {
LOG.trace("Removing head element from stack");
pop();
}

@Override
public void setValue(String name, Object value) {
LOG.trace("put {} -> {} in head stack element", name, value);
peek().setValue(name, NumberEvalHelper.coerceNumber(value ) );
}

Expand Down
Loading

0 comments on commit 0077171

Please sign in to comment.