LCOV - code coverage report
Current view: top level - src/IR - CNF.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 92 139 66.2 %
Date: 2025-03-25 01:19:55 Functions: 17 24 70.8 %
Branches: 86 265 32.5 %

           Branch data     Line data    Source code
       1                 :            : #include <mutable/IR/CNF.hpp>
       2                 :            : 
       3                 :            : #include <mutable/parse/AST.hpp>
       4                 :            : #include "parse/ASTPrinter.hpp"
       5                 :            : #include <mutable/util/fn.hpp>
       6                 :            : #include <mutable/util/macro.hpp>
       7                 :            : 
       8                 :            : 
       9                 :            : using namespace m;
      10                 :            : using namespace m::ast;
      11                 :            : using namespace m::cnf;
      12                 :            : 
      13                 :            : 
      14                 :            : /*======================================================================================================================
      15                 :            :  * CNF Operations
      16                 :            :  *====================================================================================================================*/
      17                 :            : 
      18                 :        152 : bool cnf::Clause::operator<=(const Clause &other) const
      19                 :            : {
      20         [ +  + ]:        344 :     for (auto pred : *this) {
      21         [ +  + ]:        230 :         if (not contains(other, pred))
      22                 :         38 :             return false;
      23                 :            :     }
      24                 :        114 :     return true;
      25                 :        152 : }
      26                 :            : 
      27                 :         35 : bool CNF::operator<=(const CNF &other) const
      28                 :            : {
      29         [ +  + ]:         71 :     for (auto clause : *this) {
      30   [ +  -  +  + ]:         41 :         if (not contains(other, clause))
      31                 :          5 :             return false;
      32      [ -  +  + ]:         41 :     }
      33                 :         30 :     return true;
      34                 :         35 : }
      35                 :            : 
      36                 :         13 : cnf::Clause cnf::operator||(const cnf::Clause &lhs, const cnf::Clause &rhs)
      37                 :            : {
      38                 :         13 :     cnf::Clause res;
      39         [ +  - ]:         13 :     res.reserve(lhs.size() + rhs.size());
      40         [ +  - ]:         13 :     res.insert(res.end(), lhs.begin(), lhs.end());
      41         [ +  - ]:         13 :     res.insert(res.end(), rhs.begin(), rhs.end());
      42                 :         13 :     return res;
      43         [ +  - ]:         13 : }
      44                 :            : 
      45                 :          1 : CNF cnf::operator&&(const cnf::Clause &lhs, const cnf::Clause &rhs)
      46                 :            : {
      47                 :          1 :     CNF res;
      48         [ +  - ]:          1 :     res.push_back(lhs);
      49         [ +  - ]:          1 :     res.push_back(rhs);
      50                 :          1 :     return res;
      51         [ +  - ]:          1 : }
      52                 :            : 
      53                 :        208 : CNF cnf::operator&&(const CNF &lhs, const CNF &rhs)
      54                 :            : {
      55                 :        208 :     CNF res;
      56         [ +  - ]:        208 :     res.reserve(lhs.size() + rhs.size());
      57         [ +  - ]:        208 :     res.insert(res.end(), lhs.begin(), lhs.end());
      58         [ +  - ]:        208 :     res.insert(res.end(), rhs.begin(), rhs.end());
      59                 :        208 :     return res;
      60         [ +  - ]:        208 : }
      61                 :            : 
      62                 :          7 : CNF cnf::operator||(const CNF &lhs, const CNF &rhs)
      63                 :            : {
      64                 :          7 :     CNF res;
      65         [ +  + ]:          7 :     if (lhs.size() == 0)
      66         [ +  - ]:          1 :         return rhs;
      67         [ -  + ]:          6 :     else if (rhs.size() == 0)
      68         [ #  # ]:          0 :         return lhs;
      69                 :            : 
      70         [ +  - ]:          6 :     res.reserve(lhs.size() * rhs.size());
      71         [ +  + ]:         14 :     for (auto &clause_left : lhs) {
      72         [ +  + ]:         20 :         for (auto &clause_right : rhs)
      73   [ +  -  +  - ]:         12 :             res.push_back(clause_left or clause_right);
      74                 :          1 :     }
      75                 :          6 :     return res;
      76                 :          7 : }
      77                 :            : 
      78                 :          3 : CNF cnf::operator!(const cnf::Clause &clause)
      79                 :            : {
      80                 :          3 :     CNF res;
      81         [ +  + ]:          9 :     for (auto &p : clause)
      82   [ +  -  -  +  :          6 :         res.emplace_back(Clause({not p}));
                   -  + ]
      83                 :          3 :     return res;
      84         [ +  - ]:          3 : }
      85                 :            : 
      86                 :          1 : CNF cnf::operator!(const CNF &cnf)
      87                 :            : {
      88                 :          1 :     CNF res;
      89         [ +  + ]:          3 :     for (auto &clause : cnf)
      90   [ +  -  +  - ]:          2 :         res = res or not clause;
      91                 :          1 :     return res;
      92         [ +  - ]:          1 : }
      93                 :            : 
      94                 :            : 
      95                 :            : /*======================================================================================================================
      96                 :            :  * Print/Dump
      97                 :            :  *====================================================================================================================*/
      98                 :            : 
      99                 :          0 : void Predicate::to_sql(std::ostream &out) const
     100                 :            : {
     101         [ #  # ]:          0 :     if (negative())
     102                 :          0 :         out << "NOT (" << expr() << ')';
     103                 :            :     else
     104                 :          0 :         out << expr();
     105                 :          0 : }
     106                 :            : 
     107                 :          0 : void cnf::Clause::to_sql(std::ostream &out) const
     108                 :            : {
     109      [ #  #  # ]:          0 :     switch (size()) {
     110                 :            :         case 0:
     111                 :          0 :             out << "TRUE";
     112                 :          0 :             return;
     113                 :            : 
     114                 :            :         case 1:
     115                 :          0 :             out << *begin();
     116                 :          0 :             return;
     117                 :            : 
     118                 :            :         default:
     119         [ #  # ]:          0 :             for (auto it = begin(); it != end(); ++it) {
     120         [ #  # ]:          0 :                 if (it != begin()) out << " OR ";
     121                 :          0 :                 out << '(' << *it << ')';
     122                 :          0 :             }
     123                 :          0 :             return;
     124                 :            :     }
     125                 :          0 : }
     126                 :            : 
     127                 :          0 : void CNF::to_sql(std::ostream &out) const
     128                 :            : {
     129      [ #  #  # ]:          0 :     switch (size()) {
     130                 :            :         case 0:
     131                 :          0 :             out << "TRUE";
     132                 :          0 :             return;
     133                 :            : 
     134                 :            :         case 1:
     135                 :          0 :             out << *begin();
     136                 :          0 :             return;
     137                 :            : 
     138                 :            :         default:
     139         [ #  # ]:          0 :             for (auto it = begin(); it != end(); ++it) {
     140         [ #  # ]:          0 :                 if (it != begin()) out << " AND ";
     141                 :          0 :                 out << *it;
     142                 :          0 :             }
     143                 :          0 :             return;
     144                 :            :     }
     145                 :          0 : }
     146                 :            : 
     147                 :            : M_LCOV_EXCL_START
     148                 :            : std::ostream & cnf::operator<<(std::ostream &out, const Predicate &pred)
     149                 :            : {
     150                 :            :     if (pred.negative())
     151                 :            :         out << '-';
     152                 :            :     ast::ASTPrinter print(out);
     153                 :            :     print.expand_nested_queries(false);
     154                 :            :     print(*pred);
     155                 :            :     return out;
     156                 :            : }
     157                 :            : 
     158                 :            : std::ostream & cnf::operator<<(std::ostream &out, const cnf::Clause &clause)
     159                 :            : {
     160                 :            :     for (auto it = clause.begin(); it != clause.end(); ++it) {
     161                 :            :         if (it != clause.begin()) out << " v ";
     162                 :            :         out << *it;
     163                 :            :     }
     164                 :            :     return out;
     165                 :            : }
     166                 :            : 
     167                 :            : std::ostream & cnf::operator<<(std::ostream &out, const CNF &cnf)
     168                 :            : {
     169                 :            :     if (cnf.empty())
     170                 :            :         out << "TRUE";
     171                 :            :     else if (cnf.size() == 1)
     172                 :            :         out << cnf[0];
     173                 :            :     else {
     174                 :            :         for (auto it = cnf.begin(); it != cnf.end(); ++it) {
     175                 :            :             if (it != cnf.begin()) out << " ^ ";
     176                 :            :             out << '(' << *it << ')';
     177                 :            :         }
     178                 :            :     }
     179                 :            :     return out;
     180                 :            : }
     181                 :            : 
     182                 :            : void Predicate::dump(std::ostream &out) const
     183                 :            : {
     184                 :            :     out << *this << std::endl;
     185                 :            : }
     186                 :            : void Predicate::dump() const { dump(std::cerr); }
     187                 :            : 
     188                 :            : void cnf::Clause::dump(std::ostream &out) const
     189                 :            : {
     190                 :            :     out << *this << std::endl;
     191                 :            : }
     192                 :            : void cnf::Clause::dump() const { dump(std::cerr); }
     193                 :            : 
     194                 :            : void CNF::dump(std::ostream &out) const
     195                 :            : {
     196                 :            :     out << *this << std::endl;
     197                 :            : }
     198                 :            : void CNF::dump() const { dump(std::cerr); }
     199                 :            : M_LCOV_EXCL_STOP
     200                 :            : 
     201                 :            : 
     202                 :            : /*======================================================================================================================
     203                 :            :  * CNF Generator
     204                 :            :  *====================================================================================================================*/
     205                 :            : 
     206                 :            : /** Helper class to convert `Expr` and `Clause` to `cnf::CNF`. */
     207                 :            : struct CNFGenerator : ConstASTExprVisitor
     208                 :            : {
     209                 :            :     private:
     210                 :        162 :     bool is_negative_ = false;
     211                 :            :     CNF result_;
     212                 :            : 
     213                 :            :     public:
     214                 :        324 :     CNFGenerator() { }
     215                 :            : 
     216                 :        162 :     CNF get() const { return result_; }
     217                 :            : 
     218                 :            :     using ConstASTExprVisitor::operator();
     219                 :            :     void operator()(Const<ErrorExpr> &e);
     220                 :            :     void operator()(Const<Designator> &e);
     221                 :            :     void operator()(Const<Constant> &e);
     222                 :            :     void operator()(Const<FnApplicationExpr> &e);
     223                 :            :     void operator()(Const<UnaryExpr> &e);
     224                 :            :     void operator()(Const<BinaryExpr> &e);
     225                 :            :     void operator()(Const<QueryExpr> &e);
     226                 :            : };
     227                 :            : 
     228                 :          0 : void CNFGenerator::operator()(Const<ErrorExpr> &e)
     229                 :            : {
     230   [ #  #  #  #  :          0 :     result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             #  #  #  # ]
     231                 :          0 : }
     232                 :            : 
     233                 :         10 : void CNFGenerator::operator()(Const<Designator> &e)
     234                 :            : {
     235   [ +  -  +  -  :         10 :     result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             +  -  #  # ]
     236                 :         10 : }
     237                 :            : 
     238                 :         27 : void CNFGenerator::operator()(Const<Constant> &e)
     239                 :            : {
     240   [ +  -  +  -  :         27 :     result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             +  -  #  # ]
     241                 :         27 : }
     242                 :            : 
     243                 :          0 : void CNFGenerator::operator()(Const<FnApplicationExpr> &e)
     244                 :            : {
     245   [ #  #  #  #  :          0 :     result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             #  #  #  # ]
     246                 :          0 : }
     247                 :            : 
     248                 :         12 : void CNFGenerator::operator()(Const<UnaryExpr> &e)
     249                 :            : {
     250         [ +  - ]:         12 :     switch (e.op().type) {
     251                 :            :         case TK_Not:
     252                 :         12 :             is_negative_ = not is_negative_;
     253                 :         12 :             (*this)(*e.expr);
     254                 :         12 :             is_negative_ = not is_negative_;
     255                 :         12 :             break;
     256                 :            : 
     257                 :            :         default:
     258   [ #  #  #  #  :          0 :             result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             #  #  #  # ]
     259                 :          0 :             break;
     260                 :            : 
     261                 :            :     }
     262                 :         12 : }
     263                 :            : 
     264                 :        519 : void CNFGenerator::operator()(Const<BinaryExpr> &e)
     265                 :            : {
     266   [ +  +  -  + ]:        519 :     if (e.lhs->type()->is_boolean() and e.rhs->type()->is_boolean()) {
     267                 :            :         /* This is an expression in predicate logic.  Convert to CNF. */
     268                 :        197 :         (*this)(*e.lhs);
     269                 :        197 :         auto cnf_lhs = result_;
     270         [ +  - ]:        197 :         (*this)(*e.rhs);
     271         [ +  - ]:        197 :         auto cnf_rhs = result_;
     272                 :            : 
     273   [ +  +  +  -  :        197 :         if ((not is_negative_ and e.op() == TK_And) or (is_negative_ and e.op() == TK_Or)) {
          +  -  +  +  +  
          +  +  -  +  -  
          +  +  +  +  +  
             +  #  #  #  
                      # ]
     274         [ +  - ]:        193 :             result_ = cnf_lhs and cnf_rhs;
     275   [ +  -  +  -  :        197 :         } else if ((not is_negative_ and e.op() == TK_Or) or (is_negative_ and e.op() == TK_And)) {
          +  -  +  -  #  
          #  #  #  #  #  
          +  -  -  +  +  
             -  #  #  #  
                      # ]
     276         [ +  - ]:          4 :             result_ = cnf_lhs or cnf_rhs;
     277   [ #  #  #  #  :          4 :         } else if (e.op() == TK_EQUAL or e.op() == TK_BANG_EQUAL) {
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     278                 :            :             /* A ↔ B ⇔ (A → B) ^ (B → A) ⇔ (¬A v B) ^ (¬B v A) */
     279   [ #  #  #  #  :          0 :             auto equiv = ((not cnf_lhs) or cnf_rhs) and ((not cnf_rhs) or cnf_lhs);
          #  #  #  #  #  
                      # ]
     280   [ #  #  #  #  :          0 :             if ((not is_negative_ and e.op() == TK_BANG_EQUAL) or (is_negative_ and e.op() == TK_EQUAL))
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     281         [ #  # ]:          0 :                 result_ = not equiv; // A ↮ B ⇔ ¬(A ↔ B)
     282                 :            :             else
     283         [ #  # ]:          0 :                 result_ = equiv;
     284                 :          0 :         } else {
     285         [ #  # ]:          0 :             M_unreachable("unsupported boolean expression");
     286                 :            :         }
     287                 :        197 :     } else {
     288                 :            :         /* This expression is a literal. */
     289   [ +  -  +  -  :        322 :         result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             +  -  #  # ]
     290                 :            :     }
     291                 :        519 : }
     292                 :            : 
     293                 :          0 : void CNFGenerator::operator()(Const<QueryExpr> &e)
     294                 :            : {
     295   [ #  #  #  #  :          0 :     result_ = CNF({cnf::Clause({Predicate::Create(&e, is_negative_)})});
             #  #  #  # ]
     296                 :          0 : }
     297                 :            : 
     298                 :        162 : CNF cnf::to_CNF(const Expr &e)
     299                 :            : {
     300                 :        162 :     CNFGenerator G;
     301         [ +  - ]:        162 :     G(e);
     302         [ +  - ]:        162 :     return G.get();
     303                 :        162 : }

Generated by: LCOV version 1.16