Skip to content

Commit b2ff8d4

Browse files
authored
Specialize higher-arity overload evaluation (#986)
Add fixed evaluators and operations for three-, four-, and five-argument overloads. Retain generic dispatch, error and unknown ordering, receiver fallback, cost, and decorator behavior for unsupported paths.
1 parent d95352d commit b2ff8d4

11 files changed

Lines changed: 2070 additions & 16 deletions

File tree

‎core/src/jmh/java/org/projectnessie/cel/interpreter/OverloadDispatchBench.java‎

Lines changed: 482 additions & 0 deletions
Large diffs are not rendered by default.

‎core/src/main/java/org/projectnessie/cel/extension/Guards.java‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.projectnessie.cel.common.types.StringT;
2626
import org.projectnessie.cel.interpreter.functions.BinaryOp;
2727
import org.projectnessie.cel.interpreter.functions.FunctionOp;
28+
import org.projectnessie.cel.interpreter.functions.QuaternaryOp;
29+
import org.projectnessie.cel.interpreter.functions.TernaryOp;
2830
import org.projectnessie.cel.interpreter.functions.UnaryOp;
2931

3032
/** function invocation guards for common call signatures within extension functions. */
@@ -57,6 +59,19 @@ public static FunctionOp callInStrIntIntOutStr(
5759
};
5860
}
5961

