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:
Alexandre Duret-Lutz 2015-09-24 19:44:00 +02:00
parent 1628b188fe
commit b77f7e24c3
177 changed files with 8295 additions and 13332 deletions

18
NEWS
View file

@ -15,6 +15,24 @@ New in spot 1.99.3a (not yet released)
(But dstar2tgba does not offer all the filtering and
transformations options of autfilt.)
* The class hierarchy for temporal formulas has been entirely
rewritten. This change is actually quite massive (~13200 lines
removed, ~8200 lines added), and brings some nice benefits:
- LTL/PSL formulas are now represented by lightweight
ltl::formula objects (instead of ltl::formula* pointers)
that perform reference counting automatically.
- There is no hierachy anymore: all operators are represented
by a single type of node in the syntax tree, and an
enumerator is used to distinguish between operators.
- Visitors have been replaced by member functions such
as map() or traverse(), that take a function (usually
written as a lambda function) and apply it to the
nodes of the tree.
- As a consequence, writing algorithms that manipulate
formula is more friendly, and several functions
algorithms that spanned a few pages have been
reduced to a few lines.
New in spot 1.99.3 (2015-08-26)
* The CGI script for LTL translation offers a HOA download link

View file

@ -25,7 +25,6 @@
#include "twaalgos/stutter.hh"
#include "twaalgos/dupexp.hh"
#include "twaalgos/stats.hh"
#include "ltlast/allnodes.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/length.hh"
#include "misc/timer.hh"
@ -64,14 +63,10 @@ namespace
}
int
process_formula(const spot::ltl::formula* f,
const char*, int)
process_formula(spot::ltl::formula f, const char*, int)
{
const spot::ltl::formula* nf =
spot::ltl::unop::instance(spot::ltl::unop::Not,
f->clone());
spot::twa_graph_ptr a = trans.run(f);
spot::twa_graph_ptr na = trans.run(nf);
spot::twa_graph_ptr na = trans.run(spot::ltl::formula::Not(f));
spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f);
bdd apdict = spot::ltl::atomic_prop_collect_as_bdd(f, a);
@ -103,9 +98,6 @@ namespace
prev = res;
}
std::cout << prev << '\n';
f->destroy();
nf->destroy();
delete ap;
return 0;
}

View file

@ -127,6 +127,5 @@ main(int argc, char** argv)
break;
}
dict->unregister_all_my_variables(&ap);
spot::ltl::destroy_atomic_prop_set(ap);
return 0;
}

View file

@ -72,10 +72,9 @@ exceptions.
int main()
{
print_latex_psl(std::cout, spot::ltl::parse_formula("[]<>p0 || <>[]p1")) << '\n';
const spot::ltl::formula* f = spot::ltl::parse_formula("& & G p0 p1 p2");
spot::ltl::formula f = spot::ltl::parse_formula("& & G p0 p1 p2");
print_lbt_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, f, true) << '\n';
f->destroy();
return 0;
}
#+END_SRC
@ -90,12 +89,6 @@ syntax the output, and the type of formula they can output. Here we
are only using LTL formulas for demonstration, so those three
functions are OK with that.
Did you notice the calls to =f->destroy()= at the end? The LTL
formula objects are implemented as DAG with sharing of subformulas.
Each (sub)formula is therefore reference counted, and currently this
is done manually by calling =f->clone()= and =f->destroy()= (do not
ever =delete= a formula, always call =f->destroy()=).
We do not recommend using this =parse_formula()= interface because of
the potential formulas (like =f= or =t=) that have different meanings
in the two parsers that are tried.
@ -118,15 +111,10 @@ Here is how to call the infix parser explicitly,:
{
std::string input = "[]<>p0 || <>[]p1";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
return 1;
print_latex_psl(std::cout, f) << '\n';
f->destroy();
return 0;
}
#+END_SRC
@ -165,14 +153,14 @@ with the "fixed" formula if you wish. Here is an example:
{
std::string input = "(a U b))";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
// Use std::cout instead of std::cerr because we can only
// show the output of std::cout in this documentation.
(void) spot::ltl::format_parse_errors(std::cout, input, pel);
if (f == nullptr)
return 1;
print_latex_psl(std::cout, f) << '\n';
f->destroy();
std::cout << "Parsed formula: ";
print_psl(std::cout, f) << '\n';
return 0;
}
#+END_SRC
@ -186,7 +174,7 @@ with the "fixed" formula if you wish. Here is an example:
: ^
: ignoring trailing garbage
:
: a \U b
: Parsed formula: a U b
The formula =f= is only returned as null when the parser really cannot
@ -207,16 +195,11 @@ of =parse_infix_psl()=.
{
std::string input = "& & G p0 p1 p2";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_prefix_ltl(input, pel);
spot::ltl::formula f = spot::ltl::parse_prefix_ltl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
return 1;
print_lbt_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, f, true) << '\n';
f->destroy();
return 0;
}
#+END_SRC
@ -254,15 +237,10 @@ For instance, let's see what happens if a PSL formulas is passed to
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
return 1;
print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0;
}
#+END_SRC
@ -289,21 +267,15 @@ The first is to simply diagnose non-LTL formulas.
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
return 1;
if (!f.is_ltl_formula())
{
if (f)
f->destroy();
return 1;
}
if (!f->is_ltl_formula())
{
f->destroy();
std::cerr << "Only LTL formulas are supported.\n";
return 1;
}
print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0;
}
#+END_SRC
@ -314,7 +286,7 @@ equivalent LTL formula. This does not always work, so you need to be
prepared to reject the formula any way. In our example, we are lucky
(maybe because it was carefully chosen...):
#+BEGIN_SRC C++ :results verbatim :exports code
#+BEGIN_SRC C++ :results verbatim :exports both
#include <string>
#include <iostream>
#include "ltlparse/public.hh"
@ -325,28 +297,20 @@ prepared to reject the formula any way. In our example, we are lucky
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
if (!f->is_ltl_formula())
return 1;
if (!f.is_ltl_formula())
{
spot::ltl::ltl_simplifier simp;
const formula* g = simp.simplify(f);
f->destroy();
f = g;
f = simp.simplify(f);
}
if (!f->is_ltl_formula())
if (!f.is_ltl_formula())
{
f->destroy();
std::cerr << "Only LTL formulas are supported.\n";
return 1;
}
print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0;
}
#+END_SRC

View file

@ -15,9 +15,9 @@ ltlfilt -ps --relabel=pnn --define -f '"Proc@Here" U ("var > 10" | "var < 4")'
#+END_SRC
#+RESULTS:
: #define p0 ((Proc@Here))
: #define p1 ((var < 4))
: #define p2 ((var > 10))
: #define p0 (Proc@Here)
: #define p1 (var < 4)
: #define p2 (var > 10)
: (p0) U ((p1) || (p2))
When is this output interesting, you may ask? It is useful for
@ -34,9 +34,9 @@ rm tmp.defs tmp.ltl
#+RESULTS:
#+begin_example
#define p0 ((Proc@Here))
#define p1 ((var < 4))
#define p2 ((var > 10))
#define p0 (Proc@Here)
#define p1 (var < 4)
#define p2 (var > 10)
never { /* (p0) U ((p1) || (p2))
*/
T0_init:
@ -88,32 +88,26 @@ destructor.
{
std::string input = "\"Proc@Here\" U (\"var > 10\" | \"var < 4\")";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
return 1;
spot::ltl::relabeling_map m;
const spot::ltl::formula* g = spot::ltl::relabel(f, spot::ltl::Pnn, &m);
f = spot::ltl::relabel(f, spot::ltl::Pnn, &m);
for (auto& i: m)
{
std::cout << "#define ";
print_psl(std::cout, i.first) << " (";
print_spin_ltl(std::cout, i.second, true) << ")\n";
}
print_spin_ltl(std::cout, g, true) << '\n';
g->destroy();
f->destroy();
print_spin_ltl(std::cout, f, true) << '\n';
return 0;
}
#+END_SRC
#+RESULTS:
: #define p0 ((Proc@Here))
: #define p1 ((var < 4))
: #define p2 ((var > 10))
: #define p0 (Proc@Here)
: #define p1 (var < 4)
: #define p2 (var > 10)
: (p0) U ((p1) || (p2))
@ -135,7 +129,7 @@ ltlfilt -ps --relabel-bool=pnn --define -f '"Proc@Here" U ("var > 10" | "var < 4
#+END_SRC
#+RESULTS:
: #define p0 ((Proc@Here))
: #define p0 (Proc@Here)
: #define p1 ((var < 4) || (var > 10))
: (p0) U (p1)
@ -151,8 +145,8 @@ ltlfilt -ps --relabel-bool=pnn --define -f 'a U (a & b)'
#+END_SRC
#+RESULTS:
: #define p0 ((a))
: #define p1 ((b))
: #define p0 (a)
: #define p1 (b)
: (p0) U ((p0) && (p1))
This "Boolean sub-expression" relabeling is available in Python and

View file

@ -138,18 +138,13 @@ never claim is done via the =print_never_claim= function.
{
std::string input = "[]<>p0 || <>[]p1";
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel);
spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
{
if (f)
f->destroy();
return 1;
}
return 1;
spot::translator trans;
trans.set_type(spot::postprocessor::BA);
spot::twa_graph_ptr aut = trans.run(f);
print_never_claim(std::cout, aut) << '\n';
f->destroy();
return 0;
}
#+END_SRC

View file

@ -4,7 +4,6 @@
#+HTML_LINK_UP: tut.html
This example demonstrates how to create an automaton in C++, and then print it.
The interface
#+BEGIN_SRC C++ :results verbatim :exports both
#include <iostream>

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE)
// Copyright (C) 2011, 2012, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE)
//
// This file is part of Spot, a model checking library.
//
@ -326,7 +326,7 @@ namespace spot
convert_aps(const ltl::atomic_prop_set* aps,
const spins_interface* d,
bdd_dict_ptr dict,
const ltl::formula* dead,
ltl::formula dead,
prop_set& out)
{
int errors = 0;
@ -359,7 +359,7 @@ namespace spot
if (*ap == dead)
continue;
std::string str = (*ap)->name();
const std::string& str = ap->ap_name();
const char* s = str.c_str();
// Skip any leading blank.
@ -602,7 +602,7 @@ namespace spot
public:
spins_kripke(const spins_interface* d, const bdd_dict_ptr& dict,
const spot::prop_set* ps, const ltl::formula* dead,
const spot::prop_set* ps, ltl::formula dead,
int compress)
: kripke(dict),
d_(d),
@ -646,12 +646,12 @@ namespace spot
// appropriately. ALIVE_PROP is the bdd that should be ANDed
// to all transitions leaving a live state, while DEAD_PROP should
// be ANDed to all transitions leaving a dead state.
if (dead == ltl::constant::false_instance())
if (dead.is_false())
{
alive_prop = bddtrue;
dead_prop = bddfalse;
}
else if (dead == ltl::constant::true_instance())
else if (dead.is_true())
{
alive_prop = bddtrue;
dead_prop = bddtrue;
@ -1016,7 +1016,7 @@ namespace spot
kripke_ptr
load_ltsmin(const std::string& file_arg, const bdd_dict_ptr& dict,
const ltl::atomic_prop_set* to_observe,
const ltl::formula* dead, int compress, bool verbose)
const ltl::formula dead, int compress, bool verbose)
{
std::string file;
if (file_arg.find_first_of("/\\") != std::string::npos)

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2014 Laboratoire de Recherche et
// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et
// Developpement de l'Epita (LRDE)
//
// This file is part of Spot, a model checking library.
@ -21,7 +21,6 @@
#include "kripke/kripke.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlast/constant.hh"
namespace spot
{
@ -59,6 +58,6 @@ namespace spot
SPOT_API kripke_ptr
load_ltsmin(const std::string& file, const bdd_dict_ptr& dict,
const ltl::atomic_prop_set* to_observe,
const ltl::formula* dead = ltl::constant::true_instance(),
ltl::formula dead = ltl::formula::tt(),
int compress = 0, bool verbose = true);
}

View file

@ -20,7 +20,6 @@
#include "ltsmin.hh"
#include "twaalgos/dot.hh"
#include "ltlenv/defaultenv.hh"
#include "ltlast/allnodes.hh"
#include "ltlparse/public.hh"
#include "twaalgos/translate.hh"
#include "twaalgos/emptiness.hh"
@ -163,16 +162,16 @@ checked_main(int argc, char **argv)
spot::emptiness_check_instantiator_ptr echeck_inst = nullptr;
int exit_code = 0;
spot::postprocessor post;
const spot::ltl::formula* deadf = nullptr;
const spot::ltl::formula* f = nullptr;
spot::ltl::formula deadf = nullptr;
spot::ltl::formula f = nullptr;
if (!dead || !strcasecmp(dead, "true"))
{
deadf = spot::ltl::constant::true_instance();
deadf = spot::ltl::formula::tt();
}
else if (!strcasecmp(dead, "false"))
{
deadf = spot::ltl::constant::false_instance();
deadf = spot::ltl::formula::ff();
}
else
{
@ -355,11 +354,6 @@ checked_main(int argc, char **argv)
}
safe_exit:
if (f)
f->destroy();
deadf->destroy();
if (use_timer)
tm.print(std::cout);
tm.reset_all(); // This helps valgrind.
@ -372,15 +366,6 @@ main(int argc, char **argv)
auto exit_code = checked_main(argc, argv);
// Additional checks to debug reference counts in formulas.
spot::ltl::atomic_prop::dump_instances(std::cerr);
spot::ltl::unop::dump_instances(std::cerr);
spot::ltl::binop::dump_instances(std::cerr);
spot::ltl::multop::dump_instances(std::cerr);
spot::ltl::bunop::dump_instances(std::cerr);
assert(spot::ltl::atomic_prop::instance_count() == 0);
assert(spot::ltl::unop::instance_count() == 0);
assert(spot::ltl::binop::instance_count() == 0);
assert(spot::ltl::multop::instance_count() == 0);
assert(spot::ltl::bunop::instance_count() == 0);
assert(spot::ltl::fnode::instances_check());
exit(exit_code);
}

View file

@ -487,7 +487,7 @@ namespace
}
int
process_formula(const spot::ltl::formula*, const char*, int)
process_formula(spot::ltl::formula, const char*, int)
{
SPOT_UNREACHABLE();
}

View file

