|
|
//==============================================================================
//
// Copyright (c) 2015-
// Authors:
// * Joachim Klein <klein@tcs.inf.tu-dresden.de>
// * David Mueller <david.mueller@tcs.inf.tu-dresden.de>
//
//------------------------------------------------------------------------------
//
// This file is part of the cpphoafparser library,
// http://automata.tools/hoa/cpphoafparser/
//
// The cpphoafparser library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// The cpphoafparser library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//==============================================================================
#ifndef CPPHOAFPARSER_BOOLEANEXPRESSION_H
#define CPPHOAFPARSER_BOOLEANEXPRESSION_H
#include <memory>
#include <iostream>
#include <sstream>
#include <stdexcept>
namespace cpphoafparser {
/**
* This represents (a node of) an abstract syntax tree * of a boolean expression, parametrized with the type * of leaf nodes (atoms). * * The nodes are designed to be immutable, which allows * sharing of subexpression in a safe way between multiple * trees. * * For unary operator (NOT), the child is stored as the * left child of the not. * * With AtomLabel, this represents a label expression * over atomic propositions, with AtomAcceptance an * expression of Fin/Inf acceptance conditions. * * @tparam <Atoms> The atoms (leaf nodes) in the abstract syntax tree. */ template <typename Atoms> class BooleanExpression { public: /** A shared_ptr to a node in this AST */ typedef std::shared_ptr< BooleanExpression<Atoms> > ptr; /** A shared_ptr to an atom (leaf node) in this AST */ typedef std::shared_ptr< Atoms> atom_ptr;
/** The node types of this AST */ enum OperatorType { EXP_AND, EXP_OR, EXP_NOT, EXP_TRUE, EXP_FALSE, EXP_ATOM };
/** Get the node type for this node */ OperatorType getType() { return kind; }
/** Static constructor for a node representing a TRUE leaf */ static ptr True() {return ptr(new BooleanExpression(true));} /** Static constructor for a node representing a FALSE leaf */ static ptr False() {return ptr(new BooleanExpression(false));} /** Static constructor for a node representing an atom leaf */ static ptr Atom(atom_ptr atom) {return ptr(new BooleanExpression(atom));}
/**
* Constructor for a node, providing the type and left and right child nodes. * * For unary operators, the `right` node should be an empty pointer. **/ BooleanExpression(OperatorType kind, ptr left, ptr right) : kind(kind), left(left), right(right), atom(nullptr) { }
/**
* Constructor for a TRUE/FALSE leaf node. */ BooleanExpression(bool value) : left(nullptr), right(nullptr), atom(nullptr) { if (value) { kind = EXP_TRUE; } else { kind = EXP_FALSE; } }
/** Constructor for an atom node */ BooleanExpression(atom_ptr atom) : kind(EXP_ATOM), left(nullptr), right(nullptr), atom(atom) { }
/** Constructor for an atom node (copies atom) */ BooleanExpression(const Atoms& atom) : kind(EXP_ATOM), left(nullptr), right(nullptr), atom(new Atoms(atom)) { }
/** Perform a deep copy (recursive) of this AST and return the result */ ptr deepCopy() { switch (kind) { case EXP_AND: return ptr(new BooleanExpression(EXP_AND, left->deepCopy(), right->deepCopy())); case EXP_OR: return ptr(new BooleanExpression(EXP_OR, left->deepCopy(), right->deepCopy())); case EXP_NOT: return ptr(new BooleanExpression(EXP_NOT, left->deepCopy(), nullptr)); case EXP_TRUE: return True(); case EXP_FALSE: return False(); case EXP_ATOM: return ptr(new BooleanExpression(*atom)); } throw std::logic_error("Unsupported operator"); }
/** Get the left child node (might be an empty pointer) */ ptr getLeft() const {return left;} /** Get the right child node (might be an empty pointer) */ ptr getRight() const {return right;} /** Get the atom for an EXP_ATOM node. May only be called if `isAtom() == true` */ const Atoms& getAtom() const { if (!isAtom()) throw std::logic_error("Illegal access"); return *atom; }
/** Returns true if this node is an EXP_AND node */ bool isAND() const {return kind==EXP_AND;} /** Returns true if this node is an EXP_OR node */ bool isOR() const {return kind==EXP_OR;} /** Returns true if this node is an EXP_NOT node */ bool isNOT() const {return kind==EXP_NOT;} /** Returns true if this node is an EXP_TRUE node */ bool isTRUE() const {return kind==EXP_TRUE;} /** Returns true if this node is an EXP_FALSE node */ bool isFALSE() const {return kind==EXP_FALSE;} /** Returns true if this node is an EXP_ATOM node */ bool isAtom() const {return kind==EXP_ATOM;}
/** Conjunction operator */ friend ptr operator&(ptr left, ptr right) { return ptr(new BooleanExpression<Atoms>(EXP_AND, left, right)); }
/** Disjunction operator */ friend ptr operator|(ptr left, ptr right) { return ptr(new BooleanExpression<Atoms>(EXP_OR, left, right)); }
/** Negation operator */ friend ptr operator!(ptr other) { return ptr(new BooleanExpression<Atoms>(EXP_NOT, other, nullptr)); }
/** Output operator, renders in HOA syntax */ friend std::ostream& operator<<(std::ostream& out, const BooleanExpression<Atoms>& expr) { switch (expr.kind) { case EXP_AND: { bool paren = expr.left->needsParentheses(EXP_AND); if (paren) out << "("; out << *expr.left; if (paren) out << ")";
out << " & ";
paren = expr.right->needsParentheses(EXP_AND); if (paren) out << "("; out << *expr.right; if (paren) out << ")"; return out; } case EXP_OR: { bool paren = expr.left->needsParentheses(EXP_OR); if (paren) out << "("; out << *expr.left; if (paren) out << ")";
out << " | ";
paren = expr.right->needsParentheses(EXP_OR); if (paren) out << "("; out << *expr.right; if (paren) out << ")"; return out; } case EXP_NOT: { bool paren = expr.left->needsParentheses(EXP_NOT); out << "!"; if (paren) out << "("; out << *expr.left; if (paren) out << ")"; return out; } case EXP_TRUE: out << "t"; return out; case EXP_FALSE: out << "f"; return out; case EXP_ATOM: out << *(expr.atom); return out; } throw std::logic_error("Unhandled operator"); }
/** Return a string representation of this AST (HOA syntax) */ std::string toString() const { std::stringstream ss; ss << *this; return ss.str(); }
/**
* Returns `true` if `expr1` and `expr2` are syntactically equal. * Two AST are syntactically equal if the trees match and the * atoms are equal. */ static bool areSyntacticallyEqual(ptr expr1, ptr expr2) { if (!expr1.get() || !expr2.get()) return false; if (expr1->getType() != expr2->getType()) return false;
switch (expr1->getType()) { case EXP_TRUE: case EXP_FALSE: return true; case EXP_AND: case EXP_OR: if (!areSyntacticallyEqual(expr1->getLeft(), expr2->getLeft())) return false; if (!areSyntacticallyEqual(expr1->getRight(), expr2->getRight())) return false; return true; case EXP_NOT: if (!areSyntacticallyEqual(expr1->getLeft(), expr2->getLeft())) return false; return true; case EXP_ATOM: return expr1->getAtom() == expr2->getAtom(); } throw std::logic_error("Unknown operator in expression: "+expr1->toString()); }
private: /** The node type */ OperatorType kind; /** The left child (if applicable) */ ptr left; /** The right child (if applicable) */ ptr right; /** The atom (if applicable) */ atom_ptr atom;
/**
* Returns true if outputing this node in infix syntax needs parentheses, * if the operator above is of `enclosingType` */ bool needsParentheses(OperatorType enclosingType) const { switch (kind) { case EXP_ATOM: case EXP_TRUE: case EXP_FALSE: return false; case EXP_AND: if (enclosingType==EXP_NOT) return true; if (enclosingType==EXP_AND) return false; if (enclosingType==EXP_OR) return false; break; case EXP_OR: if (enclosingType==EXP_NOT) return true; if (enclosingType==EXP_AND) return true; if (enclosingType==EXP_OR) return false; break; case EXP_NOT: return false; } throw std::logic_error("Unhandled operator"); }
};
}
#endif
|