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
302 lines
9.0 KiB
//==============================================================================
|
|
//
|
|
// 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
|