@ -272,7 +272,7 @@ automaton_printer::automaton_printer(stat_style input)
void
automaton_printer::print(const spot::twa_graph_ptr& aut,
const spot::ltl::formula* f,
spot::ltl::formula f,
// Input location for errors and statistics.
const char* filename,
int loc,

View file

@ -107,7 +107,7 @@ public:
std::ostream&
print(const spot::const_parsed_aut_ptr& haut,
const spot::const_twa_graph_ptr& aut,
const spot::ltl::formula* f,
spot::ltl::formula f,
const char* filename, int loc, double run_time)
{
filename_ = filename ? filename : "";
@ -225,7 +225,7 @@ public:
void
print(const spot::twa_graph_ptr& aut,
const spot::ltl::formula* f = nullptr,
spot::ltl::formula f = nullptr,
// Input location for errors and statistics.
const char* filename = nullptr,
int loc = -1,

View file

@ -75,7 +75,7 @@ parse_opt_finput(int key, char* arg, struct argp_state*)
return 0;
}
const spot::ltl::formula*
spot::ltl::formula
parse_formula(const std::string& s, spot::ltl::parse_error_list& pel)
{
if (lbt_input)
@ -108,15 +108,13 @@ job_processor::process_string(const std::string& input,
int linenum)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
auto f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::ltl::format_parse_errors(std::cerr, input, pel);
if (f)
f->destroy();
return 1;
}
return process_formula(f, filename, linenum);

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -44,7 +44,7 @@ extern const struct argp finput_argp;
int parse_opt_finput(int key, char* arg, struct argp_state* state);
const spot::ltl::formula*
spot::ltl::formula
parse_formula(const std::string& s, spot::ltl::parse_error_list& error_list);
@ -58,7 +58,7 @@ public:
virtual ~job_processor();
virtual int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0) = 0;
virtual int

View file

@ -68,7 +68,7 @@ const struct argp output_argp = { options, parse_opt_output, 0, 0, 0, 0, 0 };
static
void
report_not_ltl(const spot::ltl::formula* f,
report_not_ltl(spot::ltl::formula f,
const char* filename, int linenum, const char* syn)
{
std::string s = spot::ltl::str_psl(f);
@ -82,12 +82,12 @@ report_not_ltl(const spot::ltl::formula* f,
std::ostream&
stream_formula(std::ostream& out,
const spot::ltl::formula* f, const char* filename, int linenum)
spot::ltl::formula f, const char* filename, int linenum)
{
switch (output_format)
{
case lbt_output:
if (f->is_ltl_formula())
if (f.is_ltl_formula())
spot::ltl::print_lbt_ltl(out, f);
else
report_not_ltl(f, filename, linenum, "LBT");
@ -96,13 +96,13 @@ stream_formula(std::ostream& out,
spot::ltl::print_psl(out, f, full_parenth);
break;
case spin_output:
if (f->is_ltl_formula())
if (f.is_ltl_formula())
spot::ltl::print_spin_ltl(out, f, full_parenth);
else
report_not_ltl(f, filename, linenum, "Spin");
break;
case wring_output:
if (f->is_ltl_formula())
if (f.is_ltl_formula())
spot::ltl::print_wring_ltl(out, f);
else
report_not_ltl(f, filename, linenum, "Wring");
@ -122,7 +122,7 @@ stream_formula(std::ostream& out,
static void
stream_escapable_formula(std::ostream& os,
const spot::ltl::formula* f,
spot::ltl::formula f,
const char* filename, int linenum)
{
if (escape_csv)
@ -144,7 +144,7 @@ namespace
{
struct formula_with_location
{
const spot::ltl::formula* f;
spot::ltl::formula f;
const char* filename;
int line;
const char* prefix;
@ -258,7 +258,7 @@ parse_opt_output(int key, char* arg, struct argp_state*)
static void
output_formula(std::ostream& out,
const spot::ltl::formula* f,
spot::ltl::formula f,
const char* filename = nullptr, int linenum = 0,
const char* prefix = nullptr, const char* suffix = nullptr)
{
@ -284,7 +284,7 @@ void
}
void
output_formula_checked(const spot::ltl::formula* f,
output_formula_checked(spot::ltl::formula f,
const char* filename, int linenum,
const char* prefix, const char* suffix)
{

View file

@ -43,19 +43,19 @@ int parse_opt_output(int key, char* arg, struct argp_state* state);
// Low-level output
std::ostream&
stream_formula(std::ostream& out,
const spot::ltl::formula* f, const char* filename, int linenum);
spot::ltl::formula f, const char* filename, int linenum);
void output_formula_checked(const spot::ltl::formula* f,
void output_formula_checked(spot::ltl::formula f,
const char* filename = 0, int linenum = 0,
const char* prefix = 0, const char* suffix = 0);
class printable_formula:
public spot::printable_value<const spot::ltl::formula*>
public spot::printable_value<spot::ltl::formula>
{
public:
printable_formula&
operator=(const spot::ltl::formula* new_val)
operator=(spot::ltl::formula new_val)
{
val_ = new_val;
return *this;
@ -78,7 +78,7 @@ public:
std::ostream&
print(const spot::const_twa_graph_ptr& aut,
const spot::ltl::formula* f = 0,
spot::ltl::formula f = 0,
double run_time = -1.)
{
formula_ = f;

View file

@ -290,7 +290,7 @@ translator_runner::formula() const
}
void
translator_runner::round_formula(const spot::ltl::formula* f, unsigned serial)
translator_runner::round_formula(spot::ltl::formula f, unsigned serial)
{
if (has('f') || has('F'))
string_ltl_spot = spot::ltl::str_psl(f, true);

View file

@ -94,7 +94,7 @@ public:
bool no_output_allowed = false);
void string_to_tmp(std::string& str, unsigned n, std::string& tmpname);
const std::string& formula() const;
void round_formula(const spot::ltl::formula* f, unsigned serial);
void round_formula(spot::ltl::formula f, unsigned serial);
};

View file

@ -138,7 +138,7 @@ namespace
}
int
process_formula(const spot::ltl::formula*, const char*, int)
process_formula(spot::ltl::formula, const char*, int)
{
SPOT_UNREACHABLE();
}

View file

@ -86,8 +86,7 @@
#include <string>
#include <cstdlib>
#include <cstring>
#include "ltlast/allnodes.hh"
#include "ltlenv/defaultenv.hh"
#include "ltlast/formula.hh"
#include "ltlvisit/relabel.hh"
using namespace spot;
@ -270,37 +269,31 @@ parse_opt(int key, char* arg, struct argp_state*)
return 0;
}
environment& env(default_environment::instance());
#define G_(x) formula::G(x)
#define F_(x) formula::F(x)
#define X_(x) formula::X(x)
#define Not_(x) formula::Not(x)
#define G_(x) spot::ltl::unop::instance(spot::ltl::unop::G, (x))
#define F_(x) spot::ltl::unop::instance(spot::ltl::unop::F, (x))
#define X_(x) spot::ltl::unop::instance(spot::ltl::unop::X, (x))
#define Not_(x) spot::ltl::unop::instance(spot::ltl::unop::Not, (x))
#define Implies_(x, y) \
spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y))
#define Equiv_(x, y) \
spot::ltl::binop::instance(spot::ltl::binop::Equiv, (x), (y))
#define And_(x, y) \
spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y))
#define Or_(x, y) \
spot::ltl::multop::instance(spot::ltl::multop::Or, (x), (y))
#define U_(x, y) \
spot::ltl::binop::instance(spot::ltl::binop::U, (x), (y))
#define Implies_(x, y) formula::Implies((x), (y))
#define Equiv_(x, y) formula::Equiv((x), (y))
#define And_(x, y) formula::And({(x), (y)})
#define Or_(x, y) formula::Or({(x), (y)})
#define U_(x, y) formula::U((x), (y))
// F(p_1 & F(p_2 & F(p_3 & ... F(p_n))))
static const formula*
static formula
E_n(std::string name, int n)
{
if (n <= 0)
return constant::true_instance();
return formula::tt();
const formula* result = 0;
formula result = nullptr;
for (; n > 0; --n)
{
std::ostringstream p;
p << name << n;
const formula* f = env.require(p.str());
formula f = formula::ap(p.str());
if (result)
result = And_(f, result);
else
@ -311,43 +304,43 @@ E_n(std::string name, int n)
}
// p & X(p & X(p & ... X(p)))
static const formula*
static formula
phi_n(std::string name, int n)
{
if (n <= 0)
return constant::true_instance();
return formula::tt();
const formula* result = 0;
const formula* p = env.require(name);
formula result = nullptr;
formula p = formula::ap(name);
for (; n > 0; --n)
{
if (result)
result = And_(p->clone(), X_(result));
result = And_(p, X_(result));
else
result = p;
}
return result;
}
const formula* N_n(std::string name, int n)
formula N_n(std::string name, int n)
{
return unop::instance(unop::F, phi_n(name, n));
return formula::F(phi_n(name, n));
}
// p & X(p) & XX(p) & XXX(p) & ... X^n(p)
static const formula*
static formula
phi_prime_n(std::string name, int n)
{
if (n <= 0)
return constant::true_instance();
return formula::tt();
const formula* result = 0;
const formula* p = env.require(name);
formula result = nullptr;
formula p = formula::ap(name);
for (; n > 0; --n)
{
if (result)
{
p = X_(p->clone());
p = X_(p);
result = And_(result, p);
}
else
@ -358,7 +351,7 @@ phi_prime_n(std::string name, int n)
return result;
}
static const formula*
static formula
N_prime_n(std::string name, int n)
{
return F_(phi_prime_n(name, n));
@ -367,24 +360,24 @@ N_prime_n(std::string name, int n)
// GF(p_1) & GF(p_2) & ... & GF(p_n) if conj == true
// GF(p_1) | GF(p_2) | ... | GF(p_n) if conj == false
static const formula*
static formula
GF_n(std::string name, int n, bool conj = true)
{
if (n <= 0)
return conj ? constant::true_instance() : constant::false_instance();
return conj ? formula::tt() : formula::ff();
const formula* result = 0;
formula result = nullptr;
multop::type op = conj ? multop::And : multop::Or;
op o = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
const formula* f = G_(F_(env.require(p.str())));
formula f = G_(F_(formula::ap(p.str())));
if (result)
result = multop::instance(op, f, result);
result = formula::multop(o, {f, result});
else
result = f;
}
@ -393,24 +386,24 @@ GF_n(std::string name, int n, bool conj = true)
// FG(p_1) | FG(p_2) | ... | FG(p_n) if conj == false
// FG(p_1) & FG(p_2) & ... & FG(p_n) if conj == true
static const formula*
static formula
FG_n(std::string name, int n, bool conj = false)
{
if (n <= 0)
return conj ? constant::true_instance() : constant::false_instance();
return conj ? formula::tt() : formula::ff();
const formula* result = 0;
formula result = nullptr;
multop::type op = conj ? multop::And : multop::Or;
op o = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
const formula* f = F_(G_(env.require(p.str())));
formula f = F_(G_(formula::ap(p.str())));
if (result)
result = multop::instance(op, f, result);
result = formula::multop(o, {f, result});
else
result = f;
}
@ -419,93 +412,91 @@ FG_n(std::string name, int n, bool conj = false)
// (((p1 OP p2) OP p3)...OP pn) if right_assoc == false
// (p1 OP (p2 OP (p3 OP (... pn) if right_assoc == true
static const formula*
bin_n(std::string name, int n,
binop::type op, bool right_assoc = false)
static formula
bin_n(std::string name, int n, op o, bool right_assoc = false)
{
if (n <= 0)
n = 1;
const formula* result = 0;
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << (right_assoc ? (n + 1 - i) : i);
const formula* f = env.require(p.str());
formula f = formula::ap(p.str());
if (!result)
result = f;
else if (right_assoc)
result = binop::instance(op, f, result);
result = formula::binop(o, f, result);
else
result = binop::instance(op, result, f);
result = formula::binop(o, result, f);
}
return result;
}
// (GF(p1)|FG(p2))&(GF(p2)|FG(p3))&...&(GF(pn)|FG(p{n+1}))"
static const formula*
static formula
R_n(std::string name, int n)
{
if (n <= 0)
return constant::true_instance();
return formula::tt();
const formula* pi;
formula pi;
{
std::ostringstream p;
p << name << 1;
pi = env.require(p.str());
pi = formula::ap(p.str());
}
const formula* result = 0;
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
const formula* gf = G_(F_(pi));
formula gf = G_(F_(pi));
std::ostringstream p;
p << name << i + 1;
pi = env.require(p.str());
pi = formula::ap(p.str());
const formula* fg = F_(G_(pi->clone()));
formula fg = F_(G_(pi));
const formula* f = Or_(gf, fg);
formula f = Or_(gf, fg);
if (result)
result = And_(f, result);
else
result = f;
}
pi->destroy();
return result;
}
// (F(p1)|G(p2))&(F(p2)|G(p3))&...&(F(pn)|G(p{n+1}))"
static const formula*
static formula
Q_n(std::string name, int n)
{
if (n <= 0)
return constant::true_instance();
return formula::tt();
const formula* pi;
formula pi;
{
std::ostringstream p;
p << name << 1;
pi = env.require(p.str());
pi = formula::ap(p.str());
}
const formula* result = 0;
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
const formula* f = F_(pi);
formula f = F_(pi);
std::ostringstream p;
p << name << i + 1;
pi = env.require(p.str());
pi = formula::ap(p.str());
const formula* g = G_(pi->clone());
formula g = G_(pi);
f = Or_(f, g);
@ -514,31 +505,29 @@ Q_n(std::string name, int n)
else
result = f;
}
pi->destroy();
return result;
}
// OP(p1) | OP(p2) | ... | OP(Pn) if conj == false
// OP(p1) & OP(p2) & ... & OP(Pn) if conj == true
static const formula*
combunop_n(std::string name, int n,
unop::type op, bool conj = false)
static formula
combunop_n(std::string name, int n, op o, bool conj = false)
{
if (n <= 0)
return conj ? constant::true_instance() : constant::false_instance();
return conj ? formula::tt() : formula::ff();
const formula* result = 0;
formula result = nullptr;
multop::type cop = conj ? multop::And : multop::Or;
op cop = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
const formula* f = unop::instance(op, env.require(p.str()));
formula f = formula::unop(o, formula::ap(p.str()));
if (result)
result = multop::instance(cop, f, result);
result = formula::multop(cop, {f, result});
else
result = f;
}
@ -547,21 +536,21 @@ combunop_n(std::string name, int n,
// !((GF(p1)&GF(p2)&...&GF(pn))->G(q -> F(r)))
// From "Fast LTL to Büchi Automata Translation" [gastin.01.cav]
static const formula*
static formula
fair_response(std::string p, std::string q, std::string r, int n)
{
const formula* fair = GF_n(p, n);
const formula* resp = G_(Implies_(env.require(q), F_(env.require(r))));
formula fair = GF_n(p, n);
formula resp = G_(Implies_(formula::ap(q), F_(formula::ap(r))));
return Not_(Implies_(fair, resp));
}
// Builds X(X(...X(p))) with n occurrences of X.
static const formula*
X_n(const formula* p, int n)
static formula
X_n(formula p, int n)
{
assert(n >= 0);
const formula* res = p;
formula res = p;
while (n--)
res = X_(res);
return res;
@ -569,214 +558,174 @@ X_n(const formula* p, int n)
// Based on LTLcounter.pl from Kristin Rozier.
// http://shemesh.larc.nasa.gov/people/kyr/benchmarking_scripts/
static const formula*
static formula
ltl_counter(std::string bit, std::string marker, int n, bool linear)
{
const formula* b = env.require(bit);
const formula* neg_b = Not_(b);
const formula* m = env.require(marker);
const formula* neg_m = Not_(m); // to destroy
formula b = formula::ap(bit);
formula neg_b = Not_(b);
formula m = formula::ap(marker);
formula neg_m = Not_(m);
multop::vec* res = new multop::vec(4);
std::vector<formula> res(4);
// The marker starts with "1", followed by n-1 "0", then "1" again,
// n-1 "0", etc.
if (!linear)
{
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
multop::vec* v = new multop::vec(n);
std::vector<formula> v(n);
for (int i = 0; i + 1 < n; ++i)
(*v)[i] = X_n(neg_m->clone(), i + 1);
(*v)[n - 1] = X_n(m->clone(), n);
(*res)[0] = And_(m->clone(),
G_(Implies_(m->clone(),
multop::instance(multop::And, v))));
v[i] = X_n(neg_m, i + 1);
v[n - 1] = X_n(m, n);
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
}
else
{
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
const formula* p = m->clone();
formula p = m;
for (int i = n - 1; i > 0; --i)
p = And_(neg_m->clone(), X_(p));
(*res)[0] = And_(m->clone(),
G_(Implies_(m->clone(), X_(p))));
p = And_(neg_m, X_(p));
res[0] = And_(m, G_(Implies_(m, X_(p))));
}
// All bits are initially zero.
if (!linear)
{
// !b & X(!b) & XX(!b) [if n = 3]
multop::vec* v2 = new multop::vec(n);
std::vector<formula> v2(n);
for (int i = 0; i < n; ++i)
(*v2)[i] = X_n(neg_b->clone(), i);
(*res)[1] = multop::instance(multop::And, v2);
v2[i] = X_n(neg_b, i);
res[1] = formula::And(std::move(v2));
}
else
{
// !b & X(!b & X(!b)) [if n = 3]
const formula* p = neg_b->clone();
formula p = neg_b;
for (int i = n - 1; i > 0; --i)
p = And_(neg_b->clone(), X_(p));
(*res)[1] = p;
p = And_(neg_b, X_(p));
res[1] = p;
}
#define AndX_(x, y) (linear ? X_(And_((x), (y))) : And_(X_(x), X_(y)))
// If the least significant bit is 0, it will be 1 at the next time,
// and other bits stay the same.
const formula* Xnm1_b = X_n(b->clone(), n - 1);
const formula* Xn_b = X_(Xnm1_b); // to destroy
(*res)[2] =
G_(Implies_(And_(m->clone(), neg_b->clone()),
AndX_(Xnm1_b->clone(), U_(And_(Not_(m->clone()),
Equiv_(b->clone(),
Xn_b->clone())),
m->clone()))));
formula Xnm1_b = X_n(b, n - 1);
formula Xn_b = X_(Xnm1_b);
res[2] = G_(Implies_(And_(m, neg_b),
AndX_(Xnm1_b, U_(And_(Not_(m), Equiv_(b, Xn_b)), m))));
// From the least significant bit to the first 0, all the bits
// are flipped on the next value. Remaining bits are identical.
const formula* Xnm1_negb = X_n(neg_b, n - 1);
const formula* Xn_negb = X_(Xnm1_negb); // to destroy
(*res)[3] =
G_(Implies_(And_(m->clone(), b->clone()),
AndX_(Xnm1_negb->clone(),
U_(And_(And_(b->clone(), neg_m->clone()),
Xn_negb->clone()),
Or_(m->clone(),
And_(And_(neg_m->clone(),
neg_b->clone()),
AndX_(Xnm1_b->clone(),
U_(And_(neg_m->clone(),
Equiv_(b->clone(),
Xn_b->clone())),
m->clone()))))))));
neg_m->destroy();
Xn_b->destroy();
Xn_negb->destroy();
return multop::instance(multop::And, res);
formula Xnm1_negb = X_n(neg_b, n - 1);
formula Xn_negb = X_(Xnm1_negb);
res[3] = G_(Implies_(And_(m, b),
AndX_(Xnm1_negb,
U_(And_(And_(b, neg_m), Xn_negb),
Or_(m, And_(And_(neg_m, neg_b),
AndX_(Xnm1_b,
U_(And_(neg_m,
Equiv_(b, Xn_b)),
m))))))));
return formula::And(std::move(res));
}
static const formula*
static formula
ltl_counter_carry(std::string bit, std::string marker,
std::string carry, int n, bool linear)
{
const formula* b = env.require(bit);
const formula* neg_b = Not_(b);
const formula* m = env.require(marker);
const formula* neg_m = Not_(m); // to destroy
const formula* c = env.require(carry);
const formula* neg_c = Not_(c); // to destroy
formula b = formula::ap(bit);
formula neg_b = Not_(b);
formula m = formula::ap(marker);
formula neg_m = Not_(m);
formula c = formula::ap(carry);
formula neg_c = Not_(c);
multop::vec* res = new multop::vec(6);
std::vector<formula> res(6);
// The marker starts with "1", followed by n-1 "0", then "1" again,
// n-1 "0", etc.
if (!linear)
{
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
multop::vec* v = new multop::vec(n);
std::vector<formula> v(n);
for (int i = 0; i + 1 < n; ++i)
(*v)[i] = X_n(neg_m->clone(), i + 1);
(*v)[n - 1] = X_n(m->clone(), n);
(*res)[0] = And_(m->clone(),
G_(Implies_(m->clone(),
multop::instance(multop::And, v))));
v[i] = X_n(neg_m, i + 1);
v[n - 1] = X_n(m, n);
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
}
else
{
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
const formula* p = m->clone();
formula p = m;
for (int i = n - 1; i > 0; --i)
p = And_(neg_m->clone(), X_(p));
(*res)[0] = And_(m->clone(),
G_(Implies_(m->clone(), X_(p))));
p = And_(neg_m, X_(p));
res[0] = And_(m, G_(Implies_(m, X_(p))));
}
// All bits are initially zero.
if (!linear)
{
// !b & X(!b) & XX(!b) [if n = 3]
multop::vec* v2 = new multop::vec(n);
std::vector<formula> v2(n);
for (int i = 0; i < n; ++i)
(*v2)[i] = X_n(neg_b->clone(), i);
(*res)[1] = multop::instance(multop::And, v2);
v2[i] = X_n(neg_b, i);
res[1] = formula::And(std::move(v2));
}
else
{
// !b & X(!b & X(!b)) [if n = 3]
const formula* p = neg_b->clone();
formula p = neg_b;
for (int i = n - 1; i > 0; --i)
p = And_(neg_b->clone(), X_(p));
(*res)[1] = p;
p = And_(neg_b, X_(p));
res[1] = p;
}
const formula* Xn_b = X_n(b->clone(), n); // to destroy
const formula* Xn_negb = X_n(neg_b, n); // to destroy
formula Xn_b = X_n(b, n);
formula Xn_negb = X_n(neg_b, n);
// If m is 1 and b is 0 then c is 0 and n steps later b is 1.
(*res)[2] = G_(Implies_(And_(m->clone(), neg_b->clone()),
And_(neg_c->clone(), Xn_b->clone())));
res[2] = G_(Implies_(And_(m, neg_b), And_(neg_c, Xn_b)));
// If m is 1 and b is 1 then c is 1 and n steps later b is 0.
(*res)[3] = G_(Implies_(And_(m->clone(), b->clone()),
And_(c->clone(), Xn_negb->clone())));
res[3] = G_(Implies_(And_(m, b), And_(c, Xn_negb)));
if (!linear)
{
// If there's no carry, then all of the bits stay the same n steps later.
(*res)[4] = G_(Implies_(And_(neg_c->clone(), X_(neg_m->clone())),
And_(X_(Not_(c->clone())),
Equiv_(X_(b->clone()),
X_(Xn_b->clone())))));
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
And_(X_(Not_(c)), Equiv_(X_(b), X_(Xn_b)))));
// If there's a carry, then add one: flip the bits of b and
// adjust the carry.
(*res)[5] = G_(Implies_(c->clone(),
And_(Implies_(X_(neg_b->clone()),
And_(X_(neg_c->clone()),
X_(Xn_b->clone()))),
Implies_(X_(b->clone()),
And_(X_(c->clone()),
X_(Xn_negb->clone()))))));
res[5] = G_(Implies_(c, And_(Implies_(X_(neg_b),
And_(X_(neg_c), X_(Xn_b))),
Implies_(X_(b),
And_(X_(c), X_(Xn_negb))))));
}
else
{
// If there's no carry, then all of the bits stay the same n steps later.
(*res)[4] = G_(Implies_(And_(neg_c->clone(), X_(neg_m->clone())),
X_(And_(Not_(c->clone()),
Equiv_(b->clone(),
Xn_b->clone())))));
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
X_(And_(Not_(c), Equiv_(b, Xn_b)))));
// If there's a carry, then add one: flip the bits of b and
// adjust the carry.
(*res)[5] = G_(Implies_(c->clone(),
X_(And_(Implies_(neg_b->clone(),
And_(neg_c->clone(),
Xn_b->clone())),
Implies_(b->clone(),
And_(c->clone(),
Xn_negb->clone()))))));
res[5] = G_(Implies_(c, X_(And_(Implies_(neg_b, And_(neg_c, Xn_b)),
Implies_(b, And_(c, Xn_negb))))));
}
neg_m->destroy();
neg_c->destroy();
Xn_b->destroy();
Xn_negb->destroy();
return multop::instance(multop::And, res);
return formula::And(std::move(res));
}
static void
output_pattern(int pattern, int n)
{
const formula* f = 0;
formula f = nullptr;
switch (pattern)
{
// Keep this alphabetically-ordered!
case OPT_AND_F:
f = combunop_n("p", n, unop::F, true);
f = combunop_n("p", n, op::F, true);
break;
case OPT_AND_FG:
f = FG_n("p", n, true);
@ -785,13 +734,13 @@ output_pattern(int pattern, int n)
f = GF_n("p", n, true);
break;
case OPT_CCJ_ALPHA:
f = multop::instance(multop::And, E_n("p", n), E_n("q", n));
f = formula::And({E_n("p", n), E_n("q", n)});
break;
case OPT_CCJ_BETA:
f = multop::instance(multop::And, N_n("p", n), N_n("q", n));
f = formula::And({N_n("p", n), N_n("q", n)});
break;
case OPT_CCJ_BETA_PRIME:
f = multop::instance(multop::And, N_prime_n("p", n), N_prime_n("q", n));
f = formula::And({N_prime_n("p", n), N_prime_n("q", n)});
break;
case OPT_GH_Q:
f = Q_n("p", n);
@ -806,16 +755,16 @@ output_pattern(int pattern, int n)
f = FG_n("p", n, false);
break;
case OPT_OR_G:
f = combunop_n("p", n, unop::G, false);
f = combunop_n("p", n, op::G, false);
break;
case OPT_OR_GF:
f = GF_n("p", n, false);
break;
case OPT_R_LEFT:
f = bin_n("p", n, binop::R, false);
f = bin_n("p", n, op::R, false);
break;
case OPT_R_RIGHT:
f = bin_n("p", n, binop::R, true);
f = bin_n("p", n, op::R, true);
break;
case OPT_RV_COUNTER_CARRY:
f = ltl_counter_carry("b", "m", "c", n, false);
@ -830,10 +779,10 @@ output_pattern(int pattern, int n)
f = ltl_counter("b", "m", n, true);
break;
case OPT_U_LEFT:
f = bin_n("p", n, binop::U, false);
f = bin_n("p", n, op::U, false);
break;
case OPT_U_RIGHT:
f = bin_n("p", n, binop::U, true);
f = bin_n("p", n, op::U, true);
break;
default:
error(100, 0, "internal error: pattern not implemented");
@ -841,15 +790,10 @@ output_pattern(int pattern, int n)
// Make sure we use only "p42"-style of atomic propositions
// in lbt's output.
if (output_format == lbt_output && !f->has_lbt_atomic_props())
{
const spot::ltl::formula* r = spot::ltl::relabel(f, spot::ltl::Pnn);
f->destroy();
f = r;
}
if (output_format == lbt_output && !f.has_lbt_atomic_props())
f = relabel(f, Pnn);
output_formula_checked(f, class_name[pattern - 1], n);
f->destroy();
}
static void

View file

@ -138,14 +138,14 @@ namespace
}
int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0)
{
// This should not happen, because the parser we use can only
// read PSL/LTL formula, but since our ltl::formula* type can
// read PSL/LTL formula, but since our ltl::formula type can
// represent more than PSL formula, let's make this
// future-proof.
if (!f->is_psl_formula())
if (!f.is_psl_formula())
{
std::string s = spot::ltl::str_psl(f);
error_at_line(2, 0, filename, linenum,
@ -159,7 +159,6 @@ namespace
const double translation_time = sw.stop();
printer.print(aut, f, filename, linenum, translation_time, nullptr);
f->destroy();
return 0;
}
};

View file

@ -170,16 +170,16 @@ namespace
}
int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0)
{
auto aut = trans.run(&f);
// This should not happen, because the parser we use can only
// read PSL/LTL formula, but since our ltl::formula* type can
// read PSL/LTL formula, but since our ltl::formula type can
// represent more than PSL formula, let's make this
// future-proof.
if (!f->is_psl_formula())
if (!f.is_psl_formula())
{
std::string s = spot::ltl::str_psl(f);
error_at_line(2, 0, filename, linenum,
@ -207,7 +207,6 @@ namespace
tgta = spot::minimize_tgta(tgta);
spot::print_dot(std::cout, tgta->get_ta());
}
f->destroy();
flush_cout();
return 0;
}

View file

@ -39,7 +39,6 @@
#include "common_file.hh"
#include "common_finput.hh"
#include "parseaut/public.hh"
#include "ltlast/unop.hh"
#include "ltlvisit/print.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/mutation.hh"
@ -819,8 +818,7 @@ namespace
}
typedef
std::unordered_set<const spot::ltl::formula*,
const spot::ptr_hash<const spot::ltl::formula> > fset_t;
std::unordered_set<spot::ltl::formula> fset_t;
class processor: public job_processor
@ -834,32 +832,22 @@ namespace
{
}
~processor()
{
fset_t::iterator i = unique_set.begin();
while (i != unique_set.end())
(*i++)->destroy();
}
int
process_string(const std::string& input,
const char* filename,
int linenum)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
spot::ltl::formula f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::ltl::format_parse_errors(std::cerr, input, pel);
if (f)
f->destroy();
return 1;
}
f->clone();
int res = process_formula(f, filename, linenum);
if (res && bogus_output)
@ -867,7 +855,7 @@ namespace
if (res && grind_output)
{
std::string bogus = input;
std::vector<const spot::ltl::formula*> mutations;
std::vector<spot::ltl::formula> mutations;
unsigned mutation_count;
unsigned mutation_max;
while (res)
@ -888,15 +876,12 @@ namespace
{
std::cerr << "Mutation " << mutation_count << '/'
<< mutation_max << ": ";
f->destroy();
f = g->clone();
res = process_formula(g->clone());
f = g;
res = process_formula(g);
if (res)
break;
++mutation_count;
}
for (auto g: mutations)
g->destroy();
if (res)
{
if (lbt_input)
@ -922,8 +907,6 @@ namespace
std::cerr << ".\n\n";
grind_output->ostream() << bogus << std::endl;
}
f->destroy();
return 0;
}
@ -954,20 +937,16 @@ namespace
}
int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0)
{
static unsigned round = 0;
// If we need LBT atomic proposition in any of the input or
// output, relabel the formula.
if (!f->has_lbt_atomic_props() &&
if (!f.has_lbt_atomic_props() &&
(runner.has('l') || runner.has('L') || runner.has('T')))
{
const spot::ltl::formula* g = spot::ltl::relabel(f, spot::ltl::Pnn);
f->destroy();
f = g;
}
f = spot::ltl::relabel(f, spot::ltl::Pnn);
// ---------- Positive Formula ----------
@ -991,18 +970,13 @@ namespace
// Make sure we do not translate the same formula twice.
if (!allow_dups)
{
if (unique_set.insert(f).second)
{
f->clone();
}
else
if (!unique_set.insert(f).second)
{
std::cerr
<< ("warning: This formula or its negation has already"
" been checked.\n Use --allow-dups if it "
"should not be ignored.\n")
<< std::endl;
f->destroy();
return 0;
}
}
@ -1053,12 +1027,11 @@ namespace
nstats = &vstats[n + 1];
nstats->resize(m);
const spot::ltl::formula* nf =
spot::ltl::unop::instance(spot::ltl::unop::Not, f->clone());
spot::ltl::formula nf = spot::ltl::formula::Not(f);
if (!allow_dups)
{
bool res = unique_set.insert(nf->clone()).second;
bool res = unique_set.insert(nf).second;
// It is not possible to discover that nf has already been
// translated, otherwise that would mean that f had been
// translated too and we would have caught it before.
@ -1084,7 +1057,6 @@ namespace
|| (!want_stats && is_deterministic(neg[n]))))
comp_neg[n] = dtgba_complement(neg[n]);
}
nf->destroy();
}
spot::cleanup_tmpfiles();
@ -1171,7 +1143,6 @@ namespace
}
spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f);
f->destroy();
if (want_stats)
for (size_t i = 0; i < m; ++i)

View file

@ -238,15 +238,13 @@ namespace
int linenum)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
spot::ltl::formula f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::ltl::format_parse_errors(std::cerr, input, pel);
if (f)
f->destroy();
return 1;
}
@ -257,22 +255,20 @@ namespace
int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0)
{
std::unique_ptr<spot::ltl::relabeling_map> relmap;
// If atomic propositions are incompatible with one of the
// output, relabel the formula.
if ((!f->has_lbt_atomic_props() &&
if ((!f.has_lbt_atomic_props() &&
(runner.has('l') || runner.has('L') || runner.has('T')))
|| (!f->has_spin_atomic_props() &&
|| (!f.has_spin_atomic_props() &&
(runner.has('s') || runner.has('S'))))
{
relmap.reset(new spot::ltl::relabeling_map);
auto g = spot::ltl::relabel(f, spot::ltl::Pnn, relmap.get());
f->destroy();
f = g;
f = spot::ltl::relabel(f, spot::ltl::Pnn, relmap.get());
}
static unsigned round = 1;
@ -297,7 +293,6 @@ namespace
nullptr);
};
}
f->destroy();
spot::cleanup_tmpfiles();
++round;
return 0;

View file

@ -43,8 +43,6 @@
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/exclusive.hh"
#include "ltlvisit/print.hh"
#include "ltlast/unop.hh"
#include "ltlast/multop.hh"
#include "twaalgos/ltl2tgba_fm.hh"
#include "twaalgos/minimize.hh"
#include "twaalgos/safety.hh"
@ -261,15 +259,15 @@ static spot::exclusive_ap excl_ap;
static std::unique_ptr<output_file> output_define = nullptr;
static std::string unabbreviate;
static const spot::ltl::formula* implied_by = 0;
static const spot::ltl::formula* imply = 0;
static const spot::ltl::formula* equivalent_to = 0;
static spot::ltl::formula implied_by = nullptr;
static spot::ltl::formula imply = nullptr;
static spot::ltl::formula equivalent_to = nullptr;
static const spot::ltl::formula*
static spot::ltl::formula
parse_formula_arg(const std::string& input)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
spot::ltl::formula f = parse_formula(input, pel);
if (spot::ltl::format_parse_errors(std::cerr, input, pel))
error(2, 0, "parse error when parsing an argument");
return f;
@ -342,18 +340,16 @@ parse_opt(int key, char* arg, struct argp_state*)
break;
case OPT_IMPLIED_BY:
{
const spot::ltl::formula* i = parse_formula_arg(arg);
spot::ltl::formula i = parse_formula_arg(arg);
// a→c∧b→c ≡ (ab)→c
implied_by =
spot::ltl::multop::instance(spot::ltl::multop::Or, implied_by, i);
implied_by = spot::ltl::formula::Or({implied_by, i});
break;
}
case OPT_IMPLY:
{
// a→b∧a→c ≡ a→(b∧c)
const spot::ltl::formula* i = parse_formula_arg(arg);
imply =
spot::ltl::multop::instance(spot::ltl::multop::And, imply, i);
spot::ltl::formula i = parse_formula_arg(arg);
imply = spot::ltl::formula::And({imply, i});
break;
}
case OPT_LTL:
@ -439,8 +435,7 @@ parse_opt(int key, char* arg, struct argp_state*)
}
typedef
std::unordered_set<const spot::ltl::formula*,
const spot::ptr_hash<const spot::ltl::formula>> fset_t;
std::unordered_set<spot::ltl::formula> fset_t;
namespace
{
@ -451,22 +446,8 @@ namespace
fset_t unique_set;
spot::ltl::relabeling_map relmap;
~ltl_processor()
{
fset_t::iterator i = unique_set.begin();
while (i != unique_set.end())
(*i++)->destroy();
if (equivalent_to)
equivalent_to->destroy();
if (implied_by)
implied_by->destroy();
if (imply)
imply->destroy();
}
ltl_processor(spot::ltl::ltl_simplifier& simpl)
: simpl(simpl)
: simpl(simpl)
{
}
@ -475,7 +456,7 @@ namespace
const char* filename = 0, int linenum = 0)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
spot::ltl::formula f = parse_formula(input, pel);
if (!f || pel.size() > 0)
{
@ -486,9 +467,6 @@ namespace
spot::ltl::format_parse_errors(std::cerr, input, pel);
}
if (f)
f->destroy();
if (error_style == skip_errors)
std::cout << input << std::endl;
else
@ -508,64 +486,44 @@ namespace
}
int
process_formula(const spot::ltl::formula* f,
process_formula(spot::ltl::formula f,
const char* filename = 0, int linenum = 0)
{
if (opt_max_count >= 0 && match_count >= opt_max_count)
{
abort_run = true;
f->destroy();
return 0;
}
if (negate)
f = spot::ltl::unop::instance(spot::ltl::unop::Not, f);
f = spot::ltl::formula::Not(f);
if (remove_x)
{
// If simplification are enabled, we do them before and after.
if (simplification_level)
{
const spot::ltl::formula* res = simpl.simplify(f);
f->destroy();
f = res;
}
const spot::ltl::formula* res = spot::ltl::remove_x(f);
f->destroy();
f = res;
f = simpl.simplify(f);
f = spot::ltl::remove_x(f);
}
if (simplification_level || boolean_to_isop)
{
const spot::ltl::formula* res = simpl.simplify(f);
f->destroy();
f = res;
}
f = simpl.simplify(f);
if (nnf)
{
const spot::ltl::formula* res = simpl.negative_normal_form(f);
f->destroy();
f = res;
}
f = simpl.negative_normal_form(f);
switch (relabeling)
{
case ApRelabeling:
{
relmap.clear();
auto res = spot::ltl::relabel(f, style, &relmap);
f->destroy();
f = res;
f = spot::ltl::relabel(f, style, &relmap);
break;
}
case BseRelabeling:
{
relmap.clear();
auto res = spot::ltl::relabel_bse(f, style, &relmap);
f->destroy();
f = res;
f = spot::ltl::relabel_bse(f, style, &relmap);
break;
}
case NoRelabeling:
@ -573,32 +531,24 @@ namespace
}
if (!unabbreviate.empty())
{
auto res = spot::ltl::unabbreviate(f, unabbreviate.c_str());
f->destroy();
f = res;
}
f = spot::ltl::unabbreviate(f, unabbreviate.c_str());
if (!excl_ap.empty())
{
auto res = excl_ap.constrain(f);
f->destroy();
f = res;
}
f = excl_ap.constrain(f);
bool matched = true;
matched &= !ltl || f->is_ltl_formula();
matched &= !psl || f->is_psl_formula();
matched &= !boolean || f->is_boolean();
matched &= !universal || f->is_universal();
matched &= !eventual || f->is_eventual();
matched &= !syntactic_safety || f->is_syntactic_safety();
matched &= !syntactic_guarantee || f->is_syntactic_guarantee();
matched &= !syntactic_obligation || f->is_syntactic_obligation();
matched &= !syntactic_recurrence || f->is_syntactic_recurrence();
matched &= !syntactic_persistence || f->is_syntactic_persistence();
matched &= !syntactic_si || f->is_syntactic_stutter_invariant();
matched &= !ltl || f.is_ltl_formula();
matched &= !psl || f.is_psl_formula();
matched &= !boolean || f.is_boolean();
matched &= !universal || f.is_universal();
matched &= !eventual || f.is_eventual();
matched &= !syntactic_safety || f.is_syntactic_safety();
matched &= !syntactic_guarantee || f.is_syntactic_guarantee();
matched &= !syntactic_obligation || f.is_syntactic_obligation();
matched &= !syntactic_recurrence || f.is_syntactic_recurrence();
matched &= !syntactic_persistence || f.is_syntactic_persistence();
matched &= !syntactic_si || f.is_syntactic_stutter_invariant();
matched &= !ap || atomic_prop_collect(f)->size() == ap_n;
if (matched && (size_min > 0 || size_max >= 0))
@ -643,13 +593,8 @@ namespace
matched ^= invert;
if (unique)
{
if (unique_set.insert(f).second)
f->clone();
else
matched = false;
}
if (unique && !unique_set.insert(f).second)
matched = false;
if (matched)
{
@ -658,7 +603,7 @@ namespace
&& output_format != quiet_output)
{
// Sort the formulas alphabetically.
std::map<std::string, const spot::ltl::formula*> m;
std::map<std::string, spot::ltl::formula> m;
for (auto& p: relmap)
m.emplace(str_psl(p.first), p.second);
for (auto& p: m)
@ -670,7 +615,6 @@ namespace
output_formula_checked(f, filename, linenum, prefix, suffix);
++match_count;
}
f->destroy();
return 0;
}
};

