revamp the formula hierarchy (montro-patch)
Flatten the formula ltl::formula hiearchy into a single ltl::vnode that has an enumerator to distinguish the types of node, and a common interface to access children, update reference counts, etc. The ltl::formula class is now a thin wrapper around an ltl::vnode pointer to keep track of reference counts automatically. Visitor are not used anymore; we now have map() and traversor() methods that are more concise. This basically fixes #43, but should be followed by some fine tuning that should now be localized to the formula.hh and formula.cc files. Some statistics about this patch. I started working on it on Sep 9, had a first compiling version two weeks later on Sep 22, and it then took 5 days to fixes the ~70 distincts bugs that were introduced during the conversion. About 13200 lines were modified, and one third of those were removed. * src/ltlast/formula.cc, src/ltlast/formula.hh: Complete rewrite, including what was in separate nearby files. * src/ltlast/allnodes.hh, src/ltlast/atomic_prop.cc, src/ltlast/atomic_prop.hh, src/ltlast/binop.cc, src/ltlast/binop.hh, src/ltlast/bunop.cc, src/ltlast/bunop.hh, src/ltlast/constant.cc, src/ltlast/constant.hh, src/ltlast/multop.cc, src/ltlast/multop.hh, src/ltlast/unop.cc, src/ltlast/unop.hh, src/ltlvisit/dump.cc, src/ltlvisit/dump.hh, src/ltlast/predecl.hh: Delete these files. Their feature have been merged in formula.hh and formula.cc. * src/ltlast/visitor.hh, src/ltlvisit/clone.cc, src/ltlvisit/clone.hh, src/ltlvisit/dump.hh, src/ltlvisit/postfix.cc, src/ltlvisit/postfix.hh: Delete these files, as we do not use visitors anymore. * bench/stutter/stutter_invariance_formulas.cc, bench/stutter/stutter_invariance_randomgraph.cc, doc/org/tut01.org, doc/org/tut02.org, doc/org/tut10.org, doc/org/tut22.org, iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh, iface/ltsmin/modelcheck.cc, src/bin/autfilt.cc, src/bin/common_aoutput.cc, src/bin/common_aoutput.hh, src/bin/common_finput.cc, src/bin/common_finput.hh, src/bin/common_output.cc, src/bin/common_output.hh, src/bin/common_trans.cc, src/bin/common_trans.hh, src/bin/dstar2tgba.cc, src/bin/genltl.cc, src/bin/ltl2tgba.cc, src/bin/ltl2tgta.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc, src/bin/ltlfilt.cc, src/bin/ltlgrind.cc, src/bin/randaut.cc, src/bin/randltl.cc, src/kripke/kripkeexplicit.cc, src/kripke/kripkeexplicit.hh, src/kripkeparse/kripkeparse.yy, src/ltlast/Makefile.am, src/ltlenv/declenv.cc, src/ltlenv/declenv.hh, src/ltlenv/defaultenv.cc, src/ltlenv/defaultenv.hh, src/ltlenv/environment.hh, src/ltlparse/ltlparse.yy, src/ltlparse/public.hh, src/ltlvisit/Makefile.am, src/ltlvisit/apcollect.cc, src/ltlvisit/apcollect.hh, src/ltlvisit/contain.cc, src/ltlvisit/contain.hh, src/ltlvisit/dot.cc, src/ltlvisit/dot.hh, src/ltlvisit/exclusive.cc, src/ltlvisit/exclusive.hh, src/ltlvisit/length.cc, src/ltlvisit/length.hh, src/ltlvisit/mark.cc, src/ltlvisit/mark.hh, src/ltlvisit/mutation.cc, src/ltlvisit/mutation.hh, src/ltlvisit/nenoform.cc, src/ltlvisit/nenoform.hh, src/ltlvisit/print.cc, src/ltlvisit/print.hh, src/ltlvisit/randomltl.cc, src/ltlvisit/randomltl.hh, src/ltlvisit/relabel.cc, src/ltlvisit/relabel.hh, src/ltlvisit/remove_x.cc, src/ltlvisit/remove_x.hh, src/ltlvisit/simpfg.cc, src/ltlvisit/simpfg.hh, src/ltlvisit/simplify.cc, src/ltlvisit/simplify.hh, src/ltlvisit/snf.cc, src/ltlvisit/snf.hh, src/ltlvisit/unabbrev.cc, src/ltlvisit/unabbrev.hh, src/parseaut/parseaut.yy, src/ta/taexplicit.cc, src/ta/tgtaexplicit.cc, src/taalgos/minimize.cc, src/taalgos/tgba2ta.cc, src/tests/bare.test, src/tests/checkpsl.cc, src/tests/checkta.cc, src/tests/complementation.cc, src/tests/consterm.cc, src/tests/emptchk.cc, src/tests/equalsf.cc, src/tests/ikwiad.cc, src/tests/isop.test, src/tests/kind.cc, src/tests/length.cc, src/tests/ltldo.test, src/tests/ltlfilt.test, src/tests/ltlgrind.test, src/tests/ltlprod.cc, src/tests/ltlrel.cc, src/tests/parse_print_test.cc, src/tests/parseaut.test, src/tests/parseerr.test, src/tests/randtgba.cc, src/tests/readltl.cc, src/tests/reduc.cc, src/tests/syntimpl.cc, src/tests/taatgba.cc, src/tests/tostring.cc, src/tests/twagraph.cc, src/tests/utf8.test, src/twa/acc.cc, src/twa/bdddict.cc, src/twa/bdddict.hh, src/twa/bddprint.cc, src/twa/formula2bdd.cc, src/twa/formula2bdd.hh, src/twa/taatgba.cc, src/twa/taatgba.hh, src/twa/twa.cc, src/twa/twa.hh src/twa/twagraph.cc, src/twa/twagraph.hh, src/twa/twasafracomplement.cc, src/twaalgos/compsusp.cc, src/twaalgos/compsusp.hh, src/twaalgos/dtgbasat.cc, src/twaalgos/hoa.cc, src/twaalgos/lbtt.cc, src/twaalgos/ltl2taa.cc, src/twaalgos/ltl2taa.hh, src/twaalgos/ltl2tgba_fm.cc, src/twaalgos/ltl2tgba_fm.hh, src/twaalgos/minimize.cc, src/twaalgos/minimize.hh, src/twaalgos/neverclaim.cc, src/twaalgos/postproc.cc, src/twaalgos/postproc.hh, src/twaalgos/powerset.cc, src/twaalgos/powerset.hh, src/twaalgos/randomgraph.cc, src/twaalgos/remprop.cc, src/twaalgos/remprop.hh, src/twaalgos/stats.cc, src/twaalgos/stats.hh, src/twaalgos/stutter.cc, src/twaalgos/stutter.hh, src/twaalgos/translate.cc, src/twaalgos/translate.hh, wrap/python/ajax/spotcgi.in, wrap/python/spot.py, wrap/python/spot_impl.i, wrap/python/Makefile.am, wrap/python/tests/automata-io.ipynb, wrap/python/tests/formulas.ipynb, wrap/python/tests/ltl2tgba.py, wrap/python/tests/ltlparse.py, wrap/python/tests/ltlsimple.py, wrap/python/tests/randltl.ipynb: Adjust to use the new interface. * src/sanity/style.test: Accept more C++11 patterns. * NEWS: Mention the change.
This commit is contained in:
parent
1628b188fe
commit
b77f7e24c3
177 changed files with 8295 additions and 13332 deletions
|
|
@ -1,6 +1,6 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2009, 2010, 2011, 2013, 2014 Laboratoire de Recherche
|
||||
## et Développement de l'Epita (LRDE).
|
||||
## Copyright (C) 2009, 2010, 2011, 2013, 2014, 2015 Laboratoire de
|
||||
## Recherche et Développement de l'Epita (LRDE).
|
||||
## Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
## département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
## et Marie Curie.
|
||||
|
|
@ -26,24 +26,7 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
|||
|
||||
ltlastdir = $(pkgincludedir)/ltlast
|
||||
|
||||
ltlast_HEADERS = \
|
||||
allnodes.hh \
|
||||
atomic_prop.hh \
|
||||
binop.hh \
|
||||
bunop.hh \
|
||||
constant.hh \
|
||||
formula.hh \
|
||||
multop.hh \
|
||||
predecl.hh \
|
||||
unop.hh \
|
||||
visitor.hh
|
||||
ltlast_HEADERS = formula.hh
|
||||
|
||||
noinst_LTLIBRARIES = libltlast.la
|
||||
libltlast_la_SOURCES = \
|
||||
atomic_prop.cc \
|
||||
binop.cc \
|
||||
bunop.cc \
|
||||
constant.cc \
|
||||
formula.cc \
|
||||
multop.cc \
|
||||
unop.cc
|
||||
libltlast_la_SOURCES = formula.cc
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2010, 2014 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/allnodes.hh
|
||||
/// \brief Define all LTL node types.
|
||||
///
|
||||
/// This file is usually needed when \b defining a visitor.
|
||||
/// Prefer ltlast/predecl.hh when only \b declaring methods and functions
|
||||
/// over LTL nodes.
|
||||
#pragma once
|
||||
|
||||
#include "binop.hh"
|
||||
#include "unop.hh"
|
||||
#include "multop.hh"
|
||||
#include "atomic_prop.hh"
|
||||
#include "constant.hh"
|
||||
#include "bunop.hh"
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de
|
||||
// Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
|
||||
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include "atomic_prop.hh"
|
||||
#include "visitor.hh"
|
||||
#include "misc/bareword.hh"
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
atomic_prop::atomic_prop(const std::string& name, environment& env)
|
||||
: formula(AtomicProp), name_(name), env_(&env)
|
||||
{
|
||||
is.boolean = true;
|
||||
is.sugar_free_boolean = true;
|
||||
is.in_nenoform = true;
|
||||
is.syntactic_si = true; // Assuming LTL (for PSL a Boolean
|
||||
// term is not stared will be regarded
|
||||
// as not stuttering were this
|
||||
// matters.)
|
||||
is.sugar_free_ltl = true;
|
||||
is.ltl_formula = true;
|
||||
is.psl_formula = true;
|
||||
is.sere_formula = true;
|
||||
is.finite = true;
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
is.syntactic_safety = true;
|
||||
is.syntactic_guarantee = true;
|
||||
is.syntactic_obligation = true;
|
||||
is.syntactic_recurrence = true;
|
||||
is.syntactic_persistence = true;
|
||||
is.not_marked = true;
|
||||
is.accepting_eword = false;
|
||||
// is.lbt_atomic_props should be true if the name has the form
|
||||
// pNN where NN is any number of digit.
|
||||
std::string::const_iterator pos = name.begin();
|
||||
bool lbtap = (pos != name.end() && *pos++ == 'p');
|
||||
while (lbtap && pos != name.end())
|
||||
{
|
||||
char l = *pos++;
|
||||
lbtap = (l >= '0' && l <= '9');
|
||||
}
|
||||
is.lbt_atomic_props = lbtap;
|
||||
is.spin_atomic_props = lbtap || is_spin_ap(name.c_str());
|
||||
}
|
||||
|
||||
atomic_prop::~atomic_prop()
|
||||
{
|
||||
// Get this instance out of the instance map.
|
||||
size_t c = instances.erase(key(name(), &env()));
|
||||
assert(c == 1);
|
||||
(void) c; // For the NDEBUG case.
|
||||
}
|
||||
|
||||
std::string
|
||||
atomic_prop::dump() const
|
||||
{
|
||||
return "AP(" + name() + ")";
|
||||
}
|
||||
|
||||
void
|
||||
atomic_prop::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
atomic_prop::map atomic_prop::instances;
|
||||
|
||||
const atomic_prop*
|
||||
atomic_prop::instance(const std::string& name, environment& env)
|
||||
{
|
||||
const atomic_prop* ap;
|
||||
auto ires = instances.emplace(key(name, &env), nullptr);
|
||||
if (!ires.second)
|
||||
{
|
||||
ap = ires.first->second;
|
||||
ap->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
ap = ires.first->second = new atomic_prop(name, env);
|
||||
}
|
||||
return ap;
|
||||
}
|
||||
|
||||
unsigned
|
||||
atomic_prop::instance_count()
|
||||
{
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
atomic_prop::dump_instances(std::ostream& os)
|
||||
{
|
||||
for (const auto& i: instances)
|
||||
os << i.second << " = " << 1 + i.second->refs_
|
||||
<< " * atomic_prop(" << i.first.first << ", "
|
||||
<< i.first.second->name() << ")\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2012, 2013, 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/atomic_prop.hh
|
||||
/// \brief LTL atomic propositions
|
||||
#pragma once
|
||||
|
||||
#include "formula.hh"
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include "ltlenv/environment.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief Atomic propositions.
|
||||
class SPOT_API atomic_prop final : public formula
|
||||
{
|
||||
public:
|
||||
/// Build an atomic proposition with name \a name in
|
||||
/// environment \a env.
|
||||
static const atomic_prop*
|
||||
instance(const std::string& name, environment& env);
|
||||
|
||||
virtual void accept(visitor& visitor) const override;
|
||||
|
||||
/// Get the name of the atomic proposition.
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/// Get the environment of the atomic proposition.
|
||||
environment& env() const
|
||||
{
|
||||
return *env_;
|
||||
}
|
||||
|
||||
/// Return a canonic representation of the atomic proposition
|
||||
virtual std::string dump() const override;
|
||||
|
||||
/// Number of instantiated atomic propositions. For debugging.
|
||||
static unsigned instance_count();
|
||||
/// List all instances of atomic propositions. For debugging.
|
||||
static std::ostream& dump_instances(std::ostream& os);
|
||||
|
||||
protected:
|
||||
atomic_prop(const std::string& name, environment& env);
|
||||
virtual ~atomic_prop();
|
||||
|
||||
typedef std::pair<std::string, environment*> key;
|
||||
typedef std::map<key, const atomic_prop*> map;
|
||||
static map instances;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
environment* env_;
|
||||
};
|
||||
|
||||
inline
|
||||
const atomic_prop*
|
||||
is_atomic_prop(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::AtomicProp)
|
||||
return 0;
|
||||
return static_cast<const atomic_prop*>(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,538 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
|
||||
// de Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include "binop.hh"
|
||||
#include "unop.hh"
|
||||
#include "multop.hh"
|
||||
#include "constant.hh"
|
||||
#include "visitor.hh"
|
||||
#include <iostream>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
binop::binop(type op, const formula* first, const formula* second)
|
||||
: formula(BinOp), op_(op), first_(first), second_(second)
|
||||
{
|
||||
// Beware: (f U g) is a pure eventuality if both operands
|
||||
// are pure eventualities, unlike in the proceedings of
|
||||
// Concur'00. (The revision of the paper available at
|
||||
// http://www.bell-labs.com/project/TMP/ is fixed.) See
|
||||
// also http://arxiv.org/abs/1011.4214v2 for a discussion
|
||||
// about this problem. (Which we fixed in 2005 thanks
|
||||
// to LBTT.)
|
||||
// This means that we can use the following line to handle
|
||||
// all cases of (f U g), (f R g), (f W g), (f M g) for
|
||||
// universality and eventuality.
|
||||
props = first->get_props() & second->get_props();
|
||||
// The matter can be further refined because:
|
||||
// (f U g) is a pure eventuality if
|
||||
// g is a pure eventuality (regardless of f),
|
||||
// or f == 1
|
||||
// (g M f) is a pure eventuality if f and g are,
|
||||
// or f == 1
|
||||
// (g R f) is purely universal if
|
||||
// f is purely universal (regardless of g)
|
||||
// or g == 0
|
||||
// (f W g) is purely universal if f and g are
|
||||
// or g == 0
|
||||
switch (op)
|
||||
{
|
||||
case Xor:
|
||||
case Equiv:
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
is.sere_formula = is.boolean;
|
||||
is.sugar_free_boolean = false;
|
||||
is.in_nenoform = false;
|
||||
// is.syntactic_obligation inherited;
|
||||
is.accepting_eword = false;
|
||||
if (is.syntactic_obligation)
|
||||
{
|
||||
// Only formula that are in the intersection of
|
||||
// guarantee and safety are closed by Xor and <=>.
|
||||
bool sg = is.syntactic_safety && is.syntactic_guarantee;
|
||||
is.syntactic_safety = sg;
|
||||
is.syntactic_guarantee = sg;
|
||||
assert(is.syntactic_recurrence == true);
|
||||
assert(is.syntactic_persistence == true);
|
||||
}
|
||||
else
|
||||
{
|
||||
is.syntactic_safety = false;
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_recurrence = false;
|
||||
is.syntactic_persistence = false;
|
||||
}
|
||||
break;
|
||||
case Implies:
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
is.sere_formula = is.boolean;
|
||||
is.sugar_free_boolean = false;
|
||||
is.in_nenoform = false;
|
||||
is.syntactic_safety =
|
||||
first->is_syntactic_guarantee() && second->is_syntactic_safety();
|
||||
is.syntactic_guarantee =
|
||||
first->is_syntactic_safety() && second->is_syntactic_guarantee();
|
||||
// is.syntactic_obligation inherited
|
||||
is.syntactic_persistence = first->is_syntactic_recurrence()
|
||||
&& second->is_syntactic_persistence();
|
||||
is.syntactic_recurrence = first->is_syntactic_persistence()
|
||||
&& second->is_syntactic_recurrence();
|
||||
is.accepting_eword = false;
|
||||
break;
|
||||
case EConcatMarked:
|
||||
case EConcat:
|
||||
is.not_marked = (op != EConcatMarked);
|
||||
is.ltl_formula = false;
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.accepting_eword = false;
|
||||
is.psl_formula = true;
|
||||
|
||||
is.syntactic_guarantee = second->is_syntactic_guarantee();
|
||||
is.syntactic_persistence = second->is_syntactic_persistence();
|
||||
if (first->is_finite())
|
||||
{
|
||||
is.syntactic_safety = second->is_syntactic_safety();
|
||||
is.syntactic_obligation = second->is_syntactic_obligation();
|
||||
is.syntactic_recurrence = second->is_syntactic_recurrence();
|
||||
}
|
||||
else
|
||||
{
|
||||
is.syntactic_safety = false;
|
||||
is.syntactic_obligation = second->is_syntactic_guarantee();
|
||||
is.syntactic_recurrence = second->is_syntactic_guarantee();
|
||||
}
|
||||
assert(first->is_sere_formula());
|
||||
assert(second->is_psl_formula());
|
||||
if (first->is_boolean())
|
||||
is.syntactic_si = false;
|
||||
break;
|
||||
case UConcat:
|
||||
is.not_marked = true;
|
||||
is.ltl_formula = false;
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.accepting_eword = false;
|
||||
is.psl_formula = true;
|
||||
|
||||
is.syntactic_safety = second->is_syntactic_safety();
|
||||
is.syntactic_recurrence = second->is_syntactic_recurrence();
|
||||
if (first->is_finite())
|
||||
{
|
||||
is.syntactic_guarantee = second->is_syntactic_guarantee();
|
||||
is.syntactic_obligation = second->is_syntactic_obligation();
|
||||
is.syntactic_persistence = second->is_syntactic_persistence();
|
||||
}
|
||||
else
|
||||
{
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = second->is_syntactic_safety();
|
||||
is.syntactic_persistence = second->is_syntactic_safety();
|
||||
}
|
||||
assert(first->is_sere_formula());
|
||||
assert(second->is_psl_formula());
|
||||
if (first->is_boolean())
|
||||
is.syntactic_si = false;
|
||||
break;
|
||||
case U:
|
||||
is.not_marked = true;
|
||||
// f U g is universal if g is eventual, or if f == 1.
|
||||
is.eventual = second->is_eventual();
|
||||
is.eventual |= (first == constant::true_instance());
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.accepting_eword = false;
|
||||
|
||||
is.syntactic_safety = false;
|
||||
// is.syntactic_guarantee = Guarantee U Guarantee
|
||||
is.syntactic_obligation = // Obligation U Guarantee
|
||||
first->is_syntactic_obligation()
|
||||
&& second->is_syntactic_guarantee();
|
||||
is.syntactic_recurrence = // Recurrence U Guarantee
|
||||
first->is_syntactic_recurrence()
|
||||
&& second->is_syntactic_guarantee();
|
||||
// is.syntactic_persistence = Persistence U Persistance
|
||||
break;
|
||||
case W:
|
||||
is.not_marked = true;
|
||||
// f W g is universal if f and g are, or if g == 0.
|
||||
is.universal |= (second == constant::false_instance());
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.accepting_eword = false;
|
||||
|
||||
// is.syntactic_safety = Safety W Safety;
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = // Safety W Obligation
|
||||
first->is_syntactic_safety() && second->is_syntactic_obligation();
|
||||
// is.syntactic_recurrence = Recurrence W Recurrence
|
||||
is.syntactic_persistence = // Safety W Persistance
|
||||
first->is_syntactic_safety()
|
||||
&& second->is_syntactic_persistence();
|
||||
|
||||
break;
|
||||
case R:
|
||||
is.not_marked = true;
|
||||
// g R f is universal if f is universal, or if g == 0.
|
||||
is.universal = second->is_universal();
|
||||
is.universal |= (first == constant::false_instance());
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.accepting_eword = false;
|
||||
|
||||
// is.syntactic_safety = Safety R Safety;
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = // Obligation R Safety
|
||||
first->is_syntactic_obligation() && second->is_syntactic_safety();
|
||||
//is.syntactic_recurrence = Recurrence R Recurrence
|
||||
is.syntactic_persistence = // Persistence R Safety
|
||||
first->is_syntactic_persistence()
|
||||
&& second->is_syntactic_safety();
|
||||
|
||||
break;
|
||||
case M:
|
||||
is.not_marked = true;
|
||||
// g M f is eventual if both g and f are eventual, or if f == 1.
|
||||
is.eventual |= (second == constant::true_instance());
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.accepting_eword = false;
|
||||
|
||||
is.syntactic_safety = false;
|
||||
// is.syntactic_guarantee = Guarantee M Guarantee
|
||||
is.syntactic_obligation = // Guarantee M Obligation
|
||||
first->is_syntactic_guarantee()
|
||||
&& second->is_syntactic_obligation();
|
||||
is.syntactic_recurrence = // Guarantee M Recurrence
|
||||
first->is_syntactic_guarantee()
|
||||
&& second->is_syntactic_recurrence();
|
||||
// is.syntactic_persistence = Persistence M Persistance
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
assert((!is.syntactic_obligation) ||
|
||||
(is.syntactic_persistence && is.syntactic_recurrence));
|
||||
}
|
||||
|
||||
binop::~binop()
|
||||
{
|
||||
// Get this instance out of the instance map.
|
||||
size_t c = instances.erase(key(op(), first(), second()));
|
||||
assert(c == 1);
|
||||
(void) c; // For the NDEBUG case.
|
||||
|
||||
// Dereference children.
|
||||
first()->destroy();
|
||||
second()->destroy();
|
||||
}
|
||||
|
||||
std::string
|
||||
binop::dump() const
|
||||
{
|
||||
return (std::string("binop(") + op_name()
|
||||
+ ", " + first()->dump()
|
||||
+ ", " + second()->dump() + ")");
|
||||
}
|
||||
|
||||
void
|
||||
binop::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
const char*
|
||||
binop::op_name() const
|
||||
{
|
||||
switch (op_)
|
||||
{
|
||||
case Xor:
|
||||
return "Xor";
|
||||
case Implies:
|
||||
return "Implies";
|
||||
case Equiv:
|
||||
return "Equiv";
|
||||
case U:
|
||||
return "U";
|
||||
case R:
|
||||
return "R";
|
||||
case W:
|
||||
return "W";
|
||||
case M:
|
||||
return "M";
|
||||
case EConcat:
|
||||
return "EConcat";
|
||||
case EConcatMarked:
|
||||
return "EConcatMarked";
|
||||
case UConcat:
|
||||
return "UConcat";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
binop::map binop::instances;
|
||||
|
||||
const formula*
|
||||
binop::instance(type op, const formula* first, const formula* second)
|
||||
{
|
||||
// Sort the operands of commutative operators, so that for
|
||||
// example the formula instance for 'a xor b' is the same as
|
||||
// that for 'b xor a'.
|
||||
|
||||
// Trivial identities:
|
||||
switch (op)
|
||||
{
|
||||
case Xor:
|
||||
{
|
||||
// Xor is commutative: sort operands.
|
||||
formula_ptr_less_than_bool_first cmp;
|
||||
if (cmp(second, first))
|
||||
std::swap(second, first);
|
||||
}
|
||||
// - (1 ^ Exp) = !Exp
|
||||
// - (0 ^ Exp) = Exp
|
||||
if (first == constant::true_instance())
|
||||
return unop::instance(unop::Not, second);
|
||||
if (first == constant::false_instance())
|
||||
return second;
|
||||
if (first == second)
|
||||
{
|
||||
first->destroy();
|
||||
second->destroy();
|
||||
return constant::false_instance();
|
||||
}
|
||||
// We expect constants to appear first, because they are
|
||||
// instantiated first.
|
||||
assert(second != constant::false_instance());
|
||||
assert(second != constant::true_instance());
|
||||
break;
|
||||
case Equiv:
|
||||
{
|
||||
// Equiv is commutative: sort operands.
|
||||
formula_ptr_less_than_bool_first cmp;
|
||||
if (cmp(second, first))
|
||||
std::swap(second, first);
|
||||
}
|
||||
// - (0 <=> Exp) = !Exp
|
||||
// - (1 <=> Exp) = Exp
|
||||
// - (Exp <=> Exp) = 1
|
||||
if (first == constant::false_instance())
|
||||
return unop::instance(unop::Not, second);
|
||||
if (first == constant::true_instance())
|
||||
return second;
|
||||
if (first == second)
|
||||
{
|
||||
first->destroy();
|
||||
second->destroy();
|
||||
return constant::true_instance();
|
||||
}
|
||||
// We expect constants to appear first, because they are
|
||||
// instantiated first.
|
||||
assert(second != constant::false_instance());
|
||||
assert(second != constant::true_instance());
|
||||
break;
|
||||
case Implies:
|
||||
// - (1 => Exp) = Exp
|
||||
// - (0 => Exp) = 1
|
||||
// - (Exp => 1) = 1
|
||||
// - (Exp => 0) = !Exp
|
||||
// - (Exp => Exp) = 1
|
||||
if (first == constant::true_instance())
|
||||
return second;
|
||||
if (first == constant::false_instance())
|
||||
{
|
||||
second->destroy();
|
||||
return constant::true_instance();
|
||||
}
|
||||
if (second == constant::true_instance())
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
if (second == constant::false_instance())
|
||||
return unop::instance(unop::Not, first);
|
||||
if (first == second)
|
||||
{
|
||||
first->destroy();
|
||||
second->destroy();
|
||||
return constant::true_instance();
|
||||
}
|
||||
break;
|
||||
case U:
|
||||
// - (Exp U 1) = 1
|
||||
// - (Exp U 0) = 0
|
||||
// - (0 U Exp) = Exp
|
||||
// - (Exp U Exp) = Exp
|
||||
if (second == constant::true_instance()
|
||||
|| second == constant::false_instance()
|
||||
|| first == constant::false_instance()
|
||||
|| first == second)
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
break;
|
||||
case W:
|
||||
// - (Exp W 1) = 1
|
||||
// - (0 W Exp) = Exp
|
||||
// - (1 W Exp) = 1
|
||||
// - (Exp W Exp) = Exp
|
||||
if (second == constant::true_instance()
|
||||
|| first == constant::false_instance()
|
||||
|| first == second)
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
if (first == constant::true_instance())
|
||||
{
|
||||
second->destroy();
|
||||
return first;
|
||||
}
|
||||
break;
|
||||
case R:
|
||||
// - (Exp R 1) = 1
|
||||
// - (Exp R 0) = 0
|
||||
// - (1 R Exp) = Exp
|
||||
// - (Exp R Exp) = Exp
|
||||
if (second == constant::true_instance()
|
||||
|| second == constant::false_instance()
|
||||
|| first == constant::true_instance()
|
||||
|| first == second)
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
break;
|
||||
case M:
|
||||
// - (Exp M 0) = 0
|
||||
// - (1 M Exp) = Exp
|
||||
// - (0 M Exp) = 0
|
||||
// - (Exp M Exp) = Exp
|
||||
if (second == constant::false_instance()
|
||||
|| first == constant::true_instance()
|
||||
|| first == second)
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
if (first == constant::false_instance())
|
||||
{
|
||||
second->destroy();
|
||||
return first;
|
||||
}
|
||||
break;
|
||||
case EConcat:
|
||||
case EConcatMarked:
|
||||
// - 0 <>-> Exp = 0
|
||||
// - 1 <>-> Exp = Exp
|
||||
// - [*0] <>-> Exp = 0
|
||||
// - Exp <>-> 0 = 0
|
||||
// - boolExp <>-> Exp = boolExp & Exp
|
||||
if (first == constant::true_instance())
|
||||
return second;
|
||||
if (first == constant::false_instance()
|
||||
|| first == constant::empty_word_instance())
|
||||
{
|
||||
second->destroy();
|
||||
return constant::false_instance();
|
||||
}
|
||||
if (second == constant::false_instance())
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
if (first->is_boolean())
|
||||
return multop::instance(multop::And, first, second);
|
||||
break;
|
||||
case UConcat:
|
||||
// - 0 []-> Exp = 1
|
||||
// - 1 []-> Exp = Exp
|
||||
// - [*0] []-> Exp = 1
|
||||
// - Exp []-> 1 = 1
|
||||
// - boolExp []-> Exp = !boolExp | Exp
|
||||
if (first == constant::true_instance())
|
||||
return second;
|
||||
if (first == constant::false_instance()
|
||||
|| first == constant::empty_word_instance())
|
||||
{
|
||||
second->destroy();
|
||||
return constant::true_instance();
|
||||
}
|
||||
if (second == constant::true_instance())
|
||||
{
|
||||
first->destroy();
|
||||
return second;
|
||||
}
|
||||
if (first->is_boolean())
|
||||
return multop::instance(multop::Or,
|
||||
unop::instance(unop::Not, first), second);
|
||||
break;
|
||||
}
|
||||
|
||||
const formula* res;
|
||||
auto ires = instances.emplace(key(op, first, second), nullptr);
|
||||
if (!ires.second)
|
||||
{
|
||||
// This instance already exists.
|
||||
first->destroy();
|
||||
second->destroy();
|
||||
res = ires.first->second->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = ires.first->second = new binop(op, first, second);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned
|
||||
binop::instance_count()
|
||||
{
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
binop::dump_instances(std::ostream& os)
|
||||
{
|
||||
for (const auto& i: instances)
|
||||
os << i.second << " = "
|
||||
<< 1 + i.second->refs_ << " * "
|
||||
<< i.second->dump() << '\n';
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
|
||||
// Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/binop.hh
|
||||
/// \brief LTL binary operators
|
||||
///
|
||||
/// This does not include \c AND and \c OR operators. These are
|
||||
/// considered to be multi-operand operators (see spot::ltl::multop).
|
||||
#pragma once
|
||||
|
||||
#include "formula.hh"
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
#include <tuple>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief Binary operator.
|
||||
class SPOT_API binop final: public formula
|
||||
{
|
||||
public:
|
||||
/// Different kinds of binary opertaors
|
||||
///
|
||||
/// And and Or are not here. Because they
|
||||
/// are often nested we represent them as multops.
|
||||
enum type { Xor,
|
||||
Implies,
|
||||
Equiv,
|
||||
U, ///< until
|
||||
R, ///< release (dual of until)
|
||||
W, ///< weak until
|
||||
M, ///< strong release (dual of weak until)
|
||||
EConcat, ///< Existential Concatenation
|
||||
EConcatMarked, ///< Existential Concatenation, Marked
|
||||
UConcat ///< Universal Concatenation
|
||||
};
|
||||
|
||||
/// \brief Build a unary operator with operation \a op and
|
||||
/// children \a first and \a second.
|
||||
///
|
||||
/// Some reordering will be performed on arguments of commutative
|
||||
/// operators (Xor and Equiv) to ensure that for instance (a <=> b)
|
||||
/// is the same formula as (b <=> a).
|
||||
///
|
||||
/// Furthermore, the following trivial simplifications are
|
||||
/// performed (the left formula is rewritten as the right
|
||||
/// formula):
|
||||
/// - (1 => Exp) = Exp
|
||||
/// - (0 => Exp) = 1
|
||||
/// - (Exp => 1) = 1
|
||||
/// - (Exp => 0) = !Exp
|
||||
/// - (Exp => Exp) = 1
|
||||
/// - (1 ^ Exp) = !Exp
|
||||
/// - (0 ^ Exp) = Exp
|
||||
/// - (Exp ^ Exp) = 0
|
||||
/// - (0 <=> Exp) = !Exp
|
||||
/// - (1 <=> Exp) = Exp
|
||||
/// - (Exp <=> Exp) = Exp
|
||||
/// - (Exp U 1) = 1
|
||||
/// - (Exp U 0) = 0
|
||||
/// - (0 U Exp) = Exp
|
||||
/// - (Exp U Exp) = Exp
|
||||
/// - (Exp W 1) = 1
|
||||
/// - (0 W Exp) = Exp
|
||||
/// - (1 W Exp) = 1
|
||||
/// - (Exp W Exp) = Exp
|
||||
/// - (Exp R 1) = 1
|
||||
/// - (Exp R 0) = 0
|
||||
/// - (1 R Exp) = Exp
|
||||
/// - (Exp R Exp) = Exp
|
||||
/// - (Exp M 0) = 0
|
||||
/// - (1 M Exp) = Exp
|
||||
/// - (0 M Exp) = 0
|
||||
/// - (Exp M Exp) = Exp
|
||||
/// - 0 <>-> Exp = 0
|
||||
/// - 1 <>-> Exp = Exp
|
||||
/// - [*0] <>-> Exp = 0
|
||||
/// - Exp <>-> 0 = 0
|
||||
/// - boolExp <>-> Exp = boolExp & Exp
|
||||
/// - 0 []-> Exp = 1
|
||||
/// - 1 []-> Exp = Exp
|
||||
/// - [*0] []-> Exp = 1
|
||||
/// - Exp []-> 1 = 1
|
||||
/// - boolExp <>-> Exp = !boolExp | Exp
|
||||
static const formula* instance(type op,
|
||||
const formula* first,
|
||||
const formula* second);
|
||||
|
||||
virtual void accept(visitor& v) const override;
|
||||
|
||||
/// Get the first operand.
|
||||
const formula* first() const
|
||||
{
|
||||
return first_;
|
||||
}
|
||||
|
||||
/// Get the second operand.
|
||||
const formula* second() const
|
||||
{
|
||||
return second_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator.
|
||||
type op() const
|
||||
{
|
||||
return op_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator, as a string.
|
||||
const char* op_name() const;
|
||||
|
||||
/// Return a canonic representation of the atomic proposition
|
||||
virtual std::string dump() const override;
|
||||
|
||||
/// Number of instantiated binary operators. For debugging.
|
||||
static unsigned instance_count();
|
||||
|
||||
/// Dump all instances. For debugging.
|
||||
static std::ostream& dump_instances(std::ostream& os);
|
||||
|
||||
protected:
|
||||
typedef std::tuple<type, const formula*, const formula*> key;
|
||||
typedef std::map<key, const binop*> map;
|
||||
static map instances;
|
||||
|
||||
binop(type op, const formula* first, const formula* second);
|
||||
virtual ~binop();
|
||||
|
||||
private:
|
||||
type op_;
|
||||
const formula* first_;
|
||||
const formula* second_;
|
||||
};
|
||||
|
||||
/// \brief Cast \a f into a binop
|
||||
///
|
||||
/// Cast \a f into a binop iff it is a binop instance. Return 0
|
||||
/// otherwise. This is faster than \c dynamic_cast.
|
||||
inline
|
||||
const binop*
|
||||
is_binop(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::BinOp)
|
||||
return 0;
|
||||
return static_cast<const binop*>(f);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it has type \a op.
|
||||
///
|
||||
/// Cast \a f into a binop iff it is a unop instance with operator \a op.
|
||||
/// Returns 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_binop(const formula* f, binop::type op)
|
||||
{
|
||||
if (const binop* bo = is_binop(f))
|
||||
if (bo->op() == op)
|
||||
return bo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it has type \a op1 or \a op2.
|
||||
///
|
||||
/// Cast \a f into a binop iff it is a unop instance with operator \a op1 or
|
||||
/// \a op2. Returns 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_binop(const formula* f, binop::type op1, binop::type op2)
|
||||
{
|
||||
if (const binop* bo = is_binop(f))
|
||||
if (bo->op() == op1 || bo->op() == op2)
|
||||
return bo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it is a U.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_U(const formula* f)
|
||||
{
|
||||
return is_binop(f, binop::U);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it is a M.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_M(const formula* f)
|
||||
{
|
||||
return is_binop(f, binop::M);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it is a R.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_R(const formula* f)
|
||||
{
|
||||
return is_binop(f, binop::R);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a binop if it is a W.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const binop*
|
||||
is_W(const formula* f)
|
||||
{
|
||||
return is_binop(f, binop::W);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de
|
||||
// Recherche et Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include "bunop.hh"
|
||||
#include "visitor.hh"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "constant.hh"
|
||||
#include "unop.hh"
|
||||
#include "multop.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
// Can't build it on startup, because it uses
|
||||
// constant::true_instance that may not have been built yet...
|
||||
const formula* bunop::one_star_ = 0;
|
||||
|
||||
bunop::bunop(type op, const formula* child, unsigned min, unsigned max)
|
||||
: formula(BUnOp), op_(op), child_(child), min_(min), max_(max)
|
||||
{
|
||||
props = child->get_props();
|
||||
|
||||
assert(is.sere_formula);
|
||||
is.boolean = false;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = false;
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
is.syntactic_safety = false;
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = false;
|
||||
is.syntactic_recurrence = false;
|
||||
is.syntactic_persistence = false;
|
||||
|
||||
switch (op_)
|
||||
{
|
||||
case Star:
|
||||
if (max_ == unbounded)
|
||||
{
|
||||
is.finite = false;
|
||||
is.syntactic_si = min_ == 1 && child->is_boolean();
|
||||
}
|
||||
else
|
||||
{
|
||||
is.syntactic_si = false;
|
||||
}
|
||||
if (min_ == 0)
|
||||
is.accepting_eword = true;
|
||||
break;
|
||||
case FStar:
|
||||
is.accepting_eword = false;
|
||||
is.syntactic_si &= !child->is_boolean();
|
||||
if (max_ == unbounded)
|
||||
is.finite = false;
|
||||
if (min_ == 0)
|
||||
is.syntactic_si = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bunop::~bunop()
|
||||
{
|
||||
// one_star_ should never get deleted. Otherwise, that means it
|
||||
// has been destroyed too much, or not cloned enough.
|
||||
assert(this != one_star_);
|
||||
|
||||
// Get this instance out of the instance map.
|
||||
size_t c = instances.erase(key(op(), child(), min_, max_));
|
||||
assert(c == 1);
|
||||
(void) c; // For the NDEBUG case.
|
||||
|
||||
// Dereference child.
|
||||
child()->destroy();
|
||||
}
|
||||
|
||||
std::string
|
||||
bunop::dump() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "bunop(" << op_name() << ", "
|
||||
<< child()->dump() << ", " << min_ << ", ";
|
||||
if (max_ == unbounded)
|
||||
out << "unbounded";
|
||||
else
|
||||
out << max_;
|
||||
out << ')';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void
|
||||
bunop::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
const char*
|
||||
bunop::op_name() const
|
||||
{
|
||||
switch (op_)
|
||||
{
|
||||
case Star:
|
||||
return "Star";
|
||||
case FStar:
|
||||
return "FStar";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string
|
||||
bunop::format() const
|
||||
{
|
||||
std::ostringstream out;
|
||||
|
||||
switch (op_)
|
||||
{
|
||||
case Star:
|
||||
// Syntactic sugaring
|
||||
if (min_ == 1 && max_ == unbounded)
|
||||
return "[+]";
|
||||
out << "[*";
|
||||
break;
|
||||
case FStar:
|
||||
// Syntactic sugaring
|
||||
if (min_ == 1 && max_ == unbounded)
|
||||
return "[:+]";
|
||||
out << "[:*";
|
||||
break;
|
||||
}
|
||||
|
||||
if (min_ != 0 || max_ != unbounded)
|
||||
{
|
||||
// Always print the min_, even when it is equal to 0, this
|
||||
// way we avoid ambiguities (like when reading
|
||||
// a[*..3];b[->..2] which actually means a[*0..3];b[->1..2].
|
||||
out << min_;
|
||||
if (min_ != max_)
|
||||
{
|
||||
out << "..";
|
||||
if (max_ != unbounded)
|
||||
out << max_;
|
||||
}
|
||||
}
|
||||
out << ']';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bunop::map bunop::instances;
|
||||
|
||||
const formula*
|
||||
bunop::instance(type op, const formula* child,
|
||||
unsigned min, unsigned max)
|
||||
{
|
||||
assert(min <= max);
|
||||
|
||||
const formula* neutral = nullptr;
|
||||
switch (op)
|
||||
{
|
||||
case Star:
|
||||
neutral = constant::empty_word_instance();
|
||||
break;
|
||||
case FStar:
|
||||
neutral = constant::true_instance();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// common trivial simplifications
|
||||
|
||||
// - [*0][*min..max] = [*0]
|
||||
// - [*0][:*0..max] = 1
|
||||
// - [*0][:*min..max] = 0 if min > 0
|
||||
if (child == constant::empty_word_instance())
|
||||
switch (op)
|
||||
{
|
||||
case Star:
|
||||
return neutral;
|
||||
case FStar:
|
||||
if (min == 0)
|
||||
return neutral;
|
||||
else
|
||||
return constant::false_instance();
|
||||
}
|
||||
|
||||
// - 0[*0..max] = [*0]
|
||||
// - 0[*min..max] = 0 if min > 0
|
||||
// - b[:*0..max] = 1
|
||||
// - b[:*min..max] = 0 if min > 0
|
||||
if (child == constant::false_instance()
|
||||
|| (op == FStar && child->is_boolean()))
|
||||
{
|
||||
if (min == 0)
|
||||
{
|
||||
child->destroy();
|
||||
return neutral;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
// - Exp[*0] = [*0]
|
||||
// - Exp[:*0] = 1
|
||||
if (max == 0)
|
||||
{
|
||||
child->destroy();
|
||||
return neutral;
|
||||
}
|
||||
|
||||
// - Exp[*1] = Exp
|
||||
// - Exp[:*1] = Exp if Exp does not accept [*0]
|
||||
if (min == 1 && max == 1)
|
||||
if (op == Star || !child->accepts_eword())
|
||||
return child;
|
||||
|
||||
// - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1.
|
||||
// - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1.
|
||||
if (const bunop* s = is_bunop(child, op))
|
||||
{
|
||||
unsigned i = s->min();
|
||||
unsigned j = s->max();
|
||||
|
||||
// Exp has to be true between i*min and j*min
|
||||
// then between i*(min+1) and j*(min+1)
|
||||
// ...
|
||||
// finally between i*max and j*max
|
||||
//
|
||||
// We can merge these intervals into [i*min..j*max] iff the
|
||||
// first are adjacent or overlap, i.e. iff
|
||||
// i*(min+1) <= j*min+1.
|
||||
// (Because i<=j, this entails that the other intervals also
|
||||
// overlap).
|
||||
|
||||
const formula* exp = s->child();
|
||||
if (j == unbounded)
|
||||
{
|
||||
min *= i;
|
||||
max = unbounded;
|
||||
|
||||
// Exp[*min..max]
|
||||
exp->clone();
|
||||
child->destroy();
|
||||
child = exp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i * (min + 1) <= (j * min) + 1)
|
||||
{
|
||||
min *= i;
|
||||
if (max != unbounded)
|
||||
{
|
||||
if (j == unbounded)
|
||||
max = unbounded;
|
||||
else
|
||||
max *= j;
|
||||
}
|
||||
exp->clone();
|
||||
child->destroy();
|
||||
child = exp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const formula* res;
|
||||
auto ires = instances.emplace(key(op, child, min, max), nullptr);
|
||||
if (!ires.second)
|
||||
{
|
||||
// This instance already exists.
|
||||
child->destroy();
|
||||
res = ires.first->second->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = ires.first->second = new bunop(op, child, min, max);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const formula*
|
||||
bunop::sugar_goto(const formula* b, unsigned min, unsigned max)
|
||||
{
|
||||
assert(b->is_boolean());
|
||||
// b[->min..max] is implemented as ((!b)[*];b)[*min..max]
|
||||
const formula* s =
|
||||
bunop::instance(bunop::Star,
|
||||
unop::instance(unop::Not, b->clone()));
|
||||
return bunop::instance(bunop::Star,
|
||||
multop::instance(multop::Concat, s, b),
|
||||
min, max);
|
||||
}
|
||||
|
||||
const formula*
|
||||
bunop::sugar_equal(const formula* b, unsigned min, unsigned max)
|
||||
{
|
||||
assert(b->is_boolean());
|
||||
// b[=0..] = 1[*]
|
||||
if (min == 0 && max == unbounded)
|
||||
{
|
||||
b->destroy();
|
||||
return instance(Star, constant::true_instance());
|
||||
}
|
||||
|
||||
// b[=min..max] is implemented as ((!b)[*];b)[*min..max];(!b)[*]
|
||||
const formula* s =
|
||||
bunop::instance(bunop::Star,
|
||||
unop::instance(unop::Not, b->clone()));
|
||||
const formula* t =
|
||||
bunop::instance(bunop::Star,
|
||||
multop::instance(multop::Concat,
|
||||
s->clone(), b), min, max);
|
||||
return multop::instance(multop::Concat, t, s);
|
||||
}
|
||||
|
||||
unsigned
|
||||
bunop::instance_count()
|
||||
{
|
||||
// Don't count one_star_ since it should not be destroyed.
|
||||
return instances.size() - !!one_star_;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
bunop::dump_instances(std::ostream& os)
|
||||
{
|
||||
for (const auto& i: instances)
|
||||
os << i.second << " = "
|
||||
<< 1 + i.second->refs_ << " * "
|
||||
<< i.second->dump()
|
||||
<< '\n';
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
|
||||
// et Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/bunop.hh
|
||||
/// \brief Bounded Unary operators
|
||||
#pragma once
|
||||
|
||||
#include "formula.hh"
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
#include <tuple>
|
||||
#include "constant.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief Bounded unary operator.
|
||||
class SPOT_API bunop final : public formula
|
||||
{
|
||||
public:
|
||||
enum type { Star, FStar };
|
||||
|
||||
static const unsigned unbounded = -1U;
|
||||
|
||||
/// \brief Build a bunop with bounds \a min and \a max.
|
||||
///
|
||||
/// The following trivial simplifications are performed
|
||||
/// automatically (the left expression is rewritten as the right
|
||||
/// expression):
|
||||
/// - 0[*0..max] = [*0]
|
||||
/// - 0[*min..max] = 0 if min > 0
|
||||
/// - [*0][*min..max] = [*0]
|
||||
/// - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1.
|
||||
/// - Exp[*0] = [*0]
|
||||
/// - Exp[*1] = Exp
|
||||
/// - b[:*0..max] = 1
|
||||
/// - b[:*min..max] = b if min > 0
|
||||
/// - [*0][:*0..max] = 1
|
||||
/// - [*0][:*min..max] = 0 if min > 0
|
||||
/// - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1.
|
||||
/// - Exp[:*0] = 1
|
||||
/// - Exp[:*1] = Exp if Exp does not accept [*0]
|
||||
///
|
||||
/// These rewriting rules imply that it is not possible to build
|
||||
/// an LTL formula object that is SYNTACTICALLY equal to one of
|
||||
/// these left expressions.
|
||||
static const formula* instance(type op,
|
||||
const formula* child,
|
||||
unsigned min = 0,
|
||||
unsigned max = unbounded);
|
||||
|
||||
/// \brief Implement <code>b[->i..j]</code> using the Kleen star.
|
||||
///
|
||||
/// <code>b[->i..j]</code> is implemented as
|
||||
/// <code>((!b)[*];b)[*i..j]</code>.
|
||||
///
|
||||
/// Note that \a min defaults to 1, not 0, because [->] means
|
||||
/// [->1..].
|
||||
///
|
||||
/// \pre \a child must be a Boolean formula.
|
||||
static const formula* sugar_goto(const formula* child,
|
||||
unsigned min = 1,
|
||||
unsigned max = unbounded);
|
||||
|
||||
/// \brief Implement b[=i..j] using the Kleen star.
|
||||
///
|
||||
/// <code>b[=i..j]</code> is implemented as
|
||||
/// <code>((!b)[*];b)[*i..j];(!b)[*]</code>.
|
||||
///
|
||||
/// \pre \a child must be a Boolean formula.
|
||||
static const formula* sugar_equal(const formula* child,
|
||||
unsigned min = 0,
|
||||
unsigned max = unbounded);
|
||||
|
||||
virtual void accept(visitor& v) const override;
|
||||
|
||||
/// Get the sole operand of this operator.
|
||||
const formula* child() const
|
||||
{
|
||||
return child_;
|
||||
}
|
||||
|
||||
/// Minimum number of repetition.
|
||||
unsigned min() const
|
||||
{
|
||||
return min_;
|
||||
}
|
||||
|
||||
/// Minimum number of repetition.
|
||||
unsigned max() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// \brief A string representation of the operator.
|
||||
///
|
||||
/// For instance "[*2..]".
|
||||
std::string format() const;
|
||||
|
||||
/// Get the type of this operator.
|
||||
type op() const
|
||||
{
|
||||
return op_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator, as a string.
|
||||
const char* op_name() const;
|
||||
|
||||
/// Return a canonic representation of operation.
|
||||
virtual std::string dump() const override;
|
||||
|
||||
/// Number of instantiated unary operators. For debugging.
|
||||
static unsigned instance_count();
|
||||
|
||||
/// Dump all instances. For debugging.
|
||||
static std::ostream& dump_instances(std::ostream& os);
|
||||
|
||||
/// \brief Return a formula for <code>1[*]</code>.
|
||||
///
|
||||
/// A global instance is returned, and it should not be
|
||||
/// destroyed. Remember to clone it if you use it to build a
|
||||
/// formula.
|
||||
static const formula* one_star()
|
||||
{
|
||||
if (!one_star_)
|
||||
one_star_ = instance(Star, constant::true_instance());
|
||||
return one_star_;
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef std::tuple<type, const formula*, unsigned, unsigned> key;
|
||||
typedef std::map<key, const bunop*> map;
|
||||
static map instances;
|
||||
|
||||
bunop(type op, const formula* child, unsigned min, unsigned max);
|
||||
virtual ~bunop();
|
||||
|
||||
private:
|
||||
type op_;
|
||||
const formula* child_;
|
||||
unsigned min_;
|
||||
unsigned max_;
|
||||
static const formula* one_star_;
|
||||
};
|
||||
|
||||
/// \brief Cast \a f into a bunop.
|
||||
///
|
||||
/// Cast \a f into a bunop iff it is a bunop instance. Return 0
|
||||
/// otherwise. This is faster than \c dynamic_cast.
|
||||
inline
|
||||
const bunop*
|
||||
is_bunop(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::BUnOp)
|
||||
return 0;
|
||||
return static_cast<const bunop*>(f);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a bunop if it has type \a op.
|
||||
///
|
||||
/// Cast \a f into a bunop iff it is a bunop instance with operator \a op.
|
||||
/// Returns 0 otherwise.
|
||||
inline
|
||||
const bunop*
|
||||
is_bunop(const formula* f, bunop::type op)
|
||||
{
|
||||
if (const bunop* bo = is_bunop(f))
|
||||
if (bo->op() == op)
|
||||
return bo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a bunop if it is a Star.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const bunop*
|
||||
is_Star(const formula* f)
|
||||
{
|
||||
return is_bunop(f, bunop::Star);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a bunop if it is a Star[0..].
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const bunop*
|
||||
is_KleenStar(const formula* f)
|
||||
{
|
||||
if (const bunop* b = is_Star(f))
|
||||
if (b->min() == 0 && b->max() == bunop::unbounded)
|
||||
return b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
|
||||
// de Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include "constant.hh"
|
||||
#include "visitor.hh"
|
||||
#include <cassert>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
constant constant::true_instance_(constant::True);
|
||||
constant constant::false_instance_(constant::False);
|
||||
constant constant::empty_word_instance_(constant::EmptyWord);
|
||||
|
||||
constant::constant(type val)
|
||||
: formula(Constant), val_(val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case constant::True:
|
||||
case constant::False:
|
||||
is.boolean = true;
|
||||
is.sugar_free_boolean = true;
|
||||
is.in_nenoform = true;
|
||||
is.syntactic_si = true; // for LTL (not PSL)
|
||||
is.sugar_free_ltl = true;
|
||||
is.ltl_formula = true;
|
||||
is.psl_formula = true;
|
||||
is.sere_formula = true;
|
||||
is.finite = true;
|
||||
is.eventual = true;
|
||||
is.universal = true;
|
||||
is.syntactic_safety = true;
|
||||
is.syntactic_guarantee = true;
|
||||
is.syntactic_obligation = true;
|
||||
is.syntactic_recurrence = true;
|
||||
is.syntactic_persistence = true;
|
||||
is.not_marked = true;
|
||||
is.accepting_eword = false;
|
||||
is.lbt_atomic_props = true;
|
||||
is.spin_atomic_props = true;
|
||||
break;
|
||||
case constant::EmptyWord:
|
||||
is.boolean = false;
|
||||
is.sugar_free_boolean = false;
|
||||
is.in_nenoform = true;
|
||||
is.syntactic_si = true;
|
||||
is.sugar_free_ltl = true;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = false;
|
||||
is.sere_formula = true;
|
||||
is.finite = true;
|
||||
is.eventual = false;
|
||||
is.syntactic_safety = false;
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = false;
|
||||
is.syntactic_recurrence = false;
|
||||
is.syntactic_persistence = false;
|
||||
is.universal = false;
|
||||
is.not_marked = true;
|
||||
is.accepting_eword = true;
|
||||
is.lbt_atomic_props = true;
|
||||
is.spin_atomic_props = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constant::~constant()
|
||||
{
|
||||
}
|
||||
|
||||
std::string
|
||||
constant::dump() const
|
||||
{
|
||||
switch (val())
|
||||
{
|
||||
case True:
|
||||
return "constant(1)";
|
||||
case False:
|
||||
return "constant(0)";
|
||||
case EmptyWord:
|
||||
return "constant(e)";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
void
|
||||
constant::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
const char*
|
||||
constant::val_name() const
|
||||
{
|
||||
switch (val_)
|
||||
{
|
||||
case True:
|
||||
return "1";
|
||||
case False:
|
||||
return "0";
|
||||
case EmptyWord:
|
||||
return "[*0]";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2012, 2013, 2014 Laboratoire de Recherche
|
||||
// et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/constant.hh
|
||||
/// \brief LTL constants
|
||||
#pragma once
|
||||
|
||||
#include "formula.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief A constant (True or False)
|
||||
class SPOT_API constant final : public formula
|
||||
{
|
||||
public:
|
||||
enum type { False, True, EmptyWord };
|
||||
virtual void accept(visitor& v) const override;
|
||||
|
||||
/// Return the value of the constant.
|
||||
type val() const
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
/// Return the value of the constant as a string.
|
||||
const char* val_name() const;
|
||||
|
||||
virtual std::string dump() const override;
|
||||
|
||||
/// Get the sole instance of spot::ltl::constant::constant(True).
|
||||
static constant* true_instance() { return &true_instance_; }
|
||||
/// Get the sole instance of spot::ltl::constant::constant(False).
|
||||
static constant* false_instance() { return &false_instance_; }
|
||||
/// Get the sole instance of spot::ltl::constant::constant(EmptyWord).
|
||||
static constant* empty_word_instance() { return &empty_word_instance_; }
|
||||
|
||||
protected:
|
||||
constant(type val);
|
||||
virtual ~constant();
|
||||
|
||||
private:
|
||||
type val_;
|
||||
|
||||
static constant true_instance_;
|
||||
static constant false_instance_;
|
||||
static constant empty_word_instance_;
|
||||
// If you add new constants here, be sure to update the
|
||||
// formula::formula() constructor.
|
||||
};
|
||||
|
||||
|
||||
/// \brief Cast \a f into a constant.
|
||||
///
|
||||
/// Cast \a f into a constant iff it is a constant instance.
|
||||
/// Return 0 otherwise. This is faster than \c dynamic_cast.
|
||||
inline
|
||||
const constant*
|
||||
is_constant(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::Constant)
|
||||
return 0;
|
||||
return static_cast<const constant*>(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,699 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de
|
||||
// Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
|
||||
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include "multop.hh"
|
||||
#include "constant.hh"
|
||||
#include "bunop.hh"
|
||||
#include "visitor.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
multop::multop(type op, vec* v)
|
||||
: formula(MultOp), op_(op), children_(v)
|
||||
{
|
||||
unsigned s = v->size();
|
||||
assert(s > 1);
|
||||
|
||||
props = (*v)[0]->get_props();
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case Fusion:
|
||||
is.accepting_eword = false;
|
||||
case Concat:
|
||||
case AndNLM:
|
||||
case AndRat:
|
||||
{
|
||||
bool syntactic_si = is.syntactic_si && !is.boolean;
|
||||
// Note: AndNLM(p1,p2) and AndRat(p1,p2) are Boolean
|
||||
// formulae, but they are actually rewritten as And(p1,p2)
|
||||
// by trivial identities before this constructor is called.
|
||||
// So at this point, AndNLM/AndRat are always used with at
|
||||
// most one Boolean argument, and the result is therefore
|
||||
// NOT Boolean.
|
||||
is.boolean = false;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = false;
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
|
||||
for (unsigned i = 1; i < s; ++i)
|
||||
{
|
||||
syntactic_si &= (*v)[i]->is_syntactic_stutter_invariant()
|
||||
&& !(*v)[i]->is_boolean();
|
||||
props &= (*v)[i]->get_props();
|
||||
}
|
||||
is.syntactic_si = syntactic_si;
|
||||
break;
|
||||
}
|
||||
case And:
|
||||
for (unsigned i = 1; i < s; ++i)
|
||||
props &= (*v)[i]->get_props();
|
||||
break;
|
||||
case OrRat:
|
||||
{
|
||||
bool syntactic_si = is.syntactic_si && !is.boolean;
|
||||
// Note: OrRat(p1,p2) is a Boolean formula, but its is
|
||||
// actually rewritten as Or(p1,p2) by trivial identities
|
||||
// before this constructor is called. So at this point,
|
||||
// AndNLM is always used with at most one Boolean argument,
|
||||
// and the result is therefore NOT Boolean.
|
||||
is.boolean = false;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = false;
|
||||
is.eventual = false;
|
||||
is.universal = false;
|
||||
|
||||
bool ew = (*v)[0]->accepts_eword();
|
||||
for (unsigned i = 1; i < s; ++i)
|
||||
{
|
||||
ew |= (*v)[i]->accepts_eword();
|
||||
syntactic_si &= (*v)[i]->is_syntactic_stutter_invariant()
|
||||
&& !(*v)[i]->is_boolean();
|
||||
props &= (*v)[i]->get_props();
|
||||
}
|
||||
is.accepting_eword = ew;
|
||||
is.syntactic_si = syntactic_si;
|
||||
break;
|
||||
}
|
||||
case Or:
|
||||
{
|
||||
bool ew = (*v)[0]->accepts_eword();
|
||||
for (unsigned i = 1; i < s; ++i)
|
||||
{
|
||||
ew |= (*v)[i]->accepts_eword();
|
||||
props &= (*v)[i]->get_props();
|
||||
}
|
||||
is.accepting_eword = ew;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A concatenation is an siSERE if it contains one stared
|
||||
// Boolean, and the other operands are siSERE (i.e.,
|
||||
// sub-formulas that verify is_syntactic_stutter_invariant() and
|
||||
// !is_boolean());
|
||||
if (op == Concat)
|
||||
{
|
||||
unsigned sb = 0; // stared Boolean formulas seen
|
||||
for (unsigned i = 0; i < s; ++i)
|
||||
{
|
||||
if ((*v)[i]->is_syntactic_stutter_invariant()
|
||||
&& !(*v)[i]->is_boolean())
|
||||
continue;
|
||||
if (const bunop* b = is_Star((*v)[i]))
|
||||
{
|
||||
sb += b->child()->is_boolean();
|
||||
if (sb > 1)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
is.syntactic_si = sb == 1;
|
||||
}
|
||||
}
|
||||
|
||||
multop::~multop()
|
||||
{
|
||||
// Get this instance out of the instance map.
|
||||
size_t c = instances.erase(key(op(), children_));
|
||||
assert(c == 1);
|
||||
(void) c; // For the NDEBUG case.
|
||||
|
||||
// Dereference children.
|
||||
unsigned s = size();
|
||||
for (unsigned n = 0; n < s; ++n)
|
||||
nth(n)->destroy();
|
||||
|
||||
delete children_;
|
||||
}
|
||||
|
||||
std::string
|
||||
multop::dump() const
|
||||
{
|
||||
std::string r = "multop(";
|
||||
r += op_name();
|
||||
unsigned max = size();
|
||||
for (unsigned n = 0; n < max; ++n)
|
||||
r += ", " + nth(n)->dump();
|
||||
r += ")";
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
multop::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
const formula*
|
||||
multop::all_but(unsigned n) const
|
||||
{
|
||||
unsigned s = size();
|
||||
vec* v = new vec;
|
||||
v->reserve(s - 1);
|
||||
for (unsigned pos = 0; pos < n; ++pos)
|
||||
v->push_back(nth(pos)->clone());
|
||||
for (unsigned pos = n + 1; pos < s; ++pos)
|
||||
v->push_back(nth(pos)->clone());
|
||||
return instance(op_, v);
|
||||
}
|
||||
|
||||
unsigned
|
||||
multop::boolean_count() const
|
||||
{
|
||||
unsigned pos = 0;
|
||||
unsigned s = size();
|
||||
while (pos < s && nth(pos)->is_boolean())
|
||||
++pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
const formula*
|
||||
multop::boolean_operands(unsigned* width) const
|
||||
{
|
||||
unsigned s = boolean_count();
|
||||
if (width)
|
||||
*width = s;
|
||||
if (!s)
|
||||
return 0;
|
||||
if (s == 1)
|
||||
return nth(0)->clone();
|
||||
vec* v = new vec(children_->begin(),
|
||||
children_->begin() + s);
|
||||
for (unsigned n = 0; n < s; ++n)
|
||||
(*v)[n]->clone();
|
||||
return instance(op_, v);
|
||||
}
|
||||
|
||||
const char*
|
||||
multop::op_name() const
|
||||
{
|
||||
switch (op_)
|
||||
{
|
||||
case And:
|
||||
return "And";
|
||||
case AndRat:
|
||||
return "AndRat";
|
||||
case AndNLM:
|
||||
return "AndNLM";
|
||||
case Or:
|
||||
return "Or";
|
||||
case OrRat:
|
||||
return "OrRat";
|
||||
case Concat:
|
||||
return "Concat";
|
||||
case Fusion:
|
||||
return "Fusion";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
static void
|
||||
gather_bool(multop::vec* v, multop::type op)
|
||||
{
|
||||
// Gather all boolean terms.
|
||||
multop::vec* b = new multop::vec;
|
||||
multop::vec::iterator i = v->begin();
|
||||
while (i != v->end())
|
||||
{
|
||||
if ((*i)->is_boolean())
|
||||
{
|
||||
b->push_back(*i);
|
||||
i = v->erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// - AndNLM(Exps1...,Bool1,Exps2...,Bool2,Exps3...) =
|
||||
// AndNLM(And(Bool1,Bool2),Exps1...,Exps2...,Exps3...)
|
||||
// - AndRat(Exps1...,Bool1,Exps2...,Bool2,Exps3...) =
|
||||
// AndRat(And(Bool1,Bool2),Exps1...,Exps2...,Exps3...)
|
||||
// - OrRat(Exps1...,Bool1,Exps2...,Bool2,Exps3...) =
|
||||
// AndRat(Or(Bool1,Bool2),Exps1...,Exps2...,Exps3...)
|
||||
if (!b->empty())
|
||||
v->insert(v->begin(), multop::instance(op, b));
|
||||
else
|
||||
delete b;
|
||||
}
|
||||
}
|
||||
|
||||
multop::map multop::instances;
|
||||
|
||||
// We match equivalent formulae modulo "ACI rules"
|
||||
// (i.e. associativity, commutativity and idempotence of the
|
||||
// operator). For instance if `+' designates the OR operator and
|
||||
// `0' is false (the neutral element for `+') , then `f+f+0' is
|
||||
// equivalent to `f'.
|
||||
const formula*
|
||||
multop::instance(type op, vec* v)
|
||||
{
|
||||
// Inline children of same kind.
|
||||
//
|
||||
// When we construct a formula such as Multop(Op,X,Multop(Op,Y,Z))
|
||||
// we will want to inline it as Multop(Op,X,Y,Z).
|
||||
{
|
||||
vec inlined;
|
||||
vec::iterator i = v->begin();
|
||||
while (i != v->end())
|
||||
{
|
||||
// Some simplification routines erase terms using null
|
||||
// pointers that we must ignore.
|
||||
if ((*i) == 0)
|
||||
{
|
||||
// FIXME: For commutative operators we should replace
|
||||
// the pointer by the first non-null value at the end
|
||||
// of the array instead of calling erase.
|
||||
i = v->erase(i);
|
||||
continue;
|
||||
}
|
||||
if (const multop* p = is_multop(*i, op))
|
||||
{
|
||||
unsigned ps = p->size();
|
||||
for (unsigned n = 0; n < ps; ++n)
|
||||
inlined.push_back(p->nth(n)->clone());
|
||||
(*i)->destroy();
|
||||
// FIXME: Do not use erase. See previous FIXME.
|
||||
i = v->erase(i);
|
||||
continue;
|
||||
}
|
||||
// All operators except "Concat" and "Fusion" are
|
||||
// commutative, so we just keep a list of the inlined
|
||||
// arguments that should later be added to the vector.
|
||||
// For concat we have to keep track of the order of
|
||||
// all the arguments.
|
||||
if (op == Concat || op == Fusion)
|
||||
inlined.push_back(*i);
|
||||
++i;
|
||||
}
|
||||
if (op == Concat || op == Fusion)
|
||||
v->swap(inlined);
|
||||
else
|
||||
v->insert(v->end(), inlined.begin(), inlined.end());
|
||||
}
|
||||
|
||||
if (op != Concat && op != Fusion)
|
||||
std::sort(v->begin(), v->end(), formula_ptr_less_than_bool_first());
|
||||
|
||||
unsigned orig_size = v->size();
|
||||
|
||||
const formula* neutral;
|
||||
const formula* neutral2;
|
||||
const formula* abs;
|
||||
const formula* abs2;
|
||||
const formula* weak_abs;
|
||||
switch (op)
|
||||
{
|
||||
case And:
|
||||
neutral = constant::true_instance();
|
||||
neutral2 = 0;
|
||||
abs = constant::false_instance();
|
||||
abs2 = 0;
|
||||
weak_abs = 0;
|
||||
break;
|
||||
case AndRat:
|
||||
neutral = bunop::one_star();
|
||||
neutral2 = 0;
|
||||
abs = constant::false_instance();
|
||||
abs2 = 0;
|
||||
weak_abs = constant::empty_word_instance();
|
||||
gather_bool(v, And);
|
||||
break;
|
||||
case AndNLM:
|
||||
neutral = constant::empty_word_instance();
|
||||
neutral2 = 0;
|
||||
abs = constant::false_instance();
|
||||
abs2 = 0;
|
||||
weak_abs = constant::true_instance();
|
||||
gather_bool(v, And);
|
||||
break;
|
||||
case Or:
|
||||
neutral = constant::false_instance();
|
||||
neutral2 = 0;
|
||||
abs = constant::true_instance();
|
||||
abs2 = 0;
|
||||
weak_abs = 0;
|
||||
break;
|
||||
case OrRat:
|
||||
neutral = constant::false_instance();
|
||||
neutral2 = 0;
|
||||
abs = bunop::one_star();
|
||||
abs2 = 0;
|
||||
weak_abs = 0;
|
||||
gather_bool(v, Or);
|
||||
break;
|
||||
case Concat:
|
||||
neutral = constant::empty_word_instance();
|
||||
neutral2 = 0;
|
||||
abs = constant::false_instance();
|
||||
abs2 = 0;
|
||||
weak_abs = 0;
|
||||
|
||||
// - Concat(Exps1...,FExps2...,1[*],FExps3...,Exps4) =
|
||||
// Concat(Exps1...,1[*],Exps4)
|
||||
// If FExps2... and FExps3 all accept [*0].
|
||||
{
|
||||
vec::iterator i = v->begin();
|
||||
const formula* os = bunop::one_star();
|
||||
while (i != v->end())
|
||||
{
|
||||
while (i != v->end() && !(*i)->accepts_eword())
|
||||
++i;
|
||||
if (i == v->end())
|
||||
break;
|
||||
vec::iterator b = i;
|
||||
// b is the first expressions that accepts [*0].
|
||||
// let's find more, and locate the position of
|
||||
// 1[*] at the same time.
|
||||
bool os_seen = false;
|
||||
do
|
||||
{
|
||||
os_seen |= (*i == os);
|
||||
++i;
|
||||
}
|
||||
while (i != v->end() && (*i)->accepts_eword());
|
||||
|
||||
if (os_seen) // [b..i) is a range that contains [*].
|
||||
{
|
||||
// Place [*] at the start of the range, and erase
|
||||
// all other formulae.
|
||||
(*b)->destroy();
|
||||
*b++ = os->clone();
|
||||
for (vec::iterator c = b; c < i; ++c)
|
||||
(*c)->destroy();
|
||||
i = v->erase(b, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Fusion:
|
||||
neutral = constant::true_instance();
|
||||
neutral2 = 0;
|
||||
abs = constant::false_instance();
|
||||
abs2 = constant::empty_word_instance();
|
||||
weak_abs = 0;
|
||||
|
||||
// Make a first pass to group adjacent Boolean formulae.
|
||||
// - Fusion(Exps1...,BoolExp1...BoolExpN,Exps2,Exps3...) =
|
||||
// Fusion(Exps1...,And(BoolExp1...BoolExpN),Exps2,Exps3...)
|
||||
{
|
||||
vec::iterator i = v->begin();
|
||||
while (i != v->end())
|
||||
{
|
||||
if ((*i)->is_boolean())
|
||||
{
|
||||
vec::iterator first = i;
|
||||
++i;
|
||||
if (i == v->end())
|
||||
break;
|
||||
if (!(*i)->is_boolean())
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
do
|
||||
++i;
|
||||
while (i != v->end() && (*i)->is_boolean());
|
||||
// We have at least two adjacent Boolean formulae.
|
||||
// Replace the first one by the conjunction of all.
|
||||
vec* b = new vec;
|
||||
b->insert(b->begin(), first, i);
|
||||
i = v->erase(first + 1, i);
|
||||
*first = instance(And, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
neutral = 0;
|
||||
neutral2 = 0;
|
||||
abs = 0;
|
||||
abs2 = 0;
|
||||
weak_abs = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove duplicates (except for Concat and Fusion). We can't use
|
||||
// std::unique(), because we must destroy() any formula we drop.
|
||||
// Also ignore neutral elements and handle absorbent elements.
|
||||
{
|
||||
const formula* last = 0;
|
||||
vec::iterator i = v->begin();
|
||||
bool weak_abs_seen = false;
|
||||
while (i != v->end())
|
||||
{
|
||||
if ((*i == neutral) || (*i == neutral2) || (*i == last))
|
||||
{
|
||||
(*i)->destroy();
|
||||
i = v->erase(i);
|
||||
}
|
||||
else if (*i == abs || *i == abs2)
|
||||
{
|
||||
for (i = v->begin(); i != v->end(); ++i)
|
||||
(*i)->destroy();
|
||||
delete v;
|
||||
return abs->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
weak_abs_seen |= (*i == weak_abs);
|
||||
if (op != Concat && op != Fusion) // Don't remove duplicates
|
||||
last = *i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (weak_abs_seen)
|
||||
{
|
||||
if (op == AndRat)
|
||||
{
|
||||
// We have a* && [*0] && c = 0
|
||||
// and a* && [*0] && c* = [*0]
|
||||
// So if [*0] has been seen, check if alls term
|
||||
// recognize the empty word.
|
||||
bool acc_eword = true;
|
||||
for (i = v->begin(); i != v->end(); ++i)
|
||||
{
|
||||
acc_eword &= (*i)->accepts_eword();
|
||||
(*i)->destroy();
|
||||
}
|
||||
delete v;
|
||||
if (acc_eword)
|
||||
return weak_abs;
|
||||
else
|
||||
return abs;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Similarly, a* & 1 & (c;d) = c;d
|
||||
// a* & 1 & c* = 1
|
||||
assert(op == AndNLM);
|
||||
multop::vec tmp;
|
||||
for (i = v->begin(); i != v->end(); ++i)
|
||||
{
|
||||
if (*i == weak_abs)
|
||||
continue;
|
||||
if ((*i)->accepts_eword())
|
||||
{
|
||||
(*i)->destroy();
|
||||
continue;
|
||||
}
|
||||
tmp.push_back(*i);
|
||||
}
|
||||
if (tmp.empty())
|
||||
tmp.push_back(weak_abs);
|
||||
v->swap(tmp);
|
||||
}
|
||||
}
|
||||
else if (op == Concat || op == Fusion)
|
||||
{
|
||||
// Perform an extra loop to merge starable items.
|
||||
// f;f -> f[*2]
|
||||
// f;f[*i..j] -> f[*i+1..j+1]
|
||||
// f[*i..j];f -> f[*i+1..j+1]
|
||||
// f[*i..j];f[*k..l] -> f[*i+k..j+l]
|
||||
// same for FStar:
|
||||
// f:f -> f[:*2]
|
||||
// f:f[*i..j] -> f[:*i+1..j+1]
|
||||
// f[:*i..j];f -> f[:*i+1..j+1]
|
||||
// f[:*i..j];f[:*k..l] -> f[:*i+k..j+l]
|
||||
bunop::type bop = op == Concat ? bunop::Star : bunop::FStar;
|
||||
i = v->begin();
|
||||
while (i != v->end())
|
||||
{
|
||||
vec::iterator fpos = i;
|
||||
const formula* f;
|
||||
unsigned min;
|
||||
unsigned max;
|
||||
bool changed = false;
|
||||
if (const bunop* is = is_bunop(*i, bop))
|
||||
{
|
||||
f = is->child();
|
||||
min = is->min();
|
||||
max = is->max();
|
||||
}
|
||||
else
|
||||
{
|
||||
f = *i;
|
||||
min = max = 1;
|
||||
}
|
||||
|
||||
++i;
|
||||
while (i != v->end())
|
||||
{
|
||||
const formula* f2;
|
||||
unsigned min2;
|
||||
unsigned max2;
|
||||
if (const bunop* is = is_bunop(*i, bop))
|
||||
{
|
||||
f2 = is->child();
|
||||
if (f2 != f)
|
||||
break;
|
||||
min2 = is->min();
|
||||
max2 = is->max();
|
||||
}
|
||||
else
|
||||
{
|
||||
f2 = *i;
|
||||
if (f2 != f)
|
||||
break;
|
||||
min2 = max2 = 1;
|
||||
}
|
||||
if (min2 == bunop::unbounded)
|
||||
min = bunop::unbounded;
|
||||
else if (min != bunop::unbounded)
|
||||
min += min2;
|
||||
if (max2 == bunop::unbounded)
|
||||
max = bunop::unbounded;
|
||||
else if (max != bunop::unbounded)
|
||||
max += max2;
|
||||
(*i)->destroy();
|
||||
i = v->erase(i);
|
||||
changed = true;
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
const formula* newfs =
|
||||
bunop::instance(bop, f->clone(), min, max);
|
||||
(*fpos)->destroy();
|
||||
*fpos = newfs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec::size_type s = v->size();
|
||||
if (s == 0)
|
||||
{
|
||||
delete v;
|
||||
assert(neutral != 0);
|
||||
return neutral->clone();
|
||||
}
|
||||
else if (s == 1)
|
||||
{
|
||||
// Simply replace Multop(Op,X) by X.
|
||||
// Except we should never reduce the
|
||||
// arguments of a Fusion operator to
|
||||
// a list with a single formula that
|
||||
// accepts [*0].
|
||||
const formula* res = (*v)[0];
|
||||
if (op != Fusion || orig_size == 1
|
||||
|| !res->accepts_eword())
|
||||
{
|
||||
delete v;
|
||||
return res;
|
||||
}
|
||||
// If Fusion(f, ...) reduce to Fusion(f), emit Fusion(1,f).
|
||||
// to ensure that [*0] is not accepted.
|
||||
v->insert(v->begin(), constant::true_instance());
|
||||
}
|
||||
|
||||
const formula* res;
|
||||
// Insert the key with the dummy nullptr just
|
||||
// to check if p already exists.
|
||||
auto ires = instances.emplace(key(op, v), nullptr);
|
||||
if (!ires.second)
|
||||
{
|
||||
// The instance did already exists. Free v.
|
||||
for (auto f: *v)
|
||||
f->destroy();
|
||||
delete v;
|
||||
res = ires.first->second->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The instance did not already exist.
|
||||
res = ires.first->second = new multop(op, v);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const formula*
|
||||
multop::instance(type op, const formula* first, const formula* second)
|
||||
{
|
||||
vec* v = new vec;
|
||||
v->push_back(first);
|
||||
v->push_back(second);
|
||||
return instance(op, v);
|
||||
}
|
||||
|
||||
unsigned
|
||||
multop::instance_count()
|
||||
{
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
multop::dump_instances(std::ostream& os)
|
||||
{
|
||||
for (const auto& i: instances)
|
||||
os << i.second << " = "
|
||||
<< 1 + i.second->refs_ << " * "
|
||||
<< i.second->dump()
|
||||
<< '\n';
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
|
||||
// de Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/multop.hh
|
||||
/// \brief LTL multi-operand operators
|
||||
#pragma once
|
||||
|
||||
#include "formula.hh"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief Multi-operand operators.
|
||||
class SPOT_API multop final: public formula
|
||||
{
|
||||
public:
|
||||
enum type { Or, OrRat, And, AndRat, AndNLM, Concat, Fusion };
|
||||
|
||||
/// List of formulae.
|
||||
typedef std::vector<const formula*> vec;
|
||||
|
||||
/// \brief Build a spot::ltl::multop with two children.
|
||||
///
|
||||
/// If one of the children itself is a spot::ltl::multop
|
||||
/// with the same type, it will be inlined. I.e., children
|
||||
/// of that child will be added, and that child itself will
|
||||
/// be destroyed. This allows incremental building of
|
||||
/// n-ary ltl::multop.
|
||||
///
|
||||
/// This functions can perform slight optimizations and
|
||||
/// may not return an ltl::multop object. See the other
|
||||
/// instance function for the list of rewritings.
|
||||
static const formula*
|
||||
instance(type op, const formula* first, const formula* second);
|
||||
|
||||
/// \brief Build a spot::ltl::multop with many children.
|
||||
///
|
||||
/// Same as the other instance() function, but take a vector of
|
||||
/// formulae as argument. This vector is acquired by the
|
||||
/// spot::ltl::multop class, the caller should allocate it with
|
||||
/// \c new, but not use it (especially not destroy it) after it
|
||||
/// has been passed to spot::ltl::multop. Inside the vector,
|
||||
/// null pointers are ignored.
|
||||
///
|
||||
/// Most operators (Or, OrRat, And, AndRat, Concat) are
|
||||
/// associative, and are automatically inlined. Or, OrRat, And,
|
||||
/// and AndRat are commutative, so their arguments are also
|
||||
/// sorted, to ensure that "a & b" is equal to "b & a", also
|
||||
/// duplicate arguments are removed.
|
||||
///
|
||||
/// Furthermore this function can perform slight optimizations
|
||||
/// and may not return an ltl::multop object. For instance if
|
||||
/// the vector contains only one unique element, this this
|
||||
/// formula will be returned as-is. Neutral and absorbent element
|
||||
/// are also taken care of. The following rewritings are performed
|
||||
/// (the left patterns are rewritten as shown on the right):
|
||||
///
|
||||
/// - And(Exps1...,1,Exps2...) = And(Exps1...,Exps2...)
|
||||
/// - And(Exps1...,0,Exps2...) = 0
|
||||
/// - And(Exp) = Exp
|
||||
/// - Or(Exps1...,1,Exps2...) = 1
|
||||
/// - Or(Exps1...,0,Exps2...) = Or(Exps1...,Exps2...)
|
||||
/// - Or(Exp) = Exp
|
||||
/// - AndNLM(FExps1...,1,Exps2...) = AndNLM(Exps2...)
|
||||
/// if Fexps1... accept [*0], and Exps2... don't.
|
||||
/// - AndNLM(FExps1...,1,FExps2...) = 1
|
||||
/// if Fexps1...,FExps2... all accept[*0].
|
||||
/// - AndNLM(Exps1...,0,Exps2...) = 0
|
||||
/// - AndNLM(Exps1...,[*0],Exps2...) = AndNLM(Exps1...,Exps2...)
|
||||
/// - AndNLM(Exp) = Exp
|
||||
/// - AndNLM(Exps1...,BoolExp1,Exps2...,BoolExp2,Exps3...) =
|
||||
/// AndNLM(Exps1...,Exps2...,Exps3...,And(BoolExp1,BoolExp2))
|
||||
/// - AndRat(Exps1...,0,Exps2...) = 0
|
||||
/// - AndRat(Exps1...,BoolExp1,Exps2...,BoolExps2...) =
|
||||
/// AndRat(Exps1...,Exps2...,And(BoolExp1,BoolExps2...))
|
||||
/// - AndRat(Exps1...,[*0],Exps2...) = [*0] if all Expi accept [*0]
|
||||
/// - AndRat(Exps1...,[*0],Exps2...) = 0 if some Expi reject [*0]
|
||||
/// - AndRat(Exps1...,1[*],Exps2...) = AndRat(Exps1...,Exps2...)
|
||||
/// - OrRat(Exps1...,0,Exps2...) = OrRat(Exps1...,Exps2...)
|
||||
/// - OrRat(Exps1...,BoolExp1,Exps2...,BoolExps2...) =
|
||||
/// OrRat(Exps1...,Exps2...,Or(BoolExp1,BoolExps2...))
|
||||
/// - OrRat(Exps1...,1[*],Exps2...) = 1[*]
|
||||
/// - Concat(Exps1...,0,Exps2...) = 0
|
||||
/// - Concat(Exps1...,[*0],Exps2...) = Concat(Exps1...,Exps2...)
|
||||
/// - Concat(Exps1...,FExps2...,1[*],FExps3...,Exps4) =
|
||||
/// Concat(Exps1...,1[*],Exps4) if FExps2...FExps3... all accept [*0]
|
||||
/// - Concat(Exp) = Exp
|
||||
/// - Concat(Exps1...,E,E[*i..j],E[*k..l],Exps2...) =
|
||||
/// Concat(Exps1...,E[*1+i+k..j+l],Exps2...) and similar forms
|
||||
/// - Fusion(Exps1...1,Exps2...) = Fusion(Exps1...,Exps2...)
|
||||
/// if at least one exp reject [*0]
|
||||
/// - Fusion(Exps1...,0,Exps2...) = 0
|
||||
/// - Fusion(Exps1...,[*0],Exps2...) = 0
|
||||
/// - Fusion(Exp) = Exp
|
||||
/// - Fusion(Exps1...,BoolExp1...BoolExpN,Exps2,Exps3...) =
|
||||
/// Fusion(Exps1...,And(BoolExp1...BoolExpN),Exps2,Exps3...)
|
||||
static const formula* instance(type op, vec* v);
|
||||
|
||||
virtual void accept(visitor& v) const;
|
||||
|
||||
/// Get the number of children.
|
||||
unsigned size() const
|
||||
{
|
||||
return children_->size();
|
||||
}
|
||||
|
||||
/// \brief Get the nth child.
|
||||
///
|
||||
/// Starting with \a n = 0.
|
||||
const formula* nth(unsigned n) const
|
||||
{
|
||||
return (*children_)[n];
|
||||
}
|
||||
|
||||
/// \brief construct a formula without the nth child.
|
||||
///
|
||||
/// If the formula \c f is <code>a|b|c|d</code> and <code>c</code>
|
||||
/// is child number 2, then calling <code>f->all_but(2)</code> will
|
||||
/// return a new formula <code>a|b|d</code>.
|
||||
const formula* all_but(unsigned n) const;
|
||||
|
||||
/// \brief return the number of Boolean operands in the binop.
|
||||
///
|
||||
/// For instance if \c f <code>a|b|Xc|Gd</code>, this
|
||||
/// returns 2.
|
||||
unsigned boolean_count() const;
|
||||
|
||||
/// \brief return the Boolean part of the binop.
|
||||
///
|
||||
/// For instance if \c f <code>a|b|Xc|Gd</code>, this
|
||||
/// returns <code>a|b</code>. Return 0 if there is no
|
||||
/// Boolean operand.
|
||||
///
|
||||
/// If \a width is not null, it is filled with the number
|
||||
/// of Boolean operands extracted (i.e., the result
|
||||
/// of boolean_count())
|
||||
const formula* boolean_operands(unsigned* width = 0) const;
|
||||
|
||||
/// Get the type of this operator.
|
||||
type op() const
|
||||
{
|
||||
return op_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator, as a string.
|
||||
const char* op_name() const;
|
||||
|
||||
/// Return a canonic representation of the atomic proposition
|
||||
virtual std::string dump() const;
|
||||
|
||||
/// Number of instantiated multi-operand operators. For debugging.
|
||||
static unsigned instance_count();
|
||||
|
||||
/// Dump all instances. For debugging.
|
||||
static std::ostream& dump_instances(std::ostream& os);
|
||||
|
||||
protected:
|
||||
typedef std::pair<type, vec*> key;
|
||||
/// Comparison functor used internally by ltl::multop.
|
||||
struct paircmp
|
||||
{
|
||||
bool
|
||||
operator()(const key& p1, const key& p2) const
|
||||
{
|
||||
if (p1.first != p2.first)
|
||||
return p1.first < p2.first;
|
||||
return *p1.second < *p2.second;
|
||||
}
|
||||
};
|
||||
typedef std::map<key, const multop*, paircmp> map;
|
||||
static map instances;
|
||||
|
||||
multop(type op, vec* v);
|
||||
virtual ~multop();
|
||||
|
||||
private:
|
||||
type op_;
|
||||
vec* children_;
|
||||
};
|
||||
|
||||
|
||||
/// \brief Cast \a f into a multop.
|
||||
///
|
||||
/// Cast \a f into a multop iff it is a multop instance. Return 0
|
||||
/// otherwise. This is faster than \c dynamic_cast.
|
||||
inline
|
||||
const multop*
|
||||
is_multop(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::MultOp)
|
||||
return 0;
|
||||
return static_cast<const multop*>(f);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it has type \a op.
|
||||
///
|
||||
/// Cast \a f into a multop iff it is a multop instance with operator \a op.
|
||||
/// Returns 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_multop(const formula* f, multop::type op)
|
||||
{
|
||||
if (const multop* mo = is_multop(f))
|
||||
if (mo->op() == op)
|
||||
return mo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it has type \a op1 or \a op2.
|
||||
///
|
||||
/// Cast \a f into a multop iff it is a multop instance with
|
||||
/// operator \a op1 or \a op2. Returns 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_multop(const formula* f, multop::type op1, multop::type op2)
|
||||
{
|
||||
if (const multop* mo = is_multop(f))
|
||||
if (mo->op() == op1 || mo->op() == op2)
|
||||
return mo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is an And.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_And(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::And);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is an AndRat.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_AndRat(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::AndRat);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is an AndNLM.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_AndNLM(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::AndNLM);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is an Or.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_Or(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::Or);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is an OrRat.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_OrRat(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::OrRat);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is a Concat.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_Concat(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::Concat);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a multop if it is a Fusion.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const multop*
|
||||
is_Fusion(const formula* f)
|
||||
{
|
||||
return is_multop(f, multop::Fusion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2012, 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/predecl.hh
|
||||
/// \brief Predeclare all LTL node types.
|
||||
///
|
||||
/// This file is usually used when \b declaring methods and functions
|
||||
/// over LTL nodes.
|
||||
/// Use ltlast/allnodes.hh or an individual header when the definition of
|
||||
/// the node is actually needed.
|
||||
#pragma once
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
struct visitor;
|
||||
|
||||
class atomic_prop;
|
||||
class binop;
|
||||
class bunop;
|
||||
class constant;
|
||||
class formula;
|
||||
class multop;
|
||||
class unop;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
|
||||
// de Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "config.h"
|
||||
#include "unop.hh"
|
||||
#include "visitor.hh"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include "constant.hh"
|
||||
#include "atomic_prop.hh"
|
||||
#include "bunop.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
unop::unop(type op, const formula* child)
|
||||
: formula(UnOp), op_(op), child_(child)
|
||||
{
|
||||
props = child->get_props();
|
||||
switch (op)
|
||||
{
|
||||
case Not:
|
||||
is.not_marked = true;
|
||||
is.eventual = child->is_universal();
|
||||
is.universal = child->is_eventual();
|
||||
is.in_nenoform = (child->kind() == AtomicProp);
|
||||
is.sere_formula = is.boolean;
|
||||
|
||||
is.syntactic_safety = child->is_syntactic_guarantee();
|
||||
is.syntactic_guarantee = child->is_syntactic_safety();
|
||||
// is.syntactic_obligation inherited from child
|
||||
is.syntactic_recurrence = child->is_syntactic_persistence();
|
||||
is.syntactic_persistence = child->is_syntactic_recurrence();
|
||||
|
||||
is.accepting_eword = false;
|
||||
break;
|
||||
case X:
|
||||
is.not_marked = true;
|
||||
is.boolean = false;
|
||||
is.syntactic_si = false;
|
||||
is.sere_formula = false;
|
||||
// is.syntactic_safety inherited
|
||||
// is.syntactic_guarantee inherited
|
||||
// is.syntactic_obligation inherited
|
||||
// is.syntactic_recurrence inherited
|
||||
// is.syntactic_persistence inherited
|
||||
is.accepting_eword = false;
|
||||
break;
|
||||
case F:
|
||||
is.not_marked = true;
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.sugar_free_ltl = false;
|
||||
is.eventual = true;
|
||||
is.syntactic_safety = false;
|
||||
// is.syntactic_guarantee inherited
|
||||
is.syntactic_obligation = is.syntactic_guarantee;
|
||||
is.syntactic_recurrence = is.syntactic_guarantee;
|
||||
// is.syntactic_persistence inherited
|
||||
is.accepting_eword = false;
|
||||
break;
|
||||
case G:
|
||||
is.not_marked = true;
|
||||
is.boolean = false;
|
||||
is.sere_formula = false;
|
||||
is.finite = false;
|
||||
is.sugar_free_ltl = false;
|
||||
is.universal = true;
|
||||
// is.syntactic_safety inherited
|
||||
is.syntactic_guarantee = false;
|
||||
is.syntactic_obligation = is.syntactic_safety;
|
||||
// is.syntactic_recurrence inherited
|
||||
is.syntactic_persistence = is.syntactic_safety;
|
||||
is.accepting_eword = false;
|
||||
break;
|
||||
case NegClosure:
|
||||
case NegClosureMarked:
|
||||
is.not_marked = (op == NegClosure);
|
||||
is.boolean = false;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = true;
|
||||
is.sere_formula = false;
|
||||
is.syntactic_safety = is.finite;
|
||||
is.syntactic_guarantee = true;
|
||||
is.syntactic_obligation = true;
|
||||
is.syntactic_recurrence = true;
|
||||
is.syntactic_persistence = true;
|
||||
is.accepting_eword = false;
|
||||
assert(child->is_sere_formula());
|
||||
assert(!child->is_boolean());
|
||||
break;
|
||||
case Closure:
|
||||
is.not_marked = true;
|
||||
is.boolean = false;
|
||||
is.ltl_formula = false;
|
||||
is.psl_formula = true;
|
||||
is.sere_formula = false;
|
||||
is.syntactic_safety = true;
|
||||
is.syntactic_guarantee = is.finite;
|
||||
is.syntactic_obligation = true;
|
||||
is.syntactic_recurrence = true;
|
||||
is.syntactic_persistence = true;
|
||||
is.accepting_eword = false;
|
||||
assert(child->is_sere_formula());
|
||||
assert(!child->is_boolean());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unop::~unop()
|
||||
{
|
||||
// Get this instance out of the instance map.
|
||||
size_t c = instances.erase(key(op(), child()));
|
||||
assert(c == 1);
|
||||
(void) c; // For the NDEBUG case.
|
||||
|
||||
// Dereference child.
|
||||
child()->destroy();
|
||||
}
|
||||
|
||||
std::string
|
||||
unop::dump() const
|
||||
{
|
||||
return std::string("unop(") + op_name() + ", " + child()->dump() + ")";
|
||||
}
|
||||
|
||||
void
|
||||
unop::accept(visitor& v) const
|
||||
{
|
||||
v.visit(this);
|
||||
}
|
||||
|
||||
const char*
|
||||
unop::op_name() const
|
||||
{
|
||||
switch (op_)
|
||||
{
|
||||
case Not:
|
||||
return "Not";
|
||||
case X:
|
||||
return "X";
|
||||
case F:
|
||||
return "F";
|
||||
case G:
|
||||
return "G";
|
||||
case Closure:
|
||||
return "Closure";
|
||||
case NegClosure:
|
||||
return "NegClosure";
|
||||
case NegClosureMarked:
|
||||
return "NegClosureMarked";
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
unop::map unop::instances;
|
||||
|
||||
const formula*
|
||||
unop::instance(type op, const formula* child)
|
||||
{
|
||||
|
||||
// Some trivial simplifications.
|
||||
switch (op)
|
||||
{
|
||||
case F:
|
||||
case G:
|
||||
{
|
||||
if (const unop* u = is_unop(child))
|
||||
{
|
||||
// F and G are idempotent.
|
||||
if (u->op() == op)
|
||||
return u;
|
||||
}
|
||||
|
||||
// F(0) = G(0) = 0
|
||||
// F(1) = G(1) = 1
|
||||
if (child == constant::false_instance()
|
||||
|| child == constant::true_instance())
|
||||
return child;
|
||||
|
||||
assert(child != constant::empty_word_instance());
|
||||
}
|
||||
break;
|
||||
|
||||
case Not:
|
||||
{
|
||||
// !1 = 0
|
||||
if (child == constant::true_instance())
|
||||
return constant::false_instance();
|
||||
// !0 = 1
|
||||
if (child == constant::false_instance())
|
||||
return constant::true_instance();
|
||||
// ![*0] = 1[+]
|
||||
if (child == constant::empty_word_instance())
|
||||
return bunop::instance(bunop::Star,
|
||||
constant::true_instance(), 1);
|
||||
|
||||
if (const unop* u = is_unop(child))
|
||||
{
|
||||
// "Not" is an involution.
|
||||
if (u->op() == op)
|
||||
{
|
||||
const formula* c = u->child()->clone();
|
||||
u->destroy();
|
||||
return c;
|
||||
}
|
||||
// !Closure(Exp) = NegClosure(Exp)
|
||||
if (u->op() == Closure)
|
||||
{
|
||||
const formula* c = unop::instance(NegClosure,
|
||||
u->child()->clone());
|
||||
u->destroy();
|
||||
return c;
|
||||
}
|
||||
// !NegClosure(Exp) = Closure(Exp)
|
||||
if (u->op() == NegClosure)
|
||||
{
|
||||
const formula* c = unop::instance(Closure,
|
||||
u->child()->clone());
|
||||
u->destroy();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case X:
|
||||
// X(1) = 1, X(0) = 0
|
||||
if (child == constant::true_instance()
|
||||
|| child == constant::false_instance())
|
||||
return child;
|
||||
assert(child != constant::empty_word_instance());
|
||||
break;
|
||||
|
||||
case Closure:
|
||||
// {0} = 0, {1} = 1, {b} = b
|
||||
if (child->is_boolean())
|
||||
return child;
|
||||
// {[*0]} = 0
|
||||
if (child == constant::empty_word_instance())
|
||||
return constant::false_instance();
|
||||
break;
|
||||
|
||||
case NegClosure:
|
||||
case NegClosureMarked:
|
||||
// {1} = 0
|
||||
if (child == constant::true_instance())
|
||||
return constant::false_instance();
|
||||
// {0} = 1, {[*0]} = 1
|
||||
if (child == constant::false_instance()
|
||||
|| child == constant::empty_word_instance())
|
||||
return constant::true_instance();
|
||||
// {b} = !b
|
||||
if (child->is_boolean())
|
||||
return unop::instance(Not, child);
|
||||
break;
|
||||
}
|
||||
|
||||
const formula* res;
|
||||
auto ires = instances.emplace(key(op, child), nullptr);
|
||||
if (!ires.second)
|
||||
{
|
||||
// This instance already exists.
|
||||
child->destroy();
|
||||
res = ires.first->second->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = ires.first->second = new unop(op, child);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned
|
||||
unop::instance_count()
|
||||
{
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
unop::dump_instances(std::ostream& os)
|
||||
{
|
||||
for (const auto& i: instances)
|
||||
os << i.second << " = "
|
||||
<< 1 + i.second->refs_ << " * "
|
||||
<< i.second->dump()
|
||||
<< '\n';
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
|
||||
// Recherche et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
|
||||
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
|
||||
// Université Pierre et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/unop.hh
|
||||
/// \brief LTL unary operators
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <iosfwd>
|
||||
#include "formula.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
|
||||
/// \ingroup ltl_ast
|
||||
/// \brief Unary operators.
|
||||
class SPOT_API unop final : public formula
|
||||
{
|
||||
public:
|
||||
enum type {
|
||||
// LTL
|
||||
Not, X, F, G,
|
||||
// Closure
|
||||
Closure, NegClosure, NegClosureMarked
|
||||
};
|
||||
|
||||
/// \brief Build an unary operator with operation \a op and
|
||||
/// child \a child.
|
||||
///
|
||||
/// The following trivial simplifications are performed
|
||||
/// automatically (the left expression is rewritten as the right
|
||||
/// expression):
|
||||
/// - FF(Exp) = F(Exp)
|
||||
/// - GG(Exp) = G(Exp)
|
||||
/// - F(0) = 0
|
||||
/// - G(0) = 0
|
||||
/// - X(0) = 0
|
||||
/// - F(1) = 1
|
||||
/// - G(1) = 1
|
||||
/// - X(1) = 1
|
||||
/// - !1 = 0
|
||||
/// - !0 = 1
|
||||
/// - ![*0] = 1[+] (read below)
|
||||
/// - !!Exp = Exp
|
||||
/// - !Closure(Exp) = NegClosure(Exp)
|
||||
/// - !NegClosure(Exp) = Closure(Exp)
|
||||
/// - Closure([*0]) = 1
|
||||
/// - Closure(1) = 1
|
||||
/// - Closure(0) = 0
|
||||
/// - Closure(b) = b
|
||||
/// - NegClosure([*0]) = 0
|
||||
/// - NegClosure(1) = 0
|
||||
/// - NegClosure(0) = 1
|
||||
/// - NegClosure(b) = !b
|
||||
///
|
||||
/// This rewriting implies that it is not possible to build an
|
||||
/// LTL formula object that is SYNTACTICALLY equal to one of
|
||||
/// these left expressions.
|
||||
///
|
||||
/// Note that the "![*0]" form cannot be read using the PSL
|
||||
/// grammar. Spot cannot read it either. However some
|
||||
/// BDD-based algorithm may need to negate any constant, so we
|
||||
/// handle this one as well.
|
||||
static const formula* instance(type op, const formula* child);
|
||||
|
||||
virtual void accept(visitor& v) const override;
|
||||
|
||||
/// Get the sole operand of this operator.
|
||||
const formula* child() const
|
||||
{
|
||||
return child_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator.
|
||||
type op() const
|
||||
{
|
||||
return op_;
|
||||
}
|
||||
|
||||
/// Get the type of this operator, as a string.
|
||||
const char* op_name() const;
|
||||
|
||||
/// Return a canonic representation of the atomic proposition
|
||||
virtual std::string dump() const override;
|
||||
|
||||
/// Number of instantiated unary operators. For debugging.
|
||||
static unsigned instance_count();
|
||||
|
||||
/// Dump all instances. For debugging.
|
||||
static std::ostream& dump_instances(std::ostream& os);
|
||||
|
||||
protected:
|
||||
typedef std::pair<type, const formula*> key;
|
||||
typedef std::map<key, const unop*> map;
|
||||
static map instances;
|
||||
|
||||
unop(type op, const formula* child);
|
||||
virtual ~unop();
|
||||
|
||||
private:
|
||||
type op_;
|
||||
const formula* child_;
|
||||
};
|
||||
|
||||
|
||||
/// \brief Cast \a f into a unop
|
||||
///
|
||||
/// Cast \a f into a unop iff it is a unop instance. Return 0
|
||||
/// otherwise. This is faster than \c dynamic_cast.
|
||||
inline
|
||||
const unop*
|
||||
is_unop(const formula* f)
|
||||
{
|
||||
if (f->kind() != formula::UnOp)
|
||||
return 0;
|
||||
return static_cast<const unop*>(f);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if it has type \a op.
|
||||
///
|
||||
/// Cast \a f into a unop iff it is a unop instance with operator \a op.
|
||||
/// Returns 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_unop(const formula* f, unop::type op)
|
||||
{
|
||||
if (const unop* uo = is_unop(f))
|
||||
if (uo->op() == op)
|
||||
return uo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if it is a Not.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_Not(const formula* f)
|
||||
{
|
||||
return is_unop(f, unop::Not);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if it is a X.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_X(const formula* f)
|
||||
{
|
||||
return is_unop(f, unop::X);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if it is a F.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_F(const formula* f)
|
||||
{
|
||||
return is_unop(f, unop::F);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if it is a G.
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_G(const formula* f)
|
||||
{
|
||||
return is_unop(f, unop::G);
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if has the form GF(...).
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_GF(const formula* f)
|
||||
{
|
||||
if (const unop* op = is_G(f))
|
||||
return is_F(op->child());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Cast \a f into a unop if has the form FG(...).
|
||||
///
|
||||
/// Return 0 otherwise.
|
||||
inline
|
||||
const unop*
|
||||
is_FG(const formula* f)
|
||||
{
|
||||
if (const unop* op = is_F(f))
|
||||
return is_G(op->child());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2009, 2010, 2012, 2013, 2014 Laboratoire de Recherche
|
||||
// et Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
// et Marie Curie.
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot 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 General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// \file ltlast/visitor.hh
|
||||
/// \brief LTL visitor interface
|
||||
#pragma once
|
||||
|
||||
#include "misc/common.hh"
|
||||
#include "predecl.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
/// \ingroup ltl_essential
|
||||
/// \brief Formula visitor
|
||||
///
|
||||
/// Implementing visitors is the prefered way
|
||||
/// to traverse a formula, since it does not
|
||||
/// involve any cast.
|
||||
struct SPOT_API visitor
|
||||
{
|
||||
virtual ~visitor() {}
|
||||
virtual void visit(const atomic_prop* node) = 0;
|
||||
virtual void visit(const constant* node) = 0;
|
||||
virtual void visit(const binop* node) = 0;
|
||||
virtual void visit(const unop* node) = 0;
|
||||
virtual void visit(const multop* node) = 0;
|
||||
virtual void visit(const bunop* node) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue