Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading