Tries to merge consecutive EBV tests. @param or union or intersection @param predicate predicate test @param cc compilation context @return true if evaluation can be skipped @throws QueryException query exception
(final boolean or, final boolean predicate, final CompileContext cc)
| 181 | * @throws QueryException query exception |
| 182 | */ |
| 183 | final boolean optimizeEbv(final boolean or, final boolean predicate, final CompileContext cc) |
| 184 | throws QueryException { |
| 185 | |
| 186 | final ExprList list = new ExprList(exprs.length); |
| 187 | boolean pos = false; |
| 188 | for(final Expr expr : exprs) { |
| 189 | // pre-evaluate values |
| 190 | if(expr instanceof Value && (!predicate || !expr.seqType().mayBeNumber())) { |
| 191 | // skip evaluation: true() or $bool → true() |
| 192 | if(expr.test(cc.qc, info, 0) == or) return true; |
| 193 | // ignore result: true() and $bool → $bool |
| 194 | cc.info(QueryText.OPTREMOVE_X_X, expr, (Supplier<?>) this::description); |
| 195 | } else if(!pos && list.contains(expr) && !expr.has(Flag.NDT)) { |
| 196 | // ignore duplicates: A[$node and $node] → A[$node] |
| 197 | cc.info(QueryText.OPTREMOVE_X_X, expr, (Supplier<?>) this::description); |
| 198 | } else { |
| 199 | list.add(expr); |
| 200 | // preserve entries after positional predicates |
| 201 | if(predicate && !pos) pos = mayBePositional(expr); |
| 202 | } |
| 203 | } |
| 204 | exprs = list.next(); |
| 205 | |
| 206 | if(!(predicate && has(Flag.POS))) { |
| 207 | final Class<? extends Arr> clazz = or ? And.class : Or.class; |
| 208 | final QueryBiFunction<Boolean, Expr[], Expr> func = (invert, args) -> |
| 209 | invert == or ? new And(info, args) : new Or(info, args); |
| 210 | final Expr tmp = rewrite(clazz, func, cc); |
| 211 | if(tmp != null) { |
| 212 | exprs = new Expr[] { tmp }; |
| 213 | cc.info(QueryText.OPTREWRITE_X_X, (Supplier<?>) this::description, this); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | list.add(exprs); |
| 218 | for(int l = 0; l < list.size(); l++) { |
| 219 | for(int m = l + 1; m < list.size(); m++) { |
| 220 | final Expr expr1 = list.get(l), expr2 = list.get(m); |
| 221 | if(!expr1.has(Flag.NDT) && !(predicate && expr1.has(Flag.POS))) { |
| 222 | // A or not(A) → true() |
| 223 | // A[not(B)][B] → () |
| 224 | // empty(A) or exists(A) → true() |
| 225 | if(contradict(expr1, expr2, true) || contradict(expr2, expr1, true)) return true; |
| 226 | |
| 227 | // 'a'[. = 'a' or . = 'b'] → 'a'[. = ('a', 'b')] |
| 228 | // $v[. != 'a'][. != 'b'] → $v[not(. = ('a', 'b')] |
| 229 | final Expr merged = expr1.mergeEbv(expr2, or, cc); |
| 230 | if(merged != null) { |
| 231 | cc.info(QueryText.OPTSIMPLE_X_X, (Supplier<?>) this::description, this); |
| 232 | list.set(l, merged); |
| 233 | list.remove(m--); |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | exprs = list.next(); |
| 239 | |
| 240 | // not($a) and not($b) → not($a or $b) |
no test coverage detected