Skip to content

Commit

Permalink
DROOLS-6604 resolving symbols after list accessors (apache#4036)
Browse files Browse the repository at this point in the history
* pausing work

* DMN fix

* comment

* Revert FEELTestRigExample to main

* additional DMN test

* gwt dup
  • Loading branch information
tarilabs committed Nov 29, 2021
1 parent 59ab1a9 commit 36d51b1
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3087,4 +3087,46 @@ public void testDupContextEntryKey() {
assertThat(DMNRuntimeUtil.formatMessages(dmnResult.getMessages()), dmnResult.hasErrors(), is(true));
assertThat(dmnResult.getDecisionResultByName("hardcoded").getEvaluationStatus(), is(DecisionEvaluationStatus.FAILED));
}

@Test
public void testHyphenInPropertyOfCollectionForAccessor() {
final DMNRuntime runtime = DMNRuntimeUtil.createRuntime("testHyphenInPropertyOfCollectionForAccessor.dmn", this.getClass());
runtime.addListener(new DMNRuntimeEventListener() {});
final DMNModel dmnModel = runtime.getModel("http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0", "Drawing 1");
assertThat(dmnModel, notNullValue());
assertThat(DMNRuntimeUtil.formatMessages(dmnModel.getMessages()), dmnModel.hasErrors(), is(false));

final DMNContext context = DMNFactory.newContext();
context.set("input", Arrays.asList(prototype(entry("Primary-Key", "k987"), entry("Value", "v47"))) );

final DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
LOG.debug("{}", dmnResult);
assertThat(DMNRuntimeUtil.formatMessages(dmnResult.getMessages()), dmnResult.hasErrors(), is(false));
/*
* input.Primary-Key is a list projection: take all elements, and Stream+map using the accessor
* input[1].Primary-Key is picking the first element in the list, and using the accessor (only on the first element index=1)
*/
assertThat(dmnResult.getDecisionResultByName("decision").getResult(), is(prototype(entry("correct", Arrays.asList("k987")),entry("incorrect", "k987"))));
}

@Test
public void testHyphenInPropertyOfCollectionForAccessorMultiple() {
final DMNRuntime runtime = DMNRuntimeUtil.createRuntime("testHyphenInPropertyOfCollectionForAccessorMultiple.dmn", this.getClass());
runtime.addListener(new DMNRuntimeEventListener() {});
final DMNModel dmnModel = runtime.getModel("http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0", "Drawing 1");
assertThat(dmnModel, notNullValue());
assertThat(DMNRuntimeUtil.formatMessages(dmnModel.getMessages()), dmnModel.hasErrors(), is(false));

final DMNContext context = DMNFactory.newContext();
context.set("input", Arrays.asList(prototype(entry("Primary-Key", "k1"), entry("Value", 47)), prototype(entry("Primary-Key", "k2"), entry("Value", -9)), prototype(entry("Primary-Key", "k3"), entry("Value", 1))));

final DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
LOG.debug("{}", dmnResult);
assertThat(DMNRuntimeUtil.formatMessages(dmnResult.getMessages()), dmnResult.hasErrors(), is(false));
/*
* input.Primary-Key is a list projection: take all elements, and Stream+map using the accessor
* input[Value>0].Primary-Key is filtering, then projecting
*/
assertThat(dmnResult.getDecisionResultByName("decision").getResult(), is(prototype(entry("correct", Arrays.asList("k1", "k2", "k3")),entry("incorrect", Arrays.asList("k1", "k3")))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<semantic:definitions xmlns:semantic="https://www.omg.org/spec/DMN/20191111/MODEL/" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" xmlns:trisodmn="http://www.trisotech.com/2016/triso/dmn" xmlns:feel="https://www.omg.org/spec/DMN/20191111/FEEL/" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:drools="http://www.drools.org/kie/dmn/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rss="http://purl.org/rss/2.0/" xmlns:openapi="https://openapis.org/omg/extension/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:boxedext="https://www.trisotech.com/spec/DMN/20191111/EXT/" xmlns:i18n="http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0" xmlns="http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0" id="_b5362305-17be-42d8-acec-f2621e3cc0e0" name="Drawing 1" namespace="http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0" exporter="Decision Modeler" exporterVersion="7.10.3" xml:lang="en" triso:logoChoice="Default">
<semantic:extensionElements/>
<semantic:itemDefinition name="myType" label="myType" isCollection="true">
<semantic:itemComponent id="_0a981007-095e-4d01-8867-c5478e9bd45b" name="Primary-Key">
<semantic:typeRef>string</semantic:typeRef>
</semantic:itemComponent>
<semantic:itemComponent id="_21de9187-6259-491b-995a-cacc3b1193fb" name="Value">
<semantic:typeRef>string</semantic:typeRef>
</semantic:itemComponent>
</semantic:itemDefinition>
<semantic:inputData id="_09dcf320-da73-42ef-abf2-67834947b3a1" name="input">
<semantic:variable name="input" id="_77f92815-2182-40a6-a8f5-3d04104cd344" typeRef="myType"/>
</semantic:inputData>
<semantic:decision id="_e2e8187c-3944-4c4c-b9cd-ba3bcd579b75" name="decision">
<semantic:variable name="decision" id="_c7c846c1-a670-4a3a-b4d8-d298110b06ea" typeRef="Any"/>
<semantic:informationRequirement id="_bff2f4b7-0895-4528-a280-69f1d21da8aa">
<semantic:requiredInput href="#_09dcf320-da73-42ef-abf2-67834947b3a1"/>
</semantic:informationRequirement>
<semantic:context id="_95fdea29-70e0-4238-a7a4-8d39885aa121" triso:descriptionVisible="false" typeRef="Any" triso:expressionId="_acbf9f5d-77ad-4c96-b007-2961c56f916b">
<semantic:contextEntry id="_424b7163-0c12-4263-9d32-0ef9b2df5f97">
<semantic:variable name="correct" id="_0289a89f-c514-49b3-ab86-6b53c544364e" typeRef="Any"/>
<semantic:literalExpression id="_3b5d9f87-31e5-4dce-b8c4-fc2ffcef3a37" triso:descriptionVisible="false">
<semantic:text>input.Primary-Key</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry id="_5168ebda-0fef-436d-a7e1-28489dfcb6ab">
<semantic:variable name="incorrect" id="_22072499-2394-424d-942b-a03aee50d90f" typeRef="Any"/>
<semantic:literalExpression id="_47c4040a-7f3a-4458-a383-528f4a176b5e" triso:descriptionVisible="false">
<semantic:text>input[1].Primary-Key</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
</semantic:context>
</semantic:decision>
</semantic:definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<semantic:definitions xmlns:semantic="https://www.omg.org/spec/DMN/20191111/MODEL/" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" xmlns:trisodmn="http://www.trisotech.com/2016/triso/dmn" xmlns:feel="https://www.omg.org/spec/DMN/20191111/FEEL/" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:drools="http://www.drools.org/kie/dmn/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rss="http://purl.org/rss/2.0/" xmlns:openapi="https://openapis.org/omg/extension/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:boxedext="https://www.trisotech.com/spec/DMN/20191111/EXT/" xmlns:i18n="http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0" xmlns="http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0" id="_b5362305-17be-42d8-acec-f2621e3cc0e0" name="Drawing 1" namespace="http://www.trisotech.com/definitions/_b5362305-17be-42d8-acec-f2621e3cc0e0" exporter="Decision Modeler" exporterVersion="7.10.3" xml:lang="en" triso:logoChoice="Default">
<semantic:extensionElements/>
<semantic:itemDefinition name="myType" label="myType" isCollection="true">
<semantic:itemComponent id="_0a981007-095e-4d01-8867-c5478e9bd45b" name="Primary-Key">
<semantic:typeRef>string</semantic:typeRef>
</semantic:itemComponent>
<semantic:itemComponent id="_21de9187-6259-491b-995a-cacc3b1193fb" name="Value">
<semantic:typeRef>number</semantic:typeRef>
</semantic:itemComponent>
</semantic:itemDefinition>
<semantic:inputData id="_09dcf320-da73-42ef-abf2-67834947b3a1" name="input">
<semantic:variable name="input" id="_77f92815-2182-40a6-a8f5-3d04104cd344" typeRef="myType"/>
</semantic:inputData>
<semantic:decision id="_e2e8187c-3944-4c4c-b9cd-ba3bcd579b75" name="decision">
<semantic:variable name="decision" id="_c7c846c1-a670-4a3a-b4d8-d298110b06ea" typeRef="Any"/>
<semantic:informationRequirement id="_bff2f4b7-0895-4528-a280-69f1d21da8aa">
<semantic:requiredInput href="#_09dcf320-da73-42ef-abf2-67834947b3a1"/>
</semantic:informationRequirement>
<semantic:context id="_95fdea29-70e0-4238-a7a4-8d39885aa121" triso:descriptionVisible="false" typeRef="Any" triso:expressionId="_acbf9f5d-77ad-4c96-b007-2961c56f916b">
<semantic:contextEntry id="_424b7163-0c12-4263-9d32-0ef9b2df5f97">
<semantic:variable name="correct" id="_0289a89f-c514-49b3-ab86-6b53c544364e" typeRef="Any"/>
<semantic:literalExpression id="_3b5d9f87-31e5-4dce-b8c4-fc2ffcef3a37" triso:descriptionVisible="false">
<semantic:text>input.Primary-Key</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry id="_5168ebda-0fef-436d-a7e1-28489dfcb6ab">
<semantic:variable name="incorrect" id="_22072499-2394-424d-942b-a03aee50d90f" typeRef="Any"/>
<semantic:literalExpression id="_47c4040a-7f3a-4458-a383-528f4a176b5e" triso:descriptionVisible="false">
<semantic:text>input[Value>0].Primary-Key</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
</semantic:context>
</semantic:decision>
</semantic:definitions>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Stack;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
Expand All @@ -41,6 +42,8 @@
import org.kie.dmn.feel.lang.types.ScopeImpl;
import org.kie.dmn.feel.lang.types.SymbolTable;
import org.kie.dmn.feel.lang.types.VariableSymbol;
import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser.FilterPathExpressionContext;
import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser.QualifiedNameContext;
import org.kie.dmn.feel.runtime.events.UnknownVariableErrorEvent;
import org.kie.dmn.feel.util.EvalHelper;

Expand Down Expand Up @@ -280,6 +283,46 @@ public static String getOriginalText(final ParserRuleContext ctx) {
Interval interval = new Interval(a, b);
return ctx.getStart().getInputStream().getText(interval);
}

/**
* a specific heuristic for scope retrieval for filterPathExpression
*/
public int fphStart(ParserRuleContext ctx, Parser parser) {
if (!(ctx instanceof FEEL_1_1Parser.FilterPathExpressionContext)) { // I expect in `var[1].name` for this param ctx=`var[1]` to be a filterPathExpression
return 0;
}
FilterPathExpressionContext ctx0 = (FEEL_1_1Parser.FilterPathExpressionContext) ctx;
boolean ctxSquared = ctx0.filter != null && ctx0.n0 != null;
if (!ctxSquared) { // I expect `var[1]` to be in the squared form `...[...]`
return 0;
}
ParserRuleContext ctx1 = ctx0.n0.getRuleContext(FEEL_1_1Parser.NonSignedUnaryExpressionContext.class, 0);
if (ctx1 == null) {
return 0;
}
ParserRuleContext ctx2 = ctx1.getRuleContext(FEEL_1_1Parser.UenpmPrimaryContext.class, 0);
if (ctx2 == null) {
return 0;
}
ParserRuleContext ctx3 = ctx2.getRuleContext(FEEL_1_1Parser.PrimaryNameContext.class, 0);
if (ctx3 == null) {
return 0;
}
QualifiedNameContext ctx4 = ctx3.getRuleContext(FEEL_1_1Parser.QualifiedNameContext.class, 0);
if (ctx4 == null) {
return 0;
} // I expect in this param ctx=`var[1]` for `var` to be a qualifiedName
for (String n : ctx4.qns) {
recoverScope(n);
}
return ctx4.qns.size();
}

public void fphEnd(int times) {
for (int i = 0; i < times; i++) {
dismissScope();
}
}

public static List<Token> getAllTokens(final ParseTree ctx,
final List<Token> tokens) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,12 @@ powerExpression
;

filterPathExpression
@init {
int count = 0;
}
: unaryExpression
| filterPathExpression LBRACK {helper.enableDynamicResolution();} filter=expression {helper.disableDynamicResolution();} RBRACK
| filterPathExpression DOT {helper.enableDynamicResolution();} qualifiedName {helper.disableDynamicResolution();}
| n0=filterPathExpression LBRACK {helper.enableDynamicResolution();} filter=expression {helper.disableDynamicResolution();} RBRACK
| n1=filterPathExpression DOT {count = helper.fphStart($n1.ctx, this); helper.enableDynamicResolution();} qualifiedName {helper.disableDynamicResolution(); helper.fphEnd(count);}
;

unaryExpression
Expand Down Expand Up @@ -389,12 +392,14 @@ interval

// #20
qualifiedName
locals [ java.util.List<String> qns ]
@init {
String name = null;
int count = 0;
java.util.List<String> qn = new java.util.ArrayList<String>();
}
@after {
$qns = qn;
for( int i = 0; i < count; i++ )
helper.dismissScope();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Stack;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
Expand All @@ -41,6 +42,8 @@
import org.kie.dmn.feel.lang.types.ScopeImpl;
import org.kie.dmn.feel.lang.types.SymbolTable;
import org.kie.dmn.feel.lang.types.VariableSymbol;
import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser.FilterPathExpressionContext;
import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser.QualifiedNameContext;
import org.kie.dmn.feel.runtime.events.UnknownVariableErrorEvent;
import org.kie.dmn.feel.util.EvalHelper;
import org.slf4j.Logger;
Expand Down Expand Up @@ -291,6 +294,46 @@ public static String getOriginalText(ParserRuleContext ctx) {
Interval interval = new Interval( a, b );
return ctx.getStart().getInputStream().getText( interval );
}

/**
* a specific heuristic for scope retrieval for filterPathExpression
*/
public int fphStart(ParserRuleContext ctx, Parser parser) {
if (!(ctx instanceof FEEL_1_1Parser.FilterPathExpressionContext)) { // I expect in `var[1].name` for this param ctx=`var[1]` to be a filterPathExpression
return 0;
}
FilterPathExpressionContext ctx0 = (FEEL_1_1Parser.FilterPathExpressionContext) ctx;
boolean ctxSquared = ctx0.filter != null && ctx0.n0 != null;
if (!ctxSquared) { // I expect `var[1]` to be in the squared form `...[...]`
return 0;
}
ParserRuleContext ctx1 = ctx0.n0.getRuleContext(FEEL_1_1Parser.NonSignedUnaryExpressionContext.class, 0);
if (ctx1 == null) {
return 0;
}
ParserRuleContext ctx2 = ctx1.getRuleContext(FEEL_1_1Parser.UenpmPrimaryContext.class, 0);
if (ctx2 == null) {
return 0;
}
ParserRuleContext ctx3 = ctx2.getRuleContext(FEEL_1_1Parser.PrimaryNameContext.class, 0);
if (ctx3 == null) {
return 0;
}
QualifiedNameContext ctx4 = ctx3.getRuleContext(FEEL_1_1Parser.QualifiedNameContext.class, 0);
if (ctx4 == null) {
return 0;
} // I expect in this param ctx=`var[1]` for `var` to be a qualifiedName
for (String n : ctx4.qns) {
recoverScope(n);
}
return ctx4.qns.size();
}

public void fphEnd(int times) {
for (int i = 0; i < times; i++) {
dismissScope();
}
}

public static List<Token> getAllTokens(
ParseTree ctx,
Expand Down
Loading

0 comments on commit 36d51b1

Please sign in to comment.