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 (But dstar2tgba does not offer all the filtering and
transformations options of autfilt.) 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) New in spot 1.99.3 (2015-08-26)
* The CGI script for LTL translation offers a HOA download link * The CGI script for LTL translation offers a HOA download link

View file

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

View file

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

View file

@ -72,10 +72,9 @@ exceptions.
int main() int main()
{ {
print_latex_psl(std::cout, spot::ltl::parse_formula("[]<>p0 || <>[]p1")) << '\n'; 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_lbt_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, f, true) << '\n'; print_spin_ltl(std::cout, f, true) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+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 are only using LTL formulas for demonstration, so those three
functions are OK with that. 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 We do not recommend using this =parse_formula()= interface because of
the potential formulas (like =f= or =t=) that have different meanings the potential formulas (like =f= or =t=) that have different meanings
in the two parsers that are tried. 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"; std::string input = "[]<>p0 || <>[]p1";
spot::ltl::parse_error_list pel; 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 (spot::ltl::format_parse_errors(std::cerr, input, pel))
{ return 1;
if (f)
f->destroy();
return 1;
}
print_latex_psl(std::cout, f) << '\n'; print_latex_psl(std::cout, f) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+END_SRC
@ -165,14 +153,14 @@ with the "fixed" formula if you wish. Here is an example:
{ {
std::string input = "(a U b))"; std::string input = "(a U b))";
spot::ltl::parse_error_list pel; 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 // Use std::cout instead of std::cerr because we can only
// show the output of std::cout in this documentation. // show the output of std::cout in this documentation.
(void) spot::ltl::format_parse_errors(std::cout, input, pel); (void) spot::ltl::format_parse_errors(std::cout, input, pel);
if (f == nullptr) if (f == nullptr)
return 1; return 1;
print_latex_psl(std::cout, f) << '\n'; std::cout << "Parsed formula: ";
f->destroy(); print_psl(std::cout, f) << '\n';
return 0; return 0;
} }
#+END_SRC #+END_SRC
@ -186,7 +174,7 @@ with the "fixed" formula if you wish. Here is an example:
: ^ : ^
: ignoring trailing garbage : ignoring trailing garbage
: :
: a \U b : Parsed formula: a U b
The formula =f= is only returned as null when the parser really cannot 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"; std::string input = "& & G p0 p1 p2";
spot::ltl::parse_error_list pel; 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 (spot::ltl::format_parse_errors(std::cerr, input, pel))
{ return 1;
if (f)
f->destroy();
return 1;
}
print_lbt_ltl(std::cout, f) << '\n'; print_lbt_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, f, true) << '\n'; print_spin_ltl(std::cout, f, true) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+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))"; std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel; 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 (spot::ltl::format_parse_errors(std::cerr, input, pel))
{ return 1;
if (f)
f->destroy();
return 1;
}
print_spin_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+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))"; std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel; 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 (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"; std::cerr << "Only LTL formulas are supported.\n";
return 1; return 1;
} }
print_spin_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+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 prepared to reject the formula any way. In our example, we are lucky
(maybe because it was carefully chosen...): (maybe because it was carefully chosen...):
#+BEGIN_SRC C++ :results verbatim :exports code #+BEGIN_SRC C++ :results verbatim :exports both
#include <string> #include <string>
#include <iostream> #include <iostream>
#include "ltlparse/public.hh" #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))"; std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::ltl::parse_error_list pel; 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 (spot::ltl::format_parse_errors(std::cerr, input, pel))
{ return 1;
if (f) if (!f.is_ltl_formula())
f->destroy();
return 1;
}
if (!f->is_ltl_formula())
{ {
spot::ltl::ltl_simplifier simp; spot::ltl::ltl_simplifier simp;
const formula* g = simp.simplify(f); f = simp.simplify(f);
f->destroy();
f = g;
} }
if (!f->is_ltl_formula()) if (!f.is_ltl_formula())
{ {
f->destroy();
std::cerr << "Only LTL formulas are supported.\n"; std::cerr << "Only LTL formulas are supported.\n";
return 1; return 1;
} }
print_spin_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f) << '\n';
f->destroy();
return 0; return 0;
} }
#+END_SRC #+END_SRC

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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) // Developpement de l'Epita (LRDE)
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
@ -21,7 +21,6 @@
#include "kripke/kripke.hh" #include "kripke/kripke.hh"
#include "ltlvisit/apcollect.hh" #include "ltlvisit/apcollect.hh"
#include "ltlast/constant.hh"
namespace spot namespace spot
{ {
@ -59,6 +58,6 @@ namespace spot
SPOT_API kripke_ptr SPOT_API kripke_ptr
load_ltsmin(const std::string& file, const bdd_dict_ptr& dict, load_ltsmin(const std::string& file, const bdd_dict_ptr& dict,
const ltl::atomic_prop_set* to_observe, 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); int compress = 0, bool verbose = true);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -68,7 +68,7 @@ const struct argp output_argp = { options, parse_opt_output, 0, 0, 0, 0, 0 };
static static
void void
report_not_ltl(const spot::ltl::formula* f, report_not_ltl(spot::ltl::formula f,
const char* filename, int linenum, const char* syn) const char* filename, int linenum, const char* syn)
{ {
std::string s = spot::ltl::str_psl(f); std::string s = spot::ltl::str_psl(f);
@ -82,12 +82,12 @@ report_not_ltl(const spot::ltl::formula* f,
std::ostream& std::ostream&
stream_formula(std::ostream& out, 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) switch (output_format)
{ {
case lbt_output: case lbt_output:
if (f->is_ltl_formula()) if (f.is_ltl_formula())
spot::ltl::print_lbt_ltl(out, f); spot::ltl::print_lbt_ltl(out, f);
else else
report_not_ltl(f, filename, linenum, "LBT"); report_not_ltl(f, filename, linenum, "LBT");
@ -96,13 +96,13 @@ stream_formula(std::ostream& out,
spot::ltl::print_psl(out, f, full_parenth); spot::ltl::print_psl(out, f, full_parenth);
break; break;
case spin_output: case spin_output:
if (f->is_ltl_formula()) if (f.is_ltl_formula())
spot::ltl::print_spin_ltl(out, f, full_parenth); spot::ltl::print_spin_ltl(out, f, full_parenth);
else else
report_not_ltl(f, filename, linenum, "Spin"); report_not_ltl(f, filename, linenum, "Spin");
break; break;
case wring_output: case wring_output:
if (f->is_ltl_formula()) if (f.is_ltl_formula())
spot::ltl::print_wring_ltl(out, f); spot::ltl::print_wring_ltl(out, f);
else else
report_not_ltl(f, filename, linenum, "Wring"); report_not_ltl(f, filename, linenum, "Wring");
@ -122,7 +122,7 @@ stream_formula(std::ostream& out,
static void static void
stream_escapable_formula(std::ostream& os, stream_escapable_formula(std::ostream& os,
const spot::ltl::formula* f, spot::ltl::formula f,
const char* filename, int linenum) const char* filename, int linenum)
{ {
if (escape_csv) if (escape_csv)
@ -144,7 +144,7 @@ namespace
{ {
struct formula_with_location struct formula_with_location
{ {
const spot::ltl::formula* f; spot::ltl::formula f;
const char* filename; const char* filename;
int line; int line;
const char* prefix; const char* prefix;
@ -258,7 +258,7 @@ parse_opt_output(int key, char* arg, struct argp_state*)
static void static void
output_formula(std::ostream& out, output_formula(std::ostream& out,
const spot::ltl::formula* f, spot::ltl::formula f,
const char* filename = nullptr, int linenum = 0, const char* filename = nullptr, int linenum = 0,
const char* prefix = nullptr, const char* suffix = nullptr) const char* prefix = nullptr, const char* suffix = nullptr)
{ {
@ -284,7 +284,7 @@ void
} }
void void
output_formula_checked(const spot::ltl::formula* f, output_formula_checked(spot::ltl::formula f,
const char* filename, int linenum, const char* filename, int linenum,
const char* prefix, const char* suffix) 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 // Low-level output
std::ostream& std::ostream&
stream_formula(std::ostream& out, 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* filename = 0, int linenum = 0,
const char* prefix = 0, const char* suffix = 0); const char* prefix = 0, const char* suffix = 0);
class printable_formula: class printable_formula:
public spot::printable_value<const spot::ltl::formula*> public spot::printable_value<spot::ltl::formula>
{ {
public: public:
printable_formula& printable_formula&
operator=(const spot::ltl::formula* new_val) operator=(spot::ltl::formula new_val)
{ {
val_ = new_val; val_ = new_val;
return *this; return *this;
@ -78,7 +78,7 @@ public:
std::ostream& std::ostream&
print(const spot::const_twa_graph_ptr& aut, print(const spot::const_twa_graph_ptr& aut,
const spot::ltl::formula* f = 0, spot::ltl::formula f = 0,
double run_time = -1.) double run_time = -1.)
{ {
formula_ = f; formula_ = f;

View file

@ -290,7 +290,7 @@ translator_runner::formula() const
} }
void 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')) if (has('f') || has('F'))
string_ltl_spot = spot::ltl::str_psl(f, true); string_ltl_spot = spot::ltl::str_psl(f, true);

View file

@ -94,7 +94,7 @@ public:
bool no_output_allowed = false); bool no_output_allowed = false);
void string_to_tmp(std::string& str, unsigned n, std::string& tmpname); void string_to_tmp(std::string& str, unsigned n, std::string& tmpname);
const std::string& formula() const; 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 int
process_formula(const spot::ltl::formula*, const char*, int) process_formula(spot::ltl::formula, const char*, int)
{ {
SPOT_UNREACHABLE(); SPOT_UNREACHABLE();
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,10 +27,6 @@
#include "common_output.hh" #include "common_output.hh"
#include "common_conv.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" #include "ltlvisit/mutation.hh"
enum { enum {
@ -100,17 +96,13 @@ namespace
{ {
public: public:
int 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) int linenum = 0)
{ {
auto mutations = auto mutations =
spot::ltl::mutate(f, mut_opts, max_output, mutation_nb, opt_sort); spot::ltl::mutate(f, mut_opts, max_output, mutation_nb, opt_sort);
f->destroy();
for (auto g: mutations) for (auto g: mutations)
{ output_formula_checked(g, filename, linenum);
output_formula_checked(g, filename, linenum);
g->destroy();
}
return 0; return 0;
} }
}; };

View file

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

View file

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

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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) // Developpement de l'Epita (LRDE)
// //
// This file is part of Spot, a model checking library. // 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, void kripke_explicit::add_condition(ltl::formula f, std::string on_me)
std::string on_me)
{ {
add_conditions(formula_to_bdd(f, get_dict(), this), on_me); add_conditions(formula_to_bdd(f, get_dict(), this), on_me);
f->destroy();
} }

View file

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

View file

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

View file

@ -1,6 +1,6 @@
## -*- coding: utf-8 -*- ## -*- coding: utf-8 -*-
## Copyright (C) 2009, 2010, 2011, 2013, 2014 Laboratoire de Recherche ## Copyright (C) 2009, 2010, 2011, 2013, 2014, 2015 Laboratoire de
## et Développement de l'Epita (LRDE). ## Recherche et Développement de l'Epita (LRDE).
## Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), ## Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
## département Systèmes Répartis Coopératifs (SRC), Université Pierre ## département Systèmes Répartis Coopératifs (SRC), Université Pierre
## et Marie Curie. ## et Marie Curie.
@ -26,24 +26,7 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS)
ltlastdir = $(pkgincludedir)/ltlast ltlastdir = $(pkgincludedir)/ltlast
ltlast_HEADERS = \ ltlast_HEADERS = formula.hh
allnodes.hh \
atomic_prop.hh \
binop.hh \
bunop.hh \
constant.hh \
formula.hh \
multop.hh \
predecl.hh \
unop.hh \
visitor.hh
noinst_LTLIBRARIES = libltlast.la noinst_LTLIBRARIES = libltlast.la
libltlast_la_SOURCES = \ libltlast_la_SOURCES = formula.cc
atomic_prop.cc \
binop.cc \
bunop.cc \
constant.cc \
formula.cc \
multop.cc \
unop.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 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2009, 2012, 2014 Laboratoire de Recherche et Développement // Copyright (C) 2009, 2012, 2014, 2015 Laboratoire de Recherche et
// de l'Epita (LRDE). // Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // 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 bool
declarative_environment::declare(const std::string& prop_str) declarative_environment::declare(const std::string& prop_str)
{ {
if (props_.find(prop_str) != props_.end()) if (props_.find(prop_str) != props_.end())
return false; return false;
props_[prop_str] = ltl::atomic_prop::instance(prop_str, *this); props_[prop_str] = formula::ap(prop_str);
return true; return true;
} }
const formula* formula
declarative_environment::require(const std::string& prop_str) declarative_environment::require(const std::string& prop_str)
{ {
prop_map::iterator i = props_.find(prop_str); prop_map::iterator i = props_.find(prop_str);
if (i == props_.end()) if (i == props_.end())
return 0; return nullptr;
return i->second->clone(); return i->second;
} }
const std::string& const std::string&

View file

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

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement // Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et
// de l'Epita (LRDE). // Développement de l'Epita (LRDE).
// Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -31,10 +31,10 @@ namespace spot
{ {
} }
const atomic_prop* formula
default_environment::require(const std::string& s) default_environment::require(const std::string& s)
{ {
return atomic_prop::instance(s, *this); return formula::ap(s);
} }
const std::string& const std::string&

View file

@ -1,7 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et // Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement // Développement de l'Epita (LRDE).
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -24,7 +23,7 @@
#pragma once #pragma once
#include "environment.hh" #include "environment.hh"
#include "ltlast/atomic_prop.hh" #include "ltlast/formula.hh"
namespace spot namespace spot
{ {
@ -42,7 +41,7 @@ namespace spot
{ {
public: public:
virtual ~default_environment(); 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; virtual const std::string& name() const;
/// Get the sole instance of spot::ltl::default_environment. /// Get the sole instance of spot::ltl::default_environment.

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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). // Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // 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, /// Usually \a prop_str, is the name of an atomic proposition,
/// and spot::ltl::require simply returns the associated /// 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 /// Note this is not a \c const method. Some environments will
/// "create" the atomic proposition when requested. /// "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, /// \return 0 iff \a prop_str is not part of the environment,
/// or the associated spot::ltl::formula otherwise. /// 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. /// Get the name of the environment.
virtual const std::string& name() const = 0; virtual const std::string& name() const = 0;

View file

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

View file

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

View file

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

View file

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

View file

@ -22,7 +22,7 @@
#pragma once #pragma once
#include "ltlast/atomic_prop.hh" #include "ltlast/formula.hh"
#include <set> #include <set>
#include <bddx.h> #include <bddx.h>
#include "twa/fwd.hh" #include "twa/fwd.hh"
@ -35,18 +35,12 @@ namespace spot
/// @{ /// @{
/// Set of atomic propositions. /// Set of atomic propositions.
typedef std::set<const atomic_prop*, typedef std::set<formula> atomic_prop_set;
formula_ptr_less_than> atomic_prop_set;
/// \brief construct an atomic_prop_set with n propositions /// \brief construct an atomic_prop_set with n propositions
SPOT_API SPOT_API
atomic_prop_set create_atomic_prop_set(unsigned n); 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. /// \brief Return the set of atomic propositions occurring in a formula.
/// ///
/// \param f the formula to inspect /// \param f the formula to inspect
@ -55,10 +49,8 @@ namespace spot
/// \return A pointer to the supplied set, \c s, augmented with /// \return A pointer to the supplied set, \c s, augmented with
/// atomic propositions occurring in \c f; or a newly allocated /// atomic propositions occurring in \c f; or a newly allocated
/// set containing all these atomic propositions if \c s is 0. /// 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* 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 /// \brief Return the set of atomic propositions occurring in a
/// formula, as a BDD. /// formula, as a BDD.
@ -67,8 +59,7 @@ namespace spot
/// \param a that automaton that should register the BDD variables used. /// \param a that automaton that should register the BDD variables used.
/// \return A conjunction the atomic propositions. /// \return A conjunction the atomic propositions.
SPOT_API bdd SPOT_API bdd
atomic_prop_collect_as_bdd(const formula* f, atomic_prop_collect_as_bdd(formula f, const twa_ptr& a);
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 "contain.hh"
#include "simplify.hh" #include "simplify.hh"
#include "ltlast/unop.hh" #include "ltlast/formula.hh"
#include "ltlast/binop.hh"
#include "ltlast/multop.hh"
#include "ltlast/constant.hh"
#include "twaalgos/product.hh" #include "twaalgos/product.hh"
namespace spot namespace spot
@ -50,13 +47,7 @@ namespace spot
void void
language_containment_checker::clear() language_containment_checker::clear()
{ {
while (!translated_.empty()) translated_.clear();
{
trans_map::iterator i = translated_.begin();
const formula* f = i->first;
translated_.erase(i);
f->destroy();
}
} }
bool bool
@ -75,31 +66,26 @@ namespace spot
// Check whether L(l) is a subset of L(g). // Check whether L(l) is a subset of L(g).
bool bool
language_containment_checker::contained(const formula* l, language_containment_checker::contained(formula l,
const formula* g) formula g)
{ {
if (l == g) if (l == g)
return true; return true;
record_* rl = register_formula_(l); record_* rl = register_formula_(l);
const formula* ng = unop::instance(unop::Not, g->clone()); record_* rng = register_formula_(formula::Not(g));
record_* rng = register_formula_(ng);
ng->destroy();
return incompatible_(rl, rng); return incompatible_(rl, rng);
} }
// Check whether L(!l) is a subset of L(g). // Check whether L(!l) is a subset of L(g).
bool bool
language_containment_checker::neg_contained(const formula* l, language_containment_checker::neg_contained(formula l,
const formula* g) formula g)
{ {
if (l == g) if (l == g)
return false; return false;
const formula* nl = unop::instance(unop::Not, l->clone()); formula nl = formula::Not(l);
record_* rnl = register_formula_(nl); record_* rnl = register_formula_(nl);
const formula* ng = unop::instance(unop::Not, g->clone()); record_* rng = register_formula_(formula::Not(g));
record_* rng = register_formula_(ng);
nl->destroy();
ng->destroy();
if (nl == g) if (nl == g)
return true; return true;
return incompatible_(rnl, rng); return incompatible_(rnl, rng);
@ -107,8 +93,8 @@ namespace spot
// Check whether L(l) is a subset of L(!g). // Check whether L(l) is a subset of L(!g).
bool bool
language_containment_checker::contained_neg(const formula* l, language_containment_checker::contained_neg(formula l,
const formula* g) formula g)
{ {
if (l == g) if (l == g)
return false; return false;
@ -119,13 +105,13 @@ namespace spot
// Check whether L(l) = L(g). // Check whether L(l) = L(g).
bool 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); return contained(l, g) && contained(g, l);
} }
language_containment_checker::record_* 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); trans_map::iterator i = translated_.find(f);
if (i != translated_.end()) if (i != translated_.end())
@ -133,17 +119,17 @@ namespace spot
auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_, auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_,
branching_postponement_, fair_loop_approx_); branching_postponement_, fair_loop_approx_);
record_& r = translated_[f->clone()]; record_& r = translated_[f];
r.translation = e; r.translation = e;
return &r; return &r;
} }
const formula* formula
reduce_tau03(const formula* f, bool stronger) reduce_tau03(formula f, bool stronger)
{ {
if (!f->is_psl_formula()) if (!f.is_psl_formula())
return f->clone(); return f;
ltl_simplifier_options opt(false, false, false, ltl_simplifier_options opt(false, false, false,
true, stronger); true, stronger);

View file

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

View file

@ -20,10 +20,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "misc/hash.hh"
#include "dot.hh" #include "dot.hh"
#include "ltlast/visitor.hh" #include "ltlast/formula.hh"
#include "ltlast/allnodes.hh" #include <unordered_map>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
@ -33,160 +32,106 @@ namespace spot
{ {
namespace namespace
{ {
class dotty_visitor: public visitor struct dot_printer final
{ {
public: std::ostream& os_;
typedef std::unordered_map<const formula*, int, ptr_hash<formula>> map; std::unordered_map<formula, int> node_;
dotty_visitor(std::ostream& os, map& m) std::ostringstream* sinks_;
: os_(os), father_(-1), node_(m), sinks_(new std::ostringstream)
{
}
virtual dot_printer(std::ostream& os, formula f)
~dotty_visitor() : os_(os), sinks_(new std::ostringstream)
{ {
} os_ << "digraph G {\n";
rec(f);
os_ << " subgraph atoms {\n rank=sink;\n"
<< sinks_->str() << " }\n}\n";
}
void ~dot_printer()
visit(const atomic_prop* ap)
{ {
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_; delete sinks_;
} }
int childnum; int rec(formula f)
private:
std::ostream& os_;
int father_;
map& node_;
std::ostringstream* sinks_;
bool
draw_node_(const formula* f, const std::string& str, bool sink = false)
{ {
map::iterator i = node_.find(f); auto i = node_.emplace(f, node_.size());
int node; int src = i.first->second;
bool node_exists = false; if (!i.second)
if (i != node_.end()) return src;
{
node = i->second; op o = f.kind();
node_exists = true; 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 else
os_ << " " << src << " [label=\"" << str << "\"];\n";
int childnum = 0;
switch (o)
{ {
node = node_.size(); case op::False:
node_[f] = node; 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) if (childnum > 0)
os_ << " [taillabel=\"" << childnum << "\"]"; os_ << " [taillabel=\"" << childnum << "\"]";
if (childnum == -1) if (childnum == -2)
os_ << " [taillabel=\"L\"]"; os_ << " [taillabel=\"L\"]";
else if (childnum == -2) else if (childnum == -1)
os_ << " [taillabel=\"R\"]"; os_ << " [taillabel=\"R\"]";
os_ << ";\n"; os_ << ";\n";
++childnum;
} }
father_ = node;
// the node return src;
if (node_exists)
return false;
if (!sink)
{
os_ << " " << node << " [label=\"" << str << "\"];";
}
else
{
*sinks_ << " " << node
<< " [label=\"" << str << "\", shape=box];\n";
}
return true;
} }
}; };
} }
std::ostream& std::ostream&
print_dot_psl(std::ostream& os, const formula* f) print_dot_psl(std::ostream& os, formula f)
{ {
dotty_visitor::map m; dot_printer p(os, f);
dotty_visitor v(os, m);
os << "digraph G {\n";
f->accept(v);
v.finish();
os << '}' << std::endl;
return os; return os;
} }
} }
} }

View file

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

View file

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

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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). // Développement de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // 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/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "length.hh" #include "length.hh"
#include "ltlvisit/postfix.hh" #include "ltlast/formula.hh"
#include "ltlast/multop.hh"
#include "ltlast/unop.hh"
namespace spot namespace spot
{ {
namespace ltl namespace ltl
{ {
namespace int
length(formula f)
{ {
class length_visitor: public postfix_visitor int len = 0;
{ f.traverse([&len](const formula& x)
public: {
length_visitor() auto s = x.size();
: result_(0) if (s > 1)
{ len += s - 1;
} else
++len;
int return false;
result() const });
{ return len;
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 int
length(const formula* f) length_boolone(formula f)
{ {
length_visitor v; int len = 0;
f->accept(v); f.traverse([&len](const formula& x)
return v.result(); {
} if (x.is_boolean())
{
int ++len;
length_boolone(const formula* f) return true;
{ }
length_boolone_visitor v; auto s = x.size();
f->accept(v); if (s > 2)
return v.result(); {
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 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Developement de // Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
// l'Epita (LRDE). // Développement de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -33,15 +33,14 @@ namespace spot
/// ///
/// The length of a formula is the number of atomic propositions, /// The length of a formula is the number of atomic propositions,
/// constants, and operators (logical and temporal) occurring in /// constants, and operators (logical and temporal) occurring in
/// the formula. spot::ltl::multop instances with n arguments /// the formula. n-ary operators count for n-1; for instance
/// count for n-1; for instance <code>a | b | c</code> has length /// <code>a | b | c</code> has length 5, even if there is only as
/// 5, even if there is only as single <code>|</code> node /// single <code>|</code> node internally.
/// internally.
/// ///
/// If squash_boolean is set, all Boolean formulae are assumed /// If squash_boolean is set, all Boolean formulae are assumed
/// to have length one. /// to have length one.
SPOT_API SPOT_API
int length(const formula* f); int length(formula f);
/// \ingroup ltl_misc /// \ingroup ltl_misc
/// \brief Compute the length of a formula, squashing Boolean formulae /// \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 /// This is similar to spot::ltl::length(), except all Boolean
/// formulae are assumed to have length one. /// formulae are assumed to have length one.
SPOT_API 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/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mark.hh" #include "mark.hh"
#include "ltlast/allnodes.hh"
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
#include <set> #include <set>
#include <vector> #include <vector>
#include "misc/casts.hh"
namespace spot namespace spot
{ {
namespace ltl namespace ltl
{ {
namespace formula
{ mark_tools::mark_concat_ops(formula f)
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)
{ {
f2f_map::iterator i = markops_.find(f); f2f_map::iterator i = markops_.find(f);
if (i != markops_.end()) 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] = res;
markops_[f->clone()] = r->clone(); return res;
return r;
} }
const formula* formula
mark_tools::simplify_mark(const formula* f) mark_tools::simplify_mark(formula f)
{ {
if (!f->is_marked()) if (!f.is_marked())
return f->clone(); return f;
f2f_map::iterator i = simpmark_.find(f); f2f_map::iterator i = simpmark_.find(f);
if (i != simpmark_.end()) 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 = ltl::formula res;
down_cast<simplify_mark_visitor*>(simpvisitor_)->result(); switch (f.kind())
simpmark_[f->clone()] = r->clone(); {
return r; 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 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2010, 2011, 2012, 2013 Laboratoire de Recherche et // Copyright (C) 2010, 2011, 2012, 2013, 2015 Laboratoire de Recherche
// Développement de l'Epita (LRDE). // et Développement de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -20,34 +20,27 @@
#pragma once #pragma once
#include "ltlast/formula.hh" #include "ltlast/formula.hh"
#include "ltlast/visitor.hh"
#include "misc/hash.hh" #include "misc/hash.hh"
namespace spot namespace spot
{ {
namespace ltl namespace ltl
{ {
class mark_tools class mark_tools final
{ {
public: public:
/// \ingroup ltl_rewriting /// \ingroup ltl_rewriting
/// \brief Mark operators NegClosure and EConcat. /// \brief Mark operators NegClosure and EConcat.
/// ///
/// \param f The formula to rewrite. /// \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); formula simplify_mark(formula f);
mark_tools();
~mark_tools();
private: private:
typedef std::unordered_map<const formula*, const formula*, typedef std::unordered_map<formula, formula> f2f_map;
ptr_hash<formula>> f2f_map;
f2f_map simpmark_; f2f_map simpmark_;
f2f_map markops_; 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 // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <unordered_set> #include <set>
#include <algorithm> #include <algorithm>
#include "ltlast/allnodes.hh"
#include "ltlvisit/apcollect.hh" #include "ltlvisit/apcollect.hh"
#include "ltlvisit/clone.hh"
#include "ltlvisit/mutation.hh" #include "ltlvisit/mutation.hh"
#include "ltlvisit/length.hh" #include "ltlvisit/length.hh"
#include "misc/hash.hh"
#define Implies_(x, y) \ #define And_(x, y) formula::And({(x), (y)})
spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y)) #define AndRat_(x, y) formula::AndRat({(x), (y)})
#define And_(x, y) \ #define AndNLM_(x) formula::AndNLM(x)
spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y)) #define Concat_(x, y) formula::Concat({(x), (y)})
#define AndRat_(x, y) \ #define Not_(x) formula::Not(x)
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))
namespace spot namespace spot
{ {
@ -46,355 +36,307 @@ namespace spot
{ {
namespace namespace
{ {
class replace_visitor final : public clone_visitor formula substitute_ap(formula f, formula ap_src, formula ap_dst)
{ {
public: return f.map([&](formula f)
void visit(const atomic_prop* ap) {
{ if (f == ap_src)
if (ap == ap1_) return ap_dst;
result_ = ap2_->clone(); else
else return substitute_ap(f, ap_src, ap_dst);
result_ = ap->clone(); });
} }
const formula* typedef std::vector<formula> vec;
replace(const formula* f, class mutator final
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
{ {
int mutation_counter_ = 0;
formula f_;
unsigned opts_;
public: 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; auto recurse = [this](formula f)
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)
{ {
if (!left_is_sere) return this->mutate(f);
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);
}
}
}
void visit(const bunop* bu) switch (f.kind())
{ {
const formula* c = bu->child()->clone(); case op::False:
result_ = nullptr; case op::True:
auto op = bu->op(); case op::EmptyWord:
return f;
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) case op::AP:
result_ = c; if (opts_ & Mut_Ap2Const)
if (opts_ & Mut_Simplify_Bounds)
{
auto min = bu->min();
auto max = bu->max();
if (min > 0)
{ {
if (mutation_counter_-- == 0) if (mutation_counter_-- == 0)
result_ = bunop::instance(op, c, min - 1, max); return formula::tt();
if (mutation_counter_-- == 0) if (mutation_counter_-- == 0)
result_ = bunop::instance(op, c, 0, max); return formula::ff();
}
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 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 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 (opts_ & Mut_Split_Ops && f.is(op::AndNLM))
{ {
if (mutation_counter_ < 0) if (mutation_counter_ >= 0
result_ = mo->clone(); && mutation_counter_ < 2 * (mos - 1))
else {
{ vec v1;
vec* v = new vec(); vec v2;
for (i = 0; i < mos; ++i) v1.push_back(f.nth(0));
v->push_back(recurse(mo->nth(i))); bool reverse = false;
result_ = multop::instance(mo->op(), v); 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* formula
recurse(const formula* f)
{
f->accept(*this);
return result_;
}
const formula*
get_mutation(int n) get_mutation(int n)
{ {
mutation_counter_ = n; mutation_counter_ = n;
const formula* mut = recurse(f_); formula mut = mutate(f_);
if (mut == f_) if (mut == f_)
{ return nullptr;
mut->destroy();
return 0;
}
return mut; return mut;
} }
private:
const formula* f_;
int mutation_counter_ = 0;
unsigned opts_;
}; };
bool bool
formula_length_less_than(const formula* left, const formula* right) formula_length_less_than(formula left, formula right)
{ {
assert(left); assert(left != nullptr);
assert(right); assert(right != nullptr);
if (left == right) if (left == right)
return false; 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 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) unsigned& n, unsigned m)
{ {
if (m == 0) if (m == 0)
{ {
if (mutations.insert(f).second) if (mutations.insert(f).second)
{ --n;
f->clone();
--n;
}
} }
else else
{ {
const formula* mut(nullptr); formula mut;
int i = 0; int i = 0;
mutation_visitor mv(f, opts); mutator mv(f, opts);
while (n > 0 && (mut = mv.get_mutation(i++))) while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr))
{ single_mutation_rec(mut, mutations, opts, n, m - 1);
single_mutation_rec(mut, mutations, opts, n, m - 1);
mut->destroy();
}
} }
} }
void 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) unsigned& n, unsigned m)
{ {
if (m == 0) if (m == 0)
{ {
if (mutations.insert(f).second) if (mutations.insert(f).second)
{ --n;
f->clone();
--n;
}
} }
else else
{ {
if (!n) if (!n)
return; return;
replace_visitor rv;
auto aps = auto aps =
std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f)); std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f));
for (auto ap1: *aps) for (auto ap1: *aps)
@ -402,9 +344,8 @@ namespace spot
{ {
if (ap1 == ap2) if (ap1 == ap2)
continue; continue;
auto mut = rv.replace(f, ap1, ap2); auto mut = substitute_ap(f, ap1, ap2);
replace_ap_rec(mut, mutations, opts, n, m - 1); replace_ap_rec(mut, mutations, opts, n, m - 1);
mut->destroy();
if (!n) if (!n)
return; return;
} }
@ -412,8 +353,8 @@ namespace spot
} }
} }
std::vector<const formula*> std::vector<formula>
mutate(const formula* f, unsigned opts, unsigned max_output, mutate(formula f, unsigned opts, unsigned max_output,
unsigned mutation_count, bool sort) unsigned mutation_count, bool sort)
{ {
fset_t mutations; fset_t mutations;

View file

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

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013 Laboratoire de Recherche // Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015 Laboratoire de
// et Développement de l'Epita (LRDE). // Recherche et Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie. // et Marie Curie.
@ -27,11 +27,11 @@ namespace spot
{ {
namespace ltl namespace ltl
{ {
const formula* formula
negative_normal_form(const formula* f, bool negated) negative_normal_form(formula f, bool negated)
{ {
if (!negated && f->is_in_nenoform()) if (!negated && f.is_in_nenoform())
return f->clone(); return f;
ltl_simplifier s; ltl_simplifier s;
return s.negative_normal_form(f, negated); return s.negative_normal_form(f, negated);

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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). // Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -39,11 +39,11 @@ namespace spot
/// \c !f /// \c !f
/// ///
/// Note that this will not remove abbreviated operators. If you /// Note that this will not remove abbreviated operators. If you
/// want to remove abbreviations, call spot::ltl::unabbreviate_logic /// want to remove abbreviations, call spot::ltl::unabbreviate
/// or spot::ltl::unabbreviate_ltl first. (Calling these functions /// first. (Calling this function after
/// after spot::ltl::negative_normal_form would likely produce a /// spot::ltl::negative_normal_form would likely produce a formula
/// formula which is not in negative normal form.) /// which is not in negative normal form.)
SPOT_API const formula* SPOT_API formula
negative_normal_form(const formula* f, bool negated = false); 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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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 /// \brief Convert a PSL formula into a string which is parsable
/// \param f The formula to translate. /// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output a PSL formula as an utf-8 string which is parsable.
/// \param os The stream where it should be output. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Convert a PSL formula into a utf-8 string which is parsable /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output a SERE formula as a string which is parsable.
/// \param f The formula to translate. /// \param f The formula to translate.
@ -69,14 +69,14 @@ namespace spot
/// \param full_parent Whether or not the string should by fully /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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 /// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate. /// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output a SERE formula as a utf-8 string which is parsable.
/// \param os The stream where it should be output. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output an LTL formula as a string parsable by Spin.
/// \param os The stream where it should be output. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Convert an LTL formula into a string parsable by Spin. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output an LTL formula as a string parsable by Wring.
/// \param os The stream where it should be output. /// \param os The stream where it should be output.
/// \param f The formula to translate. /// \param f The formula to translate.
SPOT_API std::ostream& 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 /// \brief Convert a formula into a string parsable by Wring
/// \param f The formula to translate. /// \param f The formula to translate.
SPOT_API std::string SPOT_API std::string
str_wring_ltl(const formula* f); str_wring_ltl(formula f);
/// \brief Output a PSL formula as a LaTeX string. /// \brief Output a PSL formula as a LaTeX string.
/// \param os The stream where it should be output. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Output a formula as a LaTeX string which is parsable. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \brief Output a SERE formula as a LaTeX string.
/// \param os The stream where it should be output. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string which is parsable. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Output a PSL formula as a self-contained LaTeX string. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::ostream& 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); bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string. /// \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 /// \param full_parent Whether or not the string should by fully
/// parenthesized. /// parenthesized.
SPOT_API std::string 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. /// \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 f The formula to translate.
/// \param os The stream where it should be output. /// \param os The stream where it should be output.
SPOT_API std::ostream& 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. /// \brief Output an LTL formula as a string in LBT's format.
/// ///
@ -218,7 +218,7 @@ namespace spot
/// ///
/// \param f The formula to translate. /// \param f The formula to translate.
SPOT_API std::string SPOT_API std::string
str_lbt_ltl(const formula* f); str_lbt_ltl(formula f);
/// @} /// @}
} }
} }

View file

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

View file

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

View file

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

View file

@ -29,25 +29,7 @@ namespace spot
{ {
enum relabeling_style { Abc, Pnn }; enum relabeling_style { Abc, Pnn };
typedef std::map<formula, formula> relabeling_map;
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();
}
};
/// \ingroup ltl_rewriting /// \ingroup ltl_rewriting
/// \brief Relabel the atomic propositions in a formula. /// \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 /// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values). /// between the new names (keys) and the old names (values).
SPOT_API SPOT_API
const formula* relabel(const formula* f, relabeling_style style, formula relabel(formula f, relabeling_style style,
relabeling_map* m = 0); relabeling_map* m = 0);
/// \ingroup ltl_rewriting /// \ingroup ltl_rewriting
@ -66,7 +48,7 @@ namespace spot
/// If \a m is non-null, it is filled with correspondence /// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values). /// between the new names (keys) and the old names (values).
SPOT_API SPOT_API
const formula* relabel_bse(const formula* f, relabeling_style style, formula relabel_bse(formula f, relabeling_style style,
relabeling_map* m = 0); relabeling_map* m = 0);
} }
} }

View file

@ -17,9 +17,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ltlast/allnodes.hh"
#include "ltlvisit/simplify.hh" #include "ltlvisit/simplify.hh"
#include "ltlvisit/clone.hh"
#include "ltlvisit/apcollect.hh" #include "ltlvisit/apcollect.hh"
#include "ltlvisit/remove_x.hh" #include "ltlvisit/remove_x.hh"
@ -29,104 +27,76 @@ namespace spot
{ {
namespace namespace
{ {
static formula
#define AND(x, y) multop::instance(multop::And, (x), (y)) remove_x_rec(formula f, atomic_prop_set& aps)
#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
{ {
typedef clone_visitor super; if (f.is_syntactic_stutter_invariant())
atomic_prop_set aps; return f;
public: auto rec = [&aps](formula f)
remove_x_visitor(const formula* f) {
{ return remove_x_rec(f, aps);
atomic_prop_collect(f, &aps); };
}
virtual if (!f.is(op::X))
~remove_x_visitor() return f.map(rec);
{
}
using super::visit; formula c = rec(f.nth(0));
void visit(const unop* uo)
{
const formula* c = recurse(uo->child());
unop::type op = uo->op(); std::vector<formula> vo;
if (op != unop::X) for (auto i: aps)
{ {
result_ = unop::instance(op, c); // First line
return; std::vector<formula> va1;
} formula npi = formula::Not(i);
multop::vec* vo = new multop::vec; va1.push_back(i);
for (atomic_prop_set::const_iterator i = aps.begin(); va1.push_back(formula::U(i, formula::And({npi, c})));
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();
}
};
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); if (f.is_syntactic_stutter_invariant())
return v.recurse(f); 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 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de // Copyright (C) 2013, 2015 Laboratoire de Recherche et Developpement
// l'Epita (LRDE). // de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -19,14 +19,12 @@
#pragma once #pragma once
#include "misc/common.hh" #include "ltlast/formula.hh"
namespace spot namespace spot
{ {
namespace ltl namespace ltl
{ {
class formula;
/// \brief Rewrite a stutter-insensitive formula \a f without /// \brief Rewrite a stutter-insensitive formula \a f without
/// using the X operator. /// using the X operator.
/// ///
@ -46,6 +44,6 @@ namespace spot
} }
\endverbatim */ \endverbatim */
SPOT_API SPOT_API
const formula* remove_x(const formula* f); formula remove_x(formula f);
} }
} }

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- 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). // Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // 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 // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ltlast/allnodes.hh"
#include "ltlvisit/clone.hh"
#include "simpfg.hh" #include "simpfg.hh"
#include <cassert>
namespace spot namespace spot
{ {
namespace ltl namespace ltl
{ {
formula simplify_f_g(formula p)
simplify_f_g_visitor::simplify_f_g_visitor()
{ {
// 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 -*- // -*- 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). // Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre // département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -22,36 +22,12 @@
#pragma once #pragma once
#include "clone.hh" #include "ltlast/formula.hh"
namespace spot namespace spot
{ {
namespace ltl 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 /// \ingroup ltl_rewriting
/// \brief Replace <code>true U f</code> and <code>false R g</code> by /// \brief Replace <code>true U f</code> and <code>false R g</code> by
/// <code>F f</code> and <code>G g</code>. /// <code>F f</code> and <code>G g</code>.
@ -63,6 +39,6 @@ namespace spot
/// - false R a = G a /// - false R a = G a
/// - a W false = 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 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et // Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// Developpement de l'Epita (LRDE). // et Developpement de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // 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 /// Simplify the formula \a f (using options supplied to the
/// constructor). /// constructor).
const formula* simplify(const formula* f); formula simplify(formula f);
/// Build the negative normal form of formula \a f. /// Build the negative normal form of formula \a f.
/// All negations of the formula are pushed in front of the /// All negations of the formula are pushed in front of the
@ -116,8 +116,8 @@ namespace spot
/// \param f The formula to normalize. /// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of /// \param negated If \c true, return the negative normal form of
/// \c !f /// \c !f
const formula* formula
negative_normal_form(const formula* f, bool negated = false); negative_normal_form(formula f, bool negated = false);
/// \brief Syntactic implication. /// \brief Syntactic implication.
/// ///
@ -138,20 +138,20 @@ namespace spot
} }
\endverbatim */ \endverbatim */
/// ///
bool syntactic_implication(const formula* f, const formula* g); bool syntactic_implication(formula f, formula g);
/// \brief Syntactic implication with one negated argument. /// \brief Syntactic implication with one negated argument.
/// ///
/// If \a right is true, this method returns whether /// If \a right is true, this method returns whether
/// \a f implies !\a g. If \a right is false, this returns /// \a f implies !\a g. If \a right is false, this returns
/// whether !\a f implies \a g. /// 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); bool right);
/// \brief check whether two formulae are equivalent. /// \brief check whether two formulae are equivalent.
/// ///
/// This costly check performs up to four translations, /// This costly check performs up to four translations,
/// two products, and two emptiness checks. /// 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. /// \brief Check whether \a f implies \a g.
@ -159,13 +159,13 @@ namespace spot
/// This operation is costlier than syntactic_implication() /// This operation is costlier than syntactic_implication()
/// because it requires two translation, one product and one /// because it requires two translation, one product and one
/// emptiness check. /// emptiness check.
bool implication(const formula* f, const formula* g); bool implication(formula f, formula g);
/// \brief Convert a Boolean formula as a BDD. /// \brief Convert a Boolean formula as a BDD.
/// ///
/// If you plan to use this method, be sure to pass a bdd_dict /// If you plan to use this method, be sure to pass a bdd_dict
/// to the constructor. /// to the constructor.
bdd as_bdd(const formula* f); bdd as_bdd(formula f);
/// \brief Clear the as_bdd() cache. /// \brief Clear the as_bdd() cache.
/// ///
@ -182,14 +182,14 @@ namespace spot
bdd_dict_ptr get_dict() const; bdd_dict_ptr get_dict() const;
/// Cached version of spot::ltl::star_normal_form(). /// 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 /// \brief Rewrite a Boolean formula \a f into as an irredundant
/// sum of product. /// sum of product.
/// ///
/// This uses a cache, so it is OK to call this with identical /// This uses a cache, so it is OK to call this with identical
/// arguments. /// arguments.
const formula* boolean_to_isop(const formula* f); formula boolean_to_isop(formula f);
/// Dump statistics about the caches. /// Dump statistics about the caches.
void print_stats(std::ostream& os) const; 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/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "snf.hh" #include "snf.hh"
#include "ltlast/allnodes.hh"
#include "ltlast/visitor.hh"
namespace spot namespace spot
{ {
@ -27,82 +25,51 @@ namespace spot
{ {
namespace namespace
{ {
// E° // E° if bounded=false
class snf_visitor: public visitor // E^□ if nounded=true
template<bool bounded>
class snf_visitor
{ {
protected: protected:
const formula* result_; formula result_;
snf_cache* cache_; snf_cache* cache_;
public: public:
snf_visitor(snf_cache* c)
snf_visitor(snf_cache* c): cache_(c) : cache_(c)
{ {
} }
const formula* formula visit(formula f)
result() const
{ {
return result_; if (!f.accepts_eword())
} return f;
void snf_cache::const_iterator i = cache_->find(f);
visit(const atomic_prop*) if (i != cache_->end())
{ return i->second;
SPOT_UNIMPLEMENTED();
}
void formula out;
visit(const constant* c) switch (f.kind())
{
assert(c == constant::empty_word_instance());
(void)c;
result_ = constant::false_instance();
}
void
visit(const bunop* bo)
{
bunop::type op = bo->op();
switch (op)
{ {
case bunop::Star: case op::EmptyWord:
assert(bo->accepts_eword()); out = formula::ff();
// Strip the star.
result_ = recurse(bo->child());
break; break;
case bunop::FStar: case op::Star:
// FIXME: Can we deal with FStar in a better way? if (!bounded)
result_ = bo->clone(); 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; break;
case op::Concat:
} if (bounded)
} {
out = f;
void break;
visit(const unop*) }
{ // Fall through
SPOT_UNIMPLEMENTED(); case op::OrRat:
} case op::AndNLM:
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:
// Let F designate expressions that accept [*0], // Let F designate expressions that accept [*0],
// and G designate expressions that do not. // and G designate expressions that do not.
@ -112,100 +79,70 @@ namespace spot
// //
// AndNLM can be dealt with similarly. // AndNLM can be dealt with similarly.
// //
// This case is already handled in recurse(). // The above cases are already handled by the
// if we reach this switch, we only have to // accepts_eword() tests at the top of this method. So
// deal with... // we reach this switch, we only have to deal with...
// //
// (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°) // (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°) // (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°)
// so we fall through to the OrRat case... // (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°)
case multop::OrRat:
assert(mo->accepts_eword());
{ {
unsigned s = mo->size(); unsigned s = f.size();
multop::vec* v = new multop::vec; std::vector<formula> v;
v->reserve(s); v.reserve(s);
for (unsigned pos = 0; pos < s; ++pos) for (unsigned pos = 0; pos < s; ++pos)
v->push_back(recurse(mo->nth(pos))); v.emplace_back(visit(f.nth(pos)));
result_ = multop::instance(multop::OrRat, v); out = formula::OrRat(v);
break;
} }
break; case op::False:
case multop::AndRat: case op::True:
// FIXME: Can we deal with AndRat in a better way case op::AP:
// when it accepts [*0]? case op::Not:
result_ = mo->clone(); 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; break;
} }
}
const formula* return (*cache_)[f] = out;
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);
} }
}; };
} }
const formula* formula
star_normal_form(const formula* sere, snf_cache* cache) star_normal_form(formula sere, snf_cache* cache)
{ {
snf_visitor v(cache); snf_visitor<false> v(cache);
return v.recurse(sere); return v.visit(sere);
} }
const formula* formula
star_normal_form_bounded(const formula* sere, snf_cache* cache) star_normal_form_bounded(formula sere, snf_cache* cache)
{ {
snf_visitor_bounded v(cache); snf_visitor<true> v(cache);
return v.recurse(sere); return v.visit(sere);
} }
} }

View file

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

View file

@ -19,8 +19,6 @@
#include "unabbrev.hh" #include "unabbrev.hh"
#include "ltlast/allnodes.hh"
#include <cassert>
namespace spot namespace spot
{ {
@ -70,242 +68,183 @@ namespace spot
} }
} }
unabbreviator::~unabbreviator() formula unabbreviator::run(formula in)
{
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)
{ {
auto entry = cache_.emplace(in, nullptr); auto entry = cache_.emplace(in, nullptr);
if (!entry.second) if (!entry.second)
return entry.first->second->clone(); return entry.first->second;
in->clone();
// Skip recursion whenever possible // Skip recursion whenever possible
bool no_boolean_rewrite = !re_some_bool_ || in->is_sugar_free_boolean(); 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_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl();
if (no_boolean_rewrite 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 this->run(f);
return in->clone(); };
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;
}
} }
return entry.first->second = out;
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();
} }
const formula* unabbreviate(const formula* in, const char* opt) formula unabbreviate(formula in, const char* opt)
{ {
unabbreviator un(opt); unabbreviator un(opt);
return un.run(in); 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_f_g_ = false; // rewrite F or G
bool re_some_other_ = false; // rewrite W, M, or R bool re_some_other_ = false; // rewrite W, M, or R
// Cache of rewritten subformulas // Cache of rewritten subformulas
std::unordered_map<const formula*, const formula*> cache_; std::unordered_map<formula, formula> cache_;
public: public:
/// \brief Constructor /// \brief Constructor
/// ///
@ -55,8 +55,7 @@ namespace spot
/// which in which each letter denote an operator (using LBT's /// which in which each letter denote an operator (using LBT's
/// convention). /// convention).
unabbreviator(const char* opt = default_unabbrev_string); unabbreviator(const char* opt = default_unabbrev_string);
const formula* run(const formula* in); formula run(formula in);
~unabbreviator();
}; };
/// \ingroup ltl_rewriting /// \ingroup ltl_rewriting
@ -66,8 +65,8 @@ namespace spot
/// The set of operators to remove should be passed as a string /// The set of operators to remove should be passed as a string
/// which in which each letter denote an operator (using LBT's /// which in which each letter denote an operator (using LBT's
/// convention). /// convention).
SPOT_API const formula* SPOT_API formula
unabbreviate(const formula* in, const char* opt= default_unabbrev_string); unabbreviate(formula in, const char* opt= default_unabbrev_string);
} }
} }

View file

@ -34,7 +34,6 @@
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#include <algorithm> #include <algorithm>
#include "ltlast/constant.hh"
#include "twa/formula2bdd.hh" #include "twa/formula2bdd.hh"
#include "public.hh" #include "public.hh"
#include "priv/accmap.hh" #include "priv/accmap.hh"
@ -45,9 +44,10 @@
typedef std::map<int, bdd> map_t; typedef std::map<int, bdd> map_t;
/* Cache parsed formulae. Labels on arcs are frequently identical /* Cache parsed formulae. Labels on arcs are frequently identical
and it would be a waste of time to parse them to formula* over and and it would be a waste of time to parse them to ltl::formula
over, and to register all their atomic_propositions in the over and over, and to register all their atomic_propositions in
bdd_dict. Keep the bdd result around so we can reuse it. */ the bdd_dict. Keep the bdd result around so we can reuse
it. */
typedef std::map<std::string, bdd> formula_cache; typedef std::map<std::string, bdd> formula_cache;
typedef std::pair<int, std::string*> pair; typedef std::pair<int, std::string*> pair;
@ -152,7 +152,6 @@
%code %code
{ {
#include <sstream> #include <sstream>
#include "ltlast/constant.hh"
#include "ltlparse/public.hh" #include "ltlparse/public.hh"
/* parseaut.hh and parsedecl.hh include each other recursively. /* parseaut.hh and parsedecl.hh include each other recursively.
@ -561,9 +560,7 @@ ap-name: STRING
std::ostringstream out; std::ostringstream out;
out << "unknown atomic proposition \"" << *$1 << "\""; out << "unknown atomic proposition \"" << *$1 << "\"";
error(@1, out.str()); error(@1, out.str());
f = spot::ltl::default_environment::instance() b = res.h->aut->register_ap("$unknown$");
.require("$unknown$");
b = res.h->aut->register_ap(f);
} }
else else
{ {
@ -575,7 +572,6 @@ ap-name: STRING
error(@1, out.str()); error(@1, out.str());
} }
} }
f->destroy();
res.ap.push_back(b); res.ap.push_back(b);
} }
delete $1; delete $1;
@ -1422,11 +1418,7 @@ nc-formula: nc-formula-or-ident
} }
bdd cond = bddfalse; bdd cond = bddfalse;
if (f) if (f)
{ cond = spot::formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
cond = spot::formula_to_bdd(f, res.h->aut->get_dict(),
res.h->aut);
f->destroy();
}
$$ = (res.fcache[*$1] = cond).id(); $$ = (res.fcache[*$1] = cond).id();
} }
else else
@ -1587,7 +1579,7 @@ lbtt-acc: { $$ = 0U; }
lbtt-guard: STRING lbtt-guard: STRING
{ {
spot::ltl::parse_error_list pel; 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()) if (!f || !pel.empty())
{ {
std::string s = "failed to parse guard: "; std::string s = "failed to parse guard: ";
@ -1611,7 +1603,7 @@ lbtt-guard: STRING
} }
else else
{ {
if (!f->is_boolean()) if (!f.is_boolean())
{ {
error(@$, error(@$,
"non-Boolean transition label (replaced by true)"); "non-Boolean transition label (replaced by true)");
@ -1622,7 +1614,6 @@ lbtt-guard: STRING
res.cur_label = res.cur_label =
formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut); formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
} }
f->destroy();
} }
delete $1; 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"} perl -pe 'sub f {my $a = shift; $a =~ s:[^\n]*://:g; return "$a"}
s,/\*(.*?)\*/,f($1),sge; s,/\*(.*?)\*/,f($1),sge;
s,//.*?\n,//\n,g; s,//.*?\n,//\n,g;
s,"[^"\n]*","",g; s,"(\\.|[^"\\\n])*","",g;
s,SPOT_API ,,g' -0777 <$file >$tmp s,SPOT_API ,,g' -0777 <$file >$tmp
$GREP '[ ]$' $tmp && $GREP '[ ]$' $tmp &&
@ -231,10 +231,14 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do
$GREP -v 'for (.*;;)' $tmp | $GREP ';[^ ")'"']" && $GREP -v 'for (.*;;)' $tmp | $GREP ';[^ ")'"']" &&
diag 'Must have space or newline after semicolon.' 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.' diag 'No two } on the same line.'
$GREP '{.*{' $tmp && $GREP '^[^()]{[^()]*{[^()]$' $tmp &&
diag 'No two { on the same line.' diag 'No two { on the same line.'
$GREP 'delete[ ]*[(][^(]*[)];' $tmp && $GREP 'delete[ ]*[(][^(]*[)];' $tmp &&

View file

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

View file

@ -17,8 +17,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // 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 "tgtaexplicit.hh"
#include "twa/formula2bdd.hh" #include "twa/formula2bdd.hh"
#include "twa/bddprint.hh" #include "twa/bddprint.hh"

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