You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
9.0 KiB

  1. //==============================================================================
  2. //
  3. // Copyright (c) 2015-
  4. // Authors:
  5. // * Joachim Klein <klein@tcs.inf.tu-dresden.de>
  6. // * David Mueller <david.mueller@tcs.inf.tu-dresden.de>
  7. //
  8. //------------------------------------------------------------------------------
  9. //
  10. // This file is part of the cpphoafparser library,
  11. // http://automata.tools/hoa/cpphoafparser/
  12. //
  13. // The cpphoafparser library is free software; you can redistribute it and/or
  14. // modify it under the terms of the GNU Lesser General Public
  15. // License as published by the Free Software Foundation; either
  16. // version 2.1 of the License, or (at your option) any later version.
  17. //
  18. // The cpphoafparser library is distributed in the hope that it will be useful,
  19. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21. // Lesser General Public License for more details.
  22. //
  23. // You should have received a copy of the GNU Lesser General Public
  24. // License along with this library; if not, write to the Free Software
  25. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  26. //
  27. //==============================================================================
  28. #ifndef CPPHOAFPARSER_BOOLEANEXPRESSION_H
  29. #define CPPHOAFPARSER_BOOLEANEXPRESSION_H
  30. #include <memory>
  31. #include <iostream>
  32. #include <sstream>
  33. #include <stdexcept>
  34. namespace cpphoafparser {
  35. /**
  36. * This represents (a node of) an abstract syntax tree
  37. * of a boolean expression, parametrized with the type
  38. * of leaf nodes (atoms).
  39. *
  40. * The nodes are designed to be immutable, which allows
  41. * sharing of subexpression in a safe way between multiple
  42. * trees.
  43. *
  44. * For unary operator (NOT), the child is stored as the
  45. * left child of the not.
  46. *
  47. * With AtomLabel, this represents a label expression
  48. * over atomic propositions, with AtomAcceptance an
  49. * expression of Fin/Inf acceptance conditions.
  50. *
  51. * @tparam <Atoms> The atoms (leaf nodes) in the abstract syntax tree.
  52. */
  53. template <typename Atoms>
  54. class BooleanExpression {
  55. public:
  56. /** A shared_ptr to a node in this AST */
  57. typedef std::shared_ptr< BooleanExpression<Atoms> > ptr;
  58. /** A shared_ptr to an atom (leaf node) in this AST */
  59. typedef std::shared_ptr< Atoms> atom_ptr;
  60. /** The node types of this AST */
  61. enum OperatorType {
  62. EXP_AND,
  63. EXP_OR,
  64. EXP_NOT,
  65. EXP_TRUE,
  66. EXP_FALSE,
  67. EXP_ATOM
  68. };
  69. /** Get the node type for this node */
  70. OperatorType getType() {
  71. return kind;
  72. }
  73. /** Static constructor for a node representing a TRUE leaf */
  74. static ptr True() {return ptr(new BooleanExpression(true));}
  75. /** Static constructor for a node representing a FALSE leaf */
  76. static ptr False() {return ptr(new BooleanExpression(false));}
  77. /** Static constructor for a node representing an atom leaf */
  78. static ptr Atom(atom_ptr atom) {return ptr(new BooleanExpression(atom));}
  79. /**
  80. * Constructor for a node, providing the type and left and right child nodes.
  81. *
  82. * For unary operators, the `right` node should be an empty pointer.
  83. **/
  84. BooleanExpression(OperatorType kind, ptr left, ptr right) :
  85. kind(kind), left(left), right(right), atom(nullptr) {
  86. }
  87. /**
  88. * Constructor for a TRUE/FALSE leaf node.
  89. */
  90. BooleanExpression(bool value) :
  91. left(nullptr), right(nullptr), atom(nullptr) {
  92. if (value) {
  93. kind = EXP_TRUE;
  94. } else {
  95. kind = EXP_FALSE;
  96. }
  97. }
  98. /** Constructor for an atom node */
  99. BooleanExpression(atom_ptr atom) :
  100. kind(EXP_ATOM), left(nullptr), right(nullptr), atom(atom) {
  101. }
  102. /** Constructor for an atom node (copies atom) */
  103. BooleanExpression(const Atoms& atom) :
  104. kind(EXP_ATOM), left(nullptr), right(nullptr), atom(new Atoms(atom)) {
  105. }
  106. /** Perform a deep copy (recursive) of this AST and return the result */
  107. ptr deepCopy() {
  108. switch (kind) {
  109. case EXP_AND:
  110. return ptr(new BooleanExpression(EXP_AND, left->deepCopy(), right->deepCopy()));
  111. case EXP_OR:
  112. return ptr(new BooleanExpression(EXP_OR, left->deepCopy(), right->deepCopy()));
  113. case EXP_NOT:
  114. return ptr(new BooleanExpression(EXP_NOT, left->deepCopy(), nullptr));
  115. case EXP_TRUE:
  116. return True();
  117. case EXP_FALSE:
  118. return False();
  119. case EXP_ATOM:
  120. return ptr(new BooleanExpression(*atom));
  121. }
  122. throw std::logic_error("Unsupported operator");
  123. }
  124. /** Get the left child node (might be an empty pointer) */
  125. ptr getLeft() const {return left;}
  126. /** Get the right child node (might be an empty pointer) */
  127. ptr getRight() const {return right;}
  128. /** Get the atom for an EXP_ATOM node. May only be called if `isAtom() == true` */
  129. const Atoms& getAtom() const {
  130. if (!isAtom()) throw std::logic_error("Illegal access");
  131. return *atom;
  132. }
  133. /** Returns true if this node is an EXP_AND node */
  134. bool isAND() const {return kind==EXP_AND;}
  135. /** Returns true if this node is an EXP_OR node */
  136. bool isOR() const {return kind==EXP_OR;}
  137. /** Returns true if this node is an EXP_NOT node */
  138. bool isNOT() const {return kind==EXP_NOT;}
  139. /** Returns true if this node is an EXP_TRUE node */
  140. bool isTRUE() const {return kind==EXP_TRUE;}
  141. /** Returns true if this node is an EXP_FALSE node */
  142. bool isFALSE() const {return kind==EXP_FALSE;}
  143. /** Returns true if this node is an EXP_ATOM node */
  144. bool isAtom() const {return kind==EXP_ATOM;}
  145. /** Conjunction operator */
  146. friend ptr operator&(ptr left, ptr right) {
  147. return ptr(new BooleanExpression<Atoms>(EXP_AND, left, right));
  148. }
  149. /** Disjunction operator */
  150. friend ptr operator|(ptr left, ptr right) {
  151. return ptr(new BooleanExpression<Atoms>(EXP_OR, left, right));
  152. }
  153. /** Negation operator */
  154. friend ptr operator!(ptr other) {
  155. return ptr(new BooleanExpression<Atoms>(EXP_NOT, other, nullptr));
  156. }
  157. /** Output operator, renders in HOA syntax */
  158. friend std::ostream& operator<<(std::ostream& out, const BooleanExpression<Atoms>& expr) {
  159. switch (expr.kind) {
  160. case EXP_AND: {
  161. bool paren = expr.left->needsParentheses(EXP_AND);
  162. if (paren) out << "(";
  163. out << *expr.left;
  164. if (paren) out << ")";
  165. out << " & ";
  166. paren = expr.right->needsParentheses(EXP_AND);
  167. if (paren) out << "(";
  168. out << *expr.right;
  169. if (paren) out << ")";
  170. return out;
  171. }
  172. case EXP_OR: {
  173. bool paren = expr.left->needsParentheses(EXP_OR);
  174. if (paren) out << "(";
  175. out << *expr.left;
  176. if (paren) out << ")";
  177. out << " | ";
  178. paren = expr.right->needsParentheses(EXP_OR);
  179. if (paren) out << "(";
  180. out << *expr.right;
  181. if (paren) out << ")";
  182. return out;
  183. }
  184. case EXP_NOT: {
  185. bool paren = expr.left->needsParentheses(EXP_NOT);
  186. out << "!";
  187. if (paren) out << "(";
  188. out << *expr.left;
  189. if (paren) out << ")";
  190. return out;
  191. }
  192. case EXP_TRUE:
  193. out << "t";
  194. return out;
  195. case EXP_FALSE:
  196. out << "f";
  197. return out;
  198. case EXP_ATOM:
  199. out << *(expr.atom);
  200. return out;
  201. }
  202. throw std::logic_error("Unhandled operator");
  203. }
  204. /** Return a string representation of this AST (HOA syntax) */
  205. std::string toString() const {
  206. std::stringstream ss;
  207. ss << *this;
  208. return ss.str();
  209. }
  210. /**
  211. * Returns `true` if `expr1` and `expr2` are syntactically equal.
  212. * Two AST are syntactically equal if the trees match and the
  213. * atoms are equal.
  214. */
  215. static bool areSyntacticallyEqual(ptr expr1, ptr expr2)
  216. {
  217. if (!expr1.get() || !expr2.get()) return false;
  218. if (expr1->getType() != expr2->getType()) return false;
  219. switch (expr1->getType()) {
  220. case EXP_TRUE:
  221. case EXP_FALSE:
  222. return true;
  223. case EXP_AND:
  224. case EXP_OR:
  225. if (!areSyntacticallyEqual(expr1->getLeft(), expr2->getLeft())) return false;
  226. if (!areSyntacticallyEqual(expr1->getRight(), expr2->getRight())) return false;
  227. return true;
  228. case EXP_NOT:
  229. if (!areSyntacticallyEqual(expr1->getLeft(), expr2->getLeft())) return false;
  230. return true;
  231. case EXP_ATOM:
  232. return expr1->getAtom() == expr2->getAtom();
  233. }
  234. throw std::logic_error("Unknown operator in expression: "+expr1->toString());
  235. }
  236. private:
  237. /** The node type */
  238. OperatorType kind;
  239. /** The left child (if applicable) */
  240. ptr left;
  241. /** The right child (if applicable) */
  242. ptr right;
  243. /** The atom (if applicable) */
  244. atom_ptr atom;
  245. /**
  246. * Returns true if outputing this node in infix syntax needs parentheses,
  247. * if the operator above is of `enclosingType`
  248. */
  249. bool needsParentheses(OperatorType enclosingType) const {
  250. switch (kind) {
  251. case EXP_ATOM:
  252. case EXP_TRUE:
  253. case EXP_FALSE:
  254. return false;
  255. case EXP_AND:
  256. if (enclosingType==EXP_NOT) return true;
  257. if (enclosingType==EXP_AND) return false;
  258. if (enclosingType==EXP_OR) return false;
  259. break;
  260. case EXP_OR:
  261. if (enclosingType==EXP_NOT) return true;
  262. if (enclosingType==EXP_AND) return true;
  263. if (enclosingType==EXP_OR) return false;
  264. break;
  265. case EXP_NOT:
  266. return false;
  267. }
  268. throw std::logic_error("Unhandled operator");
  269. }
  270. };
  271. }
  272. #endif