View file

@ -27,10 +27,6 @@
#include "common_output.hh"
#include "common_conv.hh"
#include "ltlast/allnodes.hh"
#include "ltlvisit/clone.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/length.hh"
#include "ltlvisit/mutation.hh"
enum {
@ -100,17 +96,13 @@ namespace
{
public:
int
process_formula(const spot::ltl::formula* f, const char *filename = 0,
process_formula(spot::ltl::formula f, const char *filename = 0,
int linenum = 0)
{
auto mutations =
spot::ltl::mutate(f, mut_opts, max_output, mutation_nb, opt_sort);
f->destroy();
for (auto g: mutations)
{
output_formula_checked(g, filename, linenum);
g->destroy();
}
output_formula_checked(g, filename, linenum);
return 0;
}
};

View file

@ -34,7 +34,6 @@
#include "common_aoutput.hh"
#include "common_conv.hh"
#include "ltlenv/defaultenv.hh"
#include "misc/timer.hh"
#include "misc/random.hh"
@ -252,7 +251,7 @@ parse_opt(int key, char* arg, struct argp_state* as)
aprops = spot::ltl::create_atomic_prop_set(ap_count_given.min);
break;
}
aprops.insert(spot::ltl::default_environment::instance().require(arg));
aprops.insert(spot::ltl::formula::ap(arg));
break;
default:
@ -328,7 +327,6 @@ main(int argc, char** argv)
if (ap_count_given.max > 0
&& ap_count_given.min != ap_count_given.max)
{
spot::ltl::destroy_atomic_prop_set(aprops);
int c = spot::rrand(ap_count_given.min, ap_count_given.max);
aprops = spot::ltl::create_atomic_prop_set(c);
}
@ -391,5 +389,4 @@ main(int argc, char** argv)
{
error(2, 0, "%s", e.what());
}
spot::ltl::destroy_atomic_prop_set(aprops);
}

View file

