|
28 | 28 | import static org.projectnessie.cel.interpreter.Coster.Cost.estimateCost; |
29 | 29 | import static org.projectnessie.cel.interpreter.Coster.costOf; |
30 | 30 |
|
| 31 | +import java.util.ArrayList; |
31 | 32 | import java.util.Arrays; |
32 | 33 | import java.util.HashMap; |
| 34 | +import java.util.List; |
33 | 35 | import java.util.Map; |
34 | 36 | import java.util.Objects; |
35 | 37 | import java.util.Set; |
36 | 38 | import org.projectnessie.cel.common.operators.Operator; |
37 | 39 | import org.projectnessie.cel.common.types.Err; |
38 | 40 | import org.projectnessie.cel.common.types.IterableT; |
39 | 41 | import org.projectnessie.cel.common.types.IteratorT; |
| 42 | +import org.projectnessie.cel.common.types.ListT; |
40 | 43 | import org.projectnessie.cel.common.types.MapT; |
41 | 44 | import org.projectnessie.cel.common.types.Overloads; |
42 | 45 | import org.projectnessie.cel.common.types.StringT; |
|
49 | 52 | import org.projectnessie.cel.common.types.traits.FieldTester; |
50 | 53 | import org.projectnessie.cel.common.types.traits.Negater; |
51 | 54 | import org.projectnessie.cel.common.types.traits.Receiver; |
| 55 | +import org.projectnessie.cel.common.types.traits.Sizer; |
52 | 56 | import org.projectnessie.cel.common.types.traits.Trait; |
53 | 57 | import org.projectnessie.cel.interpreter.Activation.VarActivation; |
54 | 58 | import org.projectnessie.cel.interpreter.AttributeFactory.Attribute; |
@@ -1108,6 +1112,111 @@ public String toString() { |
1108 | 1112 | } |
1109 | 1113 | } |
1110 | 1114 |
|
| 1115 | + final class EvalListFold extends AbstractEval implements Coster { |
| 1116 | + final String iterVar; |
| 1117 | + final Interpretable iterRange; |
| 1118 | + final Interpretable filter; |
| 1119 | + final Interpretable transform; |
| 1120 | + private final TypeAdapter adapter; |
| 1121 | + |
| 1122 | + EvalListFold( |
| 1123 | + long id, |
| 1124 | + String iterVar, |
| 1125 | + Interpretable iterRange, |
| 1126 | + Interpretable filter, |
| 1127 | + Interpretable transform, |
| 1128 | + TypeAdapter adapter) { |
| 1129 | + super(id); |
| 1130 | + this.iterVar = iterVar; |
| 1131 | + this.iterRange = iterRange; |
| 1132 | + this.filter = filter; |
| 1133 | + this.transform = transform; |
| 1134 | + this.adapter = adapter; |
| 1135 | + } |
| 1136 | + |
| 1137 | + @Override |
| 1138 | + public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { |
| 1139 | + Val foldRange = iterRange.eval(ctx); |
| 1140 | + if (!foldRange.type().hasTrait(Trait.IterableType)) { |
| 1141 | + return valOrErr( |
| 1142 | + foldRange, "got '%s', expected iterable type", foldRange.getClass().getName()); |
| 1143 | + } |
| 1144 | + |
| 1145 | + VarActivation iterCtx = new VarActivation(); |
| 1146 | + iterCtx.parent = ctx; |
| 1147 | + iterCtx.name = iterVar; |
| 1148 | + List<Val> values = new ArrayList<>(listCapacity(foldRange)); |
| 1149 | + IteratorT it = ((IterableT) foldRange).iterator(); |
| 1150 | + while (it.hasNext() == True) { |
| 1151 | + iterCtx.val = it.next(); |
| 1152 | + |
| 1153 | + if (filter != null) { |
| 1154 | + Val include = filter.eval(iterCtx); |
| 1155 | + if (include == False) { |
| 1156 | + continue; |
| 1157 | + } |
| 1158 | + if (include != True) { |
| 1159 | + return noSuchOverload(null, Operator.Conditional.id, include); |
| 1160 | + } |
| 1161 | + } |
| 1162 | + |
| 1163 | + Val value = transform.eval(iterCtx); |
| 1164 | + if (isUnknownOrError(value)) { |
| 1165 | + return value; |
| 1166 | + } |
| 1167 | + values.add(value); |
| 1168 | + } |
| 1169 | + return ListT.newValArrayList(adapter, values.toArray(new Val[0])); |
| 1170 | + } |
| 1171 | + |
| 1172 | + private int listCapacity(Val foldRange) { |
| 1173 | + if (foldRange.type().hasTrait(Trait.SizerType)) { |
| 1174 | + long size = ((Sizer) foldRange).size().intValue(); |
| 1175 | + if (size > 0 && size <= Integer.MAX_VALUE) { |
| 1176 | + return (int) size; |
| 1177 | + } |
| 1178 | + } |
| 1179 | + return 0; |
| 1180 | + } |
| 1181 | + |
| 1182 | + @Override |
| 1183 | + public Cost cost() { |
| 1184 | + Cost range = estimateCost(iterRange); |
| 1185 | + Cost result = estimateCost(transform); |
| 1186 | + if (filter != null) { |
| 1187 | + result = result.add(estimateCost(filter)); |
| 1188 | + } |
| 1189 | + Val foldRange = iterRange.eval(emptyActivation()); |
| 1190 | + if (!foldRange.type().hasTrait(Trait.IterableType)) { |
| 1191 | + return Cost.Unknown; |
| 1192 | + } |
| 1193 | + long rangeCnt = 0L; |
| 1194 | + IteratorT it = ((IterableT) foldRange).iterator(); |
| 1195 | + while (it.hasNext() == True) { |
| 1196 | + it.next(); |
| 1197 | + rangeCnt++; |
| 1198 | + } |
| 1199 | + return range.add(result.multiply(rangeCnt)); |
| 1200 | + } |
| 1201 | + |
| 1202 | + @Override |
| 1203 | + public String toString() { |
| 1204 | + return "EvalListFold{" |
| 1205 | + + "id=" |
| 1206 | + + id |
| 1207 | + + ", iterVar='" |
| 1208 | + + iterVar |
| 1209 | + + '\'' |
| 1210 | + + ", iterRange=" |
| 1211 | + + iterRange |
| 1212 | + + ", filter=" |
| 1213 | + + filter |
| 1214 | + + ", transform=" |
| 1215 | + + transform |
| 1216 | + + '}'; |
| 1217 | + } |
| 1218 | + } |
| 1219 | + |
1111 | 1220 | // Optional Intepretable implementations that specialize, subsume, or extend the core evaluation |
1112 | 1221 | // plan via decorators. |
1113 | 1222 |
|
@@ -1677,6 +1786,65 @@ public String toString() { |
1677 | 1786 | } |
1678 | 1787 | } |
1679 | 1788 |
|
| 1789 | + /** EvalExhaustiveListFold evaluates every filter and transform without short-circuiting. */ |
| 1790 | + final class EvalExhaustiveListFold extends AbstractEval implements Coster { |
| 1791 | + private final EvalListFold fold; |
| 1792 | + |
| 1793 | + EvalExhaustiveListFold(EvalListFold fold) { |
| 1794 | + super(fold.id); |
| 1795 | + this.fold = fold; |
| 1796 | + } |
| 1797 | + |
| 1798 | + @Override |
| 1799 | + public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { |
| 1800 | + Val foldRange = fold.iterRange.eval(ctx); |
| 1801 | + if (!foldRange.type().hasTrait(Trait.IterableType)) { |
| 1802 | + return valOrErr( |
| 1803 | + foldRange, "got '%s', expected iterable type", foldRange.getClass().getName()); |
| 1804 | + } |
| 1805 | + |
| 1806 | + VarActivation iterCtx = new VarActivation(); |
| 1807 | + iterCtx.parent = ctx; |
| 1808 | + iterCtx.name = fold.iterVar; |
| 1809 | + List<Val> values = new ArrayList<>(fold.listCapacity(foldRange)); |
| 1810 | + Val result = null; |
| 1811 | + IteratorT it = ((IterableT) foldRange).iterator(); |
| 1812 | + while (it.hasNext() == True) { |
| 1813 | + iterCtx.val = it.next(); |
| 1814 | + |
| 1815 | + Val include = fold.filter != null ? fold.filter.eval(iterCtx) : True; |
| 1816 | + Val value = fold.transform.eval(iterCtx); |
| 1817 | + if (include == False) { |
| 1818 | + continue; |
| 1819 | + } |
| 1820 | + if (include != True) { |
| 1821 | + result = noSuchOverload(null, Operator.Conditional.id, include); |
| 1822 | + continue; |
| 1823 | + } |
| 1824 | + if (result == null) { |
| 1825 | + if (isUnknownOrError(value)) { |
| 1826 | + result = value; |
| 1827 | + } else { |
| 1828 | + values.add(value); |
| 1829 | + } |
| 1830 | + } |
| 1831 | + } |
| 1832 | + return result != null |
| 1833 | + ? result |
| 1834 | + : ListT.newValArrayList(fold.adapter, values.toArray(new Val[0])); |
| 1835 | + } |
| 1836 | + |
| 1837 | + @Override |
| 1838 | + public Cost cost() { |
| 1839 | + return fold.cost(); |
| 1840 | + } |
| 1841 | + |
| 1842 | + @Override |
| 1843 | + public String toString() { |
| 1844 | + return "EvalExhaustiveListFold{" + fold + '}'; |
| 1845 | + } |
| 1846 | + } |
| 1847 | + |
1680 | 1848 | /** evalAttr evaluates an Attribute value. */ |
1681 | 1849 | final class EvalAttr extends AbstractEval |
1682 | 1850 | implements InterpretableAttribute, Coster, Qualifier, Attribute { |
|
0 commit comments