62+
public static TernaryOp callInStrIntIntOutStrTernary(
63+
TriFunction<String, Integer, Integer, String> func) {
64+
return (first, second, third) -> {
65+
try {
66+
return StringT.stringOf(
67+
func.apply(
68+
(String) first.value(), getIntValue((IntT) second), getIntValue((IntT) third)));
69+
} catch (RuntimeException e) {
70+
return Err.newErr(e, "%s", e.getMessage());
71+
}
72+
};
73+
}
74+
6075
public static BinaryOp callInStrStrOutInt(BiFunction<String, String, Integer> func) {
6176
return (lhs, rhs) -> {
6277
try {
@@ -82,6 +97,18 @@ public static FunctionOp callInStrStrIntOutInt(
8297
};
8398
}
8499

100+
public static TernaryOp callInStrStrIntOutIntTernary(
101+
TriFunction<String, String, Integer, Integer> func) {
102+
return (first, second, third) -> {
103+
try {
104+
return IntT.intOf(
105+
func.apply((String) first.value(), (String) second.value(), getIntValue((IntT) third)));
106+
} catch (RuntimeException e) {
107+
return Err.newErr(e, "%s", e.getMessage());
108+
}
109+
};
110+
}
111+
85112
public static BinaryOp callInStrStrOutStrArr(BiFunction<String, String, String[]> func) {
86113
return (lhs, rhs) -> {
87114
try {
@@ -107,6 +134,18 @@ public static FunctionOp callInStrStrIntOutStrArr(
107134
};
108135
}
109136

137+
public static TernaryOp callInStrStrIntOutStrArrTernary(
138+
TriFunction<String, String, Integer, String[]> func) {
139+
return (first, second, third) -> {
140+
try {
141+
return ListT.newStringArrayList(
142+
func.apply((String) first.value(), (String) second.value(), getIntValue((IntT) third)));
143+
} catch (RuntimeException e) {
144+
return Err.newErr(e, "%s", e.getMessage());
145+
}
146+
};
147+
}
148+
110149
public static FunctionOp callInStrStrStrOutStr(TriFunction<String, String, String, String> func) {
111150
return values -> {
112151
try {
@@ -121,6 +160,18 @@ public static FunctionOp callInStrStrStrOutStr(TriFunction<String, String, Strin
121160
};
122161
}
123162

163+
public static TernaryOp callInStrStrStrOutStrTernary(
164+
TriFunction<String, String, String, String> func) {
165+
return (first, second, third) -> {
166+
try {
167+
return StringT.stringOf(
168+
func.apply((String) first.value(), (String) second.value(), (String) third.value()));
169+
} catch (RuntimeException e) {
170+
return Err.newErr(e, "%s", e.getMessage());
171+
}
172+
};
173+
}
174+
124175
public static FunctionOp callInStrStrStrIntOutStr(
125176
QuadFunction<String, String, String, Integer, String> func) {
126177
return values -> {
@@ -137,6 +188,22 @@ public static FunctionOp callInStrStrStrIntOutStr(
137188
};
138189
}
139190

191+
public static QuaternaryOp callInStrStrStrIntOutStrQuaternary(
192+
QuadFunction<String, String, String, Integer, String> func) {
193+
return (first, second, third, fourth) -> {
194+
try {
195+
return StringT.stringOf(
196+
func.apply(
197+
(String) first.value(),
198+
(String) second.value(),
199+
(String) third.value(),
200+
getIntValue((IntT) fourth)));
201+
} catch (RuntimeException e) {
202+
return Err.newErr(e, "%s", e.getMessage());
203+
}
204+
};
205+
}
206+
140207
public static UnaryOp callInStrOutStr(UnaryOperator<String> func) {
141208
return val -> {
142209
try {

‎core/src/main/java/org/projectnessie/cel/extension/MathLib.java‎

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
import org.projectnessie.cel.common.types.UintT;
3737
import org.projectnessie.cel.common.types.ref.Val;
3838
import org.projectnessie.cel.common.types.traits.Lister;
39+
import org.projectnessie.cel.interpreter.functions.FunctionOp;
3940
import org.projectnessie.cel.interpreter.functions.Overload;
41+
import org.projectnessie.cel.interpreter.functions.QuaternaryOp;
42+
import org.projectnessie.cel.interpreter.functions.QuinaryOp;
43+
import org.projectnessie.cel.interpreter.functions.TernaryOp;
4044

4145
/** MathLib provides CEL helper functions from the standard math extension library. */
4246
public final class MathLib implements Library {
@@ -91,10 +95,34 @@ public List<EnvOption> getCompileOptions() {
9195
public List<ProgramOption> getProgramOptions() {
9296
List<Overload> overloads = new ArrayList<>();
9397
overloads.add(
94-
Overload.overload(GREATEST, null, MathLib::greatest, MathLib::greatest, MathLib::greatest));
95-
overloads.add(Overload.overload(LEAST, null, MathLib::least, MathLib::least, MathLib::least));
96-
addArityOverloads(overloads, GREATEST, MathLib::greatest);
97-
addArityOverloads(overloads, LEAST, MathLib::least);
98+
Overload.overload(
99+
GREATEST,
100+
null,
101+
MathLib::greatest,
102+
MathLib::greatest,
103+
MathLib::greatest,
104+
MathLib::greatest,
105+
MathLib::greatest,
106+
MathLib::greatest));
107+
overloads.add(
108+
Overload.overload(
109+
LEAST,
110+
null,
111+
MathLib::least,
112+
MathLib::least,
113+
MathLib::least,
114+
MathLib::least,
115+
MathLib::least,
116+
MathLib::least));
117+
addArityOverloads(
118+
overloads,
119+
GREATEST,
120+
MathLib::greatest,
121+
MathLib::greatest,
122+
MathLib::greatest,
123+
MathLib::greatest);
124+
addArityOverloads(
125+
overloads, LEAST, MathLib::least, MathLib::least, MathLib::least, MathLib::least);
98126
overloads.add(Overload.unary(CEIL, MathLib::ceil));
99127
overloads.add(Overload.unary(overloadId(CEIL, 1), MathLib::ceil));
100128
overloads.add(Overload.unary(FLOOR, MathLib::floor));
@@ -164,15 +192,18 @@ private static List<Type> dynArgs(int count) {
164192
private static void addArityOverloads(
165193
List<Overload> overloads,
166194
String function,
167-
org.projectnessie.cel.interpreter.functions.FunctionOp op) {
195+
FunctionOp op,
196+
TernaryOp ternaryOp,
197+
QuaternaryOp quaternaryOp,
198+
QuinaryOp quinaryOp) {
168199
overloads.add(Overload.unary(overloadId(function, "int"), op::invoke));
169200
overloads.add(Overload.unary(overloadId(function, "uint"), op::invoke));
170201
overloads.add(Overload.unary(overloadId(function, "double"), op::invoke));
171202
overloads.add(
172203
Overload.binary(overloadId(function, 2), (left, right) -> op.invoke(left, right)));
173-
for (int arity = 3; arity <= 5; arity++) {
174-
overloads.add(Overload.function(overloadId(function, arity), op));
175-
}
204+
overloads.add(Overload.ternary(overloadId(function, 3), ternaryOp));
205+
overloads.add(Overload.quaternary(overloadId(function, 4), quaternaryOp));
206+
overloads.add(Overload.quinary(overloadId(function, 5), quinaryOp));
176207
overloads.add(Overload.unary(overloadId(function, "list"), op::invoke));
177208
}
178209

@@ -188,10 +219,87 @@ private static Val greatest(Val... values) {
188219
return minMax(values, true);
189220
}
190221

222+
private static Val greatest(Val first, Val second, Val third) {
223+
return minMax(first, second, third, true);
224+
}
225+
226+
private static Val greatest(Val first, Val second, Val third, Val fourth) {
227+
return minMax(first, second, third, fourth, true);
228+
}
229+
230+
private static Val greatest(Val first, Val second, Val third, Val fourth, Val fifth) {
231+
return minMax(first, second, third, fourth, fifth, true);
232+
}
233+
191234
private static Val least(Val... values) {
192235
return minMax(values, false);
193236
}
194237

238+
private static Val least(Val first, Val second, Val third) {
239+
return minMax(first, second, third, false);
240+
}
241+
242+
private static Val least(Val first, Val second, Val third, Val fourth) {
243+
return minMax(first, second, third, fourth, false);
244+
}
245+
246+
private static Val least(Val first, Val second, Val third, Val fourth, Val fifth) {
247+
return minMax(first, second, third, fourth, fifth, false);
248+
}
249+
250+
private static Val minMax(
251+
Val first, Val second, Val third, Val fourth, Val fifth, boolean greatest) {
252+
Val result = minMax(first, second, third, fourth, greatest);
253+
if (!isNumber(result)) {
254+
return result;
255+
}
256+
if (!isNumber(fifth)) {
257+
return noSuchOverload();
258+
}
259+
int cmp = compareNumbers(fifth, result);
260+
if ((greatest && cmp > 0) || (!greatest && cmp < 0)) {
261+
result = fifth;
262+
}
263+
return result;
264+
}
265+
266+
private static Val minMax(Val first, Val second, Val third, Val fourth, boolean greatest) {
267+
Val result = minMax(first, second, third, greatest);
268+
if (!isNumber(result)) {
269+
return result;
270+
}
271+
if (!isNumber(fourth)) {
272+
return noSuchOverload();
273+
}
274+
int cmp = compareNumbers(fourth, result);
275+
if ((greatest && cmp > 0) || (!greatest && cmp < 0)) {
276+
result = fourth;
277+
}
278+
return result;
279+
}
280+
281+
private static Val minMax(Val first, Val second, Val third, boolean greatest) {
282+
if (!isNumber(first)) {
283+
return noSuchOverload();
284+
}
285+
Val result = first;
286+
if (!isNumber(second)) {
287+
return noSuchOverload();
288+
}
289+
int cmp = compareNumbers(second, result);
290+
if ((greatest && cmp > 0) || (!greatest && cmp < 0)) {
291+
result = second;
292+
}
293+
if (!isNumber(third)) {
294+
return noSuchOverload();
295+
}
296+
cmp = compareNumbers(third, result);
297+
if ((greatest && cmp > 0) || (!greatest && cmp < 0)) {
298+
result = third;
299+
}
300+
return result;
301+
}
302+
195303
private static Val minMax(Val[] values, boolean greatest) {
196304
List<Val> candidates = candidates(values);
197305
if (candidates.isEmpty()) {

‎core/src/main/java/org/projectnessie/cel/extension/StringsLib.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ public List<ProgramOption> getProgramOptions() {
383383
null,
384384
null,
385385
Guards.callInStrStrOutInt(StringsLib::indexOf),
386+
Guards.callInStrStrIntOutIntTernary(StringsLib::indexOfOffset),
386387
values ->
387388
values.length == 3
388389
? Guards.callInStrStrIntOutInt(StringsLib::indexOfOffset).invoke(values)
@@ -398,6 +399,7 @@ public List<ProgramOption> getProgramOptions() {
398399
null,
399400
null,
400401
Guards.callInStrStrOutInt(StringsLib::lastIndexOf),
402+
Guards.callInStrStrIntOutIntTernary(StringsLib::lastIndexOfOffset),
401403
values ->
402404
values.length == 3
403405
? Guards.callInStrStrIntOutInt(StringsLib::lastIndexOfOffset).invoke(values)
@@ -408,6 +410,8 @@ public List<ProgramOption> getProgramOptions() {
408410
null,
409411
null,
410412
null,
413+
Guards.callInStrStrStrOutStrTernary(StringsLib::replace),
414+
Guards.callInStrStrStrIntOutStrQuaternary(StringsLib::replaceN),
411415
values -> {
412416
if (values.length == 3) {
413417
return Guards.callInStrStrStrOutStr(StringsLib::replace).invoke(values);
@@ -423,6 +427,7 @@ public List<ProgramOption> getProgramOptions() {
423427
null,
424428
null,
425429
Guards.callInStrStrOutStrArr(StringsLib::split),
430+
Guards.callInStrStrIntOutStrArrTernary(StringsLib::splitN),
426431
values ->
427432
values.length == 3
428433
? Guards.callInStrStrIntOutStrArr(StringsLib::splitN).invoke(values)
@@ -432,6 +437,7 @@ public List<ProgramOption> getProgramOptions() {
432437
null,
433438
null,
434439
Guards.callInStrIntOutStr(StringsLib::substr),
440+
Guards.callInStrIntIntOutStrTernary(StringsLib::substrRange),
435441
values ->
436442
values.length == 3
437443
? Guards.callInStrIntIntOutStr(StringsLib::substrRange).invoke(values)

0 commit comments

Comments
 (0)