4141import dev .cel .common .CelSource ;
4242import dev .cel .common .ast .CelConstant ;
4343import dev .cel .common .ast .CelExpr ;
44+ import dev .cel .common .ast .CelExpr .CelComprehension ;
4445import dev .cel .common .exceptions .CelDivideByZeroException ;
4546import dev .cel .common .exceptions .CelInvalidArgumentException ;
4647import dev .cel .common .internal .CelDescriptorPool ;
7071import dev .cel .expr .conformance .proto3 .TestAllTypes ;
7172import dev .cel .expr .conformance .proto3 .TestAllTypes .NestedMessage ;
7273import dev .cel .extensions .CelExtensions ;
74+ import dev .cel .parser .CelMacro ;
7375import dev .cel .parser .CelStandardMacro ;
7476import dev .cel .runtime .CelAsyncEvaluationOptions ;
7577import dev .cel .runtime .CelAttribute ;
@@ -135,9 +137,47 @@ public final class ProgramPlannerTest {
135137 CelAsyncEvaluationOptions .defaultOptions (),
136138 /* asyncExecutor= */ null );
137139
140+ // Raw comprehensions: compre(iterVar, accuVar, iterRange, accuInit, loopCondition, loopStep,
141+ // result), its two-variable form compre2(iterVar, iterVar2, accuVar, ...), and insert(map, ...)
142+ // for cel.@mapInsert, which cannot be written in CEL source.
143+ private static final ImmutableList <CelMacro > COMPREHENSION_TEST_MACROS =
144+ ImmutableList .of (
145+ CelMacro .newGlobalMacro (
146+ "compre" ,
147+ 7 ,
148+ (factory , unused , args ) ->
149+ Optional .of (
150+ factory .fold (
151+ args .get (0 ).ident ().name (),
152+ args .get (2 ),
153+ args .get (1 ).ident ().name (),
154+ args .get (3 ),
155+ args .get (4 ),
156+ args .get (5 ),
157+ args .get (6 )))),
158+ CelMacro .newGlobalMacro (
159+ "compre2" ,
160+ 8 ,
161+ (factory , unused , args ) ->
162+ Optional .of (
163+ factory .fold (
164+ args .get (0 ).ident ().name (),
165+ args .get (1 ).ident ().name (),
166+ args .get (3 ),
167+ args .get (2 ).ident ().name (),
168+ args .get (4 ),
169+ args .get (5 ),
170+ args .get (6 ),
171+ args .get (7 )))),
172+ CelMacro .newGlobalVarArgMacro (
173+ "insert" ,
174+ (factory , unused , args ) ->
175+ Optional .of (factory .newGlobalCall ("cel.@mapInsert" , args ))));
176+
138177 private static final CelCompiler CEL_COMPILER =
139178 CelCompilerFactory .standardCelCompilerBuilder ()
140179 .setStandardMacros (CelStandardMacro .STANDARD_MACROS )
180+ .addMacros (COMPREHENSION_TEST_MACROS )
141181 .addFunctionDeclarations (
142182 newFunctionDeclaration (
143183 "late_bound_func" ,
@@ -191,6 +231,7 @@ private static DefaultDispatcher newDispatcher() {
191231 StandardFunction .DIVIDE ,
192232 StandardFunction .EQUALS ,
193233 StandardFunction .NOT_STRICTLY_FALSE ,
234+ StandardFunction .SIZE ,
194235 StandardFunction .DYN )
195236 .build ();
196237 addBindingsToDispatcher (
@@ -1014,6 +1055,13 @@ public void plan_select_badPresenceTest_throws() throws Exception {
10141055 @ TestParameters (
10151056 "{expression: 'cel.bind(x, [1, 2], [x + [3], x + [4]]) == [[1, 2, 3], [1, 2, 4]]'}" )
10161057 @ TestParameters ("{expression: 'cel.bind(m, {\" a\" : 1}, [m]) == [{\" a\" : 1}]'}" )
1058+ @ TestParameters (
1059+ "{expression: 'compre(i, acc, [1, 2], [], true, acc + [size(acc + [0])], acc) == [1, 2]'}" )
1060+ @ TestParameters (
1061+ "{expression: 'compre(i, acc, [1, 2], [10], true, acc + [acc[0]], acc) == [10, 10, 10]'}" )
1062+ @ TestParameters (
1063+ "{expression: 'compre(i, acc, [1, 2, 3], [], size(acc + [i]) < 3, acc + [i], acc)"
1064+ + " == [1, 2]'}" )
10171065 public void plan_comprehension_lists (String expression ) throws Exception {
10181066 CelAbstractSyntaxTree ast = compile (expression );
10191067 Program program = PLANNER .plan (ast );
@@ -1038,6 +1086,48 @@ public void plan_comprehension_maps(String expression) throws Exception {
10381086 assertThat (result ).isTrue ();
10391087 }
10401088
1089+ @ Test
1090+ @ TestParameters ("{expression: '[1].map(x, x)', safe: true}" )
1091+ @ TestParameters ("{expression: '[1].filter(x, x > 0)', safe: true}" )
1092+ @ TestParameters ("{expression: '{1: 2}.transformMap(k, v, v)', safe: true}" )
1093+ @ TestParameters ("{expression: '[1].exists(x, x > 0)', safe: false}" )
1094+ @ TestParameters ("{expression: 'compre(i, acc, [1], [], true, acc + [i], acc)', safe: false}" )
1095+ public void isMutableAccuSafe (String expression , boolean safe ) throws Exception {
1096+ CelComprehension comprehension =
1097+ CEL_COMPILER .parse (expression ).getAst ().getExpr ().comprehension ();
1098+
1099+ assertThat (ProgramPlanner .isMutableAccuSafe (comprehension )).isEqualTo (safe );
1100+ }
1101+
1102+ @ Test
1103+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, a + [i], a)', expected: true}" )
1104+ @ TestParameters (
1105+ "{expression: 'compre(i, a, [1], [], true, i > 0 ? a + [i] : a, a)', expected: true}" )
1106+ @ TestParameters (
1107+ "{expression: 'compre2(k, v, a, {1: 2}, {}, true, insert(a, k, v), a)', expected: true}" )
1108+ @ TestParameters ("{expression: 'compre(a, a, [1], [], true, a + [a], a)', expected: false}" )
1109+ @ TestParameters (
1110+ "{expression: 'compre2(k, a, a, {1: 2}, {}, true, insert(a, k, a), a)', expected: false}" )
1111+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, a + [i], [a])', expected: false}" )
1112+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, a, a)', expected: false}" )
1113+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, i + [i], a)', expected: false}" )
1114+ @ TestParameters ("{expression: 'compre(i, a, [1], [0], true, a + [i], a)', expected: false}" )
1115+ @ TestParameters ("{expression: 'compre(i, a, [1], dyn([]), true, a + [i], a)', expected: false}" )
1116+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, a + a, a)', expected: false}" )
1117+ @ TestParameters ("{expression: 'compre(i, a, [1], [], true, a + [i, i], a)', expected: false}" )
1118+ @ TestParameters (
1119+ "{expression: 'compre(i, a, [1], {}, true, insert(a, i, i), a)', expected: false}" )
1120+ @ TestParameters (
1121+ "{expression: 'compre2(k, v, a, {1: 2}, [], true, insert(a, k, v), a)', expected: false}" )
1122+ @ TestParameters (
1123+ "{expression: 'compre2(k, v, a, {1: 2}, {}, true, foo(a, k, v), a)', expected: false}" )
1124+ public void isStandardMacroShape (String expression , boolean expected ) throws Exception {
1125+ CelComprehension comprehension =
1126+ CEL_COMPILER .parse (expression ).getAst ().getExpr ().comprehension ();
1127+
1128+ assertThat (ProgramPlanner .isStandardMacroShape (comprehension )).isEqualTo (expected );
1129+ }
1130+
10411131 @ Test
10421132 @ TestParameters ("{expression: '[1, 2, 3, 4, 5, 6].map(x, x)'}" )
10431133 @ TestParameters ("{expression: '[1, 2, 3].map(x, [1, 2].map(y, x + y))'}" )
0 commit comments