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

//==============================================================================
//
// 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