Branch data Line data Source code
1 : : /******************************************************************************
2 : : * This file is part of the cvc5 project.
3 : : *
4 : : * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
5 : : * in the top-level source directory and their institutional affiliations.
6 : : * All rights reserved. See the file COPYING in the top-level source
7 : : * directory for licensing information.
8 : : * ****************************************************************************
9 : : *
10 : : * Symbolic Regular Expresion Operations
11 : : */
12 : :
13 : : #include "theory/strings/regexp_operation.h"
14 : :
15 : : #include <limits>
16 : : #include <sstream>
17 : :
18 : : #include "expr/node_algorithm.h"
19 : : #include "options/strings_options.h"
20 : : #include "theory/rewriter.h"
21 : : #include "theory/strings/regexp_entail.h"
22 : : #include "theory/strings/theory_strings_utils.h"
23 : : #include "theory/strings/word.h"
24 : : #include "util/regexp.h"
25 : :
26 : : using namespace cvc5::internal::kind;
27 : :
28 : : namespace cvc5::internal {
29 : : namespace theory {
30 : : namespace strings {
31 : :
32 : 28914 : RegExpOpr::RegExpOpr(Env& env, SkolemCache* sc)
33 : : : EnvObj(env),
34 : 28914 : d_true(nodeManager()->mkConst(true)),
35 : 28914 : d_false(nodeManager()->mkConst(false)),
36 : 57828 : d_emptyRegexp(
37 : 57828 : nodeManager()->mkNode(Kind::REGEXP_NONE, std::vector<Node>{})),
38 : 28914 : d_zero(nodeManager()->mkConstInt(Rational(0))),
39 : 28914 : d_one(nodeManager()->mkConstInt(Rational(1))),
40 : 28914 : d_sigma(nodeManager()->mkNode(Kind::REGEXP_ALLCHAR, std::vector<Node>{})),
41 : 28914 : d_sigma_star(nodeManager()->mkNode(Kind::REGEXP_STAR, d_sigma)),
42 : 86742 : d_sc(sc)
43 : : {
44 : 28914 : d_emptyString = Word::mkEmptyWord(nodeManager()->stringType());
45 : :
46 : : d_emptySingleton =
47 : 28914 : nodeManager()->mkNode(Kind::STRING_TO_REGEXP, d_emptyString);
48 : 28914 : d_lastchar = options().strings.stringsAlphaCard - 1;
49 : 28914 : }
50 : :
51 : 28901 : RegExpOpr::~RegExpOpr() {}
52 : :
53 : 3037 : bool RegExpOpr::checkConstRegExp(Node r)
54 : : {
55 [ - + ][ - + ]: 3037 : Assert(r.getType().isRegExp());
[ - - ]
56 [ + - ]: 6074 : Trace("strings-regexp-cstre")
57 : 3037 : << "RegExpOpr::checkConstRegExp /" << mkString(r) << "/" << std::endl;
58 : 3037 : RegExpConstType rct = getRegExpConstType(r);
59 : 3037 : return rct != RE_C_VARIABLE;
60 : : }
61 : :
62 : 3046 : RegExpConstType RegExpOpr::getRegExpConstType(Node r)
63 : : {
64 [ - + ][ - + ]: 3046 : Assert(r.getType().isRegExp());
[ - - ]
65 : 3046 : std::unordered_map<Node, RegExpConstType>::iterator it;
66 : 3046 : std::vector<TNode> visit;
67 : 3046 : TNode cur;
68 : 3046 : visit.push_back(r);
69 : : do
70 : : {
71 : 8601 : cur = visit.back();
72 : 8601 : visit.pop_back();
73 : 8601 : it = d_constCache.find(cur);
74 : :
75 : 8601 : Kind ck = cur.getKind();
76 [ + + ]: 8601 : if (it == d_constCache.end())
77 : : {
78 [ + + ]: 3186 : if (ck == Kind::STRING_TO_REGEXP)
79 : : {
80 : 1031 : Node tmp = rewrite(cur[0]);
81 : 1031 : d_constCache[cur] =
82 [ + + ]: 1031 : tmp.isConst() ? RE_C_CONCRETE_CONSTANT : RE_C_VARIABLE;
83 : 1031 : }
84 [ + + ][ + + ]: 2155 : else if (ck == Kind::REGEXP_ALLCHAR || ck == Kind::REGEXP_RANGE)
85 : : {
86 : 312 : d_constCache[cur] = RE_C_CONSTANT;
87 : : }
88 [ - + ]: 1843 : else if (!utils::isRegExpKind(ck))
89 : : {
90 : : // non-regular expression applications, e.g. function applications
91 : : // with regular expression return type are treated as variables.
92 : 0 : d_constCache[cur] = RE_C_VARIABLE;
93 : : }
94 : : else
95 : : {
96 : 1843 : d_constCache[cur] = RE_C_UNKNOWN;
97 : 1843 : visit.push_back(cur);
98 : 1843 : visit.insert(visit.end(), cur.begin(), cur.end());
99 : : }
100 : : }
101 [ + + ]: 5415 : else if (it->second == RE_C_UNKNOWN)
102 : : {
103 : 1843 : RegExpConstType ret = ck == Kind::REGEXP_COMPLEMENT
104 [ + + ]: 1843 : ? RE_C_CONSTANT
105 : : : RE_C_CONCRETE_CONSTANT;
106 [ + + ]: 5555 : for (const Node& cn : cur)
107 : : {
108 : 3712 : it = d_constCache.find(cn);
109 [ - + ][ - + ]: 3712 : Assert(it != d_constCache.end());
[ - - ]
110 [ + + ]: 3712 : if (it->second > ret)
111 : : {
112 : 1002 : ret = it->second;
113 : : }
114 : 3712 : }
115 : 1843 : d_constCache[cur] = ret;
116 : : }
117 [ + + ]: 8601 : } while (!visit.empty());
118 [ - + ][ - + ]: 3046 : Assert(d_constCache.find(r) != d_constCache.end());
[ - - ]
119 : 6092 : return d_constCache[r];
120 : 3046 : }
121 : :
122 : : // 0-unknown, 1-yes, 2-no
123 : 21 : int RegExpOpr::delta(Node r, Node& exp)
124 : : {
125 : : std::map<Node, std::pair<int, Node> >::const_iterator itd =
126 : 21 : d_delta_cache.find(r);
127 [ + + ]: 21 : if (itd != d_delta_cache.end())
128 : : {
129 : : // already computed
130 : 7 : exp = itd->second.second;
131 : 7 : return itd->second.first;
132 : : }
133 [ + - ]: 14 : Trace("regexp-delta") << "RegExpOpr::delta: " << r << std::endl;
134 : 14 : int ret = 0;
135 : 14 : NodeManager* nm = nodeManager();
136 : 14 : Kind k = r.getKind();
137 [ - + ][ + - ]: 14 : switch (k)
[ - - ][ - - ]
138 : : {
139 : 0 : case Kind::REGEXP_NONE:
140 : : case Kind::REGEXP_ALLCHAR:
141 : : case Kind::REGEXP_RANGE:
142 : : {
143 : : // does not contain empty string
144 : 0 : ret = 2;
145 : 0 : break;
146 : : }
147 : 7 : case Kind::STRING_TO_REGEXP:
148 : : {
149 : 14 : Node tmp = rewrite(r[0]);
150 [ + - ]: 7 : if (tmp.isConst())
151 : : {
152 [ + - ]: 7 : if (tmp == d_emptyString)
153 : : {
154 : 7 : ret = 1;
155 : : }
156 : : else
157 : : {
158 : 0 : ret = 2;
159 : : }
160 : : }
161 : : else
162 : : {
163 : 0 : ret = 0;
164 [ - - ]: 0 : if (tmp.getKind() == Kind::STRING_CONCAT)
165 : : {
166 [ - - ]: 0 : for (const Node& tmpc : tmp)
167 : : {
168 [ - - ]: 0 : if (tmpc.isConst())
169 : : {
170 : 0 : ret = 2;
171 : 0 : break;
172 : : }
173 [ - - ]: 0 : }
174 : : }
175 [ - - ]: 0 : if (ret == 0)
176 : : {
177 : 0 : exp = r[0].eqNode(d_emptyString);
178 : : }
179 : : }
180 : 7 : break;
181 : 7 : }
182 : 7 : case Kind::REGEXP_CONCAT:
183 : : case Kind::REGEXP_UNION:
184 : : case Kind::REGEXP_INTER:
185 : : {
186 : : // has there been an unknown child?
187 : 7 : bool hasUnknownChild = false;
188 : 7 : std::vector<Node> vec;
189 [ + - ]: 7 : int checkTmp = k == Kind::REGEXP_UNION ? 1 : 2;
190 [ + - ]: 7 : int retTmp = k == Kind::REGEXP_UNION ? 2 : 1;
191 [ + - ]: 7 : for (const Node& rc : r)
192 : : {
193 : 7 : Node exp2;
194 : 7 : int tmp = delta(rc, exp2);
195 [ + - ]: 7 : if (tmp == checkTmp)
196 : : {
197 : : // return is implied by the child's return value
198 : 7 : ret = checkTmp;
199 : 7 : break;
200 : : }
201 [ - - ]: 0 : else if (tmp == 0)
202 : : {
203 : : // unknown if child contains empty string
204 : 0 : Assert(!exp2.isNull());
205 : 0 : vec.push_back(exp2);
206 : 0 : hasUnknownChild = true;
207 : : }
208 [ - + ][ - + ]: 14 : }
209 [ - + ]: 7 : if (ret != checkTmp)
210 : : {
211 [ - - ]: 0 : if (!hasUnknownChild)
212 : : {
213 : 0 : ret = retTmp;
214 : : }
215 : : else
216 : : {
217 [ - - ]: 0 : Kind kr = k == Kind::REGEXP_UNION ? Kind::OR : Kind::AND;
218 [ - - ]: 0 : exp = vec.size() == 1 ? vec[0] : nm->mkNode(kr, vec);
219 : : }
220 : : }
221 : 7 : break;
222 : 7 : }
223 : 0 : case Kind::REGEXP_STAR:
224 : : case Kind::REGEXP_OPT:
225 : : {
226 : : // contains empty string
227 : 0 : ret = 1;
228 : 0 : break;
229 : : }
230 : 0 : case Kind::REGEXP_PLUS:
231 : : {
232 : 0 : ret = delta(r[0], exp);
233 : 0 : break;
234 : : }
235 : 0 : case Kind::REGEXP_LOOP:
236 : : {
237 : 0 : uint32_t lo = utils::getLoopMinOccurrences(r);
238 [ - - ]: 0 : if (lo == 0)
239 : : {
240 : 0 : ret = 1;
241 : : }
242 : : else
243 : : {
244 : 0 : ret = delta(r[0], exp);
245 : : }
246 : 0 : break;
247 : : }
248 : 0 : case Kind::REGEXP_COMPLEMENT:
249 : : {
250 : 0 : int tmp = delta(r[0], exp);
251 : : // flip the result if known
252 [ - - ]: 0 : ret = tmp == 0 ? 0 : (3 - tmp);
253 [ - - ]: 0 : exp = exp.isNull() ? exp : exp.negate();
254 : 0 : break;
255 : : }
256 : 0 : default:
257 : : {
258 : 0 : Assert(!utils::isRegExpKind(k));
259 : 0 : break;
260 : : }
261 : : }
262 [ - + ]: 14 : if (!exp.isNull())
263 : : {
264 : 0 : exp = rewrite(exp);
265 : : }
266 : 14 : std::pair<int, Node> p(ret, exp);
267 : 14 : d_delta_cache[r] = p;
268 [ + - ]: 28 : Trace("regexp-delta") << "RegExpOpr::delta returns " << ret << " for " << r
269 : 14 : << ", expr = " << exp << std::endl;
270 : 14 : return ret;
271 : : }
272 : :
273 : : // 0-unknown, 1-yes, 2-no
274 : 42 : int RegExpOpr::derivativeS(Node r, cvc5::internal::String c, Node& retNode)
275 : : {
276 [ - + ][ - + ]: 42 : Assert(c.size() < 2);
[ - - ]
277 : 84 : Trace("regexp-derive") << "RegExp-derive starts with /" << mkString(r)
278 : 42 : << "/, c=" << c << std::endl;
279 : :
280 : 42 : int ret = 1;
281 : 42 : retNode = d_emptyRegexp;
282 : 42 : NodeManager* nm = nodeManager();
283 : :
284 : 42 : PairNodeStr dv = std::make_pair(r, c);
285 [ + + ]: 42 : if (d_deriv_cache.find(dv) != d_deriv_cache.end())
286 : : {
287 : 7 : retNode = d_deriv_cache[dv].first;
288 : 7 : ret = d_deriv_cache[dv].second;
289 : : }
290 [ - + ]: 35 : else if (c.empty())
291 : : {
292 : 0 : Node expNode;
293 : 0 : ret = delta(r, expNode);
294 [ - - ]: 0 : if (ret == 0)
295 : : {
296 : 0 : retNode = nodeManager()->mkNode(Kind::ITE, expNode, r, d_emptyRegexp);
297 : : }
298 [ - - ]: 0 : else if (ret == 1)
299 : : {
300 : 0 : retNode = r;
301 : : }
302 : 0 : std::pair<Node, int> p(retNode, ret);
303 : 0 : d_deriv_cache[dv] = p;
304 : 0 : }
305 : : else
306 : : {
307 [ - - ][ - + ]: 35 : switch (r.getKind())
[ + + ][ - + ]
[ - - ][ - ]
308 : : {
309 : 0 : case Kind::REGEXP_NONE:
310 : : {
311 : 0 : ret = 2;
312 : 0 : break;
313 : : }
314 : 0 : case Kind::REGEXP_ALLCHAR:
315 : : {
316 : 0 : retNode = d_emptySingleton;
317 : 0 : break;
318 : : }
319 : 0 : case Kind::REGEXP_RANGE:
320 : : {
321 : 0 : cvc5::internal::String a = r[0].getConst<String>();
322 : 0 : cvc5::internal::String b = r[1].getConst<String>();
323 : 0 : retNode = (a <= c && c <= b) ? d_emptySingleton : d_emptyRegexp;
324 : 0 : break;
325 : 0 : }
326 : 14 : case Kind::STRING_TO_REGEXP:
327 : : {
328 : 28 : Node tmp = rewrite(r[0]);
329 [ + - ]: 14 : if (tmp.isConst())
330 : : {
331 [ + + ]: 14 : if (tmp == d_emptyString)
332 : : {
333 : 7 : ret = 2;
334 : : }
335 : : else
336 : : {
337 [ - + ]: 7 : if (tmp.getConst<String>().front() == c.front())
338 : : {
339 : : retNode =
340 : 0 : nm->mkNode(Kind::STRING_TO_REGEXP,
341 : 0 : Word::getLength(tmp) == 1 ? d_emptyString
342 : 0 : : Word::substr(tmp, 1));
343 : : }
344 : : else
345 : : {
346 : 7 : ret = 2;
347 : : }
348 : : }
349 : : }
350 : : else
351 : : {
352 : 0 : ret = 0;
353 : 0 : Node rest;
354 [ - - ]: 0 : if (tmp.getKind() == Kind::STRING_CONCAT)
355 : : {
356 : 0 : Node t2 = tmp[0];
357 [ - - ]: 0 : if (t2.isConst())
358 : : {
359 [ - - ]: 0 : if (t2.getConst<String>().front() == c.front())
360 : : {
361 : : Node n = nm->mkNode(Kind::STRING_TO_REGEXP,
362 : 0 : Word::getLength(tmp) == 1
363 : 0 : ? d_emptyString
364 : 0 : : Word::substr(tmp, 1));
365 : 0 : std::vector<Node> vec_nodes;
366 : 0 : vec_nodes.push_back(n);
367 [ - - ]: 0 : for (unsigned i = 1; i < tmp.getNumChildren(); i++)
368 : : {
369 : 0 : vec_nodes.push_back(tmp[i]);
370 : : }
371 : 0 : retNode = nm->mkNode(Kind::REGEXP_CONCAT, vec_nodes);
372 : 0 : ret = 1;
373 : 0 : }
374 : : else
375 : : {
376 : 0 : ret = 2;
377 : : }
378 : : }
379 : : else
380 : : {
381 : 0 : tmp = tmp[0];
382 : 0 : std::vector<Node> vec_nodes;
383 [ - - ]: 0 : for (unsigned i = 1; i < tmp.getNumChildren(); i++)
384 : : {
385 : 0 : vec_nodes.push_back(tmp[i]);
386 : : }
387 : 0 : rest = nm->mkNode(Kind::REGEXP_CONCAT, vec_nodes);
388 : 0 : }
389 : 0 : }
390 [ - - ]: 0 : if (ret == 0)
391 : : {
392 : 0 : Node sk = NodeManager::mkDummySkolem("rsp", nm->stringType());
393 : 0 : retNode = nm->mkNode(Kind::STRING_TO_REGEXP, sk);
394 [ - - ]: 0 : if (!rest.isNull())
395 : : {
396 : 0 : retNode = rewrite(nm->mkNode(Kind::REGEXP_CONCAT, retNode, rest));
397 : : }
398 : : Node exp =
399 : 0 : tmp.eqNode(nm->mkNode(Kind::STRING_CONCAT, nm->mkConst(c), sk));
400 : : retNode =
401 : 0 : rewrite(nm->mkNode(Kind::ITE, exp, retNode, d_emptyRegexp));
402 : 0 : }
403 : 0 : }
404 : 14 : break;
405 : 14 : }
406 : 7 : case Kind::REGEXP_CONCAT:
407 : : {
408 : 7 : std::vector<Node> vec_nodes;
409 : 7 : std::vector<Node> delta_nodes;
410 : 7 : Node dnode = d_true;
411 [ + + ]: 21 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
412 : : {
413 : 14 : Node dc;
414 : 14 : Node exp2;
415 : 14 : int rt = derivativeS(r[i], c, dc);
416 [ - + ]: 14 : if (rt != 2)
417 : : {
418 [ - - ]: 0 : if (rt == 0)
419 : : {
420 : 0 : ret = 0;
421 : : }
422 : 0 : std::vector<Node> vec_nodes2;
423 [ - - ]: 0 : if (dc != d_emptySingleton)
424 : : {
425 : 0 : vec_nodes2.push_back(dc);
426 : : }
427 [ - - ]: 0 : for (unsigned j = i + 1; j < r.getNumChildren(); ++j)
428 : : {
429 [ - - ]: 0 : if (r[j] != d_emptySingleton)
430 : : {
431 : 0 : vec_nodes2.push_back(r[j]);
432 : : }
433 : : }
434 : : Node tmp =
435 : 0 : vec_nodes2.size() == 0 ? d_emptySingleton
436 : 0 : : vec_nodes2.size() == 1
437 : 0 : ? vec_nodes2[0]
438 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, vec_nodes2);
439 [ - - ]: 0 : if (dnode != d_true)
440 : : {
441 : 0 : tmp = rewrite(nm->mkNode(Kind::ITE, dnode, tmp, d_emptyRegexp));
442 : 0 : ret = 0;
443 : : }
444 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), tmp)
445 [ - - ]: 0 : == vec_nodes.end())
446 : : {
447 : 0 : vec_nodes.push_back(tmp);
448 : : }
449 : 0 : }
450 : 14 : Node exp3;
451 : 14 : int rt2 = delta(r[i], exp3);
452 [ - + ]: 14 : if (rt2 == 0)
453 : : {
454 : 0 : dnode = rewrite(nm->mkNode(Kind::AND, dnode, exp3));
455 : : }
456 [ - + ]: 14 : else if (rt2 == 2)
457 : : {
458 : 0 : break;
459 : : }
460 [ + - ][ + - ]: 14 : }
[ + - ]
461 : : retNode =
462 : 7 : vec_nodes.size() == 0
463 [ + - ][ - - ]: 14 : ? d_emptyRegexp
464 : 0 : : (vec_nodes.size() == 1
465 : 0 : ? vec_nodes[0]
466 : 7 : : nodeManager()->mkNode(Kind::REGEXP_UNION, vec_nodes));
467 [ + - ]: 7 : if (retNode == d_emptyRegexp)
468 : : {
469 : 7 : ret = 2;
470 : : }
471 : 7 : break;
472 : 7 : }
473 : 7 : case Kind::REGEXP_UNION:
474 : : {
475 : 7 : std::vector<Node> vec_nodes;
476 [ + + ]: 21 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
477 : : {
478 : 14 : Node dc;
479 : 14 : int rt = derivativeS(r[i], c, dc);
480 [ - + ]: 14 : if (rt == 0)
481 : : {
482 : 0 : ret = 0;
483 : : }
484 [ - + ]: 14 : if (rt != 2)
485 : : {
486 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), dc)
487 [ - - ]: 0 : == vec_nodes.end())
488 : : {
489 : 0 : vec_nodes.push_back(dc);
490 : : }
491 : : }
492 : : // Trace("regexp-derive") << "RegExp-derive OR R[" << i << "] " <<
493 : : // mkString(r[i]) << " returns " << mkString(dc) << std::endl;
494 : 14 : }
495 : : retNode =
496 : 7 : vec_nodes.size() == 0
497 [ + - ][ - - ]: 14 : ? d_emptyRegexp
498 : 0 : : (vec_nodes.size() == 1
499 : 0 : ? vec_nodes[0]
500 : 7 : : nodeManager()->mkNode(Kind::REGEXP_UNION, vec_nodes));
501 [ + - ]: 7 : if (retNode == d_emptyRegexp)
502 : : {
503 : 7 : ret = 2;
504 : : }
505 : 7 : break;
506 : 7 : }
507 : 0 : case Kind::REGEXP_INTER:
508 : : {
509 : 0 : bool flag = true;
510 : 0 : bool flag_sg = false;
511 : 0 : std::vector<Node> vec_nodes;
512 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
513 : : {
514 : 0 : Node dc;
515 : 0 : int rt = derivativeS(r[i], c, dc);
516 [ - - ]: 0 : if (rt == 0)
517 : : {
518 : 0 : ret = 0;
519 : : }
520 [ - - ]: 0 : else if (rt == 2)
521 : : {
522 : 0 : flag = false;
523 : 0 : break;
524 : : }
525 [ - - ]: 0 : if (dc == d_sigma_star)
526 : : {
527 : 0 : flag_sg = true;
528 : : }
529 : : else
530 : : {
531 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), dc)
532 [ - - ]: 0 : == vec_nodes.end())
533 : : {
534 : 0 : vec_nodes.push_back(dc);
535 : : }
536 : : }
537 [ - - ]: 0 : }
538 [ - - ]: 0 : if (flag)
539 : : {
540 [ - - ][ - - ]: 0 : if (vec_nodes.size() == 0 && flag_sg)
[ - - ]
541 : : {
542 : 0 : retNode = d_sigma_star;
543 : : }
544 : : else
545 : : {
546 : 0 : retNode = vec_nodes.size() == 0
547 : 0 : ? d_emptyRegexp
548 : 0 : : (vec_nodes.size() == 1
549 : 0 : ? vec_nodes[0]
550 : : : nodeManager()->mkNode(Kind::REGEXP_INTER,
551 : 0 : vec_nodes));
552 [ - - ]: 0 : if (retNode == d_emptyRegexp)
553 : : {
554 : 0 : ret = 2;
555 : : }
556 : : }
557 : : }
558 : : else
559 : : {
560 : 0 : retNode = d_emptyRegexp;
561 : 0 : ret = 2;
562 : : }
563 : 0 : break;
564 : 0 : }
565 : 7 : case Kind::REGEXP_STAR:
566 : : {
567 : 7 : Node dc;
568 : 7 : ret = derivativeS(r[0], c, dc);
569 : : retNode =
570 : 7 : dc == d_emptyRegexp
571 : 14 : ? dc
572 [ - - ]: 0 : : (dc == d_emptySingleton
573 : : ? r
574 : 7 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, dc, r));
575 : 7 : break;
576 : 7 : }
577 : 0 : case Kind::REGEXP_LOOP:
578 : : {
579 : 0 : uint32_t l = utils::getLoopMinOccurrences(r);
580 : 0 : uint32_t u = utils::getLoopMaxOccurrences(r);
581 [ - - ][ - - ]: 0 : if (l == u && l == 0)
582 : : {
583 : 0 : ret = 2;
584 : : // retNode = d_emptyRegexp;
585 : : }
586 : : else
587 : : {
588 : 0 : Node dc;
589 : 0 : ret = derivativeS(r[0], c, dc);
590 [ - - ]: 0 : if (dc == d_emptyRegexp)
591 : : {
592 [ - - ]: 0 : Node lop = nm->mkConst(RegExpLoop(l == 0 ? 0 : (l - 1), u - 1));
593 : 0 : Node r2 = nm->mkNode(Kind::REGEXP_LOOP, lop, r[0]);
594 : 0 : retNode = dc == d_emptySingleton
595 : 0 : ? r2
596 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, dc, r2);
597 : 0 : }
598 : : else
599 : : {
600 : 0 : retNode = d_emptyRegexp;
601 : : }
602 : 0 : }
603 : 0 : break;
604 : : }
605 : 0 : case Kind::REGEXP_COMPLEMENT:
606 : : {
607 : : // don't know result
608 : 0 : return 0;
609 : : break;
610 : : }
611 : 0 : default:
612 : : {
613 : 0 : Assert(!utils::isRegExpKind(r.getKind()));
614 : 0 : return 0;
615 : : break;
616 : : }
617 : : }
618 [ - + ]: 35 : if (retNode != d_emptyRegexp)
619 : : {
620 : 0 : retNode = rewrite(retNode);
621 : : }
622 : 70 : std::pair<Node, int> p(retNode, ret);
623 : 35 : d_deriv_cache[dv] = p;
624 : : }
625 : :
626 : 84 : Trace("regexp-derive") << "RegExp-derive returns : /" << mkString(retNode)
627 : 42 : << "/" << std::endl;
628 : 42 : return ret;
629 : 42 : }
630 : :
631 : 0 : Node RegExpOpr::derivativeSingle(Node r, cvc5::internal::String c)
632 : : {
633 : 0 : Assert(c.size() < 2);
634 : 0 : Trace("regexp-derive") << "RegExp-derive starts with /" << mkString(r)
635 : 0 : << "/, c=" << c << std::endl;
636 : 0 : Node retNode = d_emptyRegexp;
637 : 0 : PairNodeStr dv = std::make_pair(r, c);
638 : 0 : NodeManager* nm = nodeManager();
639 [ - - ]: 0 : if (d_dv_cache.find(dv) != d_dv_cache.end())
640 : : {
641 : 0 : retNode = d_dv_cache[dv];
642 : : }
643 [ - - ]: 0 : else if (c.empty())
644 : : {
645 : 0 : Node exp;
646 : 0 : int tmp = delta(r, exp);
647 [ - - ]: 0 : if (tmp == 0)
648 : : {
649 : : // TODO variable
650 : 0 : retNode = d_emptyRegexp;
651 : : }
652 [ - - ]: 0 : else if (tmp == 1)
653 : : {
654 : 0 : retNode = r;
655 : : }
656 : : else
657 : : {
658 : 0 : retNode = d_emptyRegexp;
659 : : }
660 : 0 : }
661 : : else
662 : : {
663 : 0 : Kind k = r.getKind();
664 [ - - ][ - - ]: 0 : switch (k)
[ - - ][ - - ]
[ - - ]
665 : : {
666 : 0 : case Kind::REGEXP_NONE:
667 : : {
668 : 0 : retNode = d_emptyRegexp;
669 : 0 : break;
670 : : }
671 : 0 : case Kind::REGEXP_ALLCHAR:
672 : : {
673 : 0 : retNode = nodeManager()->mkNode(Kind::STRING_TO_REGEXP, d_emptyString);
674 : 0 : break;
675 : : }
676 : 0 : case Kind::REGEXP_RANGE:
677 : : {
678 : 0 : cvc5::internal::String a = r[0].getConst<String>();
679 : 0 : cvc5::internal::String b = r[1].getConst<String>();
680 : 0 : retNode = (a <= c && c <= b) ? d_emptySingleton : d_emptyRegexp;
681 : 0 : break;
682 : 0 : }
683 : 0 : case Kind::STRING_TO_REGEXP:
684 : : {
685 [ - - ]: 0 : if (r[0].isConst())
686 : : {
687 [ - - ]: 0 : if (r[0] == d_emptyString)
688 : : {
689 : 0 : retNode = d_emptyRegexp;
690 : : }
691 : : else
692 : : {
693 [ - - ]: 0 : if (r[0].getConst<String>().front() == c.front())
694 : : {
695 : 0 : retNode = nm->mkNode(Kind::STRING_TO_REGEXP,
696 : 0 : Word::getLength(r[0]) == 1
697 : 0 : ? d_emptyString
698 : 0 : : Word::substr(r[0], 1));
699 : : }
700 : : else
701 : : {
702 : 0 : retNode = d_emptyRegexp;
703 : : }
704 : : }
705 : : }
706 : : else
707 : : {
708 : : // TODO variable
709 : 0 : retNode = d_emptyRegexp;
710 : : }
711 : 0 : break;
712 : : }
713 : 0 : case Kind::REGEXP_CONCAT:
714 : : {
715 : : Node rees =
716 : 0 : nodeManager()->mkNode(Kind::STRING_TO_REGEXP, d_emptyString);
717 : 0 : std::vector<Node> vec_nodes;
718 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
719 : : {
720 : 0 : Node dc = derivativeSingle(r[i], c);
721 [ - - ]: 0 : if (dc != d_emptyRegexp)
722 : : {
723 : 0 : std::vector<Node> vec_nodes2;
724 [ - - ]: 0 : if (dc != rees)
725 : : {
726 : 0 : vec_nodes2.push_back(dc);
727 : : }
728 [ - - ]: 0 : for (unsigned j = i + 1; j < r.getNumChildren(); ++j)
729 : : {
730 [ - - ]: 0 : if (r[j] != rees)
731 : : {
732 : 0 : vec_nodes2.push_back(r[j]);
733 : : }
734 : : }
735 : : Node tmp =
736 : 0 : vec_nodes2.size() == 0 ? rees
737 : 0 : : vec_nodes2.size() == 1
738 : 0 : ? vec_nodes2[0]
739 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, vec_nodes2);
740 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), tmp)
741 [ - - ]: 0 : == vec_nodes.end())
742 : : {
743 : 0 : vec_nodes.push_back(tmp);
744 : : }
745 : 0 : }
746 : 0 : Node exp;
747 [ - - ]: 0 : if (delta(r[i], exp) != 1)
748 : : {
749 : 0 : break;
750 : : }
751 [ - - ][ - - ]: 0 : }
752 : : retNode =
753 : 0 : vec_nodes.size() == 0
754 : 0 : ? d_emptyRegexp
755 : 0 : : (vec_nodes.size() == 1
756 : 0 : ? vec_nodes[0]
757 : 0 : : nodeManager()->mkNode(Kind::REGEXP_UNION, vec_nodes));
758 : 0 : break;
759 : 0 : }
760 : 0 : case Kind::REGEXP_UNION:
761 : : {
762 : 0 : std::vector<Node> vec_nodes;
763 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
764 : : {
765 : 0 : Node dc = derivativeSingle(r[i], c);
766 [ - - ]: 0 : if (dc != d_emptyRegexp)
767 : : {
768 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), dc)
769 [ - - ]: 0 : == vec_nodes.end())
770 : : {
771 : 0 : vec_nodes.push_back(dc);
772 : : }
773 : : }
774 : : // Trace("regexp-derive") << "RegExp-derive OR R[" << i << "] /" <<
775 : : // mkString(r[i]) << "/ returns /" << mkString(dc) << "/" <<
776 : : // std::endl;
777 : 0 : }
778 : : retNode =
779 : 0 : vec_nodes.size() == 0
780 : 0 : ? d_emptyRegexp
781 : 0 : : (vec_nodes.size() == 1
782 : 0 : ? vec_nodes[0]
783 : 0 : : nodeManager()->mkNode(Kind::REGEXP_UNION, vec_nodes));
784 : 0 : break;
785 : 0 : }
786 : 0 : case Kind::REGEXP_INTER:
787 : : {
788 : 0 : bool flag = true;
789 : 0 : bool flag_sg = false;
790 : 0 : std::vector<Node> vec_nodes;
791 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
792 : : {
793 : 0 : Node dc = derivativeSingle(r[i], c);
794 [ - - ]: 0 : if (dc != d_emptyRegexp)
795 : : {
796 [ - - ]: 0 : if (dc == d_sigma_star)
797 : : {
798 : 0 : flag_sg = true;
799 : : }
800 : : else
801 : : {
802 : 0 : if (std::find(vec_nodes.begin(), vec_nodes.end(), dc)
803 [ - - ]: 0 : == vec_nodes.end())
804 : : {
805 : 0 : vec_nodes.push_back(dc);
806 : : }
807 : : }
808 : : }
809 : : else
810 : : {
811 : 0 : flag = false;
812 : 0 : break;
813 : : }
814 [ - - ]: 0 : }
815 [ - - ]: 0 : if (flag)
816 : : {
817 [ - - ][ - - ]: 0 : if (vec_nodes.size() == 0 && flag_sg)
[ - - ]
818 : : {
819 : 0 : retNode = d_sigma_star;
820 : : }
821 : : else
822 : : {
823 : 0 : retNode = vec_nodes.size() == 0
824 : 0 : ? d_emptyRegexp
825 : 0 : : (vec_nodes.size() == 1
826 : 0 : ? vec_nodes[0]
827 : : : nodeManager()->mkNode(Kind::REGEXP_INTER,
828 : 0 : vec_nodes));
829 : : }
830 : : }
831 : : else
832 : : {
833 : 0 : retNode = d_emptyRegexp;
834 : : }
835 : 0 : break;
836 : 0 : }
837 : 0 : case Kind::REGEXP_STAR:
838 : : {
839 : 0 : Node dc = derivativeSingle(r[0], c);
840 [ - - ]: 0 : if (dc != d_emptyRegexp)
841 : : {
842 : 0 : retNode = dc == d_emptySingleton
843 : 0 : ? r
844 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, dc, r);
845 : : }
846 : : else
847 : : {
848 : 0 : retNode = d_emptyRegexp;
849 : : }
850 : 0 : break;
851 : 0 : }
852 : 0 : case Kind::REGEXP_LOOP:
853 : : {
854 : 0 : uint32_t l = utils::getLoopMinOccurrences(r);
855 : 0 : uint32_t u = utils::getLoopMaxOccurrences(r);
856 [ - - ][ - - ]: 0 : if (l == u || l == 0)
857 : : {
858 : 0 : retNode = d_emptyRegexp;
859 : : }
860 : : else
861 : : {
862 : 0 : Node dc = derivativeSingle(r[0], c);
863 [ - - ]: 0 : if (dc != d_emptyRegexp)
864 : : {
865 [ - - ]: 0 : Node lop = nm->mkConst(RegExpLoop(l == 0 ? 0 : (l - 1), u - 1));
866 : 0 : Node r2 = nm->mkNode(Kind::REGEXP_LOOP, lop, r[0]);
867 : 0 : retNode = dc == d_emptySingleton
868 : 0 : ? r2
869 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, dc, r2);
870 : 0 : }
871 : : else
872 : : {
873 : 0 : retNode = d_emptyRegexp;
874 : : }
875 : 0 : }
876 : : // Trace("regexp-derive") << "RegExp-derive : REGEXP_LOOP returns /" <<
877 : : // mkString(retNode) << "/" << std::endl;
878 : 0 : break;
879 : : }
880 : 0 : case Kind::REGEXP_COMPLEMENT:
881 : : default:
882 : : {
883 : 0 : Trace("strings-error") << "Unsupported term: " << mkString(r)
884 : 0 : << " in derivative of RegExp." << std::endl;
885 : 0 : Unreachable();
886 : : break;
887 : : }
888 : : }
889 [ - - ]: 0 : if (retNode != d_emptyRegexp)
890 : : {
891 : 0 : retNode = rewrite(retNode);
892 : : }
893 : 0 : d_dv_cache[dv] = retNode;
894 : : }
895 : 0 : Trace("regexp-derive") << "RegExp-derive returns : /" << mkString(retNode)
896 : 0 : << "/" << std::endl;
897 : 0 : return retNode;
898 : 0 : }
899 : :
900 : 0 : void RegExpOpr::firstChars(Node r, std::set<unsigned>& pcset, SetNodes& pvset)
901 : : {
902 : 0 : Trace("regexp-fset") << "Start FSET(" << mkString(r) << ")" << std::endl;
903 : : std::map<Node, std::pair<std::set<unsigned>, SetNodes> >::const_iterator itr =
904 : 0 : d_fset_cache.find(r);
905 [ - - ]: 0 : if (itr != d_fset_cache.end())
906 : : {
907 : 0 : pcset.insert((itr->second).first.begin(), (itr->second).first.end());
908 : 0 : pvset.insert((itr->second).second.begin(), (itr->second).second.end());
909 : : }
910 : : else
911 : : {
912 : : // cset is code points
913 : 0 : std::set<unsigned> cset;
914 : 0 : SetNodes vset;
915 : 0 : Kind k = r.getKind();
916 [ - - ][ - - ]: 0 : switch (k)
[ - - ][ - - ]
[ - ]
917 : : {
918 : 0 : case Kind::REGEXP_NONE:
919 : : {
920 : 0 : break;
921 : : }
922 : 0 : case Kind::REGEXP_RANGE:
923 : : {
924 : 0 : unsigned a = r[0].getConst<String>().front();
925 : 0 : unsigned b = r[1].getConst<String>().front();
926 : 0 : Assert(a < b);
927 : 0 : Assert(b < std::numeric_limits<unsigned>::max());
928 [ - - ]: 0 : for (unsigned c = a; c <= b; c++)
929 : : {
930 : 0 : cset.insert(c);
931 : : }
932 : 0 : break;
933 : : }
934 : 0 : case Kind::STRING_TO_REGEXP:
935 : : {
936 : 0 : Node st = rewrite(r[0]);
937 [ - - ]: 0 : if (st.isConst())
938 : : {
939 : 0 : String s = st.getConst<String>();
940 [ - - ]: 0 : if (s.size() != 0)
941 : : {
942 : 0 : unsigned sc = s.front();
943 : 0 : cset.insert(sc);
944 : : }
945 : 0 : }
946 [ - - ]: 0 : else if (st.getKind() == Kind::STRING_CONCAT)
947 : : {
948 [ - - ]: 0 : if (st[0].isConst())
949 : : {
950 : 0 : String s = st[0].getConst<String>();
951 : 0 : unsigned sc = s.front();
952 : 0 : cset.insert(sc);
953 : 0 : }
954 : : else
955 : : {
956 : 0 : vset.insert(st[0]);
957 : : }
958 : : }
959 : : else
960 : : {
961 : 0 : vset.insert(st);
962 : : }
963 : 0 : break;
964 : 0 : }
965 : 0 : case Kind::REGEXP_CONCAT:
966 : : {
967 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); i++)
968 : : {
969 : 0 : firstChars(r[i], cset, vset);
970 : 0 : Node n = r[i];
971 : 0 : Node exp;
972 [ - - ]: 0 : if (delta(n, exp) != 1)
973 : : {
974 : 0 : break;
975 : : }
976 [ - - ][ - - ]: 0 : }
977 : 0 : break;
978 : : }
979 : 0 : case Kind::REGEXP_UNION:
980 : : {
981 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); i++)
982 : : {
983 : 0 : firstChars(r[i], cset, vset);
984 : : }
985 : 0 : break;
986 : : }
987 : 0 : case Kind::REGEXP_INTER:
988 : : {
989 : : // TODO: Overapproximation for now
990 : : // for(unsigned i=0; i<r.getNumChildren(); i++) {
991 : : // firstChars(r[i], cset, vset);
992 : : // }
993 : 0 : firstChars(r[0], cset, vset);
994 : 0 : break;
995 : : }
996 : 0 : case Kind::REGEXP_STAR:
997 : : {
998 : 0 : firstChars(r[0], cset, vset);
999 : 0 : break;
1000 : : }
1001 : 0 : case Kind::REGEXP_LOOP:
1002 : : {
1003 : 0 : firstChars(r[0], cset, vset);
1004 : 0 : break;
1005 : : }
1006 : 0 : case Kind::REGEXP_ALLCHAR:
1007 : : case Kind::REGEXP_COMPLEMENT:
1008 : : default:
1009 : : {
1010 : : // we do not expect to call this function on regular expressions that
1011 : : // aren't a standard regular expression kind. However, if we do, then
1012 : : // the following code is conservative and says that the current
1013 : : // regular expression can begin with any character.
1014 : 0 : Assert(utils::isRegExpKind(k));
1015 : : // can start with any character
1016 : 0 : Assert(d_lastchar < std::numeric_limits<unsigned>::max());
1017 [ - - ]: 0 : for (unsigned i = 0; i <= d_lastchar; i++)
1018 : : {
1019 : 0 : cset.insert(i);
1020 : : }
1021 : 0 : break;
1022 : : }
1023 : : }
1024 : 0 : pcset.insert(cset.begin(), cset.end());
1025 : 0 : pvset.insert(vset.begin(), vset.end());
1026 : 0 : std::pair<std::set<unsigned>, SetNodes> p(cset, vset);
1027 : 0 : d_fset_cache[r] = p;
1028 : 0 : }
1029 : :
1030 [ - - ]: 0 : if (TraceIsOn("regexp-fset"))
1031 : : {
1032 : 0 : Trace("regexp-fset") << "END FSET(" << mkString(r) << ") = {";
1033 : 0 : for (std::set<unsigned>::const_iterator it = pcset.begin();
1034 [ - - ]: 0 : it != pcset.end();
1035 : 0 : ++it)
1036 : : {
1037 [ - - ]: 0 : if (it != pcset.begin())
1038 : : {
1039 [ - - ]: 0 : Trace("regexp-fset") << ",";
1040 : : }
1041 [ - - ]: 0 : Trace("regexp-fset") << (*it);
1042 : : }
1043 [ - - ]: 0 : Trace("regexp-fset") << "}" << std::endl;
1044 : : }
1045 : 0 : }
1046 : :
1047 : 922 : Node RegExpOpr::simplify(Node t, bool polarity)
1048 : : {
1049 [ + - ]: 1844 : Trace("strings-regexp-simpl")
1050 : 922 : << "RegExpOpr::simplify: " << t << ", polarity=" << polarity << std::endl;
1051 [ - + ][ - + ]: 922 : Assert(t.getKind() == Kind::STRING_IN_REGEXP);
[ - - ]
1052 [ + + ]: 922 : Node tlit = polarity ? t : t.notNode();
1053 : 922 : Node conc;
1054 : 922 : std::map<Node, Node>::const_iterator itr = d_simpCache.find(tlit);
1055 [ + + ]: 922 : if (itr != d_simpCache.end())
1056 : : {
1057 : 8 : return itr->second;
1058 : : }
1059 [ + + ]: 914 : if (polarity)
1060 : : {
1061 : 843 : std::vector<Node> newSkolems;
1062 : 843 : conc = reduceRegExpPos(nodeManager(), tlit, d_sc, newSkolems);
1063 : 843 : }
1064 : : else
1065 : : {
1066 : : // see if we can use an optimized version of the reduction for re.++.
1067 : 71 : Node r = t[1];
1068 [ + + ]: 71 : if (r.getKind() == Kind::REGEXP_CONCAT)
1069 : : {
1070 : : // the index we are removing from the RE concatenation
1071 : : bool isRev;
1072 : : // As an optimization to the reduction, if we can determine that
1073 : : // all strings in the language of R1 have the same length, say n,
1074 : : // then the conclusion of the reduction is quantifier-free:
1075 : : // ~( substr(s,0,n) in R1 ) OR ~( substr(s,len(s)-n,n) in R2)
1076 : 65 : Node reLen = getRegExpConcatFixed(r, isRev);
1077 [ + + ]: 65 : if (!reLen.isNull())
1078 : : {
1079 : 59 : conc = reduceRegExpNegConcatFixed(nodeManager(), tlit, reLen, isRev);
1080 : : }
1081 : 65 : }
1082 [ + + ]: 71 : if (conc.isNull())
1083 : : {
1084 : 12 : conc = reduceRegExpNeg(nodeManager(), tlit);
1085 : : }
1086 : 71 : }
1087 : 914 : d_simpCache[tlit] = conc;
1088 [ + - ]: 1828 : Trace("strings-regexp-simpl")
1089 : 914 : << "RegExpOpr::simplify: returns " << conc << std::endl;
1090 : 914 : return conc;
1091 : 922 : }
1092 : :
1093 : 93 : Node RegExpOpr::getRegExpConcatFixed(Node r, bool& isRev)
1094 : : {
1095 [ - + ][ - + ]: 93 : Assert(r.getKind() == Kind::REGEXP_CONCAT);
[ - - ]
1096 : 93 : isRev = false;
1097 : 186 : Node reLen = RegExpEntail::getFixedLengthForRegexp(r[0]);
1098 [ + + ]: 93 : if (!reLen.isNull())
1099 : : {
1100 : 50 : return reLen;
1101 : : }
1102 : : // try from the opposite end
1103 : 43 : size_t indexE = r.getNumChildren() - 1;
1104 : 43 : reLen = RegExpEntail::getFixedLengthForRegexp(r[indexE]);
1105 [ + + ]: 43 : if (!reLen.isNull())
1106 : : {
1107 : 37 : isRev = true;
1108 : 37 : return reLen;
1109 : : }
1110 : 6 : return Node::null();
1111 : 93 : }
1112 : :
1113 : 12 : Node RegExpOpr::reduceRegExpNeg(NodeManager* nm, Node mem)
1114 : : {
1115 : 12 : Assert(mem.getKind() == Kind::NOT
1116 : : && mem[0].getKind() == Kind::STRING_IN_REGEXP);
1117 : 12 : Node s = mem[0][0];
1118 : 12 : Node r = mem[0][1];
1119 : 12 : Kind k = r.getKind();
1120 : 12 : Node zero = nm->mkConstInt(Rational(0));
1121 : 12 : Node conc;
1122 [ + + ]: 12 : if (k == Kind::REGEXP_CONCAT)
1123 : : {
1124 : : // do not use length entailment, call regular expression concat
1125 : 6 : Node reLen;
1126 : 6 : conc = reduceRegExpNegConcatFixed(nm, mem, reLen, false);
1127 : 6 : }
1128 [ + - ]: 6 : else if (k == Kind::REGEXP_STAR)
1129 : : {
1130 : 6 : Node emp = Word::mkEmptyWord(s.getType());
1131 : 6 : Node lens = nm->mkNode(Kind::STRING_LENGTH, s);
1132 : 6 : Node sne = s.eqNode(emp).negate();
1133 : 6 : Node b1 = SkolemCache::mkIndexVar(nm, mem);
1134 : 6 : Node b1v = nm->mkNode(Kind::BOUND_VAR_LIST, b1);
1135 : 12 : Node g11n = nm->mkNode(Kind::LEQ, b1, zero);
1136 : 12 : Node g12n = nm->mkNode(Kind::LT, lens, b1);
1137 : : // internal
1138 : 12 : Node s1 = utils::mkPrefix(s, b1);
1139 : 12 : Node s2 = utils::mkSuffix(s, b1);
1140 : 12 : Node s1r1 = nm->mkNode(Kind::STRING_IN_REGEXP, s1, r[0]).negate();
1141 : 12 : Node s2r2 = nm->mkNode(Kind::STRING_IN_REGEXP, s2, r).negate();
1142 : :
1143 [ + + ][ - - ]: 30 : conc = nm->mkNode(Kind::OR, {g11n, g12n, s1r1, s2r2});
1144 : : // must mark as an internal quantifier
1145 : 6 : conc = utils::mkForallInternal(nm, b1v, conc);
1146 : 6 : conc = nm->mkNode(Kind::AND, sne, conc);
1147 : 6 : }
1148 : : else
1149 : : {
1150 : 0 : Assert(!utils::isRegExpKind(k));
1151 : : }
1152 : 24 : return conc;
1153 : 12 : }
1154 : :
1155 : 107 : Node RegExpOpr::reduceRegExpNegConcatFixed(NodeManager* nm,
1156 : : Node mem,
1157 : : Node reLen,
1158 : : bool isRev)
1159 : : {
1160 : 107 : Assert(mem.getKind() == Kind::NOT
1161 : : && mem[0].getKind() == Kind::STRING_IN_REGEXP);
1162 : 107 : Node s = mem[0][0];
1163 : 107 : Node r = mem[0][1];
1164 [ - + ][ - + ]: 107 : Assert(r.getKind() == Kind::REGEXP_CONCAT);
[ - - ]
1165 : 107 : Node zero = nm->mkConstInt(Rational(0));
1166 : : // The following simplification states that
1167 : : // ~( s in R1 ++ R2 ++... ++ Rn )
1168 : : // is equivalent to
1169 : : // forall x.
1170 : : // 0 <= x <= len(s) =>
1171 : : // ~(substr(s,0,x) in R1) OR ~(substr(s,x,len(s)-x) in R2 ++ ... ++ Rn)
1172 : : // Index is the child index of r that we are stripping off, which is either
1173 : : // from the beginning or the end.
1174 : 107 : Node lens = nm->mkNode(Kind::STRING_LENGTH, s);
1175 : 107 : Node b1;
1176 : 107 : Node b1v;
1177 : 107 : Node guard1n, guard2n;
1178 [ + + ]: 107 : if (reLen.isNull())
1179 : : {
1180 : 6 : b1 = SkolemCache::mkIndexVar(nm, mem);
1181 : 6 : b1v = nm->mkNode(Kind::BOUND_VAR_LIST, b1);
1182 : 6 : guard1n = nm->mkNode(Kind::LT, b1, zero);
1183 : 6 : guard2n = nm->mkNode(Kind::LT, nm->mkNode(Kind::STRING_LENGTH, s), b1);
1184 : : }
1185 : : else
1186 : : {
1187 : 101 : b1 = reLen;
1188 : : }
1189 : 107 : Node s1;
1190 : 107 : Node s2;
1191 [ + + ]: 107 : if (!isRev)
1192 : : {
1193 : 64 : s1 = utils::mkPrefix(s, b1);
1194 : 64 : s2 = utils::mkSuffix(s, b1);
1195 : : }
1196 : : else
1197 : : {
1198 : 43 : s1 = utils::mkSuffixOfLen(s, b1);
1199 : 43 : s2 = utils::mkPrefix(s, nm->mkNode(Kind::SUB, lens, b1));
1200 : : }
1201 [ + + ]: 107 : size_t index = isRev ? r.getNumChildren() - 1 : 0;
1202 : 214 : Node s1r1 = nm->mkNode(Kind::STRING_IN_REGEXP, s1, r[index]).negate();
1203 : 107 : std::vector<Node> nvec;
1204 [ + + ]: 427 : for (unsigned i = 0, nchild = r.getNumChildren(); i < nchild; i++)
1205 : : {
1206 [ + + ]: 320 : if (i != index)
1207 : : {
1208 : 213 : nvec.push_back(r[i]);
1209 : : }
1210 : : }
1211 [ + + ]: 107 : Node r2 = nvec.size() == 1 ? nvec[0] : nm->mkNode(Kind::REGEXP_CONCAT, nvec);
1212 : 214 : Node s2r2 = nm->mkNode(Kind::STRING_IN_REGEXP, s2, r2).negate();
1213 : 107 : Node conc;
1214 [ + + ]: 107 : if (!b1v.isNull())
1215 : : {
1216 [ + + ][ - - ]: 30 : conc = nm->mkNode(Kind::OR, {guard1n, guard2n, s1r1, s2r2});
1217 : : // must mark as an internal quantifier
1218 : 6 : conc = utils::mkForallInternal(nm, b1v, conc);
1219 : : }
1220 : : else
1221 : : {
1222 : 101 : conc = nm->mkNode(Kind::OR, s1r1, s2r2);
1223 : : }
1224 : 214 : return conc;
1225 : 107 : }
1226 : :
1227 : 1832 : Node RegExpOpr::reduceRegExpPos(NodeManager* nm,
1228 : : Node mem,
1229 : : SkolemCache* sc,
1230 : : std::vector<Node>& newSkolems)
1231 : : {
1232 [ - + ][ - + ]: 1832 : Assert(mem.getKind() == Kind::STRING_IN_REGEXP);
[ - - ]
1233 : 1832 : Node s = mem[0];
1234 : 1832 : Node r = mem[1];
1235 : 1832 : Kind k = r.getKind();
1236 : 1832 : Node conc;
1237 [ + + ]: 1832 : if (k == Kind::REGEXP_CONCAT)
1238 : : {
1239 : 1336 : std::vector<Node> nvec;
1240 : 1336 : std::vector<Node> cc;
1241 : 1336 : SkolemManager* sm = nm->getSkolemManager();
1242 : : // Look up skolems for each of the components. If sc has optimizations
1243 : : // enabled, this will return arguments of str.to_re.
1244 [ + + ]: 5026 : for (unsigned i = 0, nchild = r.getNumChildren(); i < nchild; ++i)
1245 : : {
1246 [ + + ]: 3690 : if (r[i].getKind() == Kind::STRING_TO_REGEXP)
1247 : : {
1248 : : // optimization, just take the body
1249 : 888 : newSkolems.push_back(r[i][0]);
1250 : : }
1251 : : else
1252 : : {
1253 : 2802 : Node ivalue = nm->mkConstInt(Rational(i));
1254 : 8406 : Node sk = sm->mkSkolemFunction(SkolemId::RE_UNFOLD_POS_COMPONENT,
1255 : 5604 : {mem[0], mem[1], ivalue});
1256 : 2802 : newSkolems.push_back(sk);
1257 : 2802 : nvec.push_back(nm->mkNode(Kind::STRING_IN_REGEXP, newSkolems[i], r[i]));
1258 : 2802 : }
1259 : : }
1260 : : // (str.in_re x (re.++ R0 .... Rn)) =>
1261 : : // (and (= x (str.++ k0 ... kn)) (str.in_re k0 R0) ... (str.in_re kn Rn) )
1262 : 1336 : Node lem = s.eqNode(nm->mkNode(Kind::STRING_CONCAT, newSkolems));
1263 : 1336 : nvec.insert(nvec.begin(), lem);
1264 [ - + ]: 1336 : conc = nvec.size() == 1 ? nvec[0] : nm->mkNode(Kind::AND, nvec);
1265 : 1336 : }
1266 [ + - ]: 496 : else if (k == Kind::REGEXP_STAR)
1267 : : {
1268 : 496 : Node emp = Word::mkEmptyWord(s.getType());
1269 : 496 : Node se = s.eqNode(emp);
1270 : 992 : Node sinr = nm->mkNode(Kind::STRING_IN_REGEXP, s, r[0]);
1271 : 496 : Node empr = nm->mkNode(Kind::STRING_TO_REGEXP, emp);
1272 : 992 : Node rd = nm->mkNode(Kind::REGEXP_DIFF, r[0], empr);
1273 : 992 : Node reExpand = nm->mkNode(Kind::REGEXP_CONCAT, rd, r, rd);
1274 : 992 : Node sinRExp = nm->mkNode(Kind::STRING_IN_REGEXP, s, reExpand);
1275 : : // We unfold `x in R*` by considering three cases: `x` is empty, `x`
1276 : : // is matched by `R`, or `x` is matched by two or more `R`s. For the
1277 : : // last case, `x` will break into three pieces, making the beginning
1278 : : // and the end each match `R` and the middle match `R*`. Matching the
1279 : : // beginning and the end with `R` allows us to reason about the
1280 : : // beginning and the end of `x` simultaneously.
1281 : : //
1282 : : // x in R* ---> (x = "") v (x in R) v (x in (re.++ R (re.* R) R))
1283 : :
1284 : : // We also immediately unfold the last disjunct for re.*. The advantage
1285 : : // of doing this is that we use the same scheme for skolems above.
1286 : 496 : std::vector<Node> newSkolemsC;
1287 : 496 : sinRExp = reduceRegExpPos(nm, sinRExp, sc, newSkolemsC);
1288 [ - + ][ - + ]: 496 : Assert(newSkolemsC.size() == 3);
[ - - ]
1289 : : // make the return lemma
1290 : : // can also assume the component match the first and last R are non-empty.
1291 : : // This means that the overall conclusion is:
1292 : : // (x = "") v (x in R) v (x = (str.++ k1 k2 k3) ^
1293 : : // k1 in (R \ "") ^ k2 in (re.* R) ^ k3 in (R \ ""))
1294 : 496 : conc = nm->mkNode(Kind::OR, se, sinr, sinRExp);
1295 : 496 : }
1296 : : else
1297 : : {
1298 : 0 : Assert(!utils::isRegExpKind(k));
1299 : : }
1300 : 3664 : return conc;
1301 : 1832 : }
1302 : :
1303 : 0 : bool RegExpOpr::isPairNodesInSet(std::set<PairNodes>& s, Node n1, Node n2)
1304 : : {
1305 [ - - ]: 0 : for (std::set<PairNodes>::const_iterator itr = s.begin(); itr != s.end();
1306 : 0 : ++itr)
1307 : : {
1308 [ - - ]: 0 : if ((itr->first == n1 && itr->second == n2)
1309 : 0 : || (itr->first == n2 && itr->second == n1))
1310 : : {
1311 : 0 : return true;
1312 : : }
1313 : : }
1314 : 0 : return false;
1315 : : }
1316 : :
1317 : 0 : bool RegExpOpr::containC2(unsigned cnt, Node n)
1318 : : {
1319 [ - - ]: 0 : if (n.getKind() == Kind::REGEXP_RV)
1320 : : {
1321 : 0 : Assert(n[0].getConst<Rational>() <= Rational(String::maxSize()))
1322 : 0 : << "Exceeded UINT32_MAX in RegExpOpr::containC2";
1323 : 0 : unsigned y = n[0].getConst<Rational>().getNumerator().toUnsignedInt();
1324 : 0 : return cnt == y;
1325 : : }
1326 [ - - ]: 0 : else if (n.getKind() == Kind::REGEXP_CONCAT)
1327 : : {
1328 [ - - ]: 0 : for (unsigned i = 0; i < n.getNumChildren(); i++)
1329 : : {
1330 [ - - ]: 0 : if (containC2(cnt, n[i]))
1331 : : {
1332 : 0 : return true;
1333 : : }
1334 : : }
1335 : : }
1336 [ - - ]: 0 : else if (n.getKind() == Kind::REGEXP_STAR)
1337 : : {
1338 : 0 : return containC2(cnt, n[0]);
1339 : : }
1340 [ - - ]: 0 : else if (n.getKind() == Kind::REGEXP_LOOP)
1341 : : {
1342 : 0 : return containC2(cnt, n[0]);
1343 : : }
1344 [ - - ]: 0 : else if (n.getKind() == Kind::REGEXP_UNION)
1345 : : {
1346 [ - - ]: 0 : for (unsigned i = 0; i < n.getNumChildren(); i++)
1347 : : {
1348 [ - - ]: 0 : if (containC2(cnt, n[i]))
1349 : : {
1350 : 0 : return true;
1351 : : }
1352 : : }
1353 : : }
1354 : 0 : return false;
1355 : : }
1356 : 0 : Node RegExpOpr::convert1(unsigned cnt, Node n)
1357 : : {
1358 [ - - ]: 0 : Trace("regexp-debug") << "Converting " << n << " at " << cnt << "... "
1359 : 0 : << std::endl;
1360 : 0 : Node r1, r2;
1361 : 0 : convert2(cnt, n, r1, r2);
1362 [ - - ]: 0 : Trace("regexp-debug") << "... getting r1=" << r1 << ", and r2=" << r2
1363 : 0 : << std::endl;
1364 : : Node ret =
1365 : 0 : r1 == d_emptySingleton
1366 : : ? r2
1367 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT,
1368 : 0 : nodeManager()->mkNode(Kind::REGEXP_STAR, r1),
1369 : 0 : r2);
1370 : 0 : ret = rewrite(ret);
1371 [ - - ]: 0 : Trace("regexp-debug") << "... done convert at " << cnt << ", with return "
1372 : 0 : << ret << std::endl;
1373 : 0 : return ret;
1374 : 0 : }
1375 : 0 : void RegExpOpr::convert2(unsigned cnt, Node n, Node& r1, Node& r2)
1376 : : {
1377 [ - - ]: 0 : if (n == d_emptyRegexp)
1378 : : {
1379 : 0 : r1 = d_emptyRegexp;
1380 : 0 : r2 = d_emptyRegexp;
1381 : 0 : return;
1382 : : }
1383 [ - - ]: 0 : else if (n == d_emptySingleton)
1384 : : {
1385 : 0 : r1 = d_emptySingleton;
1386 : 0 : r2 = d_emptySingleton;
1387 : : }
1388 : 0 : Kind nk = n.getKind();
1389 [ - - ]: 0 : if (nk == Kind::REGEXP_RV)
1390 : : {
1391 : 0 : Assert(n[0].getConst<Rational>() <= Rational(String::maxSize()))
1392 : 0 : << "Exceeded UINT32_MAX in RegExpOpr::convert2";
1393 : 0 : unsigned y = n[0].getConst<Rational>().getNumerator().toUnsignedInt();
1394 : 0 : r1 = d_emptySingleton;
1395 [ - - ]: 0 : if (cnt == y)
1396 : : {
1397 : 0 : r2 = d_emptyRegexp;
1398 : : }
1399 : : else
1400 : : {
1401 : 0 : r2 = n;
1402 : : }
1403 : : }
1404 [ - - ]: 0 : else if (nk == Kind::REGEXP_CONCAT)
1405 : : {
1406 : 0 : bool flag = true;
1407 : 0 : std::vector<Node> vr1, vr2;
1408 [ - - ]: 0 : for (unsigned i = 0; i < n.getNumChildren(); i++)
1409 : : {
1410 [ - - ]: 0 : if (containC2(cnt, n[i]))
1411 : : {
1412 : 0 : Node t1, t2;
1413 : 0 : convert2(cnt, n[i], t1, t2);
1414 : 0 : vr1.push_back(t1);
1415 : 0 : r1 = vr1.size() == 0 ? d_emptyRegexp
1416 : 0 : : vr1.size() == 1
1417 : 0 : ? vr1[0]
1418 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, vr1);
1419 : 0 : vr2.push_back(t2);
1420 [ - - ]: 0 : for (unsigned j = i + 1; j < n.getNumChildren(); j++)
1421 : : {
1422 : 0 : vr2.push_back(n[j]);
1423 : : }
1424 : 0 : r2 = vr2.size() == 0 ? d_emptyRegexp
1425 : 0 : : vr2.size() == 1
1426 : 0 : ? vr2[0]
1427 : 0 : : nodeManager()->mkNode(Kind::REGEXP_CONCAT, vr2);
1428 : 0 : flag = false;
1429 : 0 : break;
1430 : 0 : }
1431 : : else
1432 : : {
1433 : 0 : vr1.push_back(n[i]);
1434 : : }
1435 : : }
1436 [ - - ]: 0 : if (flag)
1437 : : {
1438 : 0 : r1 = d_emptySingleton;
1439 : 0 : r2 = n;
1440 : : }
1441 : 0 : }
1442 [ - - ]: 0 : else if (nk == Kind::REGEXP_UNION)
1443 : : {
1444 : 0 : std::vector<Node> vr1, vr2;
1445 [ - - ]: 0 : for (unsigned i = 0; i < n.getNumChildren(); i++)
1446 : : {
1447 : 0 : Node t1, t2;
1448 : 0 : convert2(cnt, n[i], t1, t2);
1449 : 0 : vr1.push_back(t1);
1450 : 0 : vr2.push_back(t2);
1451 : 0 : }
1452 : 0 : r1 = nodeManager()->mkNode(Kind::REGEXP_UNION, vr1);
1453 : 0 : r2 = nodeManager()->mkNode(Kind::REGEXP_UNION, vr2);
1454 : 0 : }
1455 [ - - ][ - - ]: 0 : else if (nk == Kind::STRING_TO_REGEXP || nk == Kind::REGEXP_ALLCHAR
1456 [ - - ][ - - ]: 0 : || nk == Kind::REGEXP_RANGE || nk == Kind::REGEXP_COMPLEMENT
1457 [ - - ]: 0 : || nk == Kind::REGEXP_LOOP)
1458 : : {
1459 : : // this leaves n unchanged
1460 : 0 : r1 = d_emptySingleton;
1461 : 0 : r2 = n;
1462 : : }
1463 : : else
1464 : : {
1465 : : // is it possible?
1466 : 0 : Unreachable();
1467 : : }
1468 : : }
1469 : :
1470 : 0 : Node RegExpOpr::intersectInternal(Node r1,
1471 : : Node r2,
1472 : : std::map<PairNodes, Node> cache,
1473 : : unsigned cnt)
1474 : : {
1475 : : // Assert(checkConstRegExp(r1) && checkConstRegExp(r2));
1476 [ - - ]: 0 : if (r1 > r2)
1477 : : {
1478 : 0 : TNode tmpNode = r1;
1479 : 0 : r1 = r2;
1480 : 0 : r2 = tmpNode;
1481 : 0 : }
1482 : 0 : NodeManager* nm = nodeManager();
1483 [ - - ]: 0 : Trace("regexp-int") << "Starting INTERSECT(" << cnt << "):\n "
1484 : 0 : << mkString(r1) << ",\n " << mkString(r2) << std::endl;
1485 : 0 : std::pair<Node, Node> p(r1, r2);
1486 : 0 : std::map<PairNodes, Node>::const_iterator itr = d_inter_cache.find(p);
1487 : 0 : Node rNode;
1488 [ - - ]: 0 : if (itr != d_inter_cache.end())
1489 : : {
1490 : 0 : rNode = itr->second;
1491 : : }
1492 : : else
1493 : : {
1494 [ - - ]: 0 : Trace("regexp-int-debug") << " ... not in cache" << std::endl;
1495 : 0 : if (r1 == d_emptyRegexp || r2 == d_emptyRegexp)
1496 : : {
1497 [ - - ]: 0 : Trace("regexp-int-debug") << " ... one is empty set" << std::endl;
1498 : 0 : rNode = d_emptyRegexp;
1499 : : }
1500 : 0 : else if (r1 == d_emptySingleton || r2 == d_emptySingleton)
1501 : : {
1502 [ - - ]: 0 : Trace("regexp-int-debug") << " ... one is empty singleton" << std::endl;
1503 : 0 : Node exp;
1504 [ - - ]: 0 : int r = delta((r1 == d_emptySingleton ? r2 : r1), exp);
1505 [ - - ]: 0 : if (r == 0)
1506 : : {
1507 : : // TODO: variable
1508 : 0 : Unreachable();
1509 : : }
1510 [ - - ]: 0 : else if (r == 1)
1511 : : {
1512 : 0 : rNode = d_emptySingleton;
1513 : : }
1514 : : else
1515 : : {
1516 : 0 : rNode = d_emptyRegexp;
1517 : : }
1518 : 0 : }
1519 [ - - ]: 0 : else if (r1 == r2)
1520 : : {
1521 [ - - ]: 0 : Trace("regexp-int-debug") << " ... equal" << std::endl;
1522 : 0 : rNode = r1; // convert1(cnt, r1);
1523 : : }
1524 : : else
1525 : : {
1526 [ - - ]: 0 : Trace("regexp-int-debug") << " ... normal checking" << std::endl;
1527 : 0 : std::map<PairNodes, Node>::const_iterator itrcache = cache.find(p);
1528 [ - - ]: 0 : if (itrcache != cache.end())
1529 : : {
1530 : 0 : rNode = itrcache->second;
1531 : : }
1532 : : else
1533 : : {
1534 [ - - ]: 0 : Trace("regexp-int-debug") << " ... normal without cache" << std::endl;
1535 : 0 : std::vector<unsigned> cset;
1536 : 0 : std::set<unsigned> cset1, cset2;
1537 : 0 : std::set<Node> vset1, vset2;
1538 : 0 : firstChars(r1, cset1, vset1);
1539 : 0 : firstChars(r2, cset2, vset2);
1540 [ - - ]: 0 : Trace("regexp-int-debug") << " ... got fset" << std::endl;
1541 : 0 : std::set_intersection(cset1.begin(),
1542 : : cset1.end(),
1543 : : cset2.begin(),
1544 : : cset2.end(),
1545 : : std::inserter(cset, cset.begin()));
1546 : 0 : std::vector<Node> vec_nodes;
1547 : 0 : Node delta_exp;
1548 [ - - ]: 0 : Trace("regexp-int-debug") << " ... try delta" << std::endl;
1549 : 0 : int flag = delta(r1, delta_exp);
1550 : 0 : int flag2 = delta(r2, delta_exp);
1551 [ - - ]: 0 : Trace("regexp-int-debug")
1552 : 0 : << " ... delta1=" << flag << ", delta2=" << flag2 << std::endl;
1553 [ - - ][ - - ]: 0 : if (flag != 2 && flag2 != 2)
1554 : : {
1555 [ - - ][ - - ]: 0 : if (flag == 1 && flag2 == 1)
1556 : : {
1557 : 0 : vec_nodes.push_back(d_emptySingleton);
1558 : : }
1559 : : else
1560 : : {
1561 : : // TODO: variable
1562 : 0 : Unreachable();
1563 : : }
1564 : : }
1565 [ - - ]: 0 : if (TraceIsOn("regexp-int-debug"))
1566 : : {
1567 [ - - ]: 0 : Trace("regexp-int-debug") << "Try CSET(" << cset.size() << ") = {";
1568 : 0 : for (std::vector<unsigned>::const_iterator it = cset.begin();
1569 [ - - ]: 0 : it != cset.end();
1570 : 0 : ++it)
1571 : : {
1572 [ - - ]: 0 : if (it != cset.begin())
1573 : : {
1574 [ - - ]: 0 : Trace("regexp-int-debug") << ", ";
1575 : : }
1576 [ - - ]: 0 : Trace("regexp-int-debug") << (*it);
1577 : : }
1578 [ - - ]: 0 : Trace("regexp-int-debug") << std::endl;
1579 : : }
1580 : 0 : std::map<PairNodes, Node> cacheX;
1581 : 0 : for (std::vector<unsigned>::const_iterator it = cset.begin();
1582 [ - - ]: 0 : it != cset.end();
1583 : 0 : ++it)
1584 : : {
1585 : 0 : std::vector<unsigned> cvec;
1586 : 0 : cvec.push_back(*it);
1587 : 0 : String c(cvec);
1588 [ - - ]: 0 : Trace("regexp-int-debug")
1589 : 0 : << "Try character " << c << " ... " << std::endl;
1590 : 0 : Node r1l = derivativeSingle(r1, c);
1591 : 0 : Node r2l = derivativeSingle(r2, c);
1592 [ - - ]: 0 : Trace("regexp-int-debug")
1593 : 0 : << " ... got partial(r1,c) = " << mkString(r1l) << std::endl;
1594 [ - - ]: 0 : Trace("regexp-int-debug")
1595 : 0 : << " ... got partial(r2,c) = " << mkString(r2l) << std::endl;
1596 : 0 : Node rt;
1597 : :
1598 [ - - ]: 0 : if (r1l > r2l)
1599 : : {
1600 : 0 : Node tnode = r1l;
1601 : 0 : r1l = r2l;
1602 : 0 : r2l = tnode;
1603 : 0 : }
1604 : 0 : PairNodes pp(r1l, r2l);
1605 : 0 : std::map<PairNodes, Node>::const_iterator itr2 = cacheX.find(pp);
1606 [ - - ]: 0 : if (itr2 != cacheX.end())
1607 : : {
1608 : 0 : rt = itr2->second;
1609 : : }
1610 : : else
1611 : : {
1612 : 0 : std::map<PairNodes, Node> cache2(cache);
1613 : 0 : cache2[p] =
1614 : 0 : nm->mkNode(Kind::REGEXP_RV, nm->mkConstInt(Rational(cnt)));
1615 : 0 : rt = intersectInternal(r1l, r2l, cache2, cnt + 1);
1616 : 0 : cacheX[pp] = rt;
1617 : 0 : }
1618 : :
1619 : 0 : rt = rewrite(
1620 : 0 : nm->mkNode(Kind::REGEXP_CONCAT,
1621 : 0 : nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(c)),
1622 : 0 : rt));
1623 : :
1624 [ - - ]: 0 : Trace("regexp-int-debug")
1625 : 0 : << " ... got p(r1,c) && p(r2,c) = " << mkString(rt) << std::endl;
1626 : 0 : vec_nodes.push_back(rt);
1627 : 0 : }
1628 : 0 : rNode = rewrite(vec_nodes.size() == 0 ? d_emptyRegexp
1629 : 0 : : vec_nodes.size() == 1
1630 : 0 : ? vec_nodes[0]
1631 : 0 : : nm->mkNode(Kind::REGEXP_UNION, vec_nodes));
1632 : 0 : rNode = convert1(cnt, rNode);
1633 : 0 : rNode = rewrite(rNode);
1634 : 0 : }
1635 : : }
1636 [ - - ]: 0 : Trace("regexp-int-debug")
1637 : 0 : << " ... try testing no RV of " << mkString(rNode) << std::endl;
1638 [ - - ]: 0 : if (!expr::hasSubtermKind(Kind::REGEXP_RV, rNode))
1639 : : {
1640 : 0 : d_inter_cache[p] = rNode;
1641 : : }
1642 : : }
1643 : 0 : Trace("regexp-int") << "End(" << cnt << ") of INTERSECT( " << mkString(r1)
1644 : 0 : << ", " << mkString(r2) << " ) = " << mkString(rNode)
1645 : 0 : << std::endl;
1646 : 0 : return rNode;
1647 : 0 : }
1648 : :
1649 : 0 : Node RegExpOpr::removeIntersection(Node r)
1650 : : {
1651 : 0 : Assert(checkConstRegExp(r));
1652 : 0 : NodeManager* nm = nodeManager();
1653 : 0 : std::unordered_map<TNode, Node> visited;
1654 : 0 : std::unordered_map<TNode, Node>::iterator it;
1655 : 0 : std::vector<TNode> visit;
1656 : 0 : TNode cur;
1657 : 0 : visit.push_back(r);
1658 : : do
1659 : : {
1660 : 0 : cur = visit.back();
1661 : 0 : visit.pop_back();
1662 : 0 : it = visited.find(cur);
1663 : :
1664 [ - - ]: 0 : if (it == visited.end())
1665 : : {
1666 : 0 : visited[cur] = Node::null();
1667 : 0 : visit.push_back(cur);
1668 [ - - ]: 0 : for (const Node& cn : cur)
1669 : : {
1670 : 0 : visit.push_back(cn);
1671 : 0 : }
1672 : : }
1673 [ - - ]: 0 : else if (it->second.isNull())
1674 : : {
1675 : 0 : Kind ck = cur.getKind();
1676 : 0 : Node ret;
1677 : 0 : bool childChanged = false;
1678 : 0 : std::vector<Node> children;
1679 [ - - ]: 0 : if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
1680 : : {
1681 : 0 : children.push_back(cur.getOperator());
1682 : : }
1683 [ - - ]: 0 : for (const Node& cn : cur)
1684 : : {
1685 : 0 : it = visited.find(cn);
1686 : 0 : Assert(it != visited.end());
1687 : 0 : Assert(!it->second.isNull());
1688 [ - - ]: 0 : if (ck == Kind::REGEXP_INTER)
1689 : : {
1690 [ - - ]: 0 : if (ret.isNull())
1691 : : {
1692 : 0 : ret = it->second;
1693 : : }
1694 : : else
1695 : : {
1696 : 0 : ret = intersect(ret, it->second);
1697 : : }
1698 : : }
1699 : : else
1700 : : {
1701 : : // will construct below
1702 : 0 : childChanged = childChanged || cn != it->second;
1703 : 0 : children.push_back(it->second);
1704 : : }
1705 : 0 : }
1706 [ - - ]: 0 : if (ck != Kind::REGEXP_INTER)
1707 : : {
1708 [ - - ]: 0 : if (childChanged)
1709 : : {
1710 : 0 : ret = nm->mkNode(cur.getKind(), children);
1711 : : }
1712 : : else
1713 : : {
1714 : 0 : ret = cur;
1715 : : }
1716 : : }
1717 : 0 : visited[cur] = ret;
1718 : 0 : }
1719 [ - - ]: 0 : } while (!visit.empty());
1720 : 0 : Assert(visited.find(r) != visited.end());
1721 : 0 : Assert(!visited.find(r)->second.isNull());
1722 [ - - ]: 0 : if (TraceIsOn("regexp-intersect"))
1723 : : {
1724 : 0 : Trace("regexp-intersect") << "Remove INTERSECTION( " << mkString(r)
1725 : 0 : << " ) = " << mkString(visited[r]) << std::endl;
1726 : : }
1727 : 0 : return visited[r];
1728 : 0 : }
1729 : :
1730 : 0 : Node RegExpOpr::intersect(Node r1, Node r2)
1731 : : {
1732 : 0 : if (!checkConstRegExp(r1) || !checkConstRegExp(r2)
1733 : 0 : || expr::hasSubtermKind(Kind::REGEXP_COMPLEMENT, r1)
1734 : 0 : || expr::hasSubtermKind(Kind::REGEXP_COMPLEMENT, r2))
1735 : : {
1736 : 0 : return Node::null();
1737 : : }
1738 : 0 : Node rr1 = removeIntersection(r1);
1739 : 0 : Node rr2 = removeIntersection(r2);
1740 : 0 : std::map<PairNodes, Node> cache;
1741 [ - - ]: 0 : Trace("regexp-intersect-node") << "Intersect (1): " << rr1 << std::endl;
1742 [ - - ]: 0 : Trace("regexp-intersect-node") << "Intersect (2): " << rr2 << std::endl;
1743 : 0 : Trace("regexp-intersect") << "Start INTERSECTION(\n\t" << mkString(r1)
1744 : 0 : << ",\n\t" << mkString(r2) << ")" << std::endl;
1745 : 0 : Node retNode = intersectInternal(rr1, rr2, cache, 1);
1746 [ - - ]: 0 : Trace("regexp-intersect")
1747 : 0 : << "End INTERSECTION(\n\t" << mkString(r1) << ",\n\t" << mkString(r2)
1748 : 0 : << ") =\n\t" << mkString(retNode) << std::endl;
1749 [ - - ]: 0 : Trace("regexp-intersect-node") << "Intersect finished." << std::endl;
1750 : 0 : return retNode;
1751 : 0 : }
1752 : :
1753 : : // printing
1754 : 0 : std::string RegExpOpr::niceChar(Node r)
1755 : : {
1756 [ - - ]: 0 : if (r.isConst())
1757 : : {
1758 : 0 : std::string s = r.getConst<String>().toString();
1759 : 0 : return s == "." ? "\\." : s;
1760 : 0 : }
1761 : : else
1762 : : {
1763 : 0 : std::string ss = "$" + r.toString();
1764 : 0 : return ss;
1765 : 0 : }
1766 : : }
1767 : 0 : std::string RegExpOpr::mkString(Node r)
1768 : : {
1769 : 0 : std::string retStr;
1770 [ - - ]: 0 : if (r.isNull())
1771 : : {
1772 : 0 : retStr = "\\E";
1773 : : }
1774 : : else
1775 : : {
1776 : 0 : Kind k = r.getKind();
1777 [ - - ][ - - ]: 0 : switch (k)
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
1778 : : {
1779 : 0 : case Kind::REGEXP_NONE:
1780 : : {
1781 : 0 : retStr += "\\E";
1782 : 0 : break;
1783 : : }
1784 : 0 : case Kind::REGEXP_ALLCHAR:
1785 : : {
1786 : 0 : retStr += ".";
1787 : 0 : break;
1788 : : }
1789 : 0 : case Kind::STRING_TO_REGEXP:
1790 : : {
1791 : 0 : std::string tmp(niceChar(r[0]));
1792 : 0 : retStr += tmp.size() == 1 ? tmp : "(" + tmp + ")";
1793 : 0 : break;
1794 : 0 : }
1795 : 0 : case Kind::REGEXP_CONCAT:
1796 : : {
1797 : 0 : retStr += "(";
1798 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
1799 : : {
1800 : : // if(i != 0) retStr += ".";
1801 : 0 : retStr += mkString(r[i]);
1802 : : }
1803 : 0 : retStr += ")";
1804 : 0 : break;
1805 : : }
1806 : 0 : case Kind::REGEXP_UNION:
1807 : : {
1808 : 0 : retStr += "(";
1809 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
1810 : : {
1811 [ - - ]: 0 : if (i != 0) retStr += "|";
1812 : 0 : retStr += mkString(r[i]);
1813 : : }
1814 : 0 : retStr += ")";
1815 : 0 : break;
1816 : : }
1817 : 0 : case Kind::REGEXP_INTER:
1818 : : {
1819 : 0 : retStr += "(";
1820 [ - - ]: 0 : for (unsigned i = 0; i < r.getNumChildren(); ++i)
1821 : : {
1822 [ - - ]: 0 : if (i != 0) retStr += "&";
1823 : 0 : retStr += mkString(r[i]);
1824 : : }
1825 : 0 : retStr += ")";
1826 : 0 : break;
1827 : : }
1828 : 0 : case Kind::REGEXP_STAR:
1829 : : {
1830 : 0 : retStr += mkString(r[0]);
1831 : 0 : retStr += "*";
1832 : 0 : break;
1833 : : }
1834 : 0 : case Kind::REGEXP_PLUS:
1835 : : {
1836 : 0 : retStr += mkString(r[0]);
1837 : 0 : retStr += "+";
1838 : 0 : break;
1839 : : }
1840 : 0 : case Kind::REGEXP_OPT:
1841 : : {
1842 : 0 : retStr += mkString(r[0]);
1843 : 0 : retStr += "?";
1844 : 0 : break;
1845 : : }
1846 : 0 : case Kind::REGEXP_RANGE:
1847 : : {
1848 : 0 : retStr += "[";
1849 : 0 : retStr += niceChar(r[0]);
1850 : 0 : retStr += "-";
1851 : 0 : retStr += niceChar(r[1]);
1852 : 0 : retStr += "]";
1853 : 0 : break;
1854 : : }
1855 : 0 : case Kind::REGEXP_LOOP:
1856 : : {
1857 : 0 : uint32_t l = utils::getLoopMinOccurrences(r);
1858 : 0 : std::stringstream ss;
1859 : 0 : ss << "(" << mkString(r[0]) << "){" << l << ",";
1860 [ - - ]: 0 : if (r.getNumChildren() == 3)
1861 : : {
1862 : 0 : uint32_t u = utils::getLoopMaxOccurrences(r);
1863 : 0 : ss << u;
1864 : : }
1865 : 0 : ss << "}";
1866 : 0 : retStr += ss.str();
1867 : 0 : break;
1868 : 0 : }
1869 : 0 : case Kind::REGEXP_RV:
1870 : : {
1871 : 0 : retStr += "<";
1872 : 0 : retStr += r[0].getConst<Rational>().getNumerator().toString();
1873 : 0 : retStr += ">";
1874 : 0 : break;
1875 : : }
1876 : 0 : case Kind::REGEXP_COMPLEMENT:
1877 : : {
1878 : 0 : retStr += "^(";
1879 : 0 : retStr += mkString(r[0]);
1880 : 0 : retStr += ")";
1881 : 0 : break;
1882 : : }
1883 : 0 : default:
1884 : : {
1885 : 0 : std::stringstream ss;
1886 : 0 : ss << r;
1887 : 0 : retStr = ss.str();
1888 : 0 : Assert(!utils::isRegExpKind(r.getKind()));
1889 : 0 : break;
1890 : 0 : }
1891 : : }
1892 : : }
1893 : :
1894 : 0 : return retStr;
1895 : 0 : }
1896 : :
1897 : 1777 : bool RegExpOpr::regExpIncludes(Node r1, Node r2)
1898 : : {
1899 : 1777 : return RegExpEntail::regExpIncludes(r1, r2, d_inclusionCache);
1900 : : }
1901 : :
1902 : : } // namespace strings
1903 : : } // namespace theory
1904 : : } // namespace cvc5::internal
|