@ -33,14 +33,9 @@
#include "common_conv.hh"
#include <sstream>
#include "ltlast/multop.hh"
#include "ltlast/unop.hh"
#include "ltlvisit/randomltl.hh"
#include "ltlvisit/length.hh"
#include "ltlvisit/simplify.hh"
#include "ltlenv/defaultenv.hh"
#include "misc/random.hh"
#include "misc/hash.hh"
#include "misc/optionmap.hh"
const char argp_program_doc[] ="\
@ -253,8 +248,8 @@ main(int argc, char** argv)
opts.set("output", output);
opts.set("tree_size_min", opt_tree_size.min);
opts.set("tree_size_max", opt_tree_size.max);
opts.set("opt_wf", opt_wf);
opts.set("opt_seed", opt_seed);
opts.set("wf", opt_wf);
opts.set("seed", opt_seed);
opts.set("simplification_level", simplification_level);
return opts;
}(), opt_pL, opt_pS, opt_pB);
@ -291,14 +286,13 @@ main(int argc, char** argv)
default:
error(2, 0, "internal error: unknown type of output");
}
destroy_atomic_prop_set(aprops);
exit(0);
}
while (opt_formulas < 0 || opt_formulas--)
{
static int count = 0;
const spot::ltl::formula* f = rg.next();
spot::ltl::formula f = rg.next();
if (!f)
{
error(2, 0, "failed to generate a new unique formula after %d " \
@ -307,21 +301,17 @@ main(int argc, char** argv)
else
{
output_formula_checked(f, 0, ++count);
f->destroy();
}
};
}
catch (const std::runtime_error& e)
{
destroy_atomic_prop_set(aprops);
error(2, 0, "%s", e.what());
}
catch (const std::invalid_argument& e)
{
destroy_atomic_prop_set(aprops);
error(2, 0, "%s", e.what());
}
destroy_atomic_prop_set(aprops);
return 0;
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Developpement de l'Epita (LRDE)
//
// This file is part of Spot, a model checking library.
@ -260,11 +260,9 @@ namespace spot
}
void kripke_explicit::add_condition(const ltl::formula* f,
std::string on_me)
void kripke_explicit::add_condition(ltl::formula f, std::string on_me)
{
add_conditions(formula_to_bdd(f, get_dict(), this), on_me);
f->destroy();
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et
// Développement de l'Epita (LRDE)
// Copyright (C) 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.
//
@ -151,8 +151,7 @@ namespace spot
///
/// \param f the formula to add.
/// \param on_me the state where to add.
void add_condition(const ltl::formula* f,
std::string on_me);
void add_condition(ltl::formula f, std::string on_me);
/// \brief Return map between states and their names.
const std::map<const state_kripke*, std::string>&

View file

@ -48,7 +48,6 @@ typedef std::map<std::string, bdd> formula_cache;
{
int token;
std::string* str;
const spot::ltl::formula* f;
std::list<std::string*>* list;
}
@ -110,8 +109,8 @@ strident "," condition "," follow_list ";"
if (i == fcache.end())
{
parse_error_list pel;
const formula* f = spot::ltl::parse_infix_boolean(*$3, pel,
parse_environment);
formula f = spot::ltl::parse_infix_boolean(*$3, pel,
parse_environment);
for (parse_error_list::iterator i = pel.begin();
i != pel.end(); ++i)
{

View file

@ -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

View file

@ -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"

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
};
}
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2012, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2009, 2012, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
@ -31,28 +31,22 @@ namespace spot
{
}
declarative_environment::~declarative_environment()
{
for (prop_map::iterator i = props_.begin(); i != props_.end(); ++i)
i->second->destroy();
}
bool
declarative_environment::declare(const std::string& prop_str)
{
if (props_.find(prop_str) != props_.end())
return false;
props_[prop_str] = ltl::atomic_prop::instance(prop_str, *this);
props_[prop_str] = formula::ap(prop_str);
return true;
}
const formula*
formula
declarative_environment::require(const std::string& prop_str)
{
prop_map::iterator i = props_.find(prop_str);
if (i == props_.end())
return 0;
return i->second->clone();
return nullptr;
return i->second;
}
const std::string&

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2012, 2013, 2014 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2009, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
@ -25,7 +25,7 @@
#include "environment.hh"
#include <string>
#include <map>
#include "ltlast/atomic_prop.hh"
#include "ltlast/formula.hh"
namespace spot
{
@ -41,18 +41,18 @@ namespace spot
{
public:
declarative_environment();
~declarative_environment();
~declarative_environment() = default;
/// Declare an atomic proposition. Return false iff the
/// proposition was already declared.
bool declare(const std::string& prop_str);
virtual const formula* require(const std::string& prop_str);
virtual formula require(const std::string& prop_str);
/// Get the name of the environment.
virtual const std::string& name() const;
typedef std::map<const std::string, const atomic_prop*> prop_map;
typedef std::map<const std::string, formula> prop_map;
/// Get the map of atomic proposition known to this environment.
const prop_map& get_prop_map() const;

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2012, 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.
@ -31,10 +31,10 @@ namespace spot
{
}
const atomic_prop*
formula
default_environment::require(const std::string& s)
{
return atomic_prop::instance(s, *this);
return formula::ap(s);
}
const std::string&

View file

@ -1,7 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement
// de l'Epita (LRDE).
// 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.
@ -24,7 +23,7 @@
#pragma once
#include "environment.hh"
#include "ltlast/atomic_prop.hh"
#include "ltlast/formula.hh"
namespace spot
{
@ -42,7 +41,7 @@ namespace spot
{
public:
virtual ~default_environment();
virtual const atomic_prop* require(const std::string& prop_str);
virtual formula require(const std::string& prop_str);
virtual const std::string& name() const;
/// Get the sole instance of spot::ltl::default_environment.

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2012, 2014 Laboratoire de Recherche et
// Copyright (C) 2008, 2012, 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),
@ -38,20 +38,14 @@ namespace spot
///
/// Usually \a prop_str, is the name of an atomic proposition,
/// and spot::ltl::require simply returns the associated
/// spot::ltl::atomic_prop.
/// spot::ltl::formula.
///
/// Note this is not a \c const method. Some environments will
/// "create" the atomic proposition when requested.
///
/// We return a spot::ltl::formula instead of an
/// spot::ltl::atomic_prop, because this
/// will allow nifty tricks (e.g., we could name formulae in an
/// environment, and let the parser build a larger tree from
/// these).
///
/// \return 0 iff \a prop_str is not part of the environment,
/// or the associated spot::ltl::formula otherwise.
virtual const formula* require(const std::string& prop_str) = 0;
virtual formula require(const std::string& prop_str) = 0;
/// Get the name of the environment.
virtual const std::string& name() const = 0;

View file

@ -35,7 +35,7 @@
#include <string>
#include <sstream>
#include "public.hh"
#include "ltlast/allnodes.hh"
#include "ltlast/formula.hh"
#include "ltlvisit/print.hh"
struct minmax_t { unsigned min, max; };
@ -43,11 +43,11 @@
%parse-param {spot::ltl::parse_error_list &error_list}
%parse-param {spot::ltl::environment &parse_environment}
%parse-param {const spot::ltl::formula* &result}
%parse-param {spot::ltl::formula &result}
%union
{
std::string* str;
const spot::ltl::formula* ltl;
const spot::ltl::fnode* ltl;
unsigned num;
minmax_t minmax;
}
@ -67,7 +67,7 @@ using namespace spot::ltl;
do \
{ \
missing_right_op_msg(op, str); \
res = constant::false_instance(); \
res = fnode::ff(); \
} \
while (0);
@ -91,7 +91,7 @@ using namespace spot::ltl;
enum parser_type { parser_ltl, parser_bool, parser_sere };
static const formula*
static formula
try_recursive_parse(const std::string& str,
const spot::location& location,
spot::ltl::environment& env,
@ -117,11 +117,11 @@ using namespace spot::ltl;
if (str.empty())
{
error_list.emplace_back(location, "unexpected empty block");
return 0;
return nullptr;
}
spot::ltl::parse_error_list suberror;
const spot::ltl::formula* f = 0;
formula f;
switch (type)
{
case parser_sere:
@ -138,9 +138,6 @@ using namespace spot::ltl;
if (suberror.empty())
return f;
if (f)
f->destroy();
f = env.require(str);
if (!f)
{
@ -240,72 +237,76 @@ using namespace spot::ltl;
%destructor { $$->destroy(); } <ltl>
%printer { debug_stream() << *$$; } <str>
%printer { spot::ltl::print_psl(debug_stream(), $$); } <ltl>
%printer { spot::ltl::print_sere(debug_stream(), $$); } sere bracedsere
%printer { spot::ltl::print_psl(debug_stream(), formula($$)); } <ltl>
%printer { spot::ltl::print_sere(debug_stream(), formula($$)); } sere bracedsere
%printer { debug_stream() << $$; } <num>
%printer { debug_stream() << $$.min << ".." << $$.max; } <minmax>
%%
result: START_LTL subformula END_OF_INPUT
{ result = $2;
{
result = formula($2);
YYACCEPT;
}
| START_LTL enderror
{
result = 0;
result = nullptr;
YYABORT;
}
| START_LTL subformula enderror
{
result = $2;
result = formula($2);
YYACCEPT;
}
| START_LTL emptyinput
{ YYABORT; }
| START_BOOL boolformula END_OF_INPUT
{ result = $2;
{
result = formula($2);
YYACCEPT;
}
| START_BOOL enderror
{
result = 0;
result = nullptr;
YYABORT;
}
| START_BOOL boolformula enderror
{
result = $2;
result = formula($2);
YYACCEPT;
}
| START_BOOL emptyinput
{ YYABORT; }
| START_SERE sere END_OF_INPUT
{ result = $2;
{
result = formula($2);
YYACCEPT;
}
| START_SERE enderror
{
result = 0;
result = nullptr;
YYABORT;
}
| START_SERE sere enderror
{
result = $2;
result = formula($2);
YYACCEPT;
}
| START_SERE emptyinput
{ YYABORT; }
| START_LBT lbtformula END_OF_INPUT
{ result = $2;
{
result = formula($2);
YYACCEPT;
}
| START_LBT enderror
{
result = 0;
result = nullptr;
YYABORT;
}
| START_LBT lbtformula enderror
{
result = $2;
result = formula($2);
YYACCEPT;
}
| START_LBT emptyinput
@ -314,7 +315,7 @@ result: START_LTL subformula END_OF_INPUT
emptyinput: END_OF_INPUT
{
error_list.emplace_back(@$, "empty input");
result = 0;
result = nullptr;
}
enderror: error END_OF_INPUT
@ -331,11 +332,11 @@ error_opt: | error
sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $1; $$.max = $3; }
| OP_SQBKT_NUM OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE
{ $$.min = $1; $$.max = bunop::unbounded; }
{ $$.min = $1; $$.max = fnode::unbounded(); }
| OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = 0U; $$.max = $2; }
| OP_SQBKT_SEP_opt OP_SQBKT_CLOSE
{ $$.min = 0U; $$.max = bunop::unbounded; }
{ $$.min = 0U; $$.max = fnode::unbounded(); }
| OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $$.max = $1; }
@ -343,11 +344,11 @@ sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
gotoargs: OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $2; $$.max = $4; }
| OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE
{ $$.min = $2; $$.max = bunop::unbounded; }
{ $$.min = $2; $$.max = fnode::unbounded(); }
| OP_GOTO_OPEN OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = 1U; $$.max = $3; }
| OP_GOTO_OPEN OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE
{ $$.min = 1U; $$.max = bunop::unbounded; }
{ $$.min = 1U; $$.max = fnode::unbounded(); }
| OP_GOTO_OPEN OP_SQBKT_CLOSE
{ $$.min = $$.max = 1U; }
| OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_CLOSE
@ -363,28 +364,28 @@ gotoargs: OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
kleen_star: OP_STAR | OP_BSTAR
starargs: kleen_star
{ $$.min = 0U; $$.max = bunop::unbounded; }
{ $$.min = 0U; $$.max = fnode::unbounded(); }
| OP_PLUS
{ $$.min = 1U; $$.max = bunop::unbounded; }
{ $$.min = 1U; $$.max = fnode::unbounded(); }
| OP_STAR_OPEN sqbracketargs
{ $$ = $2; }
| OP_STAR_OPEN error OP_SQBKT_CLOSE
{ error_list.emplace_back(@$, "treating this star block as [*]");
$$.min = 0U; $$.max = bunop::unbounded; }
$$.min = 0U; $$.max = fnode::unbounded(); }
| OP_STAR_OPEN error_opt END_OF_INPUT
{ error_list.emplace_back(@$, "missing closing bracket for star");
$$.min = $$.max = 0U; }
fstarargs: OP_BFSTAR
{ $$.min = 0U; $$.max = bunop::unbounded; }
{ $$.min = 0U; $$.max = fnode::unbounded(); }
| OP_FPLUS
{ $$.min = 1U; $$.max = bunop::unbounded; }
{ $$.min = 1U; $$.max = fnode::unbounded(); }
| OP_FSTAR_OPEN sqbracketargs
{ $$ = $2; }
| OP_FSTAR_OPEN error OP_SQBKT_CLOSE
{ error_list.emplace_back
(@$, "treating this fusion-star block as [:*]");
$$.min = 0U; $$.max = bunop::unbounded; }
$$.min = 0U; $$.max = fnode::unbounded(); }
| OP_FSTAR_OPEN error_opt END_OF_INPUT
{ error_list.emplace_back
(@$, "missing closing bracket for fusion-star");
@ -394,19 +395,16 @@ equalargs: OP_EQUAL_OPEN sqbracketargs
{ $$ = $2; }
| OP_EQUAL_OPEN error OP_SQBKT_CLOSE
{ error_list.emplace_back(@$, "treating this equal block as [*]");
$$.min = 0U; $$.max = bunop::unbounded; }
$$.min = 0U; $$.max = fnode::unbounded(); }
| OP_EQUAL_OPEN error_opt END_OF_INPUT
{ error_list.
emplace_back(@$, "missing closing bracket for equal operator");
$$.min = $$.max = 0U; }
/* The reason we use `constant::false_instance()' for error recovery
is that it isn't reference counted. (Hence it can't leak references.) */
booleanatom: ATOMIC_PROP
{
$$ = parse_environment.require(*$1);
$$ = parse_environment.require(*$1).to_node_();
if (! $$)
{
std::string s = "unknown atomic proposition `";
@ -423,7 +421,7 @@ booleanatom: ATOMIC_PROP
}
| ATOMIC_PROP OP_POST_POS
{
$$ = parse_environment.require(*$1);
$$ = parse_environment.require(*$1).to_node_();
if (! $$)
{
std::string s = "unknown atomic proposition `";
@ -440,7 +438,7 @@ booleanatom: ATOMIC_PROP
}
| ATOMIC_PROP OP_POST_NEG
{
$$ = parse_environment.require(*$1);
$$ = parse_environment.require(*$1).to_node_();
if (! $$)
{
std::string s = "unknown atomic proposition `";
@ -454,19 +452,19 @@ booleanatom: ATOMIC_PROP
}
else
delete $1;
$$ = unop::instance(unop::Not, $$);
$$ = fnode::unop(op::Not, $$);
}
| CONST_TRUE
{ $$ = constant::true_instance(); }
{ $$ = fnode::tt(); }
| CONST_FALSE
{ $$ = constant::false_instance(); }
{ $$ = fnode::ff(); }
sere: booleanatom
| OP_NOT sere
{
if ($2->is_boolean())
{
$$ = unop::instance(unop::Not, $2);
$$ = fnode::unop(op::Not, $2);
}
else
{
@ -475,14 +473,16 @@ sere: booleanatom
"be applied to a Boolean expression");
error_list.emplace_back(@$, "treating this block as false");
$2->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| bracedsere
| PAR_BLOCK
{
$$ = try_recursive_parse(*$1, @1, parse_environment,
debug_level(), parser_sere, error_list);
$$ =
try_recursive_parse(*$1, @1, parse_environment,
debug_level(), parser_sere, error_list)
.to_node_();
delete $1;
if (!$$)
YYERROR;
@ -493,7 +493,7 @@ sere: booleanatom
{ error_list.
emplace_back(@$,
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| PAR_OPEN sere END_OF_INPUT
{ error_list.emplace_back(@1 + @2, "missing closing parenthesis");
@ -503,28 +503,28 @@ sere: booleanatom
{ error_list.emplace_back(@$,
"missing closing parenthesis, "
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| sere OP_AND sere
{ $$ = multop::instance(multop::AndRat, $1, $3); }
{ $$ = fnode::multop(op::AndRat, {$1, $3}); }
| sere OP_AND error
{ missing_right_binop($$, $1, @2,
"length-matching and operator"); }
| sere OP_SHORT_AND sere
{ $$ = multop::instance(multop::AndNLM, $1, $3); }
{ $$ = fnode::multop(op::AndNLM, {$1, $3}); }
| sere OP_SHORT_AND error
{ missing_right_binop($$, $1, @2,
"non-length-matching and operator"); }
| sere OP_OR sere
{ $$ = multop::instance(multop::OrRat, $1, $3); }
{ $$ = fnode::multop(op::OrRat, {$1, $3}); }
| sere OP_OR error
{ missing_right_binop($$, $1, @2, "or operator"); }
| sere OP_CONCAT sere
{ $$ = multop::instance(multop::Concat, $1, $3); }
{ $$ = fnode::multop(op::Concat, {$1, $3}); }
| sere OP_CONCAT error
{ missing_right_binop($$, $1, @2, "concat operator"); }
| sere OP_FUSION sere
{ $$ = multop::instance(multop::Fusion, $1, $3); }
{ $$ = fnode::multop(op::Fusion, {$1, $3}); }
| sere OP_FUSION error
{ missing_right_binop($$, $1, @2, "fusion operator"); }
| starargs
@ -534,8 +534,7 @@ sere: booleanatom
error_list.emplace_back(@1, "reversed range");
std::swap($1.max, $1.min);
}
$$ = bunop::instance(bunop::Star, constant::true_instance(),
$1.min, $1.max);
$$ = fnode::bunop(op::Star, fnode::tt(), $1.min, $1.max);
}
| sere starargs
{
@ -544,7 +543,7 @@ sere: booleanatom
error_list.emplace_back(@2, "reversed range");
std::swap($2.max, $2.min);
}
$$ = bunop::instance(bunop::Star, $1, $2.min, $2.max);
$$ = fnode::bunop(op::Star, $1, $2.min, $2.max);
}
| sere fstarargs
{
@ -553,7 +552,7 @@ sere: booleanatom
error_list.emplace_back(@2, "reversed range");
std::swap($2.max, $2.min);
}
$$ = bunop::instance(bunop::FStar, $1, $2.min, $2.max);
$$ = fnode::bunop(op::FStar, $1, $2.min, $2.max);
}
| sere equalargs
{
@ -564,7 +563,8 @@ sere: booleanatom
}
if ($1->is_boolean())
{
$$ = bunop::sugar_equal($1, $2.min, $2.max);
$$ = formula::sugar_equal(formula($1),
$2.min, $2.max).to_node_();
}
else
{
@ -574,7 +574,7 @@ sere: booleanatom
error_list.emplace_back(@$,
"treating this block as false");
$1->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| sere gotoargs
@ -586,7 +586,8 @@ sere: booleanatom
}
if ($1->is_boolean())
{
$$ = bunop::sugar_goto($1, $2.min, $2.max);
$$ = formula::sugar_goto(formula($1),
$2.min, $2.max).to_node_();
}
else
{
@ -596,14 +597,14 @@ sere: booleanatom
error_list.emplace_back(@$,
"treating this block as false");
$1->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| sere OP_XOR sere
{
if ($1->is_boolean() && $3->is_boolean())
{
$$ = binop::instance(binop::Xor, $1, $3);
$$ = fnode::binop(op::Xor, $1, $3);
}
else
{
@ -622,7 +623,7 @@ sere: booleanatom
error_list.emplace_back(@$, "treating this block as false");
$1->destroy();
$3->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| sere OP_XOR error
@ -631,7 +632,7 @@ sere: booleanatom
{
if ($1->is_boolean())
{
$$ = binop::instance(binop::Implies, $1, $3);
$$ = fnode::binop(op::Implies, $1, $3);
}
else
{
@ -644,7 +645,7 @@ sere: booleanatom
error_list.emplace_back(@$, "treating this block as false");
$1->destroy();
$3->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| sere OP_IMPLIES error
@ -653,7 +654,7 @@ sere: booleanatom
{
if ($1->is_boolean() && $3->is_boolean())
{
$$ = binop::instance(binop::Equiv, $1, $3);
$$ = fnode::binop(op::Equiv, $1, $3);
}
else
{
@ -672,7 +673,7 @@ sere: booleanatom
error_list.emplace_back(@$, "treating this block as false");
$1->destroy();
$3->destroy();
$$ = constant::false_instance();
$$ = fnode::ff();
}
}
| sere OP_EQUIV error
@ -687,7 +688,7 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE
| BRACE_OPEN error BRACE_CLOSE
{ error_list.emplace_back(@$,
"treating this brace block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| BRACE_OPEN sere END_OF_INPUT
{ error_list.emplace_back(@1 + @2,
@ -703,13 +704,13 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE
{ error_list.emplace_back(@$,
"missing closing brace, "
"treating this brace block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| BRA_BLOCK
{
$$ = try_recursive_parse(*$1, @1, parse_environment,
debug_level(),
parser_sere, error_list);
parser_sere, error_list).to_node_();
delete $1;
if (!$$)
YYERROR;
@ -718,7 +719,8 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE
parenthesedsubformula: PAR_BLOCK
{
$$ = try_recursive_parse(*$1, @1, parse_environment,
debug_level(), parser_ltl, error_list);
debug_level(), parser_ltl, error_list)
.to_node_();
delete $1;
if (!$$)
YYERROR;
@ -732,7 +734,7 @@ parenthesedsubformula: PAR_BLOCK
| PAR_OPEN error PAR_CLOSE
{ error_list.emplace_back(@$,
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| PAR_OPEN subformula END_OF_INPUT
{ error_list.emplace_back(@1 + @2, "missing closing parenthesis");
@ -747,7 +749,7 @@ parenthesedsubformula: PAR_BLOCK
{ error_list.emplace_back(@$,
"missing closing parenthesis, "
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
@ -755,7 +757,8 @@ boolformula: booleanatom
| PAR_BLOCK
{
$$ = try_recursive_parse(*$1, @1, parse_environment,
debug_level(), parser_bool, error_list);
debug_level(), parser_bool, error_list)
.to_node_();
delete $1;
if (!$$)
YYERROR;
@ -769,7 +772,7 @@ boolformula: booleanatom
| PAR_OPEN error PAR_CLOSE
{ error_list.emplace_back(@$,
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| PAR_OPEN boolformula END_OF_INPUT
{ error_list.emplace_back(@1 + @2,
@ -785,156 +788,153 @@ boolformula: booleanatom
{ error_list.emplace_back(@$,
"missing closing parenthesis, "
"treating this parenthetical block as false");
$$ = constant::false_instance();
$$ = fnode::ff();
}
| boolformula OP_AND boolformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| boolformula OP_AND error
{ missing_right_binop($$, $1, @2, "and operator"); }
| boolformula OP_SHORT_AND boolformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| boolformula OP_SHORT_AND error
{ missing_right_binop($$, $1, @2, "and operator"); }
| boolformula OP_STAR boolformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| boolformula OP_STAR error
{ missing_right_binop($$, $1, @2, "and operator"); }
| boolformula OP_OR boolformula
{ $$ = multop::instance(multop::Or, $1, $3); }
{ $$ = fnode::multop(op::Or, {$1, $3}); }
| boolformula OP_OR error
{ missing_right_binop($$, $1, @2, "or operator"); }
| boolformula OP_XOR boolformula
{ $$ = binop::instance(binop::Xor, $1, $3); }
{ $$ = fnode::binop(op::Xor, $1, $3); }
| boolformula OP_XOR error
{ missing_right_binop($$, $1, @2, "xor operator"); }
| boolformula OP_IMPLIES boolformula
{ $$ = binop::instance(binop::Implies, $1, $3); }
{ $$ = fnode::binop(op::Implies, $1, $3); }
| boolformula OP_IMPLIES error
{ missing_right_binop($$, $1, @2, "implication operator"); }
| boolformula OP_EQUIV boolformula
{ $$ = binop::instance(binop::Equiv, $1, $3); }
{ $$ = fnode::binop(op::Equiv, $1, $3); }
| boolformula OP_EQUIV error
{ missing_right_binop($$, $1, @2, "equivalent operator"); }
| OP_NOT boolformula
{ $$ = unop::instance(unop::Not, $2); }
{ $$ = fnode::unop(op::Not, $2); }
| OP_NOT error
{ missing_right_op($$, @1, "not operator"); }
subformula: booleanatom
| parenthesedsubformula
| subformula OP_AND subformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| subformula OP_AND error
{ missing_right_binop($$, $1, @2, "and operator"); }
| subformula OP_SHORT_AND subformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| subformula OP_SHORT_AND error
{ missing_right_binop($$, $1, @2, "and operator"); }
| subformula OP_STAR subformula
{ $$ = multop::instance(multop::And, $1, $3); }
{ $$ = fnode::multop(op::And, {$1, $3}); }
| subformula OP_STAR error
{ missing_right_binop($$, $1, @2, "and operator"); }
| subformula OP_OR subformula
{ $$ = multop::instance(multop::Or, $1, $3); }
{ $$ = fnode::multop(op::Or, {$1, $3}); }
| subformula OP_OR error
{ missing_right_binop($$, $1, @2, "or operator"); }
| subformula OP_XOR subformula
{ $$ = binop::instance(binop::Xor, $1, $3); }
{ $$ = fnode::binop(op::Xor, $1, $3); }
| subformula OP_XOR error
{ missing_right_binop($$, $1, @2, "xor operator"); }
| subformula OP_IMPLIES subformula
{ $$ = binop::instance(binop::Implies, $1, $3); }
{ $$ = fnode::binop(op::Implies, $1, $3); }
| subformula OP_IMPLIES error
{ missing_right_binop($$, $1, @2, "implication operator"); }
| subformula OP_EQUIV subformula
{ $$ = binop::instance(binop::Equiv, $1, $3); }
{ $$ = fnode::binop(op::Equiv, $1, $3); }
| subformula OP_EQUIV error
{ missing_right_binop($$, $1, @2, "equivalent operator"); }
| subformula OP_U subformula
{ $$ = binop::instance(binop::U, $1, $3); }
{ $$ = fnode::binop(op::U, $1, $3); }
| subformula OP_U error
{ missing_right_binop($$, $1, @2, "until operator"); }
| subformula OP_R subformula
{ $$ = binop::instance(binop::R, $1, $3); }
{ $$ = fnode::binop(op::R, $1, $3); }
| subformula OP_R error
{ missing_right_binop($$, $1, @2, "release operator"); }
| subformula OP_W subformula
{ $$ = binop::instance(binop::W, $1, $3); }
{ $$ = fnode::binop(op::W, $1, $3); }
| subformula OP_W error
{ missing_right_binop($$, $1, @2, "weak until operator"); }
| subformula OP_M subformula
{ $$ = binop::instance(binop::M, $1, $3); }
{ $$ = fnode::binop(op::M, $1, $3); }
| subformula OP_M error
{ missing_right_binop($$, $1, @2, "strong release operator"); }
| OP_F subformula
{ $$ = unop::instance(unop::F, $2); }
{ $$ = fnode::unop(op::F, $2); }
| OP_F error
{ missing_right_op($$, @1, "sometimes operator"); }
| OP_G subformula
{ $$ = unop::instance(unop::G, $2); }
{ $$ = fnode::unop(op::G, $2); }
| OP_G error
{ missing_right_op($$, @1, "always operator"); }
| OP_X subformula
{ $$ = unop::instance(unop::X, $2); }
{ $$ = fnode::unop(op::X, $2); }
| OP_X error
{ missing_right_op($$, @1, "next operator"); }
| OP_NOT subformula
{ $$ = unop::instance(unop::Not, $2); }
{ $$ = fnode::unop(op::Not, $2); }
| OP_NOT error
{ missing_right_op($$, @1, "not operator"); }
| bracedsere
{ $$ = unop::instance(unop::Closure, $1); }
{ $$ = fnode::unop(op::Closure, $1); }
| bracedsere OP_UCONCAT subformula
{ $$ = binop::instance(binop::UConcat, $1, $3); }
{ $$ = fnode::binop(op::UConcat, $1, $3); }
| bracedsere parenthesedsubformula
{ $$ = binop::instance(binop::UConcat, $1, $2); }
{ $$ = fnode::binop(op::UConcat, $1, $2); }
| bracedsere OP_UCONCAT error
{ missing_right_binop_hard($$, $1, @2,
"universal overlapping concat operator"); }
| bracedsere OP_ECONCAT subformula
{ $$ = binop::instance(binop::EConcat, $1, $3); }
{ $$ = fnode::binop(op::EConcat, $1, $3); }
| bracedsere OP_ECONCAT error
{ missing_right_binop_hard($$, $1, @2,
"existential overlapping concat operator");
}
| bracedsere OP_UCONCAT_NONO subformula
/* {SERE}[]=>EXP = {SERE;1}[]->EXP */
{ $$ = binop::instance(binop::UConcat,
multop::instance(multop::Concat, $1,
constant::true_instance()), $3);
}
{ $$ = fnode::binop(op::UConcat,
fnode::multop(op::Concat, {$1, fnode::tt()}),
$3); }
| bracedsere OP_UCONCAT_NONO error
{ missing_right_binop_hard($$, $1, @2,
"universal non-overlapping concat operator");
}
| bracedsere OP_ECONCAT_NONO subformula
/* {SERE}<>=>EXP = {SERE;1}<>->EXP */
{ $$ = binop::instance(binop::EConcat,
multop::instance(multop::Concat, $1,
constant::true_instance()), $3);
}
{ $$ = fnode::binop(op::EConcat,
fnode::multop(op::Concat, {$1, fnode::tt()}),
$3); }
| bracedsere OP_ECONCAT_NONO error
{ missing_right_binop_hard($$, $1, @2,
"existential non-overlapping concat operator");
}
| BRACE_OPEN sere BRACE_BANG_CLOSE
/* {SERE}! = {SERE} <>-> 1 */
{ $$ = binop::instance(binop::EConcat, $2,
constant::true_instance()); }
{ $$ = fnode::binop(op::EConcat, $2, fnode::tt()); }
| BRA_BANG_BLOCK
{
$$ = try_recursive_parse(*$1, @1, parse_environment,
debug_level(), parser_sere, error_list);
debug_level(), parser_sere, error_list)
.to_node_();
delete $1;
if (!$$)
YYERROR;
$$ = binop::instance(binop::EConcat, $$,
constant::true_instance());
$$ = fnode::binop(op::EConcat, $$, fnode::tt());
}
lbtformula: ATOMIC_PROP
{
$$ = parse_environment.require(*$1);
$$ = parse_environment.require(*$1).to_node_();
if (! $$)
{
std::string s = "atomic proposition `";
@ -950,37 +950,37 @@ lbtformula: ATOMIC_PROP
delete $1;
}
| '!' lbtformula
{ $$ = unop::instance(unop::Not, $2); }
{ $$ = fnode::unop(op::Not, $2); }
| '&' lbtformula lbtformula
{ $$ = multop::instance(multop::And, $2, $3); }
{ $$ = fnode::multop(op::And, {$2, $3}); }
| '|' lbtformula lbtformula
{ $$ = multop::instance(multop::Or, $2, $3); }
{ $$ = fnode::multop(op::Or, {$2, $3}); }
| '^' lbtformula lbtformula
{ $$ = binop::instance(binop::Xor, $2, $3); }
{ $$ = fnode::binop(op::Xor, $2, $3); }
| 'i' lbtformula lbtformula
{ $$ = binop::instance(binop::Implies, $2, $3); }
{ $$ = fnode::binop(op::Implies, $2, $3); }
| 'e' lbtformula lbtformula
{ $$ = binop::instance(binop::Equiv, $2, $3); }
{ $$ = fnode::binop(op::Equiv, $2, $3); }
| 'X' lbtformula
{ $$ = unop::instance(unop::X, $2); }
{ $$ = fnode::unop(op::X, $2); }
| 'F' lbtformula
{ $$ = unop::instance(unop::F, $2); }
{ $$ = fnode::unop(op::F, $2); }
| 'G' lbtformula
{ $$ = unop::instance(unop::G, $2); }
{ $$ = fnode::unop(op::G, $2); }
| 'U' lbtformula lbtformula
{ $$ = binop::instance(binop::U, $2, $3); }
{ $$ = fnode::binop(op::U, $2, $3); }
| 'V' lbtformula lbtformula
{ $$ = binop::instance(binop::R, $2, $3); }
{ $$ = fnode::binop(op::R, $2, $3); }
| 'R' lbtformula lbtformula
{ $$ = binop::instance(binop::R, $2, $3); }
{ $$ = fnode::binop(op::R, $2, $3); }
| 'W' lbtformula lbtformula
{ $$ = binop::instance(binop::W, $2, $3); }
{ $$ = fnode::binop(op::W, $2, $3); }
| 'M' lbtformula lbtformula
{ $$ = binop::instance(binop::M, $2, $3); }
{ $$ = fnode::binop(op::M, $2, $3); }
| 't'
{ $$ = constant::true_instance(); }
{ $$ = fnode::tt(); }
| 'f'
{ $$ = constant::false_instance(); }
{ $$ = fnode::ff(); }
;
%%
@ -995,13 +995,13 @@ namespace spot
{
namespace ltl
{
const formula*
formula
parse_infix_psl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env,
bool debug, bool lenient)
{
const formula* result = 0;
formula result = nullptr;
flex_set_buffer(ltl_string,
ltlyy::parser::token::START_LTL,
lenient);
@ -1012,13 +1012,13 @@ namespace spot
return result;
}
const formula*
formula
parse_infix_boolean(const std::string& ltl_string,
parse_error_list& error_list,
environment& env,
bool debug, bool lenient)
{
const formula* result = 0;
formula result = nullptr;
flex_set_buffer(ltl_string,
ltlyy::parser::token::START_BOOL,
lenient);
@ -1029,13 +1029,13 @@ namespace spot
return result;
}
const formula*
formula
parse_prefix_ltl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env,
bool debug)
{
const formula* result = 0;
formula result = nullptr;
flex_set_buffer(ltl_string,
ltlyy::parser::token::START_LBT,
false);
@ -1046,14 +1046,14 @@ namespace spot
return result;
}
const formula*
formula
parse_infix_sere(const std::string& sere_string,
parse_error_list& error_list,
environment& env,
bool debug,
bool lenient)
{
const formula* result = 0;
formula result = nullptr;
flex_set_buffer(sere_string,
ltlyy::parser::token::START_SERE,
lenient);
@ -1064,16 +1064,16 @@ namespace spot
return result;
}
const formula*
formula
parse_formula(const std::string& ltl_string, environment& env)
{
parse_error_list pel;
const formula* f = parse_infix_psl(ltl_string, pel, env);
formula f = parse_infix_psl(ltl_string, pel, env);
std::ostringstream s;
if (format_parse_errors(s, ltl_string, pel))
{
parse_error_list pel2;
const formula* g = parse_prefix_ltl(ltl_string, pel2, env);
formula g = parse_prefix_ltl(ltl_string, pel2, env);
if (pel2.empty())
return g;
else

View file

@ -56,8 +56,8 @@ namespace spot
/// \param lenient When true, parenthesized blocks that cannot be
/// parsed as subformulas will be considered as
/// atomic propositions.
/// \return A pointer to the formula built from \a ltl_string, or
/// 0 if the input was unparsable.
/// \return A formula built from \a ltl_string, or
/// formula(nullptr) if the input was unparsable.
///
/// Note that the parser usually tries to recover from errors. It can
/// return a non zero value even if it encountered error during the
@ -66,12 +66,12 @@ namespace spot
///
/// \warning This function is not reentrant.
SPOT_API
const formula* parse_infix_psl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
formula parse_infix_psl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
/// \brief Build a Boolean formula from a string.
/// \param ltl_string The string to parse.
@ -82,8 +82,8 @@ namespace spot
/// \param lenient When true, parenthesized blocks that cannot be
/// parsed as subformulas will be considered as
/// atomic propositions.
/// \return A pointer to the formula built from \a ltl_string, or
/// 0 if the input was unparsable.
/// \return A formula built from \a ltl_string, or
/// formula(nullptr) if the input was unparsable.
///
/// Note that the parser usually tries to recover from errors. It can
/// return a non zero value even if it encountered error during the
@ -92,12 +92,12 @@ namespace spot
///
/// \warning This function is not reentrant.
SPOT_API
const formula* parse_infix_boolean(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
formula parse_infix_boolean(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
/// \brief Build a formula from an LTL string in LBT's format.
/// \param ltl_string The string to parse.
@ -105,8 +105,8 @@ namespace spot
/// parse errors that occured during parsing.
/// \param env The environment into which parsing should take place.
/// \param debug When true, causes the parser to trace its execution.
/// \return A pointer to the formula built from \a ltl_string, or
/// 0 if the input was unparsable.
/// \return A formula built from \a ltl_string, or
/// formula(nullptr) if the input was unparsable.
///
/// Note that the parser usually tries to recover from errors. It can
/// return an non zero value even if it encountered error during the
@ -119,11 +119,11 @@ namespace spot
///
/// \warning This function is not reentrant.
SPOT_API
const formula* parse_prefix_ltl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false);
formula parse_prefix_ltl(const std::string& ltl_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false);
/// \brief A simple wrapper to parse_infix_psl() and parse_prefix_ltl().
///
@ -131,7 +131,7 @@ namespace spot
/// parse_infix_psl(); if this fails it tries parse_prefix_ltl();
/// and if both fails it returns the errors of the first call to
/// parse_infix_psl() as a parse_error exception.
SPOT_API const formula*
SPOT_API formula
parse_formula(const std::string& ltl_string,
environment& env = default_environment::instance());
@ -144,8 +144,8 @@ namespace spot
/// \param lenient When true, parenthesized blocks that cannot be
/// parsed as subformulas will be considered as
/// atomic propositions.
/// \return A pointer to the formula built from \a sere_string, or
/// 0 if the input was unparsable.
/// \return A formula built from \a sere_string, or
/// formula(0) if the input was unparsable.
///
/// Note that the parser usually tries to recover from errors. It can
/// return an non zero value even if it encountered error during the
@ -154,12 +154,12 @@ namespace spot
///
/// \warning This function is not reentrant.
SPOT_API
const formula* parse_infix_sere(const std::string& sere_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
formula parse_infix_sere(const std::string& sere_string,
parse_error_list& error_list,
environment& env =
default_environment::instance(),
bool debug = false,
bool lenient = false);
/// \brief Format diagnostics produced by spot::ltl::parse
/// or spot::ltl::ratexp
@ -181,11 +181,10 @@ namespace spot
/// \brief Fix location of diagnostics assuming the input is utf8.
///
/// The spot::ltl::parse() and spot::ltl::parse_sere() function
/// return a parse_error_list that contain locations specified at
/// the byte level. Although these parser recognize some
/// utf8 characters they only work byte by byte and will report
/// positions by counting byte.
/// The different parser functions return a parse_error_list that
/// contain locations specified at the byte level. Although these
/// parser recognize some utf8 characters they only work byte by
/// byte and will report positions by counting byte.
///
/// This function fixes the positions returned by the parser to
/// look correct when the string is interpreted as a utf8-encoded

View file

@ -28,15 +28,12 @@ ltlvisitdir = $(pkgincludedir)/ltlvisit
ltlvisit_HEADERS = \
apcollect.hh \
contain.hh \
clone.hh \
dot.hh \
dump.hh \
exclusive.hh \
length.hh \
mutation.hh \
nenoform.hh \
print.hh \
postfix.hh \
randomltl.hh \
relabel.hh \
remove_x.hh \
@ -49,9 +46,7 @@ noinst_LTLIBRARIES = libltlvisit.la
libltlvisit_la_SOURCES = \
apcollect.cc \
contain.cc \
clone.cc \
dot.cc \
dump.cc \
exclusive.cc \
length.cc \
mark.cc \
@ -59,7 +54,6 @@ libltlvisit_la_SOURCES = \
mutation.cc \
nenoform.cc \
print.cc \
postfix.cc \
randomltl.cc \
relabel.cc \
remove_x.cc \

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement
// Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -21,7 +21,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "apcollect.hh"
#include "ltlvisit/postfix.hh"
#include "twa/twa.hh"
#include "twa/bdddict.hh"
@ -29,70 +28,40 @@ namespace spot
{
namespace ltl
{
namespace
{
class atomic_prop_collector : public spot::ltl::postfix_visitor
{
public:
atomic_prop_collector(atomic_prop_set* s)
: postfix_visitor(), sap(s)
{
}
virtual ~atomic_prop_collector()
{
}
virtual void doit(const spot::ltl::atomic_prop* ap)
{
sap->insert(ap);
}
private:
atomic_prop_set* sap;
};
}
atomic_prop_set create_atomic_prop_set(unsigned n)
{
atomic_prop_set res;
auto& e = spot::ltl::default_environment::instance();
for (unsigned i = 0; i < n; ++i)
{
std::ostringstream p;
p << 'p' << i;
res.insert(e.require(p.str()));
res.insert(formula::ap(p.str()));
}
return res;
}
void destroy_atomic_prop_set(atomic_prop_set& aprops)
{
atomic_prop_set::const_iterator i = aprops.begin();
while (i != aprops.end())
(*(i++))->destroy();
}
atomic_prop_set*
atomic_prop_collect(const formula* f, atomic_prop_set* s)
atomic_prop_collect(formula f, atomic_prop_set* s)
{
if (!s)
s = new atomic_prop_set;
atomic_prop_collector v(s);
f->accept(v);
f.traverse([&](const formula& f)
{
if (f.is(op::AP))
s->insert(f);
return false;
});
return s;
}
bdd
atomic_prop_collect_as_bdd(const formula* f, const twa_ptr& a)
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a)
{
spot::ltl::atomic_prop_set aps;
atomic_prop_collect(f, &aps);
bdd res = bddtrue;
for (atomic_prop_set::const_iterator i = aps.begin();
i != aps.end(); ++i)
res &= bdd_ithvar(a->register_ap(*i));
for (auto f: aps)
res &= bdd_ithvar(a->register_ap(f));
return res;
}
}

View file

@ -22,7 +22,7 @@
#pragma once
#include "ltlast/atomic_prop.hh"
#include "ltlast/formula.hh"
#include <set>
#include <bddx.h>
#include "twa/fwd.hh"
@ -35,18 +35,12 @@ namespace spot
/// @{
/// Set of atomic propositions.
typedef std::set<const atomic_prop*,
formula_ptr_less_than> atomic_prop_set;
typedef std::set<formula> atomic_prop_set;
/// \brief construct an atomic_prop_set with n propositions
SPOT_API
atomic_prop_set create_atomic_prop_set(unsigned n);
/// \brief Destroy all the atomic propositions in an
/// atomic_prop_set.
SPOT_API void
destroy_atomic_prop_set(atomic_prop_set& aprops);
/// \brief Return the set of atomic propositions occurring in a formula.
///
/// \param f the formula to inspect
@ -55,10 +49,8 @@ namespace spot
/// \return A pointer to the supplied set, \c s, augmented with
/// atomic propositions occurring in \c f; or a newly allocated
/// set containing all these atomic propositions if \c s is 0.
/// The atomic propositions inserted into \a s are not cloned, so
/// they are only valid as long as \a f is.
SPOT_API atomic_prop_set*
atomic_prop_collect(const formula* f, atomic_prop_set* s = 0);
atomic_prop_collect(formula f, atomic_prop_set* s = nullptr);
/// \brief Return the set of atomic propositions occurring in a
/// formula, as a BDD.
@ -67,8 +59,7 @@ namespace spot
/// \param a that automaton that should register the BDD variables used.
/// \return A conjunction the atomic propositions.
SPOT_API bdd
atomic_prop_collect_as_bdd(const formula* f,
const twa_ptr& a);
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a);
/// @}
}

View file

@ -1,95 +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 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 "ltlast/allnodes.hh"
#include "clone.hh"
namespace spot
{
namespace ltl
{
clone_visitor::clone_visitor()
{
}
clone_visitor::~clone_visitor()
{
}
const formula*
clone_visitor::result() const
{
return result_;
}
void
clone_visitor::visit(const atomic_prop* ap)
{
result_ = ap->clone();
}
void
clone_visitor::visit(const constant* c)
{
result_ = c->clone();
}
void
clone_visitor::visit(const bunop* bo)
{
result_ = bunop::instance(bo->op(), recurse(bo->child()),
bo->min(), bo->max());
}
void
clone_visitor::visit(const unop* uo)
{
result_ = unop::instance(uo->op(), recurse(uo->child()));
}
void
clone_visitor::visit(const binop* bo)
{
const formula* first = recurse(bo->first());
result_ = binop::instance(bo->op(),
first, recurse(bo->second()));
}
void
clone_visitor::visit(const multop* mo)
{
multop::vec* res = new multop::vec;
unsigned mos = mo->size();
res->reserve(mos);
for (unsigned i = 0; i < mos; ++i)
res->push_back(recurse(mo->nth(i)));
result_ = multop::instance(mo->op(), res);
}
const formula*
clone_visitor::recurse(const formula* f)
{
f->accept(*this);
return result_;
}
}
}

View file

@ -1,60 +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),
// 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/>.
#pragma once
#include "ltlast/formula.hh"
#include "ltlast/visitor.hh"
namespace spot
{
namespace ltl
{
/// \ingroup ltl_visitor
/// \brief Clone a formula.
///
/// This visitor is public, because it's convenient
/// to derive from it and override part of its methods.
/// But if you just want the functionality, consider using
/// spot::ltl::formula::clone instead, it is way faster.
class SPOT_API clone_visitor : public visitor
{
public:
clone_visitor();
virtual ~clone_visitor();
const formula* result() const;
void visit(const atomic_prop* ap);
void visit(const unop* uo);
void visit(const binop* bo);
void visit(const multop* mo);
void visit(const constant* c);
void visit(const bunop* c);
virtual const formula* recurse(const formula* f);
protected:
const formula* result_;
};
}
}

View file

@ -22,10 +22,7 @@
#include "contain.hh"
#include "simplify.hh"
#include "ltlast/unop.hh"
#include "ltlast/binop.hh"
#include "ltlast/multop.hh"
#include "ltlast/constant.hh"
#include "ltlast/formula.hh"
#include "twaalgos/product.hh"
namespace spot
@ -50,13 +47,7 @@ namespace spot
void
language_containment_checker::clear()
{
while (!translated_.empty())
{
trans_map::iterator i = translated_.begin();
const formula* f = i->first;
translated_.erase(i);
f->destroy();
}
translated_.clear();
}
bool
@ -75,31 +66,26 @@ namespace spot
// Check whether L(l) is a subset of L(g).
bool
language_containment_checker::contained(const formula* l,
const formula* g)
language_containment_checker::contained(formula l,
formula g)
{
if (l == g)
return true;
record_* rl = register_formula_(l);
const formula* ng = unop::instance(unop::Not, g->clone());
record_* rng = register_formula_(ng);
ng->destroy();
record_* rng = register_formula_(formula::Not(g));
return incompatible_(rl, rng);
}
// Check whether L(!l) is a subset of L(g).
bool
language_containment_checker::neg_contained(const formula* l,
const formula* g)
language_containment_checker::neg_contained(formula l,
formula g)
{
if (l == g)
return false;
const formula* nl = unop::instance(unop::Not, l->clone());
formula nl = formula::Not(l);
record_* rnl = register_formula_(nl);
const formula* ng = unop::instance(unop::Not, g->clone());
record_* rng = register_formula_(ng);
nl->destroy();
ng->destroy();
record_* rng = register_formula_(formula::Not(g));
if (nl == g)
return true;
return incompatible_(rnl, rng);
@ -107,8 +93,8 @@ namespace spot
// Check whether L(l) is a subset of L(!g).
bool
language_containment_checker::contained_neg(const formula* l,
const formula* g)
language_containment_checker::contained_neg(formula l,
formula g)
{
if (l == g)
return false;
@ -119,13 +105,13 @@ namespace spot
// Check whether L(l) = L(g).
bool
language_containment_checker::equal(const formula* l, const formula* g)
language_containment_checker::equal(formula l, formula g)
{
return contained(l, g) && contained(g, l);
}
language_containment_checker::record_*
language_containment_checker::register_formula_(const formula* f)
language_containment_checker::register_formula_(formula f)
{
trans_map::iterator i = translated_.find(f);
if (i != translated_.end())
@ -133,17 +119,17 @@ namespace spot
auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_,
branching_postponement_, fair_loop_approx_);
record_& r = translated_[f->clone()];
record_& r = translated_[f];
r.translation = e;
return &r;
}
const formula*
reduce_tau03(const formula* f, bool stronger)
formula
reduce_tau03(formula f, bool stronger)
{
if (!f->is_psl_formula())
return f->clone();
if (!f.is_psl_formula())
return f;
ltl_simplifier_options opt(false, false, false,
true, stronger);

View file

@ -40,7 +40,7 @@ namespace spot
typedef std::map<const record_*, bool> incomp_map;
incomp_map incompatible;
};
typedef std::unordered_map<const formula*, record_> trans_map;
typedef std::unordered_map<formula, record_> trans_map;
public:
/// This class uses spot::ltl_to_tgba_fm to translate LTL
/// formulae. See that function for the meaning of these options.
@ -55,19 +55,19 @@ namespace spot
void clear();
/// Check whether L(l) is a subset of L(g).
bool contained(const formula* l, const formula* g);
bool contained(formula l, formula g);
/// Check whether L(!l) is a subset of L(g).
bool neg_contained(const formula* l, const formula* g);
bool neg_contained(formula l, formula g);
/// Check whether L(l) is a subset of L(!g).
bool contained_neg(const formula* l, const formula* g);
bool contained_neg(formula l, formula g);
/// Check whether L(l) = L(g).
bool equal(const formula* l, const formula* g);
bool equal(formula l, formula g);
protected:
bool incompatible_(record_* l, record_* g);
record_* register_formula_(const formula* f);
record_* register_formula_(formula f);
/* Translation options */
bdd_dict_ptr dict_;

View file

@ -20,10 +20,9 @@
// 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 "misc/hash.hh"
#include "dot.hh"
#include "ltlast/visitor.hh"
#include "ltlast/allnodes.hh"
#include "ltlast/formula.hh"
#include <unordered_map>
#include <ostream>
#include <sstream>
@ -33,160 +32,106 @@ namespace spot
{
namespace
{
class dotty_visitor: public visitor
struct dot_printer final
{
public:
typedef std::unordered_map<const formula*, int, ptr_hash<formula>> map;
dotty_visitor(std::ostream& os, map& m)
: os_(os), father_(-1), node_(m), sinks_(new std::ostringstream)
{
}
std::ostream& os_;
std::unordered_map<formula, int> node_;
std::ostringstream* sinks_;
virtual
~dotty_visitor()
{
}
dot_printer(std::ostream& os, formula f)
: os_(os), sinks_(new std::ostringstream)
{
os_ << "digraph G {\n";
rec(f);
os_ << " subgraph atoms {\n rank=sink;\n"
<< sinks_->str() << " }\n}\n";
}
void
visit(const atomic_prop* ap)
~dot_printer()
{
draw_node_(ap, ap->name(), true);
}
void
visit(const constant* c)
{
draw_node_(c, c->val_name(), true);
}
void
visit(const bunop* so)
{
if (draw_node_(so, so->format()))
{
childnum = 0;
so->child()->accept(*this);
}
}
void
visit(const binop* bo)
{
if (draw_node_(bo, bo->op_name()))
{
childnum = -1;
dotty_visitor v(*this);
bo->first()->accept(v);
childnum = -2;
bo->second()->accept(*this);
}
}
void
visit(const unop* uo)
{
if (draw_node_(uo, uo->op_name()))
{
childnum = 0;
uo->child()->accept(*this);
}
}
void
visit(const multop* mo)
{
if (!draw_node_(mo, mo->op_name()))
return;
childnum = 0;
unsigned max = mo->size();
multop::type op = mo->op();
bool update_childnum = (op == multop::Fusion ||
op == multop::Concat);
for (unsigned n = 0; n < max; ++n)
{
if (update_childnum)
++childnum;
dotty_visitor v(*this);
mo->nth(n)->accept(v);
}
}
void finish()
{
os_ << (" subgraph atoms {\n"
" rank=sink;\n")
<< sinks_->str() << " }\n";
delete sinks_;
}
int childnum;
private:
std::ostream& os_;
int father_;
map& node_;
std::ostringstream* sinks_;
bool
draw_node_(const formula* f, const std::string& str, bool sink = false)
int rec(formula f)
{
map::iterator i = node_.find(f);
int node;
bool node_exists = false;
if (i != node_.end())
{
node = i->second;
node_exists = true;
}
auto i = node_.emplace(f, node_.size());
int src = i.first->second;
if (!i.second)
return src;
op o = f.kind();
std::string str = (o == op::AP) ? f.ap_name() : f.kindstr();
if (o == op::AP || f.is_constant())
*sinks_ << " " << src << " [label=\""
<< str << "\", shape=box];\n";
else
os_ << " " << src << " [label=\"" << str << "\"];\n";
int childnum = 0;
switch (o)
{
node = node_.size();
node_[f] = node;
case op::False:
case op::True:
case op::EmptyWord:
case op::AP:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
childnum = 0; // No number for children
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
childnum = -2; // L and R markers
break;
case op::Concat:
case op::Fusion:
childnum = 1; // Numbered children
break;
}
// the link
if (father_ >= 0)
for (auto c: f)
{
os_ << " " << father_ << " -> " << node;
os_ << " " << src << " -> " << rec(c);
if (childnum > 0)
os_ << " [taillabel=\"" << childnum << "\"]";
if (childnum == -1)
if (childnum == -2)
os_ << " [taillabel=\"L\"]";
else if (childnum == -2)
else if (childnum == -1)
os_ << " [taillabel=\"R\"]";
os_ << ";\n";
++childnum;
}
father_ = node;
// the node
if (node_exists)
return false;
if (!sink)
{
os_ << " " << node << " [label=\"" << str << "\"];";
}
else
{
*sinks_ << " " << node
<< " [label=\"" << str << "\", shape=box];\n";
}
return true;
return src;
}
};
}
std::ostream&
print_dot_psl(std::ostream& os, const formula* f)
print_dot_psl(std::ostream& os, formula f)
{
dotty_visitor::map m;
dotty_visitor v(os, m);
os << "digraph G {\n";
f->accept(v);
v.finish();
os << '}' << std::endl;
dot_printer p(os, f);
return os;
}
}
}

View file

@ -23,7 +23,6 @@
#pragma once
#include <ltlast/formula.hh>
#include <iosfwd>
namespace spot
{
@ -37,6 +36,6 @@ namespace spot
/// \c dot is part of the GraphViz package
/// http://www.graphviz.org/
SPOT_API
std::ostream& print_dot_psl(std::ostream& os, const formula* f);
std::ostream& print_dot_psl(std::ostream& os, formula f);
}
}

View file

@ -1,40 +0,0 @@
// -*- coding: utf-8 -*-
// Copyright (C) 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 "dump.hh"
#include "ltlast/visitor.hh"
#include "ltlast/allnodes.hh"
#include <ostream>
namespace spot
{
namespace ltl
{
std::ostream&
dump(std::ostream& os, const formula* f)
{
os << f->dump();
return os;
}
}
}

View file

@ -1,41 +0,0 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013 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/>.
#pragma once
#include "ltlast/formula.hh"
#include <iosfwd>
namespace spot
{
namespace ltl
{
/// \ingroup ltl_io
/// \brief Dump a formula tree.
/// \param os The stream where it should be output.
/// \param f The formula to dump.
///
/// This is useful to display a formula when debugging.
SPOT_API
std::ostream& dump(std::ostream& os, const formula* f);
}
}

View file

@ -18,10 +18,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "exclusive.hh"
#include "ltlenv/defaultenv.hh"
#include "ltlast/multop.hh"
#include "ltlast/unop.hh"
#include "ltlast/constant.hh"
#include "twaalgos/mask.hh"
#include "misc/casts.hh"
#include "misc/minato.hh"
@ -29,20 +25,12 @@
namespace spot
{
exclusive_ap::~exclusive_ap()
{
for (auto& g: groups)
for (auto ap: g)
ap->destroy();
}
namespace
{
static const std::vector<const spot::ltl::atomic_prop*>
static const std::vector<ltl::formula>
split_aps(const char* arg)
{
auto& env = spot::ltl::default_environment::instance();
std::vector<const spot::ltl::atomic_prop*> group;
std::vector<ltl::formula> group;
auto start = arg;
while (*start)
{
@ -73,8 +61,7 @@ namespace spot
throw std::invalid_argument(s);
}
std::string ap(start, end - start);
auto* t = env.require(ap);
group.push_back(down_cast<const spot::ltl::atomic_prop*>(t));
group.emplace_back(ltl::formula::ap(ap));
do
++end;
while (*end == ' ' || *end == '\t');
@ -99,8 +86,7 @@ namespace spot
while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t'))
--rend;
std::string ap(start, rend - start);
auto* t = env.require(ap);
group.push_back(down_cast<const spot::ltl::atomic_prop*>(t));
group.emplace_back(ltl::formula::ap(ap));
if (*end == ',')
start = end + 1;
else
@ -116,29 +102,27 @@ namespace spot
add_group(split_aps(ap_csv));
}
void exclusive_ap::add_group(std::vector<const ltl::atomic_prop*> ap)
void exclusive_ap::add_group(std::vector<ltl::formula> ap)
{
groups.push_back(ap);
}
namespace
{
const ltl::formula*
nand(const ltl::formula* lhs, const ltl::formula* rhs)
ltl::formula
nand(ltl::formula lhs, ltl::formula rhs)
{
auto f = ltl::multop::instance(ltl::multop::And,
lhs->clone(), rhs->clone());
return ltl::unop::instance(ltl::unop::Not, f);
return ltl::formula::Not(ltl::formula::And({lhs, rhs}));
}
}
const ltl::formula*
exclusive_ap::constrain(const ltl::formula* f) const
ltl::formula
exclusive_ap::constrain(ltl::formula f) const
{
spot::ltl::atomic_prop_set* s = atomic_prop_collect(f);
auto* s = atomic_prop_collect(f);
std::vector<const ltl::atomic_prop*> group;
ltl::multop::vec* v = new ltl::multop::vec;
std::vector<ltl::formula> group;
std::vector<ltl::formula> v;
for (auto& g: groups)
{
@ -151,14 +135,11 @@ namespace spot
unsigned s = group.size();
for (unsigned j = 0; j < s; ++j)
for (unsigned k = j + 1; k < s; ++k)
v->push_back(nand(group[j], group[k]));
v.push_back(nand(group[j], group[k]));
};
delete s;
auto* c = ltl::unop::instance(ltl::unop::G,
ltl::multop::instance(ltl::multop::And, v));
return ltl::multop::instance(ltl::multop::And, f->clone(), c);
return ltl::formula::And({f, ltl::formula::G(ltl::formula::And(v))});
}
twa_graph_ptr exclusive_ap::constrain(const_twa_graph_ptr aut,

View file

@ -20,19 +20,17 @@
#pragma once
#include <vector>
#include "ltlast/atomic_prop.hh"
#include "ltlast/formula.hh"
#include "twa/twagraph.hh"
namespace spot
{
class SPOT_API exclusive_ap
class SPOT_API exclusive_ap final
{
std::vector<std::vector<const ltl::atomic_prop*>> groups;
std::vector<std::vector<ltl::formula>> groups;
public:
~exclusive_ap();
#ifndef SWIG
void add_group(std::vector<const ltl::atomic_prop*> ap);
void add_group(std::vector<ltl::formula> ap);
#endif
void add_group(const char* ap_csv);
@ -41,7 +39,7 @@ namespace spot
return groups.empty();
}
const ltl::formula* constrain(const ltl::formula* f) const;
ltl::formula constrain(ltl::formula f) const;
twa_graph_ptr constrain(const_twa_graph_ptr aut,
bool simplify_guards = false) const;
};

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2012, 2014 Laboratoire de Recherche et
// Copyright (C) 2010, 2012, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -21,111 +21,59 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "length.hh"
#include "ltlvisit/postfix.hh"
#include "ltlast/multop.hh"
#include "ltlast/unop.hh"
#include "ltlast/formula.hh"
namespace spot
{
namespace ltl
{
namespace
int
length(formula f)
{
class length_visitor: public postfix_visitor
{
public:
length_visitor()
: result_(0)
{
}
int
result() const
{
return result_;
}
virtual void
visit(const multop* mo)
{
unsigned s = mo->size();
for (unsigned i = 0; i < s; ++i)
mo->nth(i)->accept(*this);
// "a & b & c" should count for 5, even though it is
// stored as And(a,b,c).
result_ += s - 1;
}
virtual void
doit_default(const formula*)
{
++result_;
}
protected:
int result_; // size of the formula
};
class length_boolone_visitor: public length_visitor
{
virtual void
visit(const unop* uo)
{
++result_;
// Boolean formulas have length one.
if (!uo->is_boolean())
uo->child()->accept(*this);
}
virtual void
visit(const multop* mo)
{
// Boolean formulas have length one.
if (mo->is_boolean())
{
++result_;
return;
}
unsigned s = mo->size();
unsigned bool_seen = 0;
for (unsigned i = 0; i < s; ++i)
{
const formula* f = mo->nth(i);
// Ignore all Boolean children. We only want to count them once.
if (f->is_boolean())
++bool_seen;
else
f->accept(*this);
}
// "a & b & c" should count for 5, even though it is stored
// as And(a,b,c). So add the number of operators here (it
// is either s - 1 or s - bool_seen depending on whether
// Boolean children were encountered). If Boolean were
// seen, also add one (!!bool_seen) to account for the
// "global" Boolean term.
result_ += s - !bool_seen - bool_seen + !!bool_seen;
}
};
int len = 0;
f.traverse([&len](const formula& x)
{
auto s = x.size();
if (s > 1)
len += s - 1;
else
++len;
return false;
});
return len;
}
int
length(const formula* f)
length_boolone(formula f)
{
length_visitor v;
f->accept(v);
return v.result();
}
int
length_boolone(const formula* f)
{
length_boolone_visitor v;
f->accept(v);
return v.result();
int len = 0;
f.traverse([&len](const formula& x)
{
if (x.is_boolean())
{
++len;
return true;
}
auto s = x.size();
if (s > 2)
{
int b = 0;
for (const auto& y: x)
if (y.is_boolean())
++b;
len += s - b * 2 + 1;
}
else if (s > 1)
{
len += s - 1;
}
else
{
++len;
}
return false;
});
return len;
}
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Developement de
// l'Epita (LRDE).
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
@ -33,15 +33,14 @@ namespace spot
///
/// The length of a formula is the number of atomic propositions,
/// constants, and operators (logical and temporal) occurring in
/// the formula. spot::ltl::multop instances with n arguments
/// count for n-1; for instance <code>a | b | c</code> has length
/// 5, even if there is only as single <code>|</code> node
/// internally.
/// the formula. n-ary operators count for n-1; for instance
/// <code>a | b | c</code> has length 5, even if there is only as
/// single <code>|</code> node internally.
///
/// If squash_boolean is set, all Boolean formulae are assumed
/// to have length one.
SPOT_API
int length(const formula* f);
int length(formula f);
/// \ingroup ltl_misc
/// \brief Compute the length of a formula, squashing Boolean formulae
@ -49,6 +48,6 @@ namespace spot
/// This is similar to spot::ltl::length(), except all Boolean
/// formulae are assumed to have length one.
SPOT_API
int length_boolone(const formula* f);
int length_boolone(formula f);
}
}

View file

@ -18,344 +18,176 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mark.hh"
#include "ltlast/allnodes.hh"
#include <cassert>
#include <algorithm>
#include <set>
#include <vector>
#include "misc/casts.hh"
namespace spot
{
namespace ltl
{
namespace
{
class simplify_mark_visitor : public visitor
{
const formula* result_;
mark_tools* tools_;
public:
simplify_mark_visitor(mark_tools* t)
: tools_(t)
{
}
~simplify_mark_visitor()
{
}
const formula*
result()
{
return result_;
}
void
visit(const atomic_prop* ao)
{
result_ = ao->clone();
}
void
visit(const constant* c)
{
result_ = c->clone();
}
void
visit(const bunop* bo)
{
result_ = bo->clone();
}
void
visit(const unop* uo)
{
result_ = uo->clone();
}
void
visit(const multop* mo)
{
unsigned mos = mo->size();
multop::vec* res = new multop::vec;
switch (mo->op())
{
case multop::OrRat:
case multop::AndNLM:
case multop::AndRat:
case multop::Concat:
case multop::Fusion:
SPOT_UNIMPLEMENTED();
case multop::Or:
for (unsigned i = 0; i < mos; ++i)
res->push_back(recurse(mo->nth(i)));
break;
case multop::And:
{
typedef std::set<std::pair<const formula*,
const formula*> > pset;
pset empairs;
typedef std::set<const formula*> sset;
sset nmset;
typedef std::vector<const binop*> unbinop;
unbinop elist;
typedef std::vector<const unop*> ununop;
ununop nlist;
for (unsigned i = 0; i < mos; ++i)
{
const formula* f = mo->nth(i);
if (const binop* bo = is_binop(f))
{
switch (bo->op())
{
case binop::EConcatMarked:
empairs.emplace(bo->first(), bo->second());
// fall through
case binop::Xor:
case binop::Implies:
case binop::Equiv:
case binop::U:
case binop::W:
case binop::M:
case binop::R:
case binop::UConcat:
res->push_back(recurse(f));
break;
case binop::EConcat:
elist.push_back(bo);
break;
}
}
if (const unop* uo = is_unop(f))
{
switch (uo->op())
{
case unop::NegClosureMarked:
nmset.insert(uo->child());
// fall through
case unop::Not:
case unop::X:
case unop::F:
case unop::G:
case unop::Closure:
res->push_back(recurse(f));
break;
case unop::NegClosure:
nlist.push_back(uo);
break;
}
}
else
{
res->push_back(recurse(f));
}
}
// Keep only the non-marked EConcat for which we
// have not seen a similar EConcatMarked.
for (unbinop::const_iterator i = elist.begin();
i != elist.end(); ++i)
if (empairs.find(std::make_pair((*i)->first(),
(*i)->second()))
== empairs.end())
res->push_back((*i)->clone());
// Keep only the non-marked NegClosure for which we
// have not seen a similar NegClosureMarked.
for (ununop::const_iterator i = nlist.begin();
i != nlist.end(); ++i)
if (nmset.find((*i)->child()) == nmset.end())
res->push_back((*i)->clone());
}
}
result_ = multop::instance(mo->op(), res);
}
void
visit(const binop* bo)
{
result_ = bo->clone();
}
const formula*
recurse(const formula* f)
{
return tools_->simplify_mark(f);
}
};
class mark_visitor : public visitor
{
const formula* result_;
mark_tools* tools_;
public:
mark_visitor(mark_tools* t)
: tools_(t)
{
}
~mark_visitor()
{
}
const formula*
result()
{
return result_;
}
void
visit(const atomic_prop* ap)
{
result_ = ap->clone();
}
void
visit(const constant* c)
{
result_ = c->clone();
}
void
visit(const bunop* bo)
{
result_ = bo->clone();
}
void
visit(const unop* uo)
{
switch (uo->op())
{
case unop::Not:
case unop::X:
case unop::F:
case unop::G:
case unop::Closure:
case unop::NegClosureMarked:
result_ = uo->clone();
return;
case unop::NegClosure:
result_ = unop::instance(unop::NegClosureMarked,
uo->child()->clone());
return;
}
SPOT_UNREACHABLE();
}
void
visit(const multop* mo)
{
multop::vec* res = new multop::vec;
unsigned mos = mo->size();
for (unsigned i = 0; i < mos; ++i)
res->push_back(recurse(mo->nth(i)));
result_ = multop::instance(mo->op(), res);
}
void
visit(const binop* bo)
{
switch (bo->op())
{
case binop::Xor:
case binop::Implies:
case binop::Equiv:
SPOT_UNIMPLEMENTED();
case binop::U:
case binop::W:
case binop::M:
case binop::R:
case binop::UConcat:
case binop::EConcatMarked:
result_ = bo->clone();
return;
case binop::EConcat:
{
const formula* f1 = bo->first()->clone();
const formula* f2 = bo->second()->clone();
result_ = binop::instance(binop::EConcatMarked, f1, f2);
return;
}
}
SPOT_UNREACHABLE();
}
const formula*
recurse(const formula* f)
{
return tools_->mark_concat_ops(f);
}
};
}
mark_tools::mark_tools()
: simpvisitor_(new simplify_mark_visitor(this)),
markvisitor_(new mark_visitor(this))
{
}
mark_tools::~mark_tools()
{
delete simpvisitor_;
delete markvisitor_;
{
f2f_map::iterator i = simpmark_.begin();
f2f_map::iterator end = simpmark_.end();
while (i != end)
{
f2f_map::iterator old = i++;
old->second->destroy();
old->first->destroy();
}
}
{
f2f_map::iterator i = markops_.begin();
f2f_map::iterator end = markops_.end();
while (i != end)
{
f2f_map::iterator old = i++;
old->second->destroy();
old->first->destroy();
}
}
}
const formula*
mark_tools::mark_concat_ops(const formula* f)
formula
mark_tools::mark_concat_ops(formula f)
{
f2f_map::iterator i = markops_.find(f);
if (i != markops_.end())
return i->second->clone();
return i->second;
f->accept(*markvisitor_);
ltl::formula res;
switch (f.kind())
{
case op::False:
case op::True:
case op::EmptyWord:
case op::AP:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosureMarked:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcatMarked:
case op::UConcat:
case op::Concat:
case op::Fusion:
res = f;
break;
case op::NegClosure:
res = ltl::formula::NegClosureMarked(f.nth(0));
break;
case op::EConcat:
res = ltl::formula::EConcatMarked(f.nth(0), f.nth(1));
break;
case op::Or:
case op::And:
res = f.map([this](formula f)
{
return this->mark_concat_ops(f);
});
break;
case op::Xor:
case op::Implies:
case op::Equiv:
SPOT_UNIMPLEMENTED();
}
const formula* r = down_cast<mark_visitor*>(markvisitor_)->result();
markops_[f->clone()] = r->clone();
return r;
markops_[f] = res;
return res;
}
const formula*
mark_tools::simplify_mark(const formula* f)
formula
mark_tools::simplify_mark(formula f)
{
if (!f->is_marked())
return f->clone();
if (!f.is_marked())
return f;
f2f_map::iterator i = simpmark_.find(f);
if (i != simpmark_.end())
return i->second->clone();
return i->second;
f->accept(*simpvisitor_);
auto recurse = [this](formula f)
{
return this->simplify_mark(f);
};
const formula* r =
down_cast<simplify_mark_visitor*>(simpvisitor_)->result();
simpmark_[f->clone()] = r->clone();
return r;
ltl::formula res;
switch (f.kind())
{
case op::False:
case op::True:
case op::EmptyWord:
case op::AP:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
res = f;
break;
case op::Or:
res = f.map(recurse);
break;
case op::And:
{
std::set<std::pair<formula, formula>> empairs;
std::set<formula> nmset;
std::vector<formula> elist;
std::vector<formula> nlist;
std::vector<formula> v;
for (auto c: f)
{
if (c.is(op::EConcatMarked))
{
empairs.emplace(c.nth(0), c.nth(1));
v.push_back(c.map(recurse));
}
else if (c.is(op::EConcat))
{
elist.push_back(c);
}
else if (c.is(op::NegClosureMarked))
{
nmset.insert(c.nth(0));
v.push_back(c.map(recurse));
}
else if (c.is(op::NegClosure))
{
nlist.push_back(c);
}
else
{
v.push_back(c);
}
}
// Keep only the non-marked EConcat for which we
// have not seen a similar EConcatMarked.
for (auto e: elist)
if (empairs.find(std::make_pair(e.nth(0), e.nth(1)))
== empairs.end())
v.push_back(e);
// Keep only the non-marked NegClosure for which we
// have not seen a similar NegClosureMarked.
for (auto n: nlist)
if (nmset.find(n.nth(0)) == nmset.end())
v.push_back(n);
res = ltl::formula::And(v);
}
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::Concat:
case op::Fusion:
SPOT_UNIMPLEMENTED();
}
simpmark_[f] = res;
return res;
}
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2011, 2012, 2013 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2010, 2011, 2012, 2013, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -20,34 +20,27 @@
#pragma once
#include "ltlast/formula.hh"
#include "ltlast/visitor.hh"
#include "misc/hash.hh"
namespace spot
{
namespace ltl
{
class mark_tools
class mark_tools final
{
public:
/// \ingroup ltl_rewriting
/// \brief Mark operators NegClosure and EConcat.
///
/// \param f The formula to rewrite.
const formula* mark_concat_ops(const formula* f);
formula mark_concat_ops(formula f);
const formula* simplify_mark(const formula* f);
mark_tools();
~mark_tools();
formula simplify_mark(formula f);
private:
typedef std::unordered_map<const formula*, const formula*,
ptr_hash<formula>> f2f_map;
typedef std::unordered_map<formula, formula> f2f_map;
f2f_map simpmark_;
f2f_map markops_;
visitor* simpvisitor_;
visitor* markvisitor_;
};
}

View file

@ -17,28 +17,18 @@
// 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 <unordered_set>
#include <set>
#include <algorithm>
#include "ltlast/allnodes.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/clone.hh"
#include "ltlvisit/mutation.hh"
#include "ltlvisit/length.hh"
#include "misc/hash.hh"
#define Implies_(x, y) \
spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y))
#define And_(x, y) \
spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y))
#define AndRat_(x, y) \
spot::ltl::multop::instance(spot::ltl::multop::AndRat, (x), (y))
#define AndNLM_(x) \
spot::ltl::multop::instance(spot::ltl::multop::AndNLM, (x))
#define Concat_(x, y) \
spot::ltl::multop::instance(spot::ltl::multop::Concat, (x), (y))
#define Not_(x) \
spot::ltl::unop::instance(spot::ltl::unop::Not, (x))
#define And_(x, y) formula::And({(x), (y)})
#define AndRat_(x, y) formula::AndRat({(x), (y)})
#define AndNLM_(x) formula::AndNLM(x)
#define Concat_(x, y) formula::Concat({(x), (y)})
#define Not_(x) formula::Not(x)
namespace spot
{
@ -46,355 +36,307 @@ namespace spot
{
namespace
{
class replace_visitor final : public clone_visitor
formula substitute_ap(formula f, formula ap_src, formula ap_dst)
{
public:
void visit(const atomic_prop* ap)
{
if (ap == ap1_)
result_ = ap2_->clone();
else
result_ = ap->clone();
}
return f.map([&](formula f)
{
if (f == ap_src)
return ap_dst;
else
return substitute_ap(f, ap_src, ap_dst);
});
}
const formula*
replace(const formula* f,
const atomic_prop* ap1,
const atomic_prop* ap2)
{
ap1_ = ap1;
ap2_ = ap2;
return recurse(f);
}
private:
const atomic_prop* ap1_;
const atomic_prop* ap2_;
};
typedef std::vector<const formula*> vec;
class mutation_visitor final : public clone_visitor
typedef std::vector<formula> vec;
class mutator final
{
int mutation_counter_ = 0;
formula f_;
unsigned opts_;
public:
mutation_visitor(const formula* f, unsigned opts) : f_(f), opts_(opts)
mutator(formula f, unsigned opts) : f_(f), opts_(opts)
{
}
void visit(const atomic_prop* ap)
formula mutate(formula f)
{
result_ = 0;
if (opts_ & Mut_Ap2Const)
{
if (mutation_counter_-- == 0)
result_ = constant::true_instance();
if (mutation_counter_-- == 0)
result_ = constant::false_instance();
}
if (!result_)
result_ = ap->clone();
}
void visit(const unop* uo)
{
result_ = 0;
if (opts_ & Mut_Remove_Ops)
{
if ((uo->op() == unop::G
|| uo->op() == unop::F
|| uo->op() == unop::X
|| uo->op() == unop::Not)
&& mutation_counter_-- == 0)
result_ = uo->child()->clone();
}
if (!result_)
{
if (mutation_counter_ < 0)
result_ = uo->clone();
else
{
result_ = unop::instance(uo->op(), recurse(uo->child()));
}
}
}
void visit(const binop* bo)
{
const formula* first = bo->first();
const formula* second = bo->second();
result_ = 0;
auto op = bo->op();
bool left_is_sere = op == binop::EConcat
|| op == binop::EConcatMarked
|| op == binop::UConcat;
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
auto recurse = [this](formula f)
{
if (!left_is_sere)
result_ = first->clone();
else if (op == binop::UConcat)
result_ = unop::instance(unop::NegClosure, first->clone());
else // EConcat or EConcatMarked
result_ = unop::instance(unop::Closure, first->clone());
}
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
result_ = second->clone();
if (opts_ & Mut_Rewrite_Ops)
{
switch (op)
{
case binop::U:
if (mutation_counter_-- == 0)
result_ = binop::instance(binop::W, first->clone(),
second->clone());
break;
case binop::M:
if (mutation_counter_-- == 0)
result_ = binop::instance(binop::R, first->clone(),
second->clone());
if (mutation_counter_-- == 0)
result_ = binop::instance(binop::U, second->clone(),
first->clone());
break;
case binop::R:
if (mutation_counter_-- == 0)
result_ = binop::instance(binop::W, second->clone(),
first->clone());
break;
default:
break;
}
}
if (opts_ & Mut_Split_Ops)
{
switch (op)
{
case binop::Equiv:
if (mutation_counter_-- == 0)
result_ = Implies_(first->clone(), second->clone());
if (mutation_counter_-- == 0)
result_ = Implies_(second->clone(), first->clone());
if (mutation_counter_-- == 0)
result_ = And_(first->clone(), second->clone());
if (mutation_counter_-- == 0)
{
// Negate the two argument sequentially (in this
// case right before left, otherwise different
// compilers will make different choices.
auto right = Not_(second->clone());
result_ = And_(Not_(first->clone()), right);
}
break;
case binop::Xor:
if (mutation_counter_-- == 0)
result_ = And_(first->clone(), Not_(second->clone()));
if (mutation_counter_-- == 0)
result_ = And_(Not_(first->clone()), second->clone());
break;
default:
break;
}
}
if (!result_)
{
if (mutation_counter_ < 0)
{
result_ = bo->clone();
}
else
{
// For historical reasons, we evaluate the right
// side before the left one. The other order would
// be OK as well but require changing the test
// suite. Evaluating both sides during the call to
// instance() is incorrect, because each compiler
// could decide of a different order.
auto right = recurse(second);
result_ = binop::instance(op, recurse(first), right);
}
}
}
return this->mutate(f);
};
void visit(const bunop* bu)
{
const formula* c = bu->child()->clone();
result_ = nullptr;
auto op = bu->op();
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
result_ = c;
if (opts_ & Mut_Simplify_Bounds)
{
auto min = bu->min();
auto max = bu->max();
if (min > 0)
switch (f.kind())
{
case op::False:
case op::True:
case op::EmptyWord:
return f;
case op::AP:
if (opts_ & Mut_Ap2Const)
{
if (mutation_counter_-- == 0)
result_ = bunop::instance(op, c, min - 1, max);
return formula::tt();
if (mutation_counter_-- == 0)
result_ = bunop::instance(op, c, 0, max);
}
if (max != bunop::unbounded)
{
if (max > min && mutation_counter_-- == 0)
result_ = bunop::instance(op, c, min, max - 1);
if (mutation_counter_-- == 0)
result_ = bunop::instance(op, c, min, bunop::unbounded);
}
}
if (!result_)
{
c->destroy();
if (mutation_counter_ < 0)
result_ = bu->clone();
else
result_ = bunop::instance(op, recurse(c), bu->min(), bu->max());
}
}
void visit(const multop* mo)
{
int mos = mo->size();
int i;
result_ = 0;
if (opts_ & Mut_Remove_Multop_Operands)
{
for (i = 0; i < mos; ++i)
if (mutation_counter_-- == 0)
result_ = mo->all_but(i);
}
if (opts_ & Mut_Split_Ops && mo->op() == multop::AndNLM)
{
if (mutation_counter_ >= 0 && mutation_counter_ < 2 * (mos - 1))
{
vec* v1 = new vec();
vec* v2 = new vec();
v1->push_back(mo->nth(0)->clone());
bool reverse = false;
i = 1;
while (i < mos)
{
if (mutation_counter_-- == 0)
break;
if (mutation_counter_-- == 0)
{
reverse = true;
break;
}
v1->push_back(mo->nth(i++)->clone());
}
for (; i < mos; ++i)
v2->push_back(mo->nth(i)->clone());
const formula* tstar =
bunop::instance(bunop::Star, constant::true_instance(),
0,
bunop::unbounded);
const formula* first = AndNLM_(v1);
const formula* second = AndNLM_(v2);
if (!reverse)
result_ = AndRat_(Concat_(first, tstar), second);
else
result_ = AndRat_(Concat_(second, tstar), first);
return formula::ff();
}
return f;
case op::Not:
case op::X:
case op::F:
case op::G:
if ((opts_ & Mut_Remove_Ops)
&& mutation_counter_-- == 0)
return f.nth(0);
// fall through
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
if (mutation_counter_ < 0)
return f;
else
mutation_counter_ -= 2 * (mos - 1);
}
return f.map(recurse);
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
{
int mos = f.size();
if (opts_ & Mut_Remove_Multop_Operands)
{
for (int i = 0; i < mos; ++i)
if (mutation_counter_-- == 0)
return f.all_but(i);
}
if (!result_)
{
if (mutation_counter_ < 0)
result_ = mo->clone();
else
{
vec* v = new vec();
for (i = 0; i < mos; ++i)
v->push_back(recurse(mo->nth(i)));
result_ = multop::instance(mo->op(), v);
}
}
if (opts_ & Mut_Split_Ops && f.is(op::AndNLM))
{
if (mutation_counter_ >= 0
&& mutation_counter_ < 2 * (mos - 1))
{
vec v1;
vec v2;
v1.push_back(f.nth(0));
bool reverse = false;
int i = 1;
while (i < mos)
{
if (mutation_counter_-- == 0)
break;
if (mutation_counter_-- == 0)
{
reverse = true;
break;
}
v1.push_back(f.nth(i++));
}
for (; i < mos; ++i)
v2.push_back(f.nth(i));
formula first = AndNLM_(v1);
formula second = AndNLM_(v2);
formula ost = formula::one_star();
if (!reverse)
return AndRat_(Concat_(first, ost), second);
else
return AndRat_(Concat_(second, ost), first);
}
else
{
mutation_counter_ -= 2 * (mos - 1);
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
{
formula first = f.nth(0);
formula second = f.nth(1);
op o = f.kind();
bool left_is_sere = o == op::EConcat
|| o == op::EConcatMarked
|| o == op::UConcat;
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
{
if (!left_is_sere)
return first;
else if (o == op::UConcat)
return formula::NegClosure(first);
else // EConcat or EConcatMarked
return formula::Closure(first);
}
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return second;
if (opts_ & Mut_Rewrite_Ops)
{
switch (o)
{
case op::U:
if (mutation_counter_-- == 0)
return formula::W(first, second);
break;
case op::M:
if (mutation_counter_-- == 0)
return formula::R(first, second);
if (mutation_counter_-- == 0)
return formula::U(second, first);
break;
case op::R:
if (mutation_counter_-- == 0)
return formula::W(second, first);
break;
default:
break;
}
}
if (opts_ & Mut_Split_Ops)
{
switch (o)
{
case op::Equiv:
if (mutation_counter_-- == 0)
return formula::Implies(first, second);
if (mutation_counter_-- == 0)
return formula::Implies(second, first);
if (mutation_counter_-- == 0)
return formula::And({first, second});
if (mutation_counter_-- == 0)
{
// Negate the two argument sequentially (in this
// case right before left, otherwise different
// compilers will make different choices.
auto right = formula::Not(second);
return formula::And({formula::Not(first), right});
}
break;
case op::Xor:
if (mutation_counter_-- == 0)
return formula::And({first, formula::Not(second)});
if (mutation_counter_-- == 0)
return formula::And({formula::Not(first), second});
break;
default:
break;
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
case op::Star:
case op::FStar:
{
formula c = f.nth(0);
op o = f.kind();
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return c;
if (opts_ & Mut_Simplify_Bounds)
{
auto min = f.min();
auto max = f.max();
if (min > 0)
{
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min - 1, max);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, 0, max);
}
if (max != formula::unbounded())
{
if (max > min && mutation_counter_-- == 0)
return formula::bunop(o, c, min, max - 1);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min,
formula::unbounded());
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
}
SPOT_UNREACHABLE();
}
const formula*
recurse(const formula* f)
{
f->accept(*this);
return result_;
}
const formula*
formula
get_mutation(int n)
{
mutation_counter_ = n;
const formula* mut = recurse(f_);
formula mut = mutate(f_);
if (mut == f_)
{
mut->destroy();
return 0;
}
return nullptr;
return mut;
}
private:
const formula* f_;
int mutation_counter_ = 0;
unsigned opts_;
};
bool
formula_length_less_than(const formula* left, const formula* right)
formula_length_less_than(formula left, formula right)
{
assert(left);
assert(right);
assert(left != nullptr);
assert(right != nullptr);
if (left == right)
return false;
return length(left) < length(right);
auto ll = length(left);
auto lr = length(right);
if (ll < lr)
return true;
if (ll > lr)
return false;
return left < right;
}
typedef std::set<const formula*, formula_ptr_less_than> fset_t;
typedef std::set<formula> fset_t;
void
single_mutation_rec(const formula* f, fset_t& mutations, unsigned opts,
single_mutation_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
{
f->clone();
--n;
}
--n;
}
else
{
const formula* mut(nullptr);
formula mut;
int i = 0;
mutation_visitor mv(f, opts);
while (n > 0 && (mut = mv.get_mutation(i++)))
{
single_mutation_rec(mut, mutations, opts, n, m - 1);
mut->destroy();
}
mutator mv(f, opts);
while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr))
single_mutation_rec(mut, mutations, opts, n, m - 1);
}
}
void
replace_ap_rec(const formula* f, fset_t& mutations, unsigned opts,
replace_ap_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
{
f->clone();
--n;
}
--n;
}
else
{
if (!n)
return;
replace_visitor rv;
auto aps =
std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f));
for (auto ap1: *aps)
@ -402,9 +344,8 @@ namespace spot
{
if (ap1 == ap2)
continue;
auto mut = rv.replace(f, ap1, ap2);
auto mut = substitute_ap(f, ap1, ap2);
replace_ap_rec(mut, mutations, opts, n, m - 1);
mut->destroy();
if (!n)
return;
}
@ -412,8 +353,8 @@ namespace spot
}
}
std::vector<const formula*>
mutate(const formula* f, unsigned opts, unsigned max_output,
std::vector<formula>
mutate(formula f, unsigned opts, unsigned max_output,
unsigned mutation_count, bool sort)
{
fset_t mutations;

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -20,7 +20,6 @@
#pragma once
#include "ltlast/formula.hh"
#include <set>
#include <vector>
namespace spot
@ -40,10 +39,10 @@ namespace spot
};
SPOT_API
std::vector<const formula*> mutate(const formula* f,
unsigned opts = Mut_All,
unsigned max_output = -1U,
unsigned mutation_count = 1,
bool sort = true);
std::vector<formula> mutate(formula f,
unsigned opts = Mut_All,
unsigned max_output = -1U,
unsigned mutation_count = 1,
bool sort = true);
}
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 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.
@ -27,11 +27,11 @@ namespace spot
{
namespace ltl
{
const formula*
negative_normal_form(const formula* f, bool negated)
formula
negative_normal_form(formula f, bool negated)
{
if (!negated && f->is_in_nenoform())
return f->clone();
if (!negated && f.is_in_nenoform())
return f;
ltl_simplifier s;
return s.negative_normal_form(f, negated);

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013 Laboratoire de Recherche et
// Copyright (C) 2011, 2012, 2013, 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
@ -39,11 +39,11 @@ namespace spot
/// \c !f
///
/// Note that this will not remove abbreviated operators. If you
/// want to remove abbreviations, call spot::ltl::unabbreviate_logic
/// or spot::ltl::unabbreviate_ltl first. (Calling these functions
/// after spot::ltl::negative_normal_form would likely produce a
/// formula which is not in negative normal form.)
SPOT_API const formula*
negative_normal_form(const formula* f, bool negated = false);
/// want to remove abbreviations, call spot::ltl::unabbreviate
/// first. (Calling this function after
/// spot::ltl::negative_normal_form would likely produce a formula
/// which is not in negative normal form.)
SPOT_API formula
negative_normal_form(formula f, bool negated = false);
}
}

View file

@ -1,124 +0,0 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2014 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.
//
// 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 "ltlvisit/postfix.hh"
#include "ltlast/allnodes.hh"
namespace spot
{
namespace ltl
{
postfix_visitor::postfix_visitor()
{
}
postfix_visitor::~postfix_visitor()
{
}
void
postfix_visitor::visit(const atomic_prop* ap)
{
doit(ap);
}
void
postfix_visitor::visit(const unop* uo)
{
uo->child()->accept(*this);
doit(uo);
}
void
postfix_visitor::visit(const binop* bo)
{
bo->first()->accept(*this);
bo->second()->accept(*this);
doit(bo);
}
void
postfix_visitor::visit(const multop* mo)
{
unsigned s = mo->size();
for (unsigned i = 0; i < s; ++i)
mo->nth(i)->accept(*this);
doit(mo);
}
void
postfix_visitor::visit(const bunop* so)
{
so->child()->accept(*this);
doit(so);
}
void
postfix_visitor::visit(const constant* c)
{
doit(c);
}
void
postfix_visitor::doit(const atomic_prop* ap)
{
doit_default(ap);
}
void
postfix_visitor::doit(const unop* uo)
{
doit_default(uo);
}
void
postfix_visitor::doit(const binop* bo)
{
doit_default(bo);
}
void
postfix_visitor::doit(const multop* mo)
{
doit_default(mo);
}
void
postfix_visitor::doit(const bunop* so)
{
doit_default(so);
}
void
postfix_visitor::doit(const constant* c)
{
doit_default(c);
}
void
postfix_visitor::doit_default(const formula* f)
{
(void)f;
// Dummy implementation that does nothing.
}
}
}

View file

@ -1,60 +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),
// 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/>.
#pragma once
#include "ltlast/formula.hh"
#include "ltlast/visitor.hh"
namespace spot
{
namespace ltl
{
/// \ingroup ltl_visitor
/// \brief Apply an algorithm on each node of an AST,
/// during a postfix traversal.
///
/// Override one or more of the postifix_visitor::doit methods
/// with the algorithm to apply.
class SPOT_API postfix_visitor : public visitor
{
public:
postfix_visitor();
virtual ~postfix_visitor();
void visit(const atomic_prop* ap);
void visit(const unop* uo);
void visit(const binop* bo);
void visit(const multop* mo);
void visit(const constant* c);
void visit(const bunop* c);
virtual void doit(const atomic_prop* ap);
virtual void doit(const unop* uo);
virtual void doit(const binop* bo);
virtual void doit(const multop* mo);
virtual void doit(const constant* c);
virtual void doit(const bunop* c);
virtual void doit_default(const formula* f);
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -38,14 +38,14 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_psl(std::ostream& os, const formula* f, bool full_parent = false);
print_psl(std::ostream& os, formula f, bool full_parent = false);
/// \brief Convert a PSL formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_psl(const formula* f, bool full_parent = false);
str_psl(formula f, bool full_parent = false);
/// \brief Output a PSL formula as an utf-8 string which is parsable.
/// \param os The stream where it should be output.
@ -53,7 +53,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_psl(std::ostream& os, const formula* f,
print_utf8_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a PSL formula into a utf-8 string which is parsable
@ -61,7 +61,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_psl(const formula* f, bool full_parent = false);
str_utf8_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a string which is parsable.
/// \param f The formula to translate.
@ -69,14 +69,14 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sere(std::ostream& os, const formula* f, bool full_parent = false);
print_sere(std::ostream& os, formula f, bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sere(const formula* f, bool full_parent = false);
str_sere(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a utf-8 string which is parsable.
/// \param os The stream where it should be output.
@ -84,7 +84,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_sere(std::ostream& os, const formula* f,
print_utf8_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
@ -92,7 +92,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_sere(const formula* f, bool full_parent = false);
str_utf8_sere(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Spin.
/// \param os The stream where it should be output.
@ -100,7 +100,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_spin_ltl(std::ostream& os, const formula* f,
print_spin_ltl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert an LTL formula into a string parsable by Spin.
@ -108,18 +108,18 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_spin_ltl(const formula* f, bool full_parent = false);
str_spin_ltl(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Wring.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
SPOT_API std::ostream&
print_wring_ltl(std::ostream& os, const formula* f);
print_wring_ltl(std::ostream& os, formula f);
/// \brief Convert a formula into a string parsable by Wring
/// \param f The formula to translate.
SPOT_API std::string
str_wring_ltl(const formula* f);
str_wring_ltl(formula f);
/// \brief Output a PSL formula as a LaTeX string.
/// \param os The stream where it should be output.
@ -127,7 +127,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_psl(std::ostream& os, const formula* f,
print_latex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a formula as a LaTeX string which is parsable.
@ -136,7 +136,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_psl(const formula* f, bool full_parent = false);
str_latex_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string.
/// \param os The stream where it should be output.
@ -144,7 +144,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_sere(std::ostream& os, const formula* f,
print_latex_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string which is parsable.
@ -153,7 +153,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_sere(const formula* f, bool full_parent = false);
str_latex_sere(formula f, bool full_parent = false);
/// \brief Output a PSL formula as a self-contained LaTeX string.
///
@ -163,7 +163,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_psl(std::ostream& os, const formula* f,
print_sclatex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a PSL formula as a self-contained LaTeX string.
@ -173,7 +173,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_psl(const formula* f, bool full_parent = false);
str_sclatex_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string.
///
@ -183,7 +183,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_sere(std::ostream& os, const formula* f,
print_sclatex_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string.
@ -193,7 +193,7 @@ namespace spot
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_sere(const formula* f, bool full_parent = false);
str_sclatex_sere(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string in LBT's format.
///
@ -206,7 +206,7 @@ namespace spot
/// \param f The formula to translate.
/// \param os The stream where it should be output.
SPOT_API std::ostream&
print_lbt_ltl(std::ostream& os, const formula* f);
print_lbt_ltl(std::ostream& os, formula f);
/// \brief Output an LTL formula as a string in LBT's format.
///
@ -218,7 +218,7 @@ namespace spot
///
/// \param f The formula to translate.
SPOT_API std::string
str_lbt_ltl(const formula* f);
str_lbt_ltl(formula f);
/// @}
}
}

View file

@ -23,7 +23,6 @@
#include <cassert>
#include <algorithm>
#include "randomltl.hh"
#include "ltlast/allnodes.hh"
#include "misc/random.hh"
#include <iostream>
#include <cstring>
@ -37,25 +36,41 @@ namespace spot
{
namespace
{
static const formula*
static formula
ap_builder(const random_formula* rl, int n)
{
assert(n == 1);
(void) n;
atomic_prop_set::const_iterator i = rl->ap()->begin();
std::advance(i, mrand(rl->ap()->size()));
return (*i)->clone();
return *i;
}
static const formula*
static formula
true_builder(const random_formula*, int n)
{
assert(n == 1);
(void) n;
return constant::true_instance();
return formula::tt();
}
static const formula*
static formula
false_builder(const random_formula*, int n)
{
assert(n == 1);
(void) n;
return formula::ff();
}
static formula
eword_builder(const random_formula*, int n)
{
assert(n == 1);
(void) n;
return formula::eword();
}
static formula
boolform_builder(const random_formula* rl, int n)
{
assert(n >= 1);
@ -63,40 +78,24 @@ namespace spot
return rs->rb.generate(n);
}
static const formula*
false_builder(const random_formula*, int n)
{
assert(n == 1);
(void) n;
return constant::false_instance();
}
static const formula*
eword_builder(const random_formula*, int n)
{
assert(n == 1);
(void) n;
return constant::empty_word_instance();
}
template <unop::type Op>
static const formula*
template <op Op>
static formula
unop_builder(const random_formula* rl, int n)
{
assert(n >= 2);
return unop::instance(Op, rl->generate(n - 1));
return formula::unop(Op, rl->generate(n - 1));
}
static const formula*
static formula
closure_builder(const random_formula* rl, int n)
{
assert(n >= 2);
const random_psl* rp = static_cast<const random_psl*>(rl);
return unop::instance(unop::Closure, rp->rs.generate(n - 1));
return formula::Closure(rp->rs.generate(n - 1));
}
template <binop::type Op>
static const formula*
template <op Op>
static formula
binop_builder(const random_formula* rl, int n)
{
assert(n >= 3);
@ -109,11 +108,11 @@ namespace spot
// discovering that clang would perform the nested calls from
// left to right.
auto right = rl->generate(n - l);
return binop::instance(Op, rl->generate(l), right);
return formula::binop(Op, rl->generate(l), right);
}
template <binop::type Op>
static const formula*
template <op Op>
static formula
binop_SERELTL_builder(const random_formula* rl, int n)
{
assert(n >= 3);
@ -122,41 +121,41 @@ namespace spot
int l = rrand(1, n - 1);
// See comment in binop_builder.
auto right = rl->generate(n - l);
return binop::instance(Op, rp->rs.generate(l), right);
return formula::binop(Op, rp->rs.generate(l), right);
}
template <bunop::type Op>
static const formula*
template <op Op>
static formula
bunop_unbounded_builder(const random_formula* rl, int n)
{
assert(n >= 2);
return bunop::instance(Op, rl->generate(n - 1));
return formula::bunop(Op, rl->generate(n - 1));
}
template <bunop::type Op>
static const formula*
template <op Op>
static formula
bunop_bounded_builder(const random_formula* rl, int n)
{
assert(n >= 2);
int min = rrand(0, 2);
int max = rrand(min, 3);
return bunop::instance(Op, rl->generate(n - 1), min, max);
return formula::bunop(Op, rl->generate(n - 1), min, max);
}
template <bunop::type Op>
static const formula*
template <op Op>
static formula
bunop_bool_bounded_builder(const random_formula* rl, int n)
{
assert(n >= 2);
int min = rrand(0, 2);
int max = rrand(min, 3);
const random_sere* rp = static_cast<const random_sere*>(rl);
return bunop::instance(Op, rp->rb.generate(n - 1), min, max);
return formula::bunop(Op, rp->rb.generate(n - 1), min, max);
}
template <multop::type Op>
static const formula*
template <op Op>
static formula
multop_builder(const random_formula* rl, int n)
{
assert(n >= 3);
@ -164,7 +163,7 @@ namespace spot
int l = rrand(1, n - 1);
// See comment in binop_builder.
auto right = rl->generate(n - l);
return multop::instance(Op, rl->generate(l), right);
return formula::multop(Op, {rl->generate(l), right});
}
} // anonymous
@ -208,7 +207,7 @@ namespace spot
assert(total_2_and_more_ >= total_2_);
}
const formula*
formula
random_formula::generate(int n) const
{
assert(n > 0);
@ -319,15 +318,15 @@ namespace spot
proba_2_ = proba_ + 1;
proba_2_or_more_ = proba_ + 1;
proba_[1].setup("boolform", 1, boolform_builder);
proba_[2].setup("star", 2, bunop_unbounded_builder<bunop::Star>);
proba_[3].setup("star_b", 2, bunop_bounded_builder<bunop::Star>);
proba_[4].setup("fstar", 2, bunop_unbounded_builder<bunop::FStar>);
proba_[5].setup("fstar_b", 2, bunop_bounded_builder<bunop::FStar>);
proba_[6].setup("and", 3, multop_builder<multop::AndRat>);
proba_[7].setup("andNLM", 3, multop_builder<multop::AndNLM>);
proba_[8].setup("or", 3, multop_builder<multop::OrRat>);
proba_[9].setup("concat", 3, multop_builder<multop::Concat>);
proba_[10].setup("fusion", 3, multop_builder<multop::Fusion>);
proba_[2].setup("star", 2, bunop_unbounded_builder<op::Star>);
proba_[3].setup("star_b", 2, bunop_bounded_builder<op::Star>);
proba_[4].setup("fstar", 2, bunop_unbounded_builder<op::FStar>);
proba_[5].setup("fstar_b", 2, bunop_bounded_builder<op::FStar>);
proba_[6].setup("and", 3, multop_builder<op::AndRat>);
proba_[7].setup("andNLM", 3, multop_builder<op::AndNLM>);
proba_[8].setup("or", 3, multop_builder<op::OrRat>);
proba_[9].setup("concat", 3, multop_builder<op::Concat>);
proba_[10].setup("fusion", 3, multop_builder<op::Fusion>);
update_sums();
}
@ -341,12 +340,12 @@ namespace spot
proba_[1].setup("false", 1, false_builder);
proba_[2].setup("true", 1, true_builder);
proba_2_or_more_ = proba_2_ = proba_ + 3;
proba_[3].setup("not", 2, unop_builder<unop::Not>);
proba_[4].setup("equiv", 3, binop_builder<binop::Equiv>);
proba_[5].setup("implies", 3, binop_builder<binop::Implies>);
proba_[6].setup("xor", 3, binop_builder<binop::Xor>);
proba_[7].setup("and", 3, multop_builder<multop::And>);
proba_[8].setup("or", 3, multop_builder<multop::Or>);
proba_[3].setup("not", 2, unop_builder<op::Not>);
proba_[4].setup("equiv", 3, binop_builder<op::Equiv>);
proba_[5].setup("implies", 3, binop_builder<op::Implies>);
proba_[6].setup("xor", 3, binop_builder<op::Xor>);
proba_[7].setup("and", 3, multop_builder<op::And>);
proba_[8].setup("or", 3, multop_builder<op::Or>);
update_sums();
}
@ -360,19 +359,19 @@ namespace spot
proba_[1].setup("false", 1, false_builder);
proba_[2].setup("true", 1, true_builder);
proba_2_or_more_ = proba_2_ = proba_ + 3;
proba_[3].setup("not", 2, unop_builder<unop::Not>);
proba_[4].setup("F", 2, unop_builder<unop::F>);
proba_[5].setup("G", 2, unop_builder<unop::G>);
proba_[6].setup("X", 2, unop_builder<unop::X>);
proba_[7].setup("equiv", 3, binop_builder<binop::Equiv>);
proba_[8].setup("implies", 3, binop_builder<binop::Implies>);
proba_[9].setup("xor", 3, binop_builder<binop::Xor>);
proba_[10].setup("R", 3, binop_builder<binop::R>);
proba_[11].setup("U", 3, binop_builder<binop::U>);
proba_[12].setup("W", 3, binop_builder<binop::W>);
proba_[13].setup("M", 3, binop_builder<binop::M>);
proba_[14].setup("and", 3, multop_builder<multop::And>);
proba_[15].setup("or", 3, multop_builder<multop::Or>);
proba_[3].setup("not", 2, unop_builder<op::Not>);
proba_[4].setup("F", 2, unop_builder<op::F>);
proba_[5].setup("G", 2, unop_builder<op::G>);
proba_[6].setup("X", 2, unop_builder<op::X>);
proba_[7].setup("equiv", 3, binop_builder<op::Equiv>);
proba_[8].setup("implies", 3, binop_builder<op::Implies>);
proba_[9].setup("xor", 3, binop_builder<op::Xor>);
proba_[10].setup("R", 3, binop_builder<op::R>);
proba_[11].setup("U", 3, binop_builder<op::U>);
proba_[12].setup("W", 3, binop_builder<op::W>);
proba_[13].setup("M", 3, binop_builder<op::M>);
proba_[14].setup("and", 3, multop_builder<op::And>);
proba_[15].setup("or", 3, multop_builder<op::Or>);
}
random_ltl::random_ltl(const atomic_prop_set* ap)
@ -399,8 +398,8 @@ namespace spot
((proba_ + 16) - (proba_ + 7)) * sizeof(*proba_));
proba_[7].setup("Closure", 2, closure_builder);
proba_[17].setup("EConcat", 3, binop_SERELTL_builder<binop::EConcat>);
proba_[18].setup("UConcat", 3, binop_SERELTL_builder<binop::UConcat>);
proba_[17].setup("EConcat", 3, binop_SERELTL_builder<op::EConcat>);
proba_[18].setup("UConcat", 3, binop_SERELTL_builder<op::UConcat>);
update_sums();
}
@ -490,16 +489,13 @@ namespace spot
randltlgenerator::~randltlgenerator()
{
delete rf_;
// Cleanup the unicity table.
for (auto i: unique_set_)
i->destroy();
}
const formula* randltlgenerator::next()
formula randltlgenerator::next()
{
unsigned trials = MAX_TRIALS;
bool ignore;
const formula* f = nullptr;
formula f = nullptr;
do
{
ignore = false;
@ -512,29 +508,14 @@ namespace spot
{
atomic_prop_set s = aprops_;
remove_some_props(s);
f = multop::instance(multop::And,
f, GF_n());
f = formula::And({f, GF_n()});
}
if (opt_simpl_level_)
{
const spot::ltl::formula* tmp = simpl_.simplify(f);
f->destroy();
f = tmp;
}
f = simpl_.simplify(f);
if (opt_unique_)
{
if (unique_set_.insert(f).second)
{
f->clone();
}
else
{
ignore = true;
f->destroy();
}
}
if (opt_unique_ && !unique_set_.insert(f).second)
ignore = true;
} while (ignore && --trials);
if (trials <= 0)
return nullptr;
@ -557,17 +538,15 @@ namespace spot
}
// GF(p_1) & GF(p_2) & ... & GF(p_n)
const formula*
formula
randltlgenerator::GF_n()
{
const formula* res = 0;
formula res = nullptr;
for (auto v: aprops_)
{
const formula* f =
unop::instance(unop::F, v->clone());
f = unop::instance(unop::G, f);
formula f = formula::G(formula::F(v));
if (res)
res = multop::instance(multop::And, f, res);
res = formula::And({f, res});
else
res = f;
}

View file

@ -70,7 +70,7 @@ namespace spot
/// n, because some simple simplifications are performed by the
/// AST. (For instance the formula <code>a | a</code> is
/// automatically reduced to <code>a</code> by spot::ltl::multop.)
const formula* generate(int n) const;
formula generate(int n) const;
/// \brief Print the priorities of each operator, constants,
/// and atomic propositions.
@ -93,7 +93,7 @@ namespace spot
const char* name;
int min_n;
double proba;
typedef const formula* (*builder)(const random_formula* rl, int n);
typedef formula (*builder)(const random_formula* rl, int n);
builder build;
void setup(const char* name, int min_n, builder build);
};
@ -304,9 +304,7 @@ namespace spot
class SPOT_API randltlgenerator
{
typedef
std::unordered_set<const spot::ltl::formula*,
const spot::ptr_hash<const spot::ltl::formula>> fset_t;
typedef std::unordered_set<formula> fset_t;
public:
@ -322,7 +320,7 @@ namespace spot
~randltlgenerator();
const spot::ltl::formula* next();
formula next();
void dump_ltl_priorities(std::ostream& os);
void dump_bool_priorities(std::ostream& os);
@ -331,7 +329,7 @@ namespace spot
void dump_sere_bool_priorities(std::ostream& os);
void remove_some_props(atomic_prop_set& s);
const formula* GF_n();
formula GF_n();
private:
fset_t unique_set_;

View file

@ -19,10 +19,7 @@
#include "relabel.hh"
#include <sstream>
#include "clone.hh"
#include "misc/hash.hh"
#include "ltlenv/defaultenv.hh"
#include "ltlast/allnodes.hh"
#include <map>
#include <set>
#include <stack>
@ -41,7 +38,7 @@ namespace spot
{
struct ap_generator
{
virtual const formula* next() = 0;
virtual formula next() = 0;
virtual ~ap_generator() {}
};
@ -53,11 +50,11 @@ namespace spot
{
}
const formula* next()
formula next()
{
std::ostringstream s;
s << 'p' << nn++;
return default_environment::instance().require(s.str());
return formula::ap(s.str());
}
};
@ -71,7 +68,7 @@ namespace spot
unsigned nn;
const formula* next()
formula next()
{
std::string s;
unsigned n = nn++;
@ -81,16 +78,15 @@ namespace spot
n /= 26;
}
while (n);
return default_environment::instance().require(s);
return formula::ap(s);
}
};
class relabeler: public clone_visitor
class relabeler
{
public:
typedef std::unordered_map<const formula*, const formula*> map;
typedef std::unordered_map<formula, formula> map;
map newname;
ap_generator* gen;
relabeling_map* oldnames;
@ -105,29 +101,33 @@ namespace spot
delete gen;
}
const formula* rename(const formula* old)
formula rename(formula old)
{
auto r = newname.emplace(old, nullptr);
if (!r.second)
{
return r.first->second->clone();
return r.first->second;
}
else
{
const formula* res;
r.first->second = res = gen->next();
formula res = gen->next();
r.first->second = res;
if (oldnames)
(*oldnames)[res] = old->clone();
(*oldnames)[res] = old;
return res;
}
}
using clone_visitor::visit;
void
visit(const atomic_prop* ap)
formula
visit(formula f)
{
result_ = rename(ap);
if (f.is(op::AP))
return rename(f);
else
return f.map([this](formula f)
{
return this->visit(f);
});
}
};
@ -135,9 +135,8 @@ namespace spot
}
const formula*
relabel(const formula* f, relabeling_style style,
relabeling_map* m)
formula
relabel(formula f, relabeling_style style, relabeling_map* m)
{
ap_generator* gen = 0;
switch (style)
@ -149,8 +148,9 @@ namespace spot
gen = new abc_generator;
break;
}
relabeler rel(gen, m);
return rel.recurse(f);
relabeler r(gen, m);
return r.visit(f);
}
//////////////////////////////////////////////////////////////////////
@ -227,16 +227,16 @@ namespace spot
// stop a, b, and !c, producing (p0&p1)U(p1&p2).
namespace
{
typedef std::vector<const formula*> succ_vec;
typedef std::map<const formula*, succ_vec> fgraph;
typedef std::vector<formula> succ_vec;
typedef std::map<formula, succ_vec> fgraph;
// Convert the formula's syntax tree into an undirected graph
// labeled by subformulas.
class formula_to_fgraph: public visitor
class formula_to_fgraph final
{
public:
fgraph& g;
std::stack<const formula*> s;
std::stack<formula> s;
formula_to_fgraph(fgraph& g):
g(g)
@ -248,104 +248,63 @@ namespace spot
}
void
visit(const atomic_prop*)
visit(formula f)
{
}
{
// Connect to parent
auto in = g.emplace(f, succ_vec());
if (!s.empty())
{
formula top = s.top();
in.first->second.push_back(top);
g[top].push_back(f);
if (!in.second)
return;
}
else
{
assert(in.second);
}
}
s.push(f);
void
visit(const constant*)
{
}
void
visit(const bunop* bo)
{
recurse(bo->child());
}
void
visit(const unop* uo)
{
recurse(uo->child());
}
void
visit(const binop* bo)
{
const formula* l = bo->first();
recurse(l);
const formula* r = bo->second();
recurse(r);
// Link operands of Boolean operators.
if (bo->is_boolean())
{
g[l].push_back(r);
g[r].push_back(l);
}
}
void
visit(const multop* mo)
{
unsigned mos = mo->size();
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
unsigned sz = f.size();
unsigned i = 0;
const formula* b = mo->is_boolean() ? 0 : mo->boolean_operands(&i);
if (b)
if (sz > 2 && !f.is_boolean())
{
recurse(b);
b->destroy();
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
formula b = f.boolean_operands(&i);
if (b)
visit(b);
}
for (; i < mos; ++i)
recurse(mo->nth(i));
// For Boolean nodes, connect all children in a loop. This
// way the node can only be a cut-point if it separates all
// children from the reset of the graph (not only one).
if (mo->is_boolean())
for (; i < sz; ++i)
visit(f.nth(i));
if (sz > 1 && f.is_boolean())
{
const formula* pred = mo->nth(0);
for (i = 1; i < mos; ++i)
// For Boolean nodes, connect all children in a
// loop. This way the node can only be a cut-point
// if it separates all children from the reset of
// the graph (not only one).
formula pred = f.nth(0);
for (i = 1; i < sz; ++i)
{
const formula* next = mo->nth(i);
// Note that we only add an edge in one direction,
// because we are building a cycle between all
// children anyway.
formula next = f.nth(i);
// Note that we only add an edge in one
// direction, because we are building a cycle
// between all children anyway.
g[pred].push_back(next);
pred = next;
}
g[pred].push_back(mo->nth(0));
g[pred].push_back(f.nth(0));
}
}
void
recurse(const formula* f)
{
auto i = g.emplace(f, succ_vec());
if (!s.empty())
{
const formula* top = s.top();
i.first->second.push_back(top);
g[top].push_back(f);
if (!i.second)
return;
}
else
{
assert(i.second);
}
f->clone();
s.push(f);
f->accept(*this);
s.pop();
}
};
typedef std::set<const formula*> fset;
typedef std::set<formula> fset;
struct data_entry // for each node of the graph
{
unsigned num; // serial number, in pre-order
@ -355,11 +314,11 @@ namespace spot
{
}
};
typedef std::unordered_map<const formula*, data_entry> fmap_t;
typedef std::unordered_map<formula, data_entry> fmap_t;
struct stack_entry
{
const formula* grand_parent;
const formula* parent; // current node
formula grand_parent;
formula parent; // current node
succ_vec::const_iterator current_child;
succ_vec::const_iterator last_child;
};
@ -377,7 +336,7 @@ namespace spot
// cut-point, but since we only return Boolean cut-points it's
// OK: if the top-most formula is Boolean we want to replace it
// as a whole).
void cut_points(const fgraph& g, fset& c, const formula* start)
void cut_points(const fgraph& g, fset& c, formula start)
{
stack_t s;
@ -397,7 +356,7 @@ namespace spot
{
// Skip the edge if it is just the reverse of the one
// we took.
const formula* child = *e.current_child;
formula child = *e.current_child;
if (child == e.grand_parent)
{
++e.current_child;
@ -428,15 +387,15 @@ namespace spot
}
else
{
const formula* grand_parent = e.grand_parent;
const formula* parent = e.parent;
formula grand_parent = e.grand_parent;
formula parent = e.parent;
s.pop();
if (!s.empty())
{
data_entry& dparent = data[parent];
data_entry& dgrand_parent = data[grand_parent];
if (dparent.low >= dgrand_parent.num // cut-point
&& grand_parent->is_boolean())
&& grand_parent.is_boolean())
c.insert(grand_parent);
if (dparent.low < dgrand_parent.low)
dgrand_parent.low = dparent.low;
@ -450,7 +409,6 @@ namespace spot
{
public:
fset& c;
bse_relabeler(ap_generator* gen, fset& c,
relabeling_map* m)
: relabeler(gen, m), c(c)
@ -459,57 +417,51 @@ namespace spot
using relabeler::visit;
void
visit(const multop* mo)
formula
visit(formula f)
{
unsigned mos = mo->size();
if (f.is(op::AP) || (c.find(f) != c.end()))
return rename(f);
unsigned sz = f.size();
if (sz <= 2)
return f.map([this](formula f)
{
return visit(f);
});
unsigned i = 0;
std::vector<formula> res;
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
unsigned i = 0;
const formula* b = mo->is_boolean() ? 0 : mo->boolean_operands(&i);
multop::vec* res = new multop::vec;
formula b = f.boolean_operands(&i);
if (b)
{
res->reserve(mos - i + 1);
res->push_back(recurse(b));
b->destroy();
res.reserve(sz - i + 1);
res.push_back(visit(b));
}
else
{
res->reserve(mos);
res.reserve(sz);
}
for (; i < mos; ++i)
res->push_back(recurse(mo->nth(i)));
result_ = multop::instance(mo->op(), res);
}
const formula*
recurse(const formula* f)
{
fset::const_iterator it = c.find(f);
if (it != c.end())
result_ = rename(f);
else
f->accept(*this);
return result_;
for (; i < sz; ++i)
res.push_back(visit(f.nth(i)));
return formula::multop(f.kind(), res);
}
};
}
const formula*
relabel_bse(const formula* f, relabeling_style style,
relabeling_map* m)
formula
relabel_bse(formula f, relabeling_style style, relabeling_map* m)
{
fgraph g;
// Build the graph g from the formula f.
{
formula_to_fgraph conv(g);
conv.recurse(f);
conv.visit(f);
}
// Compute its cut-points
@ -529,18 +481,7 @@ namespace spot
break;
}
bse_relabeler rel(gen, c, m);
f = rel.recurse(f);
// Cleanup.
fgraph::const_iterator i = g.begin();
while (i != g.end())
{
const formula* f = i->first;
++i;
f->destroy();
}
return f;
return rel.visit(f);
}
}
}

View file

@ -29,25 +29,7 @@ namespace spot
{
enum relabeling_style { Abc, Pnn };
struct relabeling_map: public std::map<const formula*,
const formula*,
formula_ptr_less_than>
{
void clear()
{
iterator i = begin();
while (i != end())
i++->second->destroy();
this->std::map<const formula*, const formula*,
formula_ptr_less_than>::clear();
}
~relabeling_map()
{
clear();
}
};
typedef std::map<formula, formula> relabeling_map;
/// \ingroup ltl_rewriting
/// \brief Relabel the atomic propositions in a formula.
@ -55,8 +37,8 @@ namespace spot
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
const formula* relabel(const formula* f, relabeling_style style,
relabeling_map* m = 0);
formula relabel(formula f, relabeling_style style,
relabeling_map* m = 0);
/// \ingroup ltl_rewriting
@ -66,7 +48,7 @@ namespace spot
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
const formula* relabel_bse(const formula* f, relabeling_style style,
relabeling_map* m = 0);
formula relabel_bse(formula f, relabeling_style style,
relabeling_map* m = 0);
}
}

View file

@ -17,9 +17,7 @@
// 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 "ltlast/allnodes.hh"
#include "ltlvisit/simplify.hh"
#include "ltlvisit/clone.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/remove_x.hh"
@ -29,104 +27,76 @@ namespace spot
{
namespace
{
#define AND(x, y) multop::instance(multop::And, (x), (y))
#define OR(x, y) multop::instance(multop::Or, (x), (y))
#define NOT(x) unop::instance(unop::Not, (x))
#define G(x) unop::instance(unop::G, (x))
#define U(x, y) binop::instance(binop::U, (x), (y))
class remove_x_visitor final : public clone_visitor
static formula
remove_x_rec(formula f, atomic_prop_set& aps)
{
typedef clone_visitor super;
atomic_prop_set aps;
if (f.is_syntactic_stutter_invariant())
return f;
public:
remove_x_visitor(const formula* f)
{
atomic_prop_collect(f, &aps);
}
auto rec = [&aps](formula f)
{
return remove_x_rec(f, aps);
};
virtual
~remove_x_visitor()
{
}
if (!f.is(op::X))
return f.map(rec);
using super::visit;
void visit(const unop* uo)
{
const formula* c = recurse(uo->child());
formula c = rec(f.nth(0));
unop::type op = uo->op();
if (op != unop::X)
{
result_ = unop::instance(op, c);
return;
}
multop::vec* vo = new multop::vec;
for (atomic_prop_set::const_iterator i = aps.begin();
i != aps.end(); ++i)
{
// First line
multop::vec* va1 = new multop::vec;
const formula* npi = NOT((*i)->clone());
va1->push_back((*i)->clone());
va1->push_back(U((*i)->clone(), AND(npi, c->clone())));
for (atomic_prop_set::const_iterator j = aps.begin();
j != aps.end(); ++j)
if (*j != *i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = U(NOT((*j)->clone()), npi->clone());
va1->push_back(OR(U((*j)->clone(), npi->clone()), tmp));
}
vo->push_back(multop::instance(multop::And, va1));
// Second line
multop::vec* va2 = new multop::vec;
va2->push_back(npi->clone());
va2->push_back(U(npi->clone(), AND((*i)->clone(), c->clone())));
for (atomic_prop_set::const_iterator j = aps.begin();
j != aps.end(); ++j)
if (*j != *i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = U(NOT((*j)->clone()), (*i)->clone());
va2->push_back(OR(U((*j)->clone(), (*i)->clone()), tmp));
}
vo->push_back(multop::instance(multop::And, va2));
}
const formula* l12 = multop::instance(multop::Or, vo);
// Third line
multop::vec* va3 = new multop::vec;
for (atomic_prop_set::const_iterator i = aps.begin();
i != aps.end(); ++i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = G(NOT((*i)->clone()));
va3->push_back(OR(G((*i)->clone()), tmp));
}
result_ = OR(l12, AND(multop::instance(multop::And, va3), c));
return;
}
virtual const formula* recurse(const formula* f)
{
if (f->is_syntactic_stutter_invariant())
return f->clone();
f->accept(*this);
return this->result();
}
};
std::vector<formula> vo;
for (auto i: aps)
{
// First line
std::vector<formula> va1;
formula npi = formula::Not(i);
va1.push_back(i);
va1.push_back(formula::U(i, formula::And({npi, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), npi);
va1.push_back(formula::Or({formula::U(j, npi), tmp}));
}
vo.push_back(formula::And(va1));
// Second line
std::vector<formula> va2;
va2.push_back(npi);
va2.push_back(formula::U(npi, formula::And({i, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), i);
va2.push_back(formula::Or({formula::U(j, i), tmp}));
}
vo.push_back(formula::And(va2));
}
// Third line
std::vector<formula> va3;
for (auto i: aps)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::G(formula::Not(i));
va3.push_back(formula::Or({formula::G(i), tmp}));
}
va3.push_back(c);
vo.push_back(formula::And(va3));
return formula::Or(vo);
}
}
const formula* remove_x(const formula* f)
formula remove_x(formula f)
{
remove_x_visitor v(f);
return v.recurse(f);
if (f.is_syntactic_stutter_invariant())
return f;
atomic_prop_set aps;
atomic_prop_collect(f, &aps);
return remove_x_rec(f, aps);
}
}
}

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE).
// Copyright (C) 2013, 2015 Laboratoire de Recherche et Developpement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -19,14 +19,12 @@
#pragma once
#include "misc/common.hh"
#include "ltlast/formula.hh"
namespace spot
{
namespace ltl
{
class formula;
/// \brief Rewrite a stutter-insensitive formula \a f without
/// using the X operator.
///
@ -46,6 +44,6 @@ namespace spot
}
\endverbatim */
SPOT_API
const formula* remove_x(const formula* f);
formula remove_x(formula f);
}
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2012, 2014 Laboratoire de Recherche et
// Copyright (C) 2010, 2012, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -20,87 +20,28 @@
// 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 "ltlast/allnodes.hh"
#include "ltlvisit/clone.hh"
#include "simpfg.hh"
#include <cassert>
namespace spot
{
namespace ltl
{
simplify_f_g_visitor::simplify_f_g_visitor()
formula simplify_f_g(formula p)
{
// 1 U p = Fp
if (p.is(op::U) && p.nth(0).is_true())
return formula::F(p.nth(1));
// 0 R p = Gp
if (p.is(op::R) && p.nth(0).is_false())
return formula::G(p.nth(1));
// p W 0 = Gp
if (p.is(op::W) && p.nth(1).is_false())
return formula::G(p.nth(0));
// p M 1 = Fp
if (p.is(op::M) && p.nth(1).is_true())
return formula::F(p.nth(0));
return p.map(simplify_f_g);
}
simplify_f_g_visitor::~simplify_f_g_visitor()
{
}
void
simplify_f_g_visitor::visit(const binop* bo)
{
const formula* f1 = recurse(bo->first());
const formula* f2 = recurse(bo->second());
binop::type op = bo->op();
switch (op)
{
case binop::Xor:
case binop::Implies:
case binop::Equiv:
case binop::UConcat:
case binop::EConcat:
case binop::EConcatMarked:
result_ = binop::instance(op, f1, f2);
return;
/* true U f2 == F(f2) */
case binop::U:
if (f1 == constant::true_instance())
result_ = unop::instance(unop::F, f2);
else
result_ = binop::instance(binop::U, f1, f2);
return;
/* false R f2 == G(f2) */
case binop::R:
if (f1 == constant::false_instance())
result_ = unop::instance(unop::G, f2);
else
result_ = binop::instance(binop::R, f1, f2);
return;
/* f1 W false == G(f1) */
case binop::W:
if (f2 == constant::false_instance())
result_ = unop::instance(unop::G, f1);
else
result_ = binop::instance(binop::W, f1, f2);
return;
/* f1 M true == F(f1) */
case binop::M:
if (f2 == constant::true_instance())
result_ = unop::instance(unop::F, f1);
else
result_ = binop::instance(binop::M, f1, f2);
return;
}
SPOT_UNREACHABLE();
}
const formula*
simplify_f_g_visitor::recurse(const formula* f)
{
return simplify_f_g(f);
}
const formula*
simplify_f_g(const formula* f)
{
if (f->is_boolean())
return f->clone();
simplify_f_g_visitor v;
f->accept(v);
return v.result();
}
}
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2012, 2013 Laboratoire de Recherche et
// Copyright (C) 2010, 2012, 2013, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -22,36 +22,12 @@
#pragma once
#include "clone.hh"
#include "ltlast/formula.hh"
namespace spot
{
namespace ltl
{
/// \ingroup ltl_visitor
/// \brief Replace <code>true U f</code> and <code>false R g</code> by
/// <code>F f</code> and <code>G g</code>.
///
/// Perform the following rewriting (from left to right):
///
/// - true U a = F a
/// - a M true = F a
/// - false R a = G a
/// - a W false = G a
///
class SPOT_API simplify_f_g_visitor : public clone_visitor
{
typedef clone_visitor super;
public:
simplify_f_g_visitor();
virtual ~simplify_f_g_visitor();
using super::visit;
void visit(const binop* bo);
virtual const formula* recurse(const formula* f);
};
/// \ingroup ltl_rewriting
/// \brief Replace <code>true U f</code> and <code>false R g</code> by
/// <code>F f</code> and <code>G g</code>.
@ -63,6 +39,6 @@ namespace spot
/// - false R a = G a
/// - a W false = G a
///
SPOT_API const formula* simplify_f_g(const formula* f);
SPOT_API formula simplify_f_g(formula f);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et
// Developpement de l'Epita (LRDE).
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// et Developpement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -106,7 +106,7 @@ namespace spot
/// Simplify the formula \a f (using options supplied to the
/// constructor).
const formula* simplify(const formula* f);
formula simplify(formula f);
/// Build the negative normal form of formula \a f.
/// All negations of the formula are pushed in front of the
@ -116,8 +116,8 @@ namespace spot
/// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of
/// \c !f
const formula*
negative_normal_form(const formula* f, bool negated = false);
formula
negative_normal_form(formula f, bool negated = false);
/// \brief Syntactic implication.
///
@ -138,20 +138,20 @@ namespace spot
}
\endverbatim */
///
bool syntactic_implication(const formula* f, const formula* g);
bool syntactic_implication(formula f, formula g);
/// \brief Syntactic implication with one negated argument.
///
/// If \a right is true, this method returns whether
/// \a f implies !\a g. If \a right is false, this returns
/// whether !\a f implies \a g.
bool syntactic_implication_neg(const formula* f, const formula* g,
bool syntactic_implication_neg(formula f, formula g,
bool right);
/// \brief check whether two formulae are equivalent.
///
/// This costly check performs up to four translations,
/// two products, and two emptiness checks.
bool are_equivalent(const formula* f, const formula* g);
bool are_equivalent(formula f, formula g);
/// \brief Check whether \a f implies \a g.
@ -159,13 +159,13 @@ namespace spot
/// This operation is costlier than syntactic_implication()
/// because it requires two translation, one product and one
/// emptiness check.
bool implication(const formula* f, const formula* g);
bool implication(formula f, formula g);
/// \brief Convert a Boolean formula as a BDD.
///
/// If you plan to use this method, be sure to pass a bdd_dict
/// to the constructor.
bdd as_bdd(const formula* f);
bdd as_bdd(formula f);
/// \brief Clear the as_bdd() cache.
///
@ -182,14 +182,14 @@ namespace spot
bdd_dict_ptr get_dict() const;
/// Cached version of spot::ltl::star_normal_form().
const formula* star_normal_form(const formula* f);
formula star_normal_form(formula f);
/// \brief Rewrite a Boolean formula \a f into as an irredundant
/// sum of product.
///
/// This uses a cache, so it is OK to call this with identical
/// arguments.
const formula* boolean_to_isop(const formula* f);
formula boolean_to_isop(formula f);
/// Dump statistics about the caches.
void print_stats(std::ostream& os) const;

View file

@ -18,8 +18,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "snf.hh"
#include "ltlast/allnodes.hh"
#include "ltlast/visitor.hh"
namespace spot
{
@ -27,82 +25,51 @@ namespace spot
{
namespace
{
// E°
class snf_visitor: public visitor
// E° if bounded=false
// E^□ if nounded=true
template<bool bounded>
class snf_visitor
{
protected:
const formula* result_;
formula result_;
snf_cache* cache_;
public:
snf_visitor(snf_cache* c): cache_(c)
snf_visitor(snf_cache* c)
: cache_(c)
{
}
const formula*
result() const
formula visit(formula f)
{
return result_;
}
if (!f.accepts_eword())
return f;
void
visit(const atomic_prop*)
{
SPOT_UNIMPLEMENTED();
}
snf_cache::const_iterator i = cache_->find(f);
if (i != cache_->end())
return i->second;
void
visit(const constant* c)
{
assert(c == constant::empty_word_instance());
(void)c;
result_ = constant::false_instance();
}
void
visit(const bunop* bo)
{
bunop::type op = bo->op();
switch (op)
formula out;
switch (f.kind())
{
case bunop::Star:
assert(bo->accepts_eword());
// Strip the star.
result_ = recurse(bo->child());
case op::EmptyWord:
out = formula::ff();
break;
case bunop::FStar:
// FIXME: Can we deal with FStar in a better way?
result_ = bo->clone();
case op::Star:
if (!bounded)
out = visit(f.nth(0)); // Strip the star.
else
out = formula::Star(visit(f.nth(0)),
std::max(unsigned(f.min()), 1U), f.max());
break;
}
}
void
visit(const unop*)
{
SPOT_UNIMPLEMENTED();
}
void
visit(const binop*)
{
SPOT_UNIMPLEMENTED();
}
void
visit(const multop* mo)
{
multop::type op = mo->op();
switch (op)
{
case multop::And:
case multop::Or:
case multop::Fusion:
SPOT_UNIMPLEMENTED();
break;
case multop::Concat:
case multop::AndNLM:
case op::Concat:
if (bounded)
{
out = f;
break;
}
// Fall through
case op::OrRat:
case op::AndNLM:
// Let F designate expressions that accept [*0],
// and G designate expressions that do not.
@ -112,100 +79,70 @@ namespace spot
//
// AndNLM can be dealt with similarly.
//
// This case is already handled in recurse().
// if we reach this switch, we only have to
// deal with...
// The above cases are already handled by the
// accepts_eword() tests at the top of this method. So
// we reach this switch, we only have to deal with...
//
// (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°)
// so we fall through to the OrRat case...
case multop::OrRat:
assert(mo->accepts_eword());
// (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°)
{
unsigned s = mo->size();
multop::vec* v = new multop::vec;
v->reserve(s);
unsigned s = f.size();
std::vector<formula> v;
v.reserve(s);
for (unsigned pos = 0; pos < s; ++pos)
v->push_back(recurse(mo->nth(pos)));
result_ = multop::instance(multop::OrRat, v);
v.emplace_back(visit(f.nth(pos)));
out = formula::OrRat(v);
break;
}
break;
case multop::AndRat:
// FIXME: Can we deal with AndRat in a better way
// when it accepts [*0]?
result_ = mo->clone();
case op::False:
case op::True:
case op::AP:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::Fusion:
case op::Or:
case op::And:
SPOT_UNREACHABLE();
case op::AndRat: // Can AndRat be handled better?
case op::FStar: // Can FStar be handled better?
out = f;
break;
}
}
const formula*
recurse(const formula* f)
{
if (!f->accepts_eword())
return f->clone();
snf_cache::const_iterator i = cache_->find(f);
if (i != cache_->end())
return i->second->clone();
f->accept(*this);
(*cache_)[f->clone()] = result_->clone();
return result_;
}
};
// E^□
class snf_visitor_bounded: public snf_visitor
{
public:
snf_visitor_bounded(snf_cache* c): snf_visitor(c)
{
}
void
visit(const bunop* bo)
{
bunop::type op = bo->op();
switch (op)
{
case bunop::Star:
assert(bo->accepts_eword());
result_ = bunop::instance(bunop::Star,
recurse(bo->child()),
std::max(bo->min(), 1U),
bo->max());
break;
case bunop::FStar:
result_ = bo->clone();
break;
}
}
void
visit(const multop* mo)
{
if (mo->op() == multop::Concat)
result_ = mo->clone();
else
this->snf_visitor::visit(mo);
return (*cache_)[f] = out;
}
};
}
const formula*
star_normal_form(const formula* sere, snf_cache* cache)
formula
star_normal_form(formula sere, snf_cache* cache)
{
snf_visitor v(cache);
return v.recurse(sere);
snf_visitor<false> v(cache);
return v.visit(sere);
}
const formula*
star_normal_form_bounded(const formula* sere, snf_cache* cache)
formula
star_normal_form_bounded(formula sere, snf_cache* cache)
{
snf_visitor_bounded v(cache);
return v.recurse(sere);
snf_visitor<true> v(cache);
return v.visit(sere);
}
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Developpement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -20,15 +20,14 @@
#pragma once
#include "ltlast/formula.hh"
#include "misc/hash.hh"
#include <unordered_map>
namespace spot
{
namespace ltl
{
typedef std::unordered_map<const formula*, const formula*,
ptr_hash<formula>> snf_cache;
typedef std::unordered_map<formula, formula> snf_cache;
/// Helper to rewrite a sere in Star Normal Form.
///
@ -49,11 +48,11 @@ namespace spot
///
/// \param sere the SERE to rewrite
/// \param cache an optional cache
SPOT_API const formula*
star_normal_form(const formula* sere, snf_cache* cache = 0);
SPOT_API formula
star_normal_form(formula sere, snf_cache* cache = 0);
/// A variant of star_normal_form() for r[*0..j] where j < ω.
SPOT_API const formula*
star_normal_form_bounded(const formula* sere, snf_cache* cache = 0);
SPOT_API formula
star_normal_form_bounded(formula sere, snf_cache* cache = 0);
}
}

View file

@ -19,8 +19,6 @@
#include "unabbrev.hh"
#include "ltlast/allnodes.hh"
#include <cassert>
namespace spot
{
@ -70,242 +68,183 @@ namespace spot
}
}
unabbreviator::~unabbreviator()
{
auto i = cache_.begin();
auto end = cache_.end();
while (i != end)
{
auto old = i++;
old->second->destroy();
old->first->destroy();
}
}
const formula* unabbreviator::run(const formula* in)
formula unabbreviator::run(formula in)
{
auto entry = cache_.emplace(in, nullptr);
if (!entry.second)
return entry.first->second->clone();
in->clone();
return entry.first->second;
// Skip recursion whenever possible
bool no_boolean_rewrite = !re_some_bool_ || in->is_sugar_free_boolean();
bool no_f_g_rewrite = !re_some_f_g_ || in->is_sugar_free_ltl();
bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean();
bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl();
if (no_boolean_rewrite
&& (in->is_boolean() || (no_f_g_rewrite && !re_some_other_)))
&& (in.is_boolean() || (no_f_g_rewrite && !re_some_other_)))
return entry.first->second = in;
auto rec = [this](formula f)
{
entry.first->second = in->clone();
return in->clone();
return this->run(f);
};
formula out = in;
if (in.size() > 0)
out = in.map(rec);
switch (out.kind())
{
case op::False:
case op::True:
case op::EmptyWord:
case op::AP:
case op::Not:
case op::X:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::U:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
case op::Star:
case op::FStar:
break;
case op::F:
// F f = true U f
if (!re_f_)
break;
out = formula::U(formula::tt(), out.nth(0));
break;
case op::G:
// G f = false R f
// G f = f W false
// G f = !F!f
// G f = !(true U !f)
if (!re_g_)
break;
if (!re_r_)
{
out = formula::R(formula::ff(), out.nth(0));
break;
}
if (!re_w_)
{
out = formula::W(out.nth(0), formula::ff());
break;
}
{
auto nc = formula::Not(out.nth(0));
if (!re_f_)
{
out = formula::Not(formula::F(nc));
break;
}
out = formula::Not(formula::U(formula::tt(), nc));
break;
}
case op::Xor:
// f1 ^ f2 == !(f1 <-> f2)
// f1 ^ f2 == (f1 & !f2) | (f2 & !f1)
if (!re_xor_)
break;
{
auto f1 = out.nth(0);
auto f2 = out.nth(1);
if (!re_e_)
{
out = formula::Not(formula::Equiv(f1, f2));
}
else
{
auto a = formula::And({f1, formula::Not(f2)});
auto b = formula::And({f2, formula::Not(f1)});
out = formula::Or({a, b});
}
}
break;
case op::Implies:
// f1 => f2 == !f1 | f2
if (!re_i_)
break;
out = formula::Or({formula::Not(out.nth(0)), out.nth(1)});
break;
case op::Equiv:
// f1 <=> f2 == (f1 & f2) | (!f1 & !f2)
if (!re_e_)
break;
{
auto f1 = out.nth(0);
auto f2 = out.nth(1);
auto nf1 = formula::Not(f1);
auto nf2 = formula::Not(f2);
auto term1 = formula::And({f1, f2});
auto term2 = formula::And({nf1, nf2});
out = formula::Or({term1, term2});
break;
}
case op::R:
// f1 R f2 = f2 W (f1 & f2)
// f1 R f2 = f2 U ((f1 & f2) | Gf2)
// f1 R f2 = f2 U ((f1 & f2) | !F!f2)
// f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2))
if (!re_r_)
break;
{
auto f1 = out.nth(0);
auto f2 = out.nth(1);
auto f12 = formula::And({f1, f2});
if (!re_w_)
{
out = formula::W(f2, f12);
break;
}
auto gf2 = formula::G(f2);
if (re_g_)
gf2 = run(gf2);
out = formula::U(f2, formula::Or({f12, out}));
break;
}
case op::W:
// f1 W f2 = f2 R (f2 | f1)
// f1 W f2 = f1 U (f2 | G f1)
// f1 W f2 = f1 U (f2 | !F !f1)
// f1 W f2 = f1 U (f2 | !(1 U !f1))
if (!re_w_)
break;
{
auto f1 = out.nth(0);
auto f2 = out.nth(1);
if (!re_r_)
{
out = formula::R(f2, formula::Or({f2, f1}));
break;
}
auto gf1 = formula::G(f1);
if (re_g_)
gf1 = rec(gf1);
out = formula::U(f1, formula::Or({f2, out}));
break;
}
case op::M:
// f1 M f2 = f2 U (g2 & f1)
if (!re_m_)
break;
{
auto f2 = out.nth(1);
out = formula::U(f2, formula::And({f2, out.nth(0)}));
break;
}
}
const formula* out = nullptr;
switch (in->kind())
{
case formula::AtomicProp:
case formula::Constant:
out = in->clone();
break;
case formula::UnOp:
{
const unop* uo = static_cast<const unop*>(in);
auto c = run(uo->child());
switch (auto op = uo->op())
{
// F f = true U f
case unop::F:
if (!re_f_)
goto unop_clone;
out = binop::instance(binop::U, constant::true_instance(), c);
break;
// G f = false R f
// G f = f W false
// G f = !F!f
// G f = !(true U !f)
case unop::G:
if (!re_g_)
goto unop_clone;
if (!re_r_)
{
out = binop::instance(binop::R,
constant::false_instance(), c);
break;
}
if (!re_w_)
{
out = binop::instance(binop::W,
c, constant::false_instance());
break;
}
{
auto nc = unop::instance(unop::Not, c);
if (!re_f_)
{
out = unop::instance(unop::Not,
unop::instance(unop::F, nc));
break;
}
auto u = binop::instance(binop::U,
constant::true_instance(), nc);
out = unop::instance(unop::Not, u);
break;
}
case unop::Not:
case unop::X:
case unop::Closure:
case unop::NegClosure:
case unop::NegClosureMarked:
unop_clone:
out = unop::instance(op, c);
break;
}
break;
}
case formula::BinOp:
{
const binop* bo = static_cast<const binop*>(in);
auto f1 = run(bo->first());
auto f2 = run(bo->second());
switch (auto op = bo->op())
{
// f1 ^ f2 == !(f1 <-> f2)
// f1 ^ f2 == (f1 & !f2) | (f2 & !f1)
case binop::Xor:
{
if (!re_xor_)
goto binop_clone;
if (!re_e_)
{
out = unop::instance(unop::Not,
binop::instance(binop::Equiv,
f1, f2));
}
else
{
auto a = multop::instance(multop::And, f1->clone(),
unop::instance(unop::Not,
f2->clone()));
auto b = multop::instance(multop::And, f2,
unop::instance(unop::Not, f1));
out = multop::instance(multop::Or, a, b);
}
break;
}
// f1 => f2 == !f1 | f2
case binop::Implies:
if (!re_i_)
goto binop_clone;
out = multop::instance(multop::Or,
unop::instance(unop::Not, f1), f2);
break;
// f1 <=> f2 == (f1 & f2) | (!f1 & !f2)
case binop::Equiv:
if (!re_e_)
goto binop_clone;
{
auto nf1 = unop::instance(unop::Not, f1->clone());
auto nf2 = unop::instance(unop::Not, f2->clone());
auto term1 = multop::instance(multop::And, f1, f2);
auto term2 = multop::instance(multop::And, nf1, nf2);
out = multop::instance(multop::Or, term1, term2);
break;
}
// f1 W f2 = f2 R (f2 | f1)
// f1 W f2 = f1 U (f2 | G f1)
// f1 W f2 = f1 U (f2 | !F !f1)
// f1 W f2 = f1 U (f2 | !(1 U !f1))
case binop::W:
if (!re_w_)
goto binop_clone;
if (!re_r_)
{
out = binop::instance(binop::R, f2,
multop::instance(multop::Or,
f2->clone(), f1));
break;
}
f1->clone();
out = unop::instance(unop::G, f1);
if (re_g_)
{
auto tmp = out;
out = run(out);
tmp->destroy();
}
out = binop::instance(binop::U, f1,
multop::instance(multop::Or, f2, out));
break;
// f1 M f2 = f2 U (g2 & f1)
case binop::M:
if (!re_m_)
goto binop_clone;
out = binop::instance(binop::U, f2,
multop::instance(multop::And,
f2->clone(), f1));
break;
// f1 R f2 = f2 W (f1 & f2)
// f1 R f2 = f2 U ((f1 & f2) | Gf2)
// f1 R f2 = f2 U ((f1 & f2) | !F!f2)
// f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2))
case binop::R:
if (!re_r_)
goto binop_clone;
{
auto f12 = multop::instance(multop::And, f1, f2->clone());
if (!re_w_)
{
out = binop::instance(binop::W, f2, f12);
break;
}
out = unop::instance(unop::G, f2->clone());
if (re_g_)
{
auto tmp = out;
out = run(tmp);
tmp->destroy();
}
out = binop::instance(binop::U, f2,
multop::instance(multop::Or, f12, out));
}
break;
case binop::U:
case binop::UConcat:
case binop::EConcat:
case binop::EConcatMarked:
binop_clone:
out = binop::instance(op, f1, f2);
break;
}
break;
}
case formula::MultOp:
{
const multop* mo = static_cast<const multop*>(in);
multop::vec* res = new multop::vec;
unsigned mos = mo->size();
res->reserve(mos);
for (unsigned i = 0; i < mos; ++i)
res->push_back(run(mo->nth(i)));
out = multop::instance(mo->op(), res);
break;
}
case formula::BUnOp:
{
const bunop* bo = static_cast<const bunop*>(in);
out = bunop::instance(bo->op(), run(bo->child()),
bo->min(), bo->max());
break;
}
}
assert(out != nullptr);
entry.first->second = out;
return out->clone();
return entry.first->second = out;
}
const formula* unabbreviate(const formula* in, const char* opt)
formula unabbreviate(formula in, const char* opt)
{
unabbreviator un(opt);
return un.run(in);

View file

@ -47,7 +47,7 @@ namespace spot
bool re_some_f_g_ = false; // rewrite F or G
bool re_some_other_ = false; // rewrite W, M, or R
// Cache of rewritten subformulas
std::unordered_map<const formula*, const formula*> cache_;
std::unordered_map<formula, formula> cache_;
public:
/// \brief Constructor
///
@ -55,8 +55,7 @@ namespace spot
/// which in which each letter denote an operator (using LBT's
/// convention).
unabbreviator(const char* opt = default_unabbrev_string);
const formula* run(const formula* in);
~unabbreviator();
formula run(formula in);
};
/// \ingroup ltl_rewriting
@ -66,8 +65,8 @@ namespace spot
/// The set of operators to remove should be passed as a string
/// which in which each letter denote an operator (using LBT's
/// convention).
SPOT_API const formula*
unabbreviate(const formula* in, const char* opt= default_unabbrev_string);
SPOT_API formula
unabbreviate(formula in, const char* opt= default_unabbrev_string);
}
}

View file

@ -34,7 +34,6 @@
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include "ltlast/constant.hh"
#include "twa/formula2bdd.hh"
#include "public.hh"
#include "priv/accmap.hh"
@ -45,9 +44,10 @@
typedef std::map<int, bdd> map_t;
/* Cache parsed formulae. Labels on arcs are frequently identical
and it would be a waste of time to parse them to formula* over and
over, and to register all their atomic_propositions in the
bdd_dict. Keep the bdd result around so we can reuse it. */
and it would be a waste of time to parse them to ltl::formula
over and over, and to register all their atomic_propositions in
the bdd_dict. Keep the bdd result around so we can reuse
it. */
typedef std::map<std::string, bdd> formula_cache;
typedef std::pair<int, std::string*> pair;
@ -152,7 +152,6 @@
%code
{
#include <sstream>
#include "ltlast/constant.hh"
#include "ltlparse/public.hh"
/* parseaut.hh and parsedecl.hh include each other recursively.
@ -561,9 +560,7 @@ ap-name: STRING
std::ostringstream out;
out << "unknown atomic proposition \"" << *$1 << "\"";
error(@1, out.str());
f = spot::ltl::default_environment::instance()
.require("$unknown$");
b = res.h->aut->register_ap(f);
b = res.h->aut->register_ap("$unknown$");
}
else
{
@ -575,7 +572,6 @@ ap-name: STRING
error(@1, out.str());
}
}
f->destroy();
res.ap.push_back(b);
}
delete $1;
@ -1422,11 +1418,7 @@ nc-formula: nc-formula-or-ident
}
bdd cond = bddfalse;
if (f)
{
cond = spot::formula_to_bdd(f, res.h->aut->get_dict(),
res.h->aut);
f->destroy();
}
cond = spot::formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
$$ = (res.fcache[*$1] = cond).id();
}
else
@ -1587,7 +1579,7 @@ lbtt-acc: { $$ = 0U; }
lbtt-guard: STRING
{
spot::ltl::parse_error_list pel;
auto* f = spot::ltl::parse_prefix_ltl(*$1, pel, *res.env);
auto f = spot::ltl::parse_prefix_ltl(*$1, pel, *res.env);
if (!f || !pel.empty())
{
std::string s = "failed to parse guard: ";
@ -1611,7 +1603,7 @@ lbtt-guard: STRING
}
else
{
if (!f->is_boolean())
if (!f.is_boolean())
{
error(@$,
"non-Boolean transition label (replaced by true)");
@ -1622,7 +1614,6 @@ lbtt-guard: STRING
res.cur_label =
formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
}
f->destroy();
}
delete $1;
}

View file

@ -130,7 +130,7 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do
perl -pe 'sub f {my $a = shift; $a =~ s:[^\n]*://:g; return "$a"}
s,/\*(.*?)\*/,f($1),sge;
s,//.*?\n,//\n,g;
s,"[^"\n]*","",g;
s,"(\\.|[^"\\\n])*","",g;
s,SPOT_API ,,g' -0777 <$file >$tmp
$GREP '[ ]$' $tmp &&
@ -231,10 +231,14 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do
$GREP -v 'for (.*;;)' $tmp | $GREP ';[^ ")'"']" &&
diag 'Must have space or newline after semicolon.'
$GREP '}.*}' $tmp &&
# Allow several { or } on the same line only if they are mixed
# with parentheses, as this often occur with lambdas or
# initializer lists. What we want to forbid is cases where
# multiple scopes are opened/closed on the same line.
$GREP '^[^()]*}[^()]*}[^()]*$' $tmp &&
diag 'No two } on the same line.'
$GREP '{.*{' $tmp &&
$GREP '^[^()]{[^()]*{[^()]$' $tmp &&
diag 'No two { on the same line.'
$GREP 'delete[ ]*[(][^(]*[)];' $tmp &&

View file

@ -26,8 +26,6 @@
#define trace while (0) std::clog
#endif
#include "ltlast/atomic_prop.hh"
#include "ltlast/constant.hh"
#include "taexplicit.hh"
#include "twa/formula2bdd.hh"
#include <cassert>

View file

@ -17,8 +17,6 @@
// 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 "ltlast/atomic_prop.hh"
#include "ltlast/constant.hh"
#include "tgtaexplicit.hh"
#include "twa/formula2bdd.hh"
#include "twa/bddprint.hh"

Some files were not shown because too many files have changed in this diff Show more