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: 162 175 92.6 %
Date: 2024-10-23 11:38:32 Functions: 10 11 90.9 %
Branches: 115 172 66.9 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Andres Noetzli, Aina Niemetz
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2024 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Implementation of rewrite rules for string-specific operators in
      14                 :            :  * theory of strings.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include "theory/strings/strings_rewriter.h"
      18                 :            : 
      19                 :            : #include "expr/node_builder.h"
      20                 :            : #include "theory/strings/theory_strings_utils.h"
      21                 :            : #include "util/rational.h"
      22                 :            : #include "util/string.h"
      23                 :            : 
      24                 :            : using namespace cvc5::internal::kind;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace theory {
      28                 :            : namespace strings {
      29                 :            : 
      30                 :      49274 : StringsRewriter::StringsRewriter(NodeManager* nm,
      31                 :            :                                  Rewriter* r,
      32                 :            :                                  HistogramStat<Rewrite>* statistics,
      33                 :      49274 :                                  uint32_t alphaCard)
      34                 :      49274 :     : SequencesRewriter(nm, r, statistics), d_alphaCard(alphaCard)
      35                 :            : {
      36                 :      49274 : }
      37                 :            : 
      38                 :     993929 : RewriteResponse StringsRewriter::postRewrite(TNode node)
      39                 :            : {
      40         [ +  - ]:    1987860 :   Trace("strings-postrewrite")
      41                 :     993929 :       << "Strings::StringsRewriter::postRewrite start " << node << std::endl;
      42                 :            : 
      43                 :    1987860 :   Node retNode = node;
      44                 :     993929 :   Kind nk = node.getKind();
      45         [ +  + ]:     993929 :   if (nk == Kind::STRING_LT)
      46                 :            :   {
      47                 :        114 :     retNode = rewriteStringLt(node);
      48                 :            :   }
      49         [ +  + ]:     993815 :   else if (nk == Kind::STRING_LEQ)
      50                 :            :   {
      51                 :       1401 :     retNode = rewriteStringLeq(node);
      52                 :            :   }
      53 [ +  + ][ +  + ]:     992414 :   else if (nk == Kind::STRING_TO_LOWER || nk == Kind::STRING_TO_UPPER)
      54                 :            :   {
      55                 :        913 :     retNode = rewriteStrConvert(node);
      56                 :            :   }
      57         [ +  + ]:     991501 :   else if (nk == Kind::STRING_IS_DIGIT)
      58                 :            :   {
      59                 :         24 :     retNode = rewriteStringIsDigit(node);
      60                 :            :   }
      61         [ +  + ]:     991477 :   else if (nk == Kind::STRING_ITOS)
      62                 :            :   {
      63                 :       1160 :     retNode = rewriteIntToStr(node);
      64                 :            :   }
      65         [ +  + ]:     990317 :   else if (nk == Kind::STRING_STOI)
      66                 :            :   {
      67                 :       1449 :     retNode = rewriteStrToInt(node);
      68                 :            :   }
      69         [ +  + ]:     988868 :   else if (nk == Kind::STRING_TO_CODE)
      70                 :            :   {
      71                 :       9208 :     retNode = rewriteStringToCode(node);
      72                 :            :   }
      73         [ +  + ]:     979660 :   else if (nk == Kind::STRING_FROM_CODE)
      74                 :            :   {
      75                 :        273 :     retNode = rewriteStringFromCode(node);
      76                 :            :   }
      77         [ -  + ]:     979387 :   else if (nk == Kind::STRING_UNIT)
      78                 :            :   {
      79                 :          0 :     retNode = rewriteStringUnit(node);
      80                 :            :   }
      81                 :            :   else
      82                 :            :   {
      83                 :     979387 :     return SequencesRewriter::postRewrite(node);
      84                 :            :   }
      85                 :            : 
      86         [ +  - ]:      29084 :   Trace("strings-postrewrite")
      87                 :          0 :       << "Strings::StringsRewriter::postRewrite returning " << retNode
      88                 :      14542 :       << std::endl;
      89         [ +  + ]:      14542 :   if (node != retNode)
      90                 :            :   {
      91         [ +  - ]:       7756 :     Trace("strings-rewrite-debug") << "Strings::StringsRewriter::postRewrite "
      92                 :       3878 :                                    << node << " to " << retNode << std::endl;
      93                 :       3878 :     return RewriteResponse(REWRITE_AGAIN_FULL, retNode);
      94                 :            :   }
      95                 :      10664 :   return RewriteResponse(REWRITE_DONE, retNode);
      96                 :            : }
      97                 :            : 
      98                 :       1449 : Node StringsRewriter::rewriteStrToInt(Node node)
      99                 :            : {
     100 [ -  + ][ -  + ]:       1449 :   Assert(node.getKind() == Kind::STRING_STOI);
                 [ -  - ]
     101                 :       1449 :   NodeManager* nm = nodeManager();
     102         [ +  + ]:       1449 :   if (node[0].isConst())
     103                 :            :   {
     104                 :       1042 :     Node ret;
     105                 :        521 :     String s = node[0].getConst<String>();
     106         [ +  + ]:        521 :     if (s.isNumber())
     107                 :            :     {
     108                 :        240 :       ret = nm->mkConstInt(s.toNumber());
     109                 :            :     }
     110                 :            :     else
     111                 :            :     {
     112                 :        281 :       ret = nm->mkConstInt(Rational(-1));
     113                 :            :     }
     114                 :        521 :     return returnRewrite(node, ret, Rewrite::STOI_EVAL);
     115                 :            :   }
     116         [ +  + ]:        928 :   else if (node[0].getKind() == Kind::STRING_CONCAT)
     117                 :            :   {
     118         [ +  + ]:        730 :     for (TNode nc : node[0])
     119                 :            :     {
     120         [ +  + ]:        546 :       if (nc.isConst())
     121                 :            :       {
     122                 :        104 :         String t = nc.getConst<String>();
     123         [ +  + ]:        104 :         if (!t.isNumber())
     124                 :            :         {
     125                 :         14 :           Node ret = nm->mkConstInt(Rational(-1));
     126                 :         14 :           return returnRewrite(node, ret, Rewrite::STOI_CONCAT_NONNUM);
     127                 :            :         }
     128                 :            :       }
     129                 :            :     }
     130                 :            :   }
     131                 :        914 :   return node;
     132                 :            : }
     133                 :            : 
     134                 :       1160 : Node StringsRewriter::rewriteIntToStr(Node node)
     135                 :            : {
     136 [ -  + ][ -  + ]:       1160 :   Assert(node.getKind() == Kind::STRING_ITOS);
                 [ -  - ]
     137                 :       1160 :   NodeManager* nm = nodeManager();
     138         [ +  + ]:       1160 :   if (node[0].isConst())
     139                 :            :   {
     140                 :        584 :     Node ret;
     141         [ +  + ]:        584 :     if (node[0].getConst<Rational>().sgn() == -1)
     142                 :            :     {
     143                 :        245 :       ret = nm->mkConst(String(""));
     144                 :            :     }
     145                 :            :     else
     146                 :            :     {
     147                 :        678 :       std::string stmp = node[0].getConst<Rational>().getNumerator().toString();
     148 [ -  + ][ -  + ]:        339 :       Assert(stmp[0] != '-');
                 [ -  - ]
     149                 :        339 :       ret = nm->mkConst(String(stmp));
     150                 :            :     }
     151                 :        584 :     return returnRewrite(node, ret, Rewrite::ITOS_EVAL);
     152                 :            :   }
     153                 :        576 :   return node;
     154                 :            : }
     155                 :            : 
     156                 :        913 : Node StringsRewriter::rewriteStrConvert(Node node)
     157                 :            : {
     158                 :        913 :   Kind nk = node.getKind();
     159 [ +  + ][ -  + ]:        913 :   Assert(nk == Kind::STRING_TO_LOWER || nk == Kind::STRING_TO_UPPER);
         [ -  + ][ -  - ]
     160                 :        913 :   NodeManager* nm = nodeManager();
     161         [ +  + ]:        913 :   if (node[0].isConst())
     162                 :            :   {
     163                 :        460 :     std::vector<unsigned> nvec = node[0].getConst<String>().getVec();
     164         [ +  + ]:       1362 :     for (unsigned i = 0, nvsize = nvec.size(); i < nvsize; i++)
     165                 :            :     {
     166                 :       1132 :       unsigned newChar = nvec[i];
     167                 :            :       // transform it
     168                 :            :       // upper 65 ... 90
     169                 :            :       // lower 97 ... 122
     170         [ +  + ]:       1132 :       if (nk == Kind::STRING_TO_UPPER)
     171                 :            :       {
     172 [ +  + ][ +  + ]:        398 :         if (newChar >= 97 && newChar <= 122)
     173                 :            :         {
     174                 :         34 :           newChar = newChar - 32;
     175                 :            :         }
     176                 :            :       }
     177         [ +  - ]:        734 :       else if (nk == Kind::STRING_TO_LOWER)
     178                 :            :       {
     179 [ +  + ][ +  + ]:        734 :         if (newChar >= 65 && newChar <= 90)
     180                 :            :         {
     181                 :        344 :           newChar = newChar + 32;
     182                 :            :         }
     183                 :            :       }
     184                 :       1132 :       nvec[i] = newChar;
     185                 :            :     }
     186                 :        230 :     Node retNode = nm->mkConst(String(nvec));
     187                 :        230 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_CONST);
     188                 :            :   }
     189         [ +  + ]:        683 :   else if (node[0].getKind() == Kind::STRING_CONCAT)
     190                 :            :   {
     191                 :        234 :     NodeBuilder concatBuilder(Kind::STRING_CONCAT);
     192         [ +  + ]:        667 :     for (const Node& nc : node[0])
     193                 :            :     {
     194                 :        550 :       concatBuilder << nm->mkNode(nk, nc);
     195                 :            :     }
     196                 :            :     // to_lower( x1 ++ x2 ) --> to_lower( x1 ) ++ to_lower( x2 )
     197                 :        117 :     Node retNode = concatBuilder.constructNode();
     198                 :        117 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_MINSCOPE_CONCAT);
     199                 :            :   }
     200 [ +  + ][ -  - ]:        566 :   else if (node[0].getKind() == Kind::STRING_TO_LOWER
     201 [ +  + ][ +  + ]:        566 :            || node[0].getKind() == Kind::STRING_TO_UPPER)
         [ +  + ][ +  - ]
                 [ -  - ]
     202                 :            :   {
     203                 :            :     // to_lower( to_lower( x ) ) --> to_lower( x )
     204                 :            :     // to_lower( toupper( x ) ) --> to_lower( x )
     205                 :         76 :     Node retNode = nm->mkNode(nk, node[0][0]);
     206                 :         38 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_IDEM);
     207                 :            :   }
     208         [ +  + ]:        528 :   else if (node[0].getKind() == Kind::STRING_ITOS)
     209                 :            :   {
     210                 :            :     // to_lower( str.from.int( x ) ) --> str.from.int( x )
     211                 :         12 :     return returnRewrite(node, node[0], Rewrite::STR_CONV_ITOS);
     212                 :            :   }
     213                 :        516 :   return node;
     214                 :            : }
     215                 :            : 
     216                 :        114 : Node StringsRewriter::rewriteStringLt(Node n)
     217                 :            : {
     218 [ -  + ][ -  + ]:        114 :   Assert(n.getKind() == Kind::STRING_LT);
                 [ -  - ]
     219                 :        114 :   NodeManager* nm = nodeManager();
     220                 :            :   // eliminate s < t ---> s != t AND s <= t
     221                 :            :   Node retNode = nm->mkNode(Kind::AND,
     222                 :        228 :                             n[0].eqNode(n[1]).negate(),
     223                 :        456 :                             nm->mkNode(Kind::STRING_LEQ, n[0], n[1]));
     224                 :        228 :   return returnRewrite(n, retNode, Rewrite::STR_LT_ELIM);
     225                 :            : }
     226                 :            : 
     227                 :       1401 : Node StringsRewriter::rewriteStringLeq(Node n)
     228                 :            : {
     229 [ -  + ][ -  + ]:       1401 :   Assert(n.getKind() == Kind::STRING_LEQ);
                 [ -  - ]
     230                 :       1401 :   NodeManager* nm = nodeManager();
     231         [ +  + ]:       1401 :   if (n[0] == n[1])
     232                 :            :   {
     233                 :        136 :     Node ret = nm->mkConst(true);
     234                 :        136 :     return returnRewrite(n, ret, Rewrite::STR_LEQ_ID);
     235                 :            :   }
     236                 :       1265 :   if (n[0].isConst() && n[1].isConst())
     237                 :            :   {
     238                 :        384 :     String s = n[0].getConst<String>();
     239                 :        384 :     String t = n[1].getConst<String>();
     240                 :        192 :     Node ret = nm->mkConst(s.isLeq(t));
     241                 :        192 :     return returnRewrite(n, ret, Rewrite::STR_LEQ_EVAL);
     242                 :            :   }
     243                 :            :   // empty strings
     244         [ +  + ]:       3077 :   for (unsigned i = 0; i < 2; i++)
     245                 :            :   {
     246                 :       2094 :     if (n[i].isConst() && n[i].getConst<String>().empty())
     247                 :            :     {
     248                 :        166 :       Node ret = i == 0 ? nm->mkConst(true) : n[0].eqNode(n[1]);
     249                 :         90 :       return returnRewrite(n, ret, Rewrite::STR_LEQ_EMPTY);
     250                 :            :     }
     251                 :            :   }
     252                 :            : 
     253                 :       1966 :   std::vector<Node> n1;
     254                 :        983 :   utils::getConcat(n[0], n1);
     255                 :       1966 :   std::vector<Node> n2;
     256                 :        983 :   utils::getConcat(n[1], n2);
     257 [ +  - ][ +  - ]:       1966 :   Assert(!n1.empty() && !n2.empty());
         [ -  + ][ -  - ]
     258                 :            : 
     259                 :            :   // constant prefixes
     260 [ +  + ][ +  + ]:        983 :   if (n1[0].isConst() && n2[0].isConst() && n1[0] != n2[0])
         [ +  + ][ +  + ]
     261                 :            :   {
     262                 :        145 :     String s = n1[0].getConst<String>();
     263                 :        145 :     String t = n2[0].getConst<String>();
     264                 :        145 :     size_t prefixLen = std::min(s.size(), t.size());
     265                 :        145 :     s = s.prefix(prefixLen);
     266                 :        145 :     t = t.prefix(prefixLen);
     267                 :            :     // if the prefixes are not the same, then we can already decide the outcome
     268         [ +  + ]:        145 :     if (s != t)
     269                 :            :     {
     270                 :         89 :       Node ret = nm->mkConst(s.isLeq(t));
     271                 :         89 :       return returnRewrite(n, ret, Rewrite::STR_LEQ_CPREFIX);
     272                 :            :     }
     273                 :            :   }
     274                 :        894 :   return n;
     275                 :            : }
     276                 :            : 
     277                 :        273 : Node StringsRewriter::rewriteStringFromCode(Node n)
     278                 :            : {
     279 [ -  + ][ -  + ]:        273 :   Assert(n.getKind() == Kind::STRING_FROM_CODE);
                 [ -  - ]
     280                 :        273 :   NodeManager* nm = nodeManager();
     281                 :            : 
     282         [ +  + ]:        273 :   if (n[0].isConst())
     283                 :            :   {
     284                 :        106 :     Integer i = n[0].getConst<Rational>().getNumerator();
     285                 :         53 :     Node ret;
     286                 :         53 :     if (i >= 0 && i < d_alphaCard)
     287                 :            :     {
     288                 :         35 :       std::vector<unsigned> svec = {i.toUnsignedInt()};
     289                 :         35 :       ret = nm->mkConst(String(svec));
     290                 :            :     }
     291                 :            :     else
     292                 :            :     {
     293                 :         18 :       ret = nm->mkConst(String(""));
     294                 :            :     }
     295                 :         53 :     return returnRewrite(n, ret, Rewrite::FROM_CODE_EVAL);
     296                 :            :   }
     297                 :        220 :   return n;
     298                 :            : }
     299                 :            : 
     300                 :       9208 : Node StringsRewriter::rewriteStringToCode(Node n)
     301                 :            : {
     302 [ -  + ][ -  + ]:       9208 :   Assert(n.getKind() == Kind::STRING_TO_CODE);
                 [ -  - ]
     303         [ +  + ]:       9208 :   if (n[0].isConst())
     304                 :            :   {
     305                 :       1664 :     NodeManager* nm = nodeManager();
     306                 :       3328 :     String s = n[0].getConst<String>();
     307                 :       1664 :     Node ret;
     308         [ +  + ]:       1664 :     if (s.size() == 1)
     309                 :            :     {
     310                 :       1229 :       std::vector<unsigned> vec = s.getVec();
     311 [ -  + ][ -  + ]:       1229 :       Assert(vec.size() == 1);
                 [ -  - ]
     312                 :       1229 :       ret = nm->mkConstInt(Rational(vec[0]));
     313                 :            :     }
     314                 :            :     else
     315                 :            :     {
     316                 :        435 :       ret = nm->mkConstInt(Rational(-1));
     317                 :            :     }
     318                 :       1664 :     return returnRewrite(n, ret, Rewrite::TO_CODE_EVAL);
     319                 :            :   }
     320                 :       7544 :   return n;
     321                 :            : }
     322                 :            : 
     323                 :         24 : Node StringsRewriter::rewriteStringIsDigit(Node n)
     324                 :            : {
     325 [ -  + ][ -  + ]:         24 :   Assert(n.getKind() == Kind::STRING_IS_DIGIT);
                 [ -  - ]
     326                 :         24 :   NodeManager* nm = nodeManager();
     327                 :            :   // eliminate str.is_digit(s) ----> 48 <= str.to_code(s) <= 57
     328                 :         72 :   Node t = nm->mkNode(Kind::STRING_TO_CODE, n[0]);
     329                 :            :   Node retNode =
     330                 :            :       nm->mkNode(Kind::AND,
     331                 :         48 :                  nm->mkNode(Kind::LEQ, nm->mkConstInt(Rational(48)), t),
     332                 :         96 :                  nm->mkNode(Kind::LEQ, t, nm->mkConstInt(Rational(57))));
     333                 :         48 :   return returnRewrite(n, retNode, Rewrite::IS_DIGIT_ELIM);
     334                 :            : }
     335                 :            : 
     336                 :          0 : Node StringsRewriter::rewriteStringUnit(Node n)
     337                 :            : {
     338                 :          0 :   Assert(n.getKind() == Kind::STRING_UNIT);
     339                 :          0 :   NodeManager* nm = nodeManager();
     340         [ -  - ]:          0 :   if (n[0].isConst())
     341                 :            :   {
     342                 :          0 :     Integer i = n[0].getConst<Rational>().getNumerator();
     343                 :          0 :     Node ret;
     344                 :          0 :     if (i >= 0 && i < d_alphaCard)
     345                 :            :     {
     346                 :          0 :       std::vector<unsigned> svec = {i.toUnsignedInt()};
     347                 :          0 :       ret = nm->mkConst(String(svec));
     348                 :          0 :       return returnRewrite(n, ret, Rewrite::SEQ_UNIT_EVAL);
     349                 :            :     }
     350                 :            :   }
     351                 :          0 :   return n;
     352                 :            : }
     353                 :            : 
     354                 :            : }  // namespace strings
     355                 :            : }  // namespace theory
     356                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14