LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - strings_rewriter.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 186 201 92.5 %
Date: 2026-05-04 10:34:19 Functions: 10 11 90.9 %
Branches: 127 190 66.8 %

           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                 :            :  * Implementation of rewrite rules for string-specific operators in
      11                 :            :  * theory of strings.
      12                 :            :  */
      13                 :            : 
      14                 :            : #include "theory/strings/strings_rewriter.h"
      15                 :            : 
      16                 :            : #include "expr/node_builder.h"
      17                 :            : #include "theory/strings/theory_strings_utils.h"
      18                 :            : #include "util/rational.h"
      19                 :            : #include "util/string.h"
      20                 :            : 
      21                 :            : using namespace cvc5::internal::kind;
      22                 :            : 
      23                 :            : namespace cvc5::internal {
      24                 :            : namespace theory {
      25                 :            : namespace strings {
      26                 :            : 
      27                 :      51052 : StringsRewriter::StringsRewriter(NodeManager* nm,
      28                 :            :                                  ArithEntail& ae,
      29                 :            :                                  StringsEntail& se,
      30                 :            :                                  HistogramStat<Rewrite>* statistics,
      31                 :      51052 :                                  uint32_t alphaCard)
      32                 :      51052 :     : SequencesRewriter(nm, ae, se, statistics), d_alphaCard(alphaCard)
      33                 :            : {
      34                 :      51052 : }
      35                 :            : 
      36                 :    1582198 : RewriteResponse StringsRewriter::postRewrite(TNode node)
      37                 :            : {
      38         [ +  - ]:    3164396 :   Trace("strings-postrewrite")
      39                 :    1582198 :       << "Strings::StringsRewriter::postRewrite start " << node << std::endl;
      40                 :            : 
      41                 :    1582198 :   Node retNode = node;
      42                 :    1582198 :   Kind nk = node.getKind();
      43         [ +  + ]:    1582198 :   if (nk == Kind::STRING_LT)
      44                 :            :   {
      45                 :        114 :     retNode = rewriteStringLt(node);
      46                 :            :   }
      47         [ +  + ]:    1582084 :   else if (nk == Kind::STRING_LEQ)
      48                 :            :   {
      49                 :       1399 :     retNode = rewriteStringLeq(node);
      50                 :            :   }
      51 [ +  + ][ +  + ]:    1580685 :   else if (nk == Kind::STRING_TO_LOWER || nk == Kind::STRING_TO_UPPER)
      52                 :            :   {
      53                 :       1070 :     retNode = rewriteStrConvert(node);
      54                 :            :   }
      55         [ +  + ]:    1579615 :   else if (nk == Kind::STRING_IS_DIGIT)
      56                 :            :   {
      57                 :         34 :     retNode = rewriteStringIsDigit(node);
      58                 :            :   }
      59         [ +  + ]:    1579581 :   else if (nk == Kind::STRING_ITOS)
      60                 :            :   {
      61                 :       1518 :     retNode = rewriteIntToStr(node);
      62                 :            :   }
      63         [ +  + ]:    1578063 :   else if (nk == Kind::STRING_STOI)
      64                 :            :   {
      65                 :       1298 :     retNode = rewriteStrToInt(node);
      66                 :            :   }
      67         [ +  + ]:    1576765 :   else if (nk == Kind::STRING_TO_CODE)
      68                 :            :   {
      69                 :       9084 :     retNode = rewriteStringToCode(node);
      70                 :            :   }
      71         [ +  + ]:    1567681 :   else if (nk == Kind::STRING_FROM_CODE)
      72                 :            :   {
      73                 :        259 :     retNode = rewriteStringFromCode(node);
      74                 :            :   }
      75         [ -  + ]:    1567422 :   else if (nk == Kind::STRING_UNIT)
      76                 :            :   {
      77                 :          0 :     retNode = rewriteStringUnit(node);
      78                 :            :   }
      79                 :            :   else
      80                 :            :   {
      81                 :    1567422 :     return SequencesRewriter::postRewrite(node);
      82                 :            :   }
      83                 :            : 
      84         [ +  - ]:      29552 :   Trace("strings-postrewrite")
      85                 :          0 :       << "Strings::StringsRewriter::postRewrite returning " << retNode
      86                 :      14776 :       << std::endl;
      87         [ +  + ]:      14776 :   if (node != retNode)
      88                 :            :   {
      89         [ +  - ]:       7028 :     Trace("strings-rewrite-debug") << "Strings::StringsRewriter::postRewrite "
      90                 :       3514 :                                    << node << " to " << retNode << std::endl;
      91                 :       3514 :     return RewriteResponse(REWRITE_AGAIN_FULL, retNode);
      92                 :            :   }
      93                 :      11262 :   return RewriteResponse(REWRITE_DONE, retNode);
      94                 :    1582198 : }
      95                 :            : 
      96                 :       1298 : Node StringsRewriter::rewriteStrToInt(Node node)
      97                 :            : {
      98 [ -  + ][ -  + ]:       1298 :   Assert(node.getKind() == Kind::STRING_STOI);
                 [ -  - ]
      99                 :       1298 :   NodeManager* nm = nodeManager();
     100         [ +  + ]:       1298 :   if (node[0].isConst())
     101                 :            :   {
     102                 :        409 :     Node ret;
     103                 :        409 :     String s = node[0].getConst<String>();
     104         [ +  + ]:        409 :     if (s.isNumber())
     105                 :            :     {
     106                 :        209 :       ret = nm->mkConstInt(s.toNumber());
     107                 :            :     }
     108                 :            :     else
     109                 :            :     {
     110                 :        200 :       ret = nm->mkConstInt(Rational(-1));
     111                 :            :     }
     112                 :        409 :     return returnRewrite(node, ret, Rewrite::STOI_EVAL);
     113                 :        409 :   }
     114         [ +  + ]:        889 :   else if (node[0].getKind() == Kind::STRING_CONCAT)
     115                 :            :   {
     116         [ +  + ]:        644 :     for (TNode nc : node[0])
     117                 :            :     {
     118         [ +  + ]:        490 :       if (nc.isConst())
     119                 :            :       {
     120                 :         79 :         String t = nc.getConst<String>();
     121         [ +  + ]:         79 :         if (!t.isNumber())
     122                 :            :         {
     123                 :         15 :           Node ret = nm->mkConstInt(Rational(-1));
     124                 :         15 :           return returnRewrite(node, ret, Rewrite::STOI_CONCAT_NONNUM);
     125                 :         15 :         }
     126         [ +  + ]:         79 :       }
     127 [ +  + ][ +  + ]:        659 :     }
     128                 :            :   }
     129                 :        874 :   return node;
     130                 :            : }
     131                 :            : 
     132                 :       1518 : Node StringsRewriter::rewriteIntToStr(Node node)
     133                 :            : {
     134 [ -  + ][ -  + ]:       1518 :   Assert(node.getKind() == Kind::STRING_ITOS);
                 [ -  - ]
     135                 :       1518 :   NodeManager* nm = nodeManager();
     136         [ +  + ]:       1518 :   if (node[0].isConst())
     137                 :            :   {
     138                 :        350 :     Node ret;
     139         [ +  + ]:        350 :     if (node[0].getConst<Rational>().sgn() == -1)
     140                 :            :     {
     141                 :         82 :       ret = nm->mkConst(String(""));
     142                 :            :     }
     143                 :            :     else
     144                 :            :     {
     145                 :        536 :       std::string stmp = node[0].getConst<Rational>().getNumerator().toString();
     146 [ -  + ][ -  + ]:        268 :       Assert(stmp[0] != '-');
                 [ -  - ]
     147                 :        268 :       ret = nm->mkConst(String(stmp));
     148                 :        268 :     }
     149                 :        350 :     return returnRewrite(node, ret, Rewrite::ITOS_EVAL);
     150                 :        350 :   }
     151                 :       1168 :   return node;
     152                 :            : }
     153                 :            : 
     154                 :       1070 : Node StringsRewriter::rewriteStrConvert(Node node)
     155                 :            : {
     156                 :       1070 :   Kind nk = node.getKind();
     157 [ +  + ][ +  - ]:       1070 :   Assert(nk == Kind::STRING_TO_LOWER || nk == Kind::STRING_TO_UPPER);
         [ -  + ][ -  + ]
                 [ -  - ]
     158                 :       1070 :   NodeManager* nm = nodeManager();
     159         [ +  + ]:       1070 :   if (node[0].isConst())
     160                 :            :   {
     161                 :        259 :     std::vector<unsigned> nvec = node[0].getConst<String>().getVec();
     162         [ +  + ]:       1426 :     for (unsigned i = 0, nvsize = nvec.size(); i < nvsize; i++)
     163                 :            :     {
     164                 :       1167 :       unsigned newChar = nvec[i];
     165                 :            :       // transform it
     166                 :            :       // upper 65 ... 90
     167                 :            :       // lower 97 ... 122
     168         [ +  + ]:       1167 :       if (nk == Kind::STRING_TO_UPPER)
     169                 :            :       {
     170 [ +  + ][ +  + ]:        394 :         if (newChar >= 97 && newChar <= 122)
     171                 :            :         {
     172                 :         32 :           newChar = newChar - 32;
     173                 :            :         }
     174                 :            :       }
     175         [ +  - ]:        773 :       else if (nk == Kind::STRING_TO_LOWER)
     176                 :            :       {
     177 [ +  + ][ +  + ]:        773 :         if (newChar >= 65 && newChar <= 90)
     178                 :            :         {
     179                 :        359 :           newChar = newChar + 32;
     180                 :            :         }
     181                 :            :       }
     182                 :       1167 :       nvec[i] = newChar;
     183                 :            :     }
     184                 :        259 :     Node retNode = nm->mkConst(String(nvec));
     185                 :        259 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_CONST);
     186                 :        259 :   }
     187         [ +  + ]:        811 :   else if (node[0].getKind() == Kind::STRING_CONCAT)
     188                 :            :   {
     189                 :        128 :     NodeBuilder concatBuilder(nm, Kind::STRING_CONCAT);
     190         [ +  + ]:        732 :     for (const Node& nc : node[0])
     191                 :            :     {
     192                 :        604 :       concatBuilder << nm->mkNode(nk, nc);
     193                 :        732 :     }
     194                 :            :     // to_lower( x1 ++ x2 ) --> to_lower( x1 ) ++ to_lower( x2 )
     195                 :        128 :     Node retNode = concatBuilder.constructNode();
     196                 :        128 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_MINSCOPE_CONCAT);
     197                 :        128 :   }
     198 [ +  + ][ -  - ]:        683 :   else if (node[0].getKind() == Kind::STRING_TO_LOWER
     199 [ +  + ][ +  + ]:        683 :            || node[0].getKind() == Kind::STRING_TO_UPPER)
         [ +  + ][ +  - ]
                 [ -  - ]
     200                 :            :   {
     201                 :            :     // to_lower( to_lower( x ) ) --> to_lower( x )
     202                 :            :     // to_lower( toupper( x ) ) --> to_lower( x )
     203                 :         66 :     Node retNode = nm->mkNode(nk, node[0][0]);
     204                 :         33 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_IDEM);
     205                 :         33 :   }
     206         [ +  + ]:        650 :   else if (node[0].getKind() == Kind::STRING_ITOS)
     207                 :            :   {
     208                 :            :     // to_lower( str.from.int( x ) ) --> str.from.int( x )
     209                 :         20 :     return returnRewrite(node, node[0], Rewrite::STR_CONV_ITOS);
     210                 :            :   }
     211                 :        630 :   return node;
     212                 :            : }
     213                 :            : 
     214                 :        114 : Node StringsRewriter::rewriteStringLt(Node n)
     215                 :            : {
     216 [ -  + ][ -  + ]:        114 :   Assert(n.getKind() == Kind::STRING_LT);
                 [ -  - ]
     217                 :        114 :   NodeManager* nm = nodeManager();
     218                 :            :   // eliminate s < t ---> s != t AND s <= t
     219                 :        342 :   Node retNode = nm->mkNode(
     220                 :            :       Kind::AND,
     221                 :        228 :       {n[0].eqNode(n[1]).negate(), nm->mkNode(Kind::STRING_LEQ, n[0], n[1])});
     222                 :        228 :   return returnRewrite(n, retNode, Rewrite::STR_LT_ELIM);
     223                 :        114 : }
     224                 :            : 
     225                 :       1399 : Node StringsRewriter::rewriteStringLeq(Node n)
     226                 :            : {
     227 [ -  + ][ -  + ]:       1399 :   Assert(n.getKind() == Kind::STRING_LEQ);
                 [ -  - ]
     228                 :       1399 :   NodeManager* nm = nodeManager();
     229         [ +  + ]:       1399 :   if (n[0] == n[1])
     230                 :            :   {
     231                 :        115 :     Node ret = nm->mkConst(true);
     232                 :        115 :     return returnRewrite(n, ret, Rewrite::STR_LEQ_ID);
     233                 :        115 :   }
     234                 :       1284 :   if (n[0].isConst() && n[1].isConst())
     235                 :            :   {
     236                 :        254 :     String s = n[0].getConst<String>();
     237                 :        254 :     String t = n[1].getConst<String>();
     238                 :        254 :     Node ret = nm->mkConst(s.isLeq(t));
     239                 :        254 :     return returnRewrite(n, ret, Rewrite::STR_LEQ_EVAL);
     240                 :        254 :   }
     241                 :            :   // empty strings
     242         [ +  + ]:       2992 :   for (unsigned i = 0; i < 2; i++)
     243                 :            :   {
     244                 :       2028 :     if (n[i].isConst() && n[i].getConst<String>().empty())
     245                 :            :     {
     246                 :        134 :       Node ret = i == 0 ? nm->mkConst(true) : n[0].eqNode(n[1]);
     247                 :         66 :       return returnRewrite(n, ret, Rewrite::STR_LEQ_EMPTY);
     248                 :         66 :     }
     249                 :            :   }
     250                 :            : 
     251                 :        964 :   std::vector<Node> n1;
     252                 :        964 :   utils::getConcat(n[0], n1);
     253                 :        964 :   std::vector<Node> n2;
     254                 :        964 :   utils::getConcat(n[1], n2);
     255 [ +  - ][ +  - ]:        964 :   Assert(!n1.empty() && !n2.empty());
         [ -  + ][ -  + ]
                 [ -  - ]
     256                 :            : 
     257                 :            :   // constant prefixes
     258 [ +  + ][ +  + ]:        964 :   if (n1[0].isConst() && n2[0].isConst() && n1[0] != n2[0])
         [ +  + ][ +  + ]
     259                 :            :   {
     260                 :        154 :     String s = n1[0].getConst<String>();
     261                 :        154 :     String t = n2[0].getConst<String>();
     262                 :        154 :     size_t prefixLen = std::min(s.size(), t.size());
     263                 :        154 :     s = s.prefix(prefixLen);
     264                 :        154 :     t = t.prefix(prefixLen);
     265                 :            :     // if the prefixes are not the same, then we can already decide the outcome
     266         [ +  + ]:        154 :     if (s != t)
     267                 :            :     {
     268                 :         90 :       Node ret = nm->mkConst(s.isLeq(t));
     269                 :         90 :       return returnRewrite(n, ret, Rewrite::STR_LEQ_CPREFIX);
     270                 :         90 :     }
     271 [ +  + ][ +  + ]:        244 :   }
     272                 :        874 :   return n;
     273                 :        964 : }
     274                 :            : 
     275                 :        259 : Node StringsRewriter::rewriteStringFromCode(Node n)
     276                 :            : {
     277 [ -  + ][ -  + ]:        259 :   Assert(n.getKind() == Kind::STRING_FROM_CODE);
                 [ -  - ]
     278                 :        259 :   NodeManager* nm = nodeManager();
     279                 :            : 
     280         [ +  + ]:        259 :   if (n[0].isConst())
     281                 :            :   {
     282                 :         65 :     Integer i = n[0].getConst<Rational>().getNumerator();
     283                 :         65 :     Node ret;
     284                 :         65 :     if (i >= 0 && i < d_alphaCard)
     285                 :            :     {
     286                 :         51 :       std::vector<unsigned> svec = {i.toUnsignedInt()};
     287                 :         51 :       ret = nm->mkConst(String(svec));
     288                 :         51 :     }
     289                 :            :     else
     290                 :            :     {
     291                 :         14 :       ret = nm->mkConst(String(""));
     292                 :            :     }
     293                 :         65 :     return returnRewrite(n, ret, Rewrite::FROM_CODE_EVAL);
     294                 :         65 :   }
     295                 :        194 :   return n;
     296                 :            : }
     297                 :            : 
     298                 :       9084 : Node StringsRewriter::rewriteStringToCode(Node n)
     299                 :            : {
     300 [ -  + ][ -  + ]:       9084 :   Assert(n.getKind() == Kind::STRING_TO_CODE);
                 [ -  - ]
     301         [ +  + ]:       9084 :   if (n[0].isConst())
     302                 :            :   {
     303                 :       1562 :     NodeManager* nm = nodeManager();
     304                 :       1562 :     String s = n[0].getConst<String>();
     305                 :       1562 :     Node ret;
     306         [ +  + ]:       1562 :     if (s.size() == 1)
     307                 :            :     {
     308                 :       1123 :       std::vector<unsigned> vec = s.getVec();
     309 [ -  + ][ -  + ]:       1123 :       Assert(vec.size() == 1);
                 [ -  - ]
     310                 :       1123 :       ret = nm->mkConstInt(Rational(vec[0]));
     311                 :       1123 :     }
     312                 :            :     else
     313                 :            :     {
     314                 :        439 :       ret = nm->mkConstInt(Rational(-1));
     315                 :            :     }
     316                 :       1562 :     return returnRewrite(n, ret, Rewrite::TO_CODE_EVAL);
     317                 :       1562 :   }
     318                 :       7522 :   return n;
     319                 :            : }
     320                 :            : 
     321                 :         34 : Node StringsRewriter::rewriteStringIsDigit(Node n)
     322                 :            : {
     323 [ -  + ][ -  + ]:         34 :   Assert(n.getKind() == Kind::STRING_IS_DIGIT);
                 [ -  - ]
     324                 :         34 :   NodeManager* nm = nodeManager();
     325                 :            :   // eliminate str.is_digit(s) ----> 48 <= str.to_code(s) <= 57
     326                 :         68 :   Node t = nm->mkNode(Kind::STRING_TO_CODE, n[0]);
     327                 :            :   Node retNode =
     328                 :        102 :       nm->mkNode(Kind::AND,
     329                 :         68 :                  {nm->mkNode(Kind::LEQ, nm->mkConstInt(Rational(48)), t),
     330                 :         68 :                   nm->mkNode(Kind::LEQ, t, nm->mkConstInt(Rational(57)))});
     331                 :         68 :   return returnRewrite(n, retNode, Rewrite::IS_DIGIT_ELIM);
     332                 :         34 : }
     333                 :            : 
     334                 :          0 : Node StringsRewriter::rewriteStringUnit(Node n)
     335                 :            : {
     336                 :          0 :   Assert(n.getKind() == Kind::STRING_UNIT);
     337                 :          0 :   NodeManager* nm = nodeManager();
     338         [ -  - ]:          0 :   if (n[0].isConst())
     339                 :            :   {
     340                 :          0 :     Integer i = n[0].getConst<Rational>().getNumerator();
     341                 :          0 :     Node ret;
     342                 :          0 :     if (i >= 0 && i < d_alphaCard)
     343                 :            :     {
     344                 :          0 :       std::vector<unsigned> svec = {i.toUnsignedInt()};
     345                 :          0 :       ret = nm->mkConst(String(svec));
     346                 :          0 :       return returnRewrite(n, ret, Rewrite::SEQ_UNIT_EVAL);
     347                 :          0 :     }
     348 [ -  - ][ -  - ]:          0 :   }
     349                 :          0 :   return n;
     350                 :            : }
     351                 :            : 
     352                 :            : }  // namespace strings
     353                 :            : }  // namespace theory
     354                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14