diff --git a/NEWS b/NEWS index fff597b6b..b9ed0cac9 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,24 @@ New in spot 1.99.3a (not yet released) (But dstar2tgba does not offer all the filtering and transformations options of autfilt.) + * The class hierarchy for temporal formulas has been entirely + rewritten. This change is actually quite massive (~13200 lines + removed, ~8200 lines added), and brings some nice benefits: + - LTL/PSL formulas are now represented by lightweight + ltl::formula objects (instead of ltl::formula* pointers) + that perform reference counting automatically. + - There is no hierachy anymore: all operators are represented + by a single type of node in the syntax tree, and an + enumerator is used to distinguish between operators. + - Visitors have been replaced by member functions such + as map() or traverse(), that take a function (usually + written as a lambda function) and apply it to the + nodes of the tree. + - As a consequence, writing algorithms that manipulate + formula is more friendly, and several functions + algorithms that spanned a few pages have been + reduced to a few lines. + New in spot 1.99.3 (2015-08-26) * The CGI script for LTL translation offers a HOA download link diff --git a/bench/stutter/stutter_invariance_formulas.cc b/bench/stutter/stutter_invariance_formulas.cc index 1d555813d..a3bff7fe7 100644 --- a/bench/stutter/stutter_invariance_formulas.cc +++ b/bench/stutter/stutter_invariance_formulas.cc @@ -25,7 +25,6 @@ #include "twaalgos/stutter.hh" #include "twaalgos/dupexp.hh" #include "twaalgos/stats.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/apcollect.hh" #include "ltlvisit/length.hh" #include "misc/timer.hh" @@ -64,14 +63,10 @@ namespace } int - process_formula(const spot::ltl::formula* f, - const char*, int) + process_formula(spot::ltl::formula f, const char*, int) { - const spot::ltl::formula* nf = - spot::ltl::unop::instance(spot::ltl::unop::Not, - f->clone()); spot::twa_graph_ptr a = trans.run(f); - spot::twa_graph_ptr na = trans.run(nf); + spot::twa_graph_ptr na = trans.run(spot::ltl::formula::Not(f)); spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f); bdd apdict = spot::ltl::atomic_prop_collect_as_bdd(f, a); @@ -103,9 +98,6 @@ namespace prev = res; } std::cout << prev << '\n'; - - f->destroy(); - nf->destroy(); delete ap; return 0; } diff --git a/bench/stutter/stutter_invariance_randomgraph.cc b/bench/stutter/stutter_invariance_randomgraph.cc index 2c4ce6724..8c3d27750 100644 --- a/bench/stutter/stutter_invariance_randomgraph.cc +++ b/bench/stutter/stutter_invariance_randomgraph.cc @@ -127,6 +127,5 @@ main(int argc, char** argv) break; } dict->unregister_all_my_variables(&ap); - spot::ltl::destroy_atomic_prop_set(ap); return 0; } diff --git a/doc/org/tut01.org b/doc/org/tut01.org index e84efacd5..d9d12d71d 100644 --- a/doc/org/tut01.org +++ b/doc/org/tut01.org @@ -72,10 +72,9 @@ exceptions. int main() { print_latex_psl(std::cout, spot::ltl::parse_formula("[]<>p0 || <>[]p1")) << '\n'; - const spot::ltl::formula* f = spot::ltl::parse_formula("& & G p0 p1 p2"); + spot::ltl::formula f = spot::ltl::parse_formula("& & G p0 p1 p2"); print_lbt_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f, true) << '\n'; - f->destroy(); return 0; } #+END_SRC @@ -90,12 +89,6 @@ syntax the output, and the type of formula they can output. Here we are only using LTL formulas for demonstration, so those three functions are OK with that. -Did you notice the calls to =f->destroy()= at the end? The LTL -formula objects are implemented as DAG with sharing of subformulas. -Each (sub)formula is therefore reference counted, and currently this -is done manually by calling =f->clone()= and =f->destroy()= (do not -ever =delete= a formula, always call =f->destroy()=). - We do not recommend using this =parse_formula()= interface because of the potential formulas (like =f= or =t=) that have different meanings in the two parsers that are tried. @@ -118,15 +111,10 @@ Here is how to call the infix parser explicitly,: { std::string input = "[]<>p0 || <>[]p1"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } + return 1; print_latex_psl(std::cout, f) << '\n'; - f->destroy(); return 0; } #+END_SRC @@ -165,14 +153,14 @@ with the "fixed" formula if you wish. Here is an example: { std::string input = "(a U b))"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); // Use std::cout instead of std::cerr because we can only // show the output of std::cout in this documentation. (void) spot::ltl::format_parse_errors(std::cout, input, pel); if (f == nullptr) return 1; - print_latex_psl(std::cout, f) << '\n'; - f->destroy(); + std::cout << "Parsed formula: "; + print_psl(std::cout, f) << '\n'; return 0; } #+END_SRC @@ -186,7 +174,7 @@ with the "fixed" formula if you wish. Here is an example: : ^ : ignoring trailing garbage : -: a \U b +: Parsed formula: a U b The formula =f= is only returned as null when the parser really cannot @@ -207,16 +195,11 @@ of =parse_infix_psl()=. { std::string input = "& & G p0 p1 p2"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_prefix_ltl(input, pel); + spot::ltl::formula f = spot::ltl::parse_prefix_ltl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } + return 1; print_lbt_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f, true) << '\n'; - f->destroy(); return 0; } #+END_SRC @@ -254,15 +237,10 @@ For instance, let's see what happens if a PSL formulas is passed to { std::string input = "{a*;b}<>->(a U (b & GF c))"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } + return 1; print_spin_ltl(std::cout, f) << '\n'; - f->destroy(); return 0; } #+END_SRC @@ -289,21 +267,15 @@ The first is to simply diagnose non-LTL formulas. { std::string input = "{a*;b}<>->(a U (b & GF c))"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + return 1; + if (!f.is_ltl_formula()) { - if (f) - f->destroy(); - return 1; - } - if (!f->is_ltl_formula()) - { - f->destroy(); std::cerr << "Only LTL formulas are supported.\n"; return 1; } print_spin_ltl(std::cout, f) << '\n'; - f->destroy(); return 0; } #+END_SRC @@ -314,7 +286,7 @@ equivalent LTL formula. This does not always work, so you need to be prepared to reject the formula any way. In our example, we are lucky (maybe because it was carefully chosen...): -#+BEGIN_SRC C++ :results verbatim :exports code +#+BEGIN_SRC C++ :results verbatim :exports both #include #include #include "ltlparse/public.hh" @@ -325,28 +297,20 @@ prepared to reject the formula any way. In our example, we are lucky { std::string input = "{a*;b}<>->(a U (b & GF c))"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } - if (!f->is_ltl_formula()) + return 1; + if (!f.is_ltl_formula()) { spot::ltl::ltl_simplifier simp; - const formula* g = simp.simplify(f); - f->destroy(); - f = g; + f = simp.simplify(f); } - if (!f->is_ltl_formula()) + if (!f.is_ltl_formula()) { - f->destroy(); std::cerr << "Only LTL formulas are supported.\n"; return 1; } print_spin_ltl(std::cout, f) << '\n'; - f->destroy(); return 0; } #+END_SRC diff --git a/doc/org/tut02.org b/doc/org/tut02.org index b983d08a8..138e68a28 100644 --- a/doc/org/tut02.org +++ b/doc/org/tut02.org @@ -15,9 +15,9 @@ ltlfilt -ps --relabel=pnn --define -f '"Proc@Here" U ("var > 10" | "var < 4")' #+END_SRC #+RESULTS: -: #define p0 ((Proc@Here)) -: #define p1 ((var < 4)) -: #define p2 ((var > 10)) +: #define p0 (Proc@Here) +: #define p1 (var < 4) +: #define p2 (var > 10) : (p0) U ((p1) || (p2)) When is this output interesting, you may ask? It is useful for @@ -34,9 +34,9 @@ rm tmp.defs tmp.ltl #+RESULTS: #+begin_example -#define p0 ((Proc@Here)) -#define p1 ((var < 4)) -#define p2 ((var > 10)) +#define p0 (Proc@Here) +#define p1 (var < 4) +#define p2 (var > 10) never { /* (p0) U ((p1) || (p2)) */ T0_init: @@ -88,32 +88,26 @@ destructor. { std::string input = "\"Proc@Here\" U (\"var > 10\" | \"var < 4\")"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } + return 1; spot::ltl::relabeling_map m; - const spot::ltl::formula* g = spot::ltl::relabel(f, spot::ltl::Pnn, &m); + f = spot::ltl::relabel(f, spot::ltl::Pnn, &m); for (auto& i: m) { std::cout << "#define "; print_psl(std::cout, i.first) << " ("; print_spin_ltl(std::cout, i.second, true) << ")\n"; } - print_spin_ltl(std::cout, g, true) << '\n'; - g->destroy(); - f->destroy(); + print_spin_ltl(std::cout, f, true) << '\n'; return 0; } #+END_SRC #+RESULTS: -: #define p0 ((Proc@Here)) -: #define p1 ((var < 4)) -: #define p2 ((var > 10)) +: #define p0 (Proc@Here) +: #define p1 (var < 4) +: #define p2 (var > 10) : (p0) U ((p1) || (p2)) @@ -135,7 +129,7 @@ ltlfilt -ps --relabel-bool=pnn --define -f '"Proc@Here" U ("var > 10" | "var < 4 #+END_SRC #+RESULTS: -: #define p0 ((Proc@Here)) +: #define p0 (Proc@Here) : #define p1 ((var < 4) || (var > 10)) : (p0) U (p1) @@ -151,8 +145,8 @@ ltlfilt -ps --relabel-bool=pnn --define -f 'a U (a & b)' #+END_SRC #+RESULTS: -: #define p0 ((a)) -: #define p1 ((b)) +: #define p0 (a) +: #define p1 (b) : (p0) U ((p0) && (p1)) This "Boolean sub-expression" relabeling is available in Python and diff --git a/doc/org/tut10.org b/doc/org/tut10.org index 4f7c8c4ab..9b269c621 100644 --- a/doc/org/tut10.org +++ b/doc/org/tut10.org @@ -138,18 +138,13 @@ never claim is done via the =print_never_claim= function. { std::string input = "[]<>p0 || <>[]p1"; spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + spot::ltl::formula f = spot::ltl::parse_infix_psl(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) - { - if (f) - f->destroy(); - return 1; - } + return 1; spot::translator trans; trans.set_type(spot::postprocessor::BA); spot::twa_graph_ptr aut = trans.run(f); print_never_claim(std::cout, aut) << '\n'; - f->destroy(); return 0; } #+END_SRC diff --git a/doc/org/tut22.org b/doc/org/tut22.org index acfe651c4..56fc020fd 100644 --- a/doc/org/tut22.org +++ b/doc/org/tut22.org @@ -4,7 +4,6 @@ #+HTML_LINK_UP: tut.html This example demonstrates how to create an automaton in C++, and then print it. -The interface #+BEGIN_SRC C++ :results verbatim :exports both #include diff --git a/iface/ltsmin/ltsmin.cc b/iface/ltsmin/ltsmin.cc index 7b7652a60..a1d077c9c 100644 --- a/iface/ltsmin/ltsmin.cc +++ b/iface/ltsmin/ltsmin.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2012, 2014 Laboratoire de Recherche et Développement -// de l'Epita (LRDE) +// Copyright (C) 2011, 2012, 2014, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -326,7 +326,7 @@ namespace spot convert_aps(const ltl::atomic_prop_set* aps, const spins_interface* d, bdd_dict_ptr dict, - const ltl::formula* dead, + ltl::formula dead, prop_set& out) { int errors = 0; @@ -359,7 +359,7 @@ namespace spot if (*ap == dead) continue; - std::string str = (*ap)->name(); + const std::string& str = ap->ap_name(); const char* s = str.c_str(); // Skip any leading blank. @@ -602,7 +602,7 @@ namespace spot public: spins_kripke(const spins_interface* d, const bdd_dict_ptr& dict, - const spot::prop_set* ps, const ltl::formula* dead, + const spot::prop_set* ps, ltl::formula dead, int compress) : kripke(dict), d_(d), @@ -646,12 +646,12 @@ namespace spot // appropriately. ALIVE_PROP is the bdd that should be ANDed // to all transitions leaving a live state, while DEAD_PROP should // be ANDed to all transitions leaving a dead state. - if (dead == ltl::constant::false_instance()) + if (dead.is_false()) { alive_prop = bddtrue; dead_prop = bddfalse; } - else if (dead == ltl::constant::true_instance()) + else if (dead.is_true()) { alive_prop = bddtrue; dead_prop = bddtrue; @@ -1016,7 +1016,7 @@ namespace spot kripke_ptr load_ltsmin(const std::string& file_arg, const bdd_dict_ptr& dict, const ltl::atomic_prop_set* to_observe, - const ltl::formula* dead, int compress, bool verbose) + const ltl::formula dead, int compress, bool verbose) { std::string file; if (file_arg.find_first_of("/\\") != std::string::npos) diff --git a/iface/ltsmin/ltsmin.hh b/iface/ltsmin/ltsmin.hh index 75abc0b05..7daa88de2 100644 --- a/iface/ltsmin/ltsmin.hh +++ b/iface/ltsmin/ltsmin.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2013, 2014 Laboratoire de Recherche et +// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et // Developpement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. @@ -21,7 +21,6 @@ #include "kripke/kripke.hh" #include "ltlvisit/apcollect.hh" -#include "ltlast/constant.hh" namespace spot { @@ -59,6 +58,6 @@ namespace spot SPOT_API kripke_ptr load_ltsmin(const std::string& file, const bdd_dict_ptr& dict, const ltl::atomic_prop_set* to_observe, - const ltl::formula* dead = ltl::constant::true_instance(), + ltl::formula dead = ltl::formula::tt(), int compress = 0, bool verbose = true); } diff --git a/iface/ltsmin/modelcheck.cc b/iface/ltsmin/modelcheck.cc index d47bc8193..9a87b3f5d 100644 --- a/iface/ltsmin/modelcheck.cc +++ b/iface/ltsmin/modelcheck.cc @@ -20,7 +20,6 @@ #include "ltsmin.hh" #include "twaalgos/dot.hh" #include "ltlenv/defaultenv.hh" -#include "ltlast/allnodes.hh" #include "ltlparse/public.hh" #include "twaalgos/translate.hh" #include "twaalgos/emptiness.hh" @@ -163,16 +162,16 @@ checked_main(int argc, char **argv) spot::emptiness_check_instantiator_ptr echeck_inst = nullptr; int exit_code = 0; spot::postprocessor post; - const spot::ltl::formula* deadf = nullptr; - const spot::ltl::formula* f = nullptr; + spot::ltl::formula deadf = nullptr; + spot::ltl::formula f = nullptr; if (!dead || !strcasecmp(dead, "true")) { - deadf = spot::ltl::constant::true_instance(); + deadf = spot::ltl::formula::tt(); } else if (!strcasecmp(dead, "false")) { - deadf = spot::ltl::constant::false_instance(); + deadf = spot::ltl::formula::ff(); } else { @@ -355,11 +354,6 @@ checked_main(int argc, char **argv) } safe_exit: - if (f) - f->destroy(); - - deadf->destroy(); - if (use_timer) tm.print(std::cout); tm.reset_all(); // This helps valgrind. @@ -372,15 +366,6 @@ main(int argc, char **argv) auto exit_code = checked_main(argc, argv); // Additional checks to debug reference counts in formulas. - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - spot::ltl::bunop::dump_instances(std::cerr); - assert(spot::ltl::atomic_prop::instance_count() == 0); - assert(spot::ltl::unop::instance_count() == 0); - assert(spot::ltl::binop::instance_count() == 0); - assert(spot::ltl::multop::instance_count() == 0); - assert(spot::ltl::bunop::instance_count() == 0); + assert(spot::ltl::fnode::instances_check()); exit(exit_code); } diff --git a/src/bin/autfilt.cc b/src/bin/autfilt.cc index d3ec2f0a2..040860f5e 100644 --- a/src/bin/autfilt.cc +++ b/src/bin/autfilt.cc @@ -487,7 +487,7 @@ namespace } int - process_formula(const spot::ltl::formula*, const char*, int) + process_formula(spot::ltl::formula, const char*, int) { SPOT_UNREACHABLE(); } diff --git a/src/bin/common_aoutput.cc b/src/bin/common_aoutput.cc index 160ce796c..344188ee0 100644 --- a/src/bin/common_aoutput.cc +++ b/src/bin/common_aoutput.cc @@ -272,7 +272,7 @@ automaton_printer::automaton_printer(stat_style input) void automaton_printer::print(const spot::twa_graph_ptr& aut, - const spot::ltl::formula* f, + spot::ltl::formula f, // Input location for errors and statistics. const char* filename, int loc, diff --git a/src/bin/common_aoutput.hh b/src/bin/common_aoutput.hh index c742adc1c..c41cd227e 100644 --- a/src/bin/common_aoutput.hh +++ b/src/bin/common_aoutput.hh @@ -107,7 +107,7 @@ public: std::ostream& print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, - const spot::ltl::formula* f, + spot::ltl::formula f, const char* filename, int loc, double run_time) { filename_ = filename ? filename : ""; @@ -225,7 +225,7 @@ public: void print(const spot::twa_graph_ptr& aut, - const spot::ltl::formula* f = nullptr, + spot::ltl::formula f = nullptr, // Input location for errors and statistics. const char* filename = nullptr, int loc = -1, diff --git a/src/bin/common_finput.cc b/src/bin/common_finput.cc index 292fdff97..73e808d78 100644 --- a/src/bin/common_finput.cc +++ b/src/bin/common_finput.cc @@ -75,7 +75,7 @@ parse_opt_finput(int key, char* arg, struct argp_state*) return 0; } -const spot::ltl::formula* +spot::ltl::formula parse_formula(const std::string& s, spot::ltl::parse_error_list& pel) { if (lbt_input) @@ -108,15 +108,13 @@ job_processor::process_string(const std::string& input, int linenum) { spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = parse_formula(input, pel); + auto f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); spot::ltl::format_parse_errors(std::cerr, input, pel); - if (f) - f->destroy(); return 1; } return process_formula(f, filename, linenum); diff --git a/src/bin/common_finput.hh b/src/bin/common_finput.hh index 229c5d281..5d0c2a879 100644 --- a/src/bin/common_finput.hh +++ b/src/bin/common_finput.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -44,7 +44,7 @@ extern const struct argp finput_argp; int parse_opt_finput(int key, char* arg, struct argp_state* state); -const spot::ltl::formula* +spot::ltl::formula parse_formula(const std::string& s, spot::ltl::parse_error_list& error_list); @@ -58,7 +58,7 @@ public: virtual ~job_processor(); virtual int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) = 0; virtual int diff --git a/src/bin/common_output.cc b/src/bin/common_output.cc index 0418946c7..6819e0eb1 100644 --- a/src/bin/common_output.cc +++ b/src/bin/common_output.cc @@ -68,7 +68,7 @@ const struct argp output_argp = { options, parse_opt_output, 0, 0, 0, 0, 0 }; static void -report_not_ltl(const spot::ltl::formula* f, +report_not_ltl(spot::ltl::formula f, const char* filename, int linenum, const char* syn) { std::string s = spot::ltl::str_psl(f); @@ -82,12 +82,12 @@ report_not_ltl(const spot::ltl::formula* f, std::ostream& stream_formula(std::ostream& out, - const spot::ltl::formula* f, const char* filename, int linenum) + spot::ltl::formula f, const char* filename, int linenum) { switch (output_format) { case lbt_output: - if (f->is_ltl_formula()) + if (f.is_ltl_formula()) spot::ltl::print_lbt_ltl(out, f); else report_not_ltl(f, filename, linenum, "LBT"); @@ -96,13 +96,13 @@ stream_formula(std::ostream& out, spot::ltl::print_psl(out, f, full_parenth); break; case spin_output: - if (f->is_ltl_formula()) + if (f.is_ltl_formula()) spot::ltl::print_spin_ltl(out, f, full_parenth); else report_not_ltl(f, filename, linenum, "Spin"); break; case wring_output: - if (f->is_ltl_formula()) + if (f.is_ltl_formula()) spot::ltl::print_wring_ltl(out, f); else report_not_ltl(f, filename, linenum, "Wring"); @@ -122,7 +122,7 @@ stream_formula(std::ostream& out, static void stream_escapable_formula(std::ostream& os, - const spot::ltl::formula* f, + spot::ltl::formula f, const char* filename, int linenum) { if (escape_csv) @@ -144,7 +144,7 @@ namespace { struct formula_with_location { - const spot::ltl::formula* f; + spot::ltl::formula f; const char* filename; int line; const char* prefix; @@ -258,7 +258,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) static void output_formula(std::ostream& out, - const spot::ltl::formula* f, + spot::ltl::formula f, const char* filename = nullptr, int linenum = 0, const char* prefix = nullptr, const char* suffix = nullptr) { @@ -284,7 +284,7 @@ void } void -output_formula_checked(const spot::ltl::formula* f, +output_formula_checked(spot::ltl::formula f, const char* filename, int linenum, const char* prefix, const char* suffix) { diff --git a/src/bin/common_output.hh b/src/bin/common_output.hh index c16294fff..26f305ad9 100644 --- a/src/bin/common_output.hh +++ b/src/bin/common_output.hh @@ -43,19 +43,19 @@ int parse_opt_output(int key, char* arg, struct argp_state* state); // Low-level output std::ostream& stream_formula(std::ostream& out, - const spot::ltl::formula* f, const char* filename, int linenum); + spot::ltl::formula f, const char* filename, int linenum); -void output_formula_checked(const spot::ltl::formula* f, +void output_formula_checked(spot::ltl::formula f, const char* filename = 0, int linenum = 0, const char* prefix = 0, const char* suffix = 0); class printable_formula: - public spot::printable_value + public spot::printable_value { public: printable_formula& - operator=(const spot::ltl::formula* new_val) + operator=(spot::ltl::formula new_val) { val_ = new_val; return *this; @@ -78,7 +78,7 @@ public: std::ostream& print(const spot::const_twa_graph_ptr& aut, - const spot::ltl::formula* f = 0, + spot::ltl::formula f = 0, double run_time = -1.) { formula_ = f; diff --git a/src/bin/common_trans.cc b/src/bin/common_trans.cc index 447a8553d..9fea8e351 100644 --- a/src/bin/common_trans.cc +++ b/src/bin/common_trans.cc @@ -290,7 +290,7 @@ translator_runner::formula() const } void -translator_runner::round_formula(const spot::ltl::formula* f, unsigned serial) +translator_runner::round_formula(spot::ltl::formula f, unsigned serial) { if (has('f') || has('F')) string_ltl_spot = spot::ltl::str_psl(f, true); diff --git a/src/bin/common_trans.hh b/src/bin/common_trans.hh index d69905905..53e215629 100644 --- a/src/bin/common_trans.hh +++ b/src/bin/common_trans.hh @@ -94,7 +94,7 @@ public: bool no_output_allowed = false); void string_to_tmp(std::string& str, unsigned n, std::string& tmpname); const std::string& formula() const; - void round_formula(const spot::ltl::formula* f, unsigned serial); + void round_formula(spot::ltl::formula f, unsigned serial); }; diff --git a/src/bin/dstar2tgba.cc b/src/bin/dstar2tgba.cc index 8454d1227..1dc1993f6 100644 --- a/src/bin/dstar2tgba.cc +++ b/src/bin/dstar2tgba.cc @@ -138,7 +138,7 @@ namespace } int - process_formula(const spot::ltl::formula*, const char*, int) + process_formula(spot::ltl::formula, const char*, int) { SPOT_UNREACHABLE(); } diff --git a/src/bin/genltl.cc b/src/bin/genltl.cc index ea538696a..7cc890fcd 100644 --- a/src/bin/genltl.cc +++ b/src/bin/genltl.cc @@ -86,8 +86,7 @@ #include #include #include -#include "ltlast/allnodes.hh" -#include "ltlenv/defaultenv.hh" +#include "ltlast/formula.hh" #include "ltlvisit/relabel.hh" using namespace spot; @@ -270,37 +269,31 @@ parse_opt(int key, char* arg, struct argp_state*) return 0; } -environment& env(default_environment::instance()); +#define G_(x) formula::G(x) +#define F_(x) formula::F(x) +#define X_(x) formula::X(x) +#define Not_(x) formula::Not(x) -#define G_(x) spot::ltl::unop::instance(spot::ltl::unop::G, (x)) -#define F_(x) spot::ltl::unop::instance(spot::ltl::unop::F, (x)) -#define X_(x) spot::ltl::unop::instance(spot::ltl::unop::X, (x)) -#define Not_(x) spot::ltl::unop::instance(spot::ltl::unop::Not, (x)) -#define Implies_(x, y) \ - spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y)) -#define Equiv_(x, y) \ - spot::ltl::binop::instance(spot::ltl::binop::Equiv, (x), (y)) -#define And_(x, y) \ - spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y)) -#define Or_(x, y) \ - spot::ltl::multop::instance(spot::ltl::multop::Or, (x), (y)) -#define U_(x, y) \ - spot::ltl::binop::instance(spot::ltl::binop::U, (x), (y)) +#define Implies_(x, y) formula::Implies((x), (y)) +#define Equiv_(x, y) formula::Equiv((x), (y)) +#define And_(x, y) formula::And({(x), (y)}) +#define Or_(x, y) formula::Or({(x), (y)}) +#define U_(x, y) formula::U((x), (y)) // F(p_1 & F(p_2 & F(p_3 & ... F(p_n)))) -static const formula* +static formula E_n(std::string name, int n) { if (n <= 0) - return constant::true_instance(); + return formula::tt(); - const formula* result = 0; + formula result = nullptr; for (; n > 0; --n) { std::ostringstream p; p << name << n; - const formula* f = env.require(p.str()); + formula f = formula::ap(p.str()); if (result) result = And_(f, result); else @@ -311,43 +304,43 @@ E_n(std::string name, int n) } // p & X(p & X(p & ... X(p))) -static const formula* +static formula phi_n(std::string name, int n) { if (n <= 0) - return constant::true_instance(); + return formula::tt(); - const formula* result = 0; - const formula* p = env.require(name); + formula result = nullptr; + formula p = formula::ap(name); for (; n > 0; --n) { if (result) - result = And_(p->clone(), X_(result)); + result = And_(p, X_(result)); else result = p; } return result; } -const formula* N_n(std::string name, int n) +formula N_n(std::string name, int n) { - return unop::instance(unop::F, phi_n(name, n)); + return formula::F(phi_n(name, n)); } // p & X(p) & XX(p) & XXX(p) & ... X^n(p) -static const formula* +static formula phi_prime_n(std::string name, int n) { if (n <= 0) - return constant::true_instance(); + return formula::tt(); - const formula* result = 0; - const formula* p = env.require(name); + formula result = nullptr; + formula p = formula::ap(name); for (; n > 0; --n) { if (result) { - p = X_(p->clone()); + p = X_(p); result = And_(result, p); } else @@ -358,7 +351,7 @@ phi_prime_n(std::string name, int n) return result; } -static const formula* +static formula N_prime_n(std::string name, int n) { return F_(phi_prime_n(name, n)); @@ -367,24 +360,24 @@ N_prime_n(std::string name, int n) // GF(p_1) & GF(p_2) & ... & GF(p_n) if conj == true // GF(p_1) | GF(p_2) | ... | GF(p_n) if conj == false -static const formula* +static formula GF_n(std::string name, int n, bool conj = true) { if (n <= 0) - return conj ? constant::true_instance() : constant::false_instance(); + return conj ? formula::tt() : formula::ff(); - const formula* result = 0; + formula result = nullptr; - multop::type op = conj ? multop::And : multop::Or; + op o = conj ? op::And : op::Or; for (int i = 1; i <= n; ++i) { std::ostringstream p; p << name << i; - const formula* f = G_(F_(env.require(p.str()))); + formula f = G_(F_(formula::ap(p.str()))); if (result) - result = multop::instance(op, f, result); + result = formula::multop(o, {f, result}); else result = f; } @@ -393,24 +386,24 @@ GF_n(std::string name, int n, bool conj = true) // FG(p_1) | FG(p_2) | ... | FG(p_n) if conj == false // FG(p_1) & FG(p_2) & ... & FG(p_n) if conj == true -static const formula* +static formula FG_n(std::string name, int n, bool conj = false) { if (n <= 0) - return conj ? constant::true_instance() : constant::false_instance(); + return conj ? formula::tt() : formula::ff(); - const formula* result = 0; + formula result = nullptr; - multop::type op = conj ? multop::And : multop::Or; + op o = conj ? op::And : op::Or; for (int i = 1; i <= n; ++i) { std::ostringstream p; p << name << i; - const formula* f = F_(G_(env.require(p.str()))); + formula f = F_(G_(formula::ap(p.str()))); if (result) - result = multop::instance(op, f, result); + result = formula::multop(o, {f, result}); else result = f; } @@ -419,93 +412,91 @@ FG_n(std::string name, int n, bool conj = false) // (((p1 OP p2) OP p3)...OP pn) if right_assoc == false // (p1 OP (p2 OP (p3 OP (... pn) if right_assoc == true -static const formula* -bin_n(std::string name, int n, - binop::type op, bool right_assoc = false) +static formula +bin_n(std::string name, int n, op o, bool right_assoc = false) { if (n <= 0) n = 1; - const formula* result = 0; + formula result = nullptr; for (int i = 1; i <= n; ++i) { std::ostringstream p; p << name << (right_assoc ? (n + 1 - i) : i); - const formula* f = env.require(p.str()); + formula f = formula::ap(p.str()); if (!result) result = f; else if (right_assoc) - result = binop::instance(op, f, result); + result = formula::binop(o, f, result); else - result = binop::instance(op, result, f); + result = formula::binop(o, result, f); } return result; } // (GF(p1)|FG(p2))&(GF(p2)|FG(p3))&...&(GF(pn)|FG(p{n+1}))" -static const formula* +static formula R_n(std::string name, int n) { if (n <= 0) - return constant::true_instance(); + return formula::tt(); - const formula* pi; + formula pi; { std::ostringstream p; p << name << 1; - pi = env.require(p.str()); + pi = formula::ap(p.str()); } - const formula* result = 0; + formula result = nullptr; for (int i = 1; i <= n; ++i) { - const formula* gf = G_(F_(pi)); + formula gf = G_(F_(pi)); std::ostringstream p; p << name << i + 1; - pi = env.require(p.str()); + pi = formula::ap(p.str()); - const formula* fg = F_(G_(pi->clone())); + formula fg = F_(G_(pi)); - const formula* f = Or_(gf, fg); + formula f = Or_(gf, fg); if (result) result = And_(f, result); else result = f; } - pi->destroy(); return result; } // (F(p1)|G(p2))&(F(p2)|G(p3))&...&(F(pn)|G(p{n+1}))" -static const formula* +static formula Q_n(std::string name, int n) { if (n <= 0) - return constant::true_instance(); + return formula::tt(); - const formula* pi; + formula pi; { std::ostringstream p; p << name << 1; - pi = env.require(p.str()); + pi = formula::ap(p.str()); } - const formula* result = 0; + formula result = nullptr; for (int i = 1; i <= n; ++i) { - const formula* f = F_(pi); + formula f = F_(pi); std::ostringstream p; p << name << i + 1; - pi = env.require(p.str()); + pi = formula::ap(p.str()); - const formula* g = G_(pi->clone()); + formula g = G_(pi); f = Or_(f, g); @@ -514,31 +505,29 @@ Q_n(std::string name, int n) else result = f; } - pi->destroy(); return result; } // OP(p1) | OP(p2) | ... | OP(Pn) if conj == false // OP(p1) & OP(p2) & ... & OP(Pn) if conj == true -static const formula* -combunop_n(std::string name, int n, - unop::type op, bool conj = false) +static formula +combunop_n(std::string name, int n, op o, bool conj = false) { if (n <= 0) - return conj ? constant::true_instance() : constant::false_instance(); + return conj ? formula::tt() : formula::ff(); - const formula* result = 0; + formula result = nullptr; - multop::type cop = conj ? multop::And : multop::Or; + op cop = conj ? op::And : op::Or; for (int i = 1; i <= n; ++i) { std::ostringstream p; p << name << i; - const formula* f = unop::instance(op, env.require(p.str())); + formula f = formula::unop(o, formula::ap(p.str())); if (result) - result = multop::instance(cop, f, result); + result = formula::multop(cop, {f, result}); else result = f; } @@ -547,21 +536,21 @@ combunop_n(std::string name, int n, // !((GF(p1)&GF(p2)&...&GF(pn))->G(q -> F(r))) // From "Fast LTL to Büchi Automata Translation" [gastin.01.cav] -static const formula* +static formula fair_response(std::string p, std::string q, std::string r, int n) { - const formula* fair = GF_n(p, n); - const formula* resp = G_(Implies_(env.require(q), F_(env.require(r)))); + formula fair = GF_n(p, n); + formula resp = G_(Implies_(formula::ap(q), F_(formula::ap(r)))); return Not_(Implies_(fair, resp)); } // Builds X(X(...X(p))) with n occurrences of X. -static const formula* -X_n(const formula* p, int n) +static formula +X_n(formula p, int n) { assert(n >= 0); - const formula* res = p; + formula res = p; while (n--) res = X_(res); return res; @@ -569,214 +558,174 @@ X_n(const formula* p, int n) // Based on LTLcounter.pl from Kristin Rozier. // http://shemesh.larc.nasa.gov/people/kyr/benchmarking_scripts/ -static const formula* +static formula ltl_counter(std::string bit, std::string marker, int n, bool linear) { - const formula* b = env.require(bit); - const formula* neg_b = Not_(b); - const formula* m = env.require(marker); - const formula* neg_m = Not_(m); // to destroy + formula b = formula::ap(bit); + formula neg_b = Not_(b); + formula m = formula::ap(marker); + formula neg_m = Not_(m); - multop::vec* res = new multop::vec(4); + std::vector res(4); // The marker starts with "1", followed by n-1 "0", then "1" again, // n-1 "0", etc. if (!linear) { // G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3] - multop::vec* v = new multop::vec(n); + std::vector v(n); for (int i = 0; i + 1 < n; ++i) - (*v)[i] = X_n(neg_m->clone(), i + 1); - (*v)[n - 1] = X_n(m->clone(), n); - (*res)[0] = And_(m->clone(), - G_(Implies_(m->clone(), - multop::instance(multop::And, v)))); + v[i] = X_n(neg_m, i + 1); + v[n - 1] = X_n(m, n); + res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v))))); } else { // G(m -> X(!m & X(!m X(m)))) [if n = 3] - const formula* p = m->clone(); + formula p = m; for (int i = n - 1; i > 0; --i) - p = And_(neg_m->clone(), X_(p)); - (*res)[0] = And_(m->clone(), - G_(Implies_(m->clone(), X_(p)))); + p = And_(neg_m, X_(p)); + res[0] = And_(m, G_(Implies_(m, X_(p)))); } // All bits are initially zero. if (!linear) { // !b & X(!b) & XX(!b) [if n = 3] - multop::vec* v2 = new multop::vec(n); + std::vector v2(n); for (int i = 0; i < n; ++i) - (*v2)[i] = X_n(neg_b->clone(), i); - (*res)[1] = multop::instance(multop::And, v2); + v2[i] = X_n(neg_b, i); + res[1] = formula::And(std::move(v2)); } else { // !b & X(!b & X(!b)) [if n = 3] - const formula* p = neg_b->clone(); + formula p = neg_b; for (int i = n - 1; i > 0; --i) - p = And_(neg_b->clone(), X_(p)); - (*res)[1] = p; + p = And_(neg_b, X_(p)); + res[1] = p; } #define AndX_(x, y) (linear ? X_(And_((x), (y))) : And_(X_(x), X_(y))) // If the least significant bit is 0, it will be 1 at the next time, // and other bits stay the same. - const formula* Xnm1_b = X_n(b->clone(), n - 1); - const formula* Xn_b = X_(Xnm1_b); // to destroy - (*res)[2] = - G_(Implies_(And_(m->clone(), neg_b->clone()), - AndX_(Xnm1_b->clone(), U_(And_(Not_(m->clone()), - Equiv_(b->clone(), - Xn_b->clone())), - m->clone())))); + formula Xnm1_b = X_n(b, n - 1); + formula Xn_b = X_(Xnm1_b); + res[2] = G_(Implies_(And_(m, neg_b), + AndX_(Xnm1_b, U_(And_(Not_(m), Equiv_(b, Xn_b)), m)))); // From the least significant bit to the first 0, all the bits // are flipped on the next value. Remaining bits are identical. - const formula* Xnm1_negb = X_n(neg_b, n - 1); - const formula* Xn_negb = X_(Xnm1_negb); // to destroy - (*res)[3] = - G_(Implies_(And_(m->clone(), b->clone()), - AndX_(Xnm1_negb->clone(), - U_(And_(And_(b->clone(), neg_m->clone()), - Xn_negb->clone()), - Or_(m->clone(), - And_(And_(neg_m->clone(), - neg_b->clone()), - AndX_(Xnm1_b->clone(), - U_(And_(neg_m->clone(), - Equiv_(b->clone(), - Xn_b->clone())), - m->clone())))))))); - neg_m->destroy(); - Xn_b->destroy(); - Xn_negb->destroy(); - - return multop::instance(multop::And, res); + formula Xnm1_negb = X_n(neg_b, n - 1); + formula Xn_negb = X_(Xnm1_negb); + res[3] = G_(Implies_(And_(m, b), + AndX_(Xnm1_negb, + U_(And_(And_(b, neg_m), Xn_negb), + Or_(m, And_(And_(neg_m, neg_b), + AndX_(Xnm1_b, + U_(And_(neg_m, + Equiv_(b, Xn_b)), + m)))))))); + return formula::And(std::move(res)); } -static const formula* +static formula ltl_counter_carry(std::string bit, std::string marker, std::string carry, int n, bool linear) { - const formula* b = env.require(bit); - const formula* neg_b = Not_(b); - const formula* m = env.require(marker); - const formula* neg_m = Not_(m); // to destroy - const formula* c = env.require(carry); - const formula* neg_c = Not_(c); // to destroy + formula b = formula::ap(bit); + formula neg_b = Not_(b); + formula m = formula::ap(marker); + formula neg_m = Not_(m); + formula c = formula::ap(carry); + formula neg_c = Not_(c); - multop::vec* res = new multop::vec(6); + std::vector res(6); // The marker starts with "1", followed by n-1 "0", then "1" again, // n-1 "0", etc. if (!linear) { // G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3] - multop::vec* v = new multop::vec(n); + std::vector v(n); for (int i = 0; i + 1 < n; ++i) - (*v)[i] = X_n(neg_m->clone(), i + 1); - (*v)[n - 1] = X_n(m->clone(), n); - (*res)[0] = And_(m->clone(), - G_(Implies_(m->clone(), - multop::instance(multop::And, v)))); + v[i] = X_n(neg_m, i + 1); + v[n - 1] = X_n(m, n); + res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v))))); } else { // G(m -> X(!m & X(!m X(m)))) [if n = 3] - const formula* p = m->clone(); + formula p = m; for (int i = n - 1; i > 0; --i) - p = And_(neg_m->clone(), X_(p)); - (*res)[0] = And_(m->clone(), - G_(Implies_(m->clone(), X_(p)))); + p = And_(neg_m, X_(p)); + res[0] = And_(m, G_(Implies_(m, X_(p)))); } // All bits are initially zero. if (!linear) { // !b & X(!b) & XX(!b) [if n = 3] - multop::vec* v2 = new multop::vec(n); + std::vector v2(n); for (int i = 0; i < n; ++i) - (*v2)[i] = X_n(neg_b->clone(), i); - (*res)[1] = multop::instance(multop::And, v2); + v2[i] = X_n(neg_b, i); + res[1] = formula::And(std::move(v2)); } else { // !b & X(!b & X(!b)) [if n = 3] - const formula* p = neg_b->clone(); + formula p = neg_b; for (int i = n - 1; i > 0; --i) - p = And_(neg_b->clone(), X_(p)); - (*res)[1] = p; + p = And_(neg_b, X_(p)); + res[1] = p; } - const formula* Xn_b = X_n(b->clone(), n); // to destroy - const formula* Xn_negb = X_n(neg_b, n); // to destroy + formula Xn_b = X_n(b, n); + formula Xn_negb = X_n(neg_b, n); // If m is 1 and b is 0 then c is 0 and n steps later b is 1. - (*res)[2] = G_(Implies_(And_(m->clone(), neg_b->clone()), - And_(neg_c->clone(), Xn_b->clone()))); + res[2] = G_(Implies_(And_(m, neg_b), And_(neg_c, Xn_b))); // If m is 1 and b is 1 then c is 1 and n steps later b is 0. - (*res)[3] = G_(Implies_(And_(m->clone(), b->clone()), - And_(c->clone(), Xn_negb->clone()))); + res[3] = G_(Implies_(And_(m, b), And_(c, Xn_negb))); if (!linear) { // If there's no carry, then all of the bits stay the same n steps later. - (*res)[4] = G_(Implies_(And_(neg_c->clone(), X_(neg_m->clone())), - And_(X_(Not_(c->clone())), - Equiv_(X_(b->clone()), - X_(Xn_b->clone()))))); + res[4] = G_(Implies_(And_(neg_c, X_(neg_m)), + And_(X_(Not_(c)), Equiv_(X_(b), X_(Xn_b))))); // If there's a carry, then add one: flip the bits of b and // adjust the carry. - (*res)[5] = G_(Implies_(c->clone(), - And_(Implies_(X_(neg_b->clone()), - And_(X_(neg_c->clone()), - X_(Xn_b->clone()))), - Implies_(X_(b->clone()), - And_(X_(c->clone()), - X_(Xn_negb->clone())))))); + res[5] = G_(Implies_(c, And_(Implies_(X_(neg_b), + And_(X_(neg_c), X_(Xn_b))), + Implies_(X_(b), + And_(X_(c), X_(Xn_negb)))))); } else { // If there's no carry, then all of the bits stay the same n steps later. - (*res)[4] = G_(Implies_(And_(neg_c->clone(), X_(neg_m->clone())), - X_(And_(Not_(c->clone()), - Equiv_(b->clone(), - Xn_b->clone()))))); - + res[4] = G_(Implies_(And_(neg_c, X_(neg_m)), + X_(And_(Not_(c), Equiv_(b, Xn_b))))); // If there's a carry, then add one: flip the bits of b and // adjust the carry. - (*res)[5] = G_(Implies_(c->clone(), - X_(And_(Implies_(neg_b->clone(), - And_(neg_c->clone(), - Xn_b->clone())), - Implies_(b->clone(), - And_(c->clone(), - Xn_negb->clone())))))); + res[5] = G_(Implies_(c, X_(And_(Implies_(neg_b, And_(neg_c, Xn_b)), + Implies_(b, And_(c, Xn_negb)))))); } - - neg_m->destroy(); - neg_c->destroy(); - Xn_b->destroy(); - Xn_negb->destroy(); - - return multop::instance(multop::And, res); + return formula::And(std::move(res)); } static void output_pattern(int pattern, int n) { - const formula* f = 0; + formula f = nullptr; switch (pattern) { // Keep this alphabetically-ordered! case OPT_AND_F: - f = combunop_n("p", n, unop::F, true); + f = combunop_n("p", n, op::F, true); break; case OPT_AND_FG: f = FG_n("p", n, true); @@ -785,13 +734,13 @@ output_pattern(int pattern, int n) f = GF_n("p", n, true); break; case OPT_CCJ_ALPHA: - f = multop::instance(multop::And, E_n("p", n), E_n("q", n)); + f = formula::And({E_n("p", n), E_n("q", n)}); break; case OPT_CCJ_BETA: - f = multop::instance(multop::And, N_n("p", n), N_n("q", n)); + f = formula::And({N_n("p", n), N_n("q", n)}); break; case OPT_CCJ_BETA_PRIME: - f = multop::instance(multop::And, N_prime_n("p", n), N_prime_n("q", n)); + f = formula::And({N_prime_n("p", n), N_prime_n("q", n)}); break; case OPT_GH_Q: f = Q_n("p", n); @@ -806,16 +755,16 @@ output_pattern(int pattern, int n) f = FG_n("p", n, false); break; case OPT_OR_G: - f = combunop_n("p", n, unop::G, false); + f = combunop_n("p", n, op::G, false); break; case OPT_OR_GF: f = GF_n("p", n, false); break; case OPT_R_LEFT: - f = bin_n("p", n, binop::R, false); + f = bin_n("p", n, op::R, false); break; case OPT_R_RIGHT: - f = bin_n("p", n, binop::R, true); + f = bin_n("p", n, op::R, true); break; case OPT_RV_COUNTER_CARRY: f = ltl_counter_carry("b", "m", "c", n, false); @@ -830,10 +779,10 @@ output_pattern(int pattern, int n) f = ltl_counter("b", "m", n, true); break; case OPT_U_LEFT: - f = bin_n("p", n, binop::U, false); + f = bin_n("p", n, op::U, false); break; case OPT_U_RIGHT: - f = bin_n("p", n, binop::U, true); + f = bin_n("p", n, op::U, true); break; default: error(100, 0, "internal error: pattern not implemented"); @@ -841,15 +790,10 @@ output_pattern(int pattern, int n) // Make sure we use only "p42"-style of atomic propositions // in lbt's output. - if (output_format == lbt_output && !f->has_lbt_atomic_props()) - { - const spot::ltl::formula* r = spot::ltl::relabel(f, spot::ltl::Pnn); - f->destroy(); - f = r; - } + if (output_format == lbt_output && !f.has_lbt_atomic_props()) + f = relabel(f, Pnn); output_formula_checked(f, class_name[pattern - 1], n); - f->destroy(); } static void diff --git a/src/bin/ltl2tgba.cc b/src/bin/ltl2tgba.cc index e0e630274..bcea09c5e 100644 --- a/src/bin/ltl2tgba.cc +++ b/src/bin/ltl2tgba.cc @@ -138,14 +138,14 @@ namespace } int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) { // This should not happen, because the parser we use can only - // read PSL/LTL formula, but since our ltl::formula* type can + // read PSL/LTL formula, but since our ltl::formula type can // represent more than PSL formula, let's make this // future-proof. - if (!f->is_psl_formula()) + if (!f.is_psl_formula()) { std::string s = spot::ltl::str_psl(f); error_at_line(2, 0, filename, linenum, @@ -159,7 +159,6 @@ namespace const double translation_time = sw.stop(); printer.print(aut, f, filename, linenum, translation_time, nullptr); - f->destroy(); return 0; } }; diff --git a/src/bin/ltl2tgta.cc b/src/bin/ltl2tgta.cc index 13974256f..7f7280df9 100644 --- a/src/bin/ltl2tgta.cc +++ b/src/bin/ltl2tgta.cc @@ -170,16 +170,16 @@ namespace } int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) { auto aut = trans.run(&f); // This should not happen, because the parser we use can only - // read PSL/LTL formula, but since our ltl::formula* type can + // read PSL/LTL formula, but since our ltl::formula type can // represent more than PSL formula, let's make this // future-proof. - if (!f->is_psl_formula()) + if (!f.is_psl_formula()) { std::string s = spot::ltl::str_psl(f); error_at_line(2, 0, filename, linenum, @@ -207,7 +207,6 @@ namespace tgta = spot::minimize_tgta(tgta); spot::print_dot(std::cout, tgta->get_ta()); } - f->destroy(); flush_cout(); return 0; } diff --git a/src/bin/ltlcross.cc b/src/bin/ltlcross.cc index cb10aef3a..5c7ba6fa6 100644 --- a/src/bin/ltlcross.cc +++ b/src/bin/ltlcross.cc @@ -39,7 +39,6 @@ #include "common_file.hh" #include "common_finput.hh" #include "parseaut/public.hh" -#include "ltlast/unop.hh" #include "ltlvisit/print.hh" #include "ltlvisit/apcollect.hh" #include "ltlvisit/mutation.hh" @@ -819,8 +818,7 @@ namespace } typedef - std::unordered_set > fset_t; + std::unordered_set fset_t; class processor: public job_processor @@ -834,32 +832,22 @@ namespace { } - ~processor() - { - fset_t::iterator i = unique_set.begin(); - while (i != unique_set.end()) - (*i++)->destroy(); - } - int process_string(const std::string& input, const char* filename, int linenum) { spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = parse_formula(input, pel); + spot::ltl::formula f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); spot::ltl::format_parse_errors(std::cerr, input, pel); - if (f) - f->destroy(); return 1; } - f->clone(); int res = process_formula(f, filename, linenum); if (res && bogus_output) @@ -867,7 +855,7 @@ namespace if (res && grind_output) { std::string bogus = input; - std::vector mutations; + std::vector mutations; unsigned mutation_count; unsigned mutation_max; while (res) @@ -888,15 +876,12 @@ namespace { std::cerr << "Mutation " << mutation_count << '/' << mutation_max << ": "; - f->destroy(); - f = g->clone(); - res = process_formula(g->clone()); + f = g; + res = process_formula(g); if (res) break; ++mutation_count; } - for (auto g: mutations) - g->destroy(); if (res) { if (lbt_input) @@ -922,8 +907,6 @@ namespace std::cerr << ".\n\n"; grind_output->ostream() << bogus << std::endl; } - f->destroy(); - return 0; } @@ -954,20 +937,16 @@ namespace } int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) { static unsigned round = 0; // If we need LBT atomic proposition in any of the input or // output, relabel the formula. - if (!f->has_lbt_atomic_props() && + if (!f.has_lbt_atomic_props() && (runner.has('l') || runner.has('L') || runner.has('T'))) - { - const spot::ltl::formula* g = spot::ltl::relabel(f, spot::ltl::Pnn); - f->destroy(); - f = g; - } + f = spot::ltl::relabel(f, spot::ltl::Pnn); // ---------- Positive Formula ---------- @@ -991,18 +970,13 @@ namespace // Make sure we do not translate the same formula twice. if (!allow_dups) { - if (unique_set.insert(f).second) - { - f->clone(); - } - else + if (!unique_set.insert(f).second) { std::cerr << ("warning: This formula or its negation has already" " been checked.\n Use --allow-dups if it " "should not be ignored.\n") << std::endl; - f->destroy(); return 0; } } @@ -1053,12 +1027,11 @@ namespace nstats = &vstats[n + 1]; nstats->resize(m); - const spot::ltl::formula* nf = - spot::ltl::unop::instance(spot::ltl::unop::Not, f->clone()); + spot::ltl::formula nf = spot::ltl::formula::Not(f); if (!allow_dups) { - bool res = unique_set.insert(nf->clone()).second; + bool res = unique_set.insert(nf).second; // It is not possible to discover that nf has already been // translated, otherwise that would mean that f had been // translated too and we would have caught it before. @@ -1084,7 +1057,6 @@ namespace || (!want_stats && is_deterministic(neg[n])))) comp_neg[n] = dtgba_complement(neg[n]); } - nf->destroy(); } spot::cleanup_tmpfiles(); @@ -1171,7 +1143,6 @@ namespace } spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f); - f->destroy(); if (want_stats) for (size_t i = 0; i < m; ++i) diff --git a/src/bin/ltldo.cc b/src/bin/ltldo.cc index 3b2615528..f535834b2 100644 --- a/src/bin/ltldo.cc +++ b/src/bin/ltldo.cc @@ -238,15 +238,13 @@ namespace int linenum) { spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = parse_formula(input, pel); + spot::ltl::formula f = parse_formula(input, pel); if (!f || !pel.empty()) { if (filename) error_at_line(0, 0, filename, linenum, "parse error:"); spot::ltl::format_parse_errors(std::cerr, input, pel); - if (f) - f->destroy(); return 1; } @@ -257,22 +255,20 @@ namespace int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) { std::unique_ptr relmap; // If atomic propositions are incompatible with one of the // output, relabel the formula. - if ((!f->has_lbt_atomic_props() && + if ((!f.has_lbt_atomic_props() && (runner.has('l') || runner.has('L') || runner.has('T'))) - || (!f->has_spin_atomic_props() && + || (!f.has_spin_atomic_props() && (runner.has('s') || runner.has('S')))) { relmap.reset(new spot::ltl::relabeling_map); - auto g = spot::ltl::relabel(f, spot::ltl::Pnn, relmap.get()); - f->destroy(); - f = g; + f = spot::ltl::relabel(f, spot::ltl::Pnn, relmap.get()); } static unsigned round = 1; @@ -297,7 +293,6 @@ namespace nullptr); }; } - f->destroy(); spot::cleanup_tmpfiles(); ++round; return 0; diff --git a/src/bin/ltlfilt.cc b/src/bin/ltlfilt.cc index cd783cdcd..54e9b9abe 100644 --- a/src/bin/ltlfilt.cc +++ b/src/bin/ltlfilt.cc @@ -43,8 +43,6 @@ #include "ltlvisit/apcollect.hh" #include "ltlvisit/exclusive.hh" #include "ltlvisit/print.hh" -#include "ltlast/unop.hh" -#include "ltlast/multop.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/minimize.hh" #include "twaalgos/safety.hh" @@ -261,15 +259,15 @@ static spot::exclusive_ap excl_ap; static std::unique_ptr output_define = nullptr; static std::string unabbreviate; -static const spot::ltl::formula* implied_by = 0; -static const spot::ltl::formula* imply = 0; -static const spot::ltl::formula* equivalent_to = 0; +static spot::ltl::formula implied_by = nullptr; +static spot::ltl::formula imply = nullptr; +static spot::ltl::formula equivalent_to = nullptr; -static const spot::ltl::formula* +static spot::ltl::formula parse_formula_arg(const std::string& input) { spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = parse_formula(input, pel); + spot::ltl::formula f = parse_formula(input, pel); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) error(2, 0, "parse error when parsing an argument"); return f; @@ -342,18 +340,16 @@ parse_opt(int key, char* arg, struct argp_state*) break; case OPT_IMPLIED_BY: { - const spot::ltl::formula* i = parse_formula_arg(arg); + spot::ltl::formula i = parse_formula_arg(arg); // a→c∧b→c ≡ (a∨b)→c - implied_by = - spot::ltl::multop::instance(spot::ltl::multop::Or, implied_by, i); + implied_by = spot::ltl::formula::Or({implied_by, i}); break; } case OPT_IMPLY: { // a→b∧a→c ≡ a→(b∧c) - const spot::ltl::formula* i = parse_formula_arg(arg); - imply = - spot::ltl::multop::instance(spot::ltl::multop::And, imply, i); + spot::ltl::formula i = parse_formula_arg(arg); + imply = spot::ltl::formula::And({imply, i}); break; } case OPT_LTL: @@ -439,8 +435,7 @@ parse_opt(int key, char* arg, struct argp_state*) } typedef -std::unordered_set> fset_t; +std::unordered_set fset_t; namespace { @@ -451,22 +446,8 @@ namespace fset_t unique_set; spot::ltl::relabeling_map relmap; - ~ltl_processor() - { - fset_t::iterator i = unique_set.begin(); - while (i != unique_set.end()) - (*i++)->destroy(); - - if (equivalent_to) - equivalent_to->destroy(); - if (implied_by) - implied_by->destroy(); - if (imply) - imply->destroy(); - } - ltl_processor(spot::ltl::ltl_simplifier& simpl) - : simpl(simpl) + : simpl(simpl) { } @@ -475,7 +456,7 @@ namespace const char* filename = 0, int linenum = 0) { spot::ltl::parse_error_list pel; - const spot::ltl::formula* f = parse_formula(input, pel); + spot::ltl::formula f = parse_formula(input, pel); if (!f || pel.size() > 0) { @@ -486,9 +467,6 @@ namespace spot::ltl::format_parse_errors(std::cerr, input, pel); } - if (f) - f->destroy(); - if (error_style == skip_errors) std::cout << input << std::endl; else @@ -508,64 +486,44 @@ namespace } int - process_formula(const spot::ltl::formula* f, + process_formula(spot::ltl::formula f, const char* filename = 0, int linenum = 0) { if (opt_max_count >= 0 && match_count >= opt_max_count) { abort_run = true; - f->destroy(); return 0; } if (negate) - f = spot::ltl::unop::instance(spot::ltl::unop::Not, f); + f = spot::ltl::formula::Not(f); if (remove_x) { // If simplification are enabled, we do them before and after. if (simplification_level) - { - const spot::ltl::formula* res = simpl.simplify(f); - f->destroy(); - f = res; - } - - const spot::ltl::formula* res = spot::ltl::remove_x(f); - f->destroy(); - f = res; + f = simpl.simplify(f); + f = spot::ltl::remove_x(f); } if (simplification_level || boolean_to_isop) - { - const spot::ltl::formula* res = simpl.simplify(f); - f->destroy(); - f = res; - } + f = simpl.simplify(f); if (nnf) - { - const spot::ltl::formula* res = simpl.negative_normal_form(f); - f->destroy(); - f = res; - } + f = simpl.negative_normal_form(f); switch (relabeling) { case ApRelabeling: { relmap.clear(); - auto res = spot::ltl::relabel(f, style, &relmap); - f->destroy(); - f = res; + f = spot::ltl::relabel(f, style, &relmap); break; } case BseRelabeling: { relmap.clear(); - auto res = spot::ltl::relabel_bse(f, style, &relmap); - f->destroy(); - f = res; + f = spot::ltl::relabel_bse(f, style, &relmap); break; } case NoRelabeling: @@ -573,32 +531,24 @@ namespace } if (!unabbreviate.empty()) - { - auto res = spot::ltl::unabbreviate(f, unabbreviate.c_str()); - f->destroy(); - f = res; - } + f = spot::ltl::unabbreviate(f, unabbreviate.c_str()); if (!excl_ap.empty()) - { - auto res = excl_ap.constrain(f); - f->destroy(); - f = res; - } + f = excl_ap.constrain(f); bool matched = true; - matched &= !ltl || f->is_ltl_formula(); - matched &= !psl || f->is_psl_formula(); - matched &= !boolean || f->is_boolean(); - matched &= !universal || f->is_universal(); - matched &= !eventual || f->is_eventual(); - matched &= !syntactic_safety || f->is_syntactic_safety(); - matched &= !syntactic_guarantee || f->is_syntactic_guarantee(); - matched &= !syntactic_obligation || f->is_syntactic_obligation(); - matched &= !syntactic_recurrence || f->is_syntactic_recurrence(); - matched &= !syntactic_persistence || f->is_syntactic_persistence(); - matched &= !syntactic_si || f->is_syntactic_stutter_invariant(); + matched &= !ltl || f.is_ltl_formula(); + matched &= !psl || f.is_psl_formula(); + matched &= !boolean || f.is_boolean(); + matched &= !universal || f.is_universal(); + matched &= !eventual || f.is_eventual(); + matched &= !syntactic_safety || f.is_syntactic_safety(); + matched &= !syntactic_guarantee || f.is_syntactic_guarantee(); + matched &= !syntactic_obligation || f.is_syntactic_obligation(); + matched &= !syntactic_recurrence || f.is_syntactic_recurrence(); + matched &= !syntactic_persistence || f.is_syntactic_persistence(); + matched &= !syntactic_si || f.is_syntactic_stutter_invariant(); matched &= !ap || atomic_prop_collect(f)->size() == ap_n; if (matched && (size_min > 0 || size_max >= 0)) @@ -643,13 +593,8 @@ namespace matched ^= invert; - if (unique) - { - if (unique_set.insert(f).second) - f->clone(); - else - matched = false; - } + if (unique && !unique_set.insert(f).second) + matched = false; if (matched) { @@ -658,7 +603,7 @@ namespace && output_format != quiet_output) { // Sort the formulas alphabetically. - std::map m; + std::map m; for (auto& p: relmap) m.emplace(str_psl(p.first), p.second); for (auto& p: m) @@ -670,7 +615,6 @@ namespace output_formula_checked(f, filename, linenum, prefix, suffix); ++match_count; } - f->destroy(); return 0; } }; diff --git a/src/bin/ltlgrind.cc b/src/bin/ltlgrind.cc index 231b437db..f640e4024 100644 --- a/src/bin/ltlgrind.cc +++ b/src/bin/ltlgrind.cc @@ -27,10 +27,6 @@ #include "common_output.hh" #include "common_conv.hh" -#include "ltlast/allnodes.hh" -#include "ltlvisit/clone.hh" -#include "ltlvisit/apcollect.hh" -#include "ltlvisit/length.hh" #include "ltlvisit/mutation.hh" enum { @@ -100,17 +96,13 @@ namespace { public: int - process_formula(const spot::ltl::formula* f, const char *filename = 0, + process_formula(spot::ltl::formula f, const char *filename = 0, int linenum = 0) { auto mutations = spot::ltl::mutate(f, mut_opts, max_output, mutation_nb, opt_sort); - f->destroy(); for (auto g: mutations) - { - output_formula_checked(g, filename, linenum); - g->destroy(); - } + output_formula_checked(g, filename, linenum); return 0; } }; diff --git a/src/bin/randaut.cc b/src/bin/randaut.cc index 7946c3970..1ace59c57 100644 --- a/src/bin/randaut.cc +++ b/src/bin/randaut.cc @@ -34,7 +34,6 @@ #include "common_aoutput.hh" #include "common_conv.hh" -#include "ltlenv/defaultenv.hh" #include "misc/timer.hh" #include "misc/random.hh" @@ -252,7 +251,7 @@ parse_opt(int key, char* arg, struct argp_state* as) aprops = spot::ltl::create_atomic_prop_set(ap_count_given.min); break; } - aprops.insert(spot::ltl::default_environment::instance().require(arg)); + aprops.insert(spot::ltl::formula::ap(arg)); break; default: @@ -328,7 +327,6 @@ main(int argc, char** argv) if (ap_count_given.max > 0 && ap_count_given.min != ap_count_given.max) { - spot::ltl::destroy_atomic_prop_set(aprops); int c = spot::rrand(ap_count_given.min, ap_count_given.max); aprops = spot::ltl::create_atomic_prop_set(c); } @@ -391,5 +389,4 @@ main(int argc, char** argv) { error(2, 0, "%s", e.what()); } - spot::ltl::destroy_atomic_prop_set(aprops); } diff --git a/src/bin/randltl.cc b/src/bin/randltl.cc index 4acd55102..620661c48 100644 --- a/src/bin/randltl.cc +++ b/src/bin/randltl.cc @@ -33,14 +33,9 @@ #include "common_conv.hh" #include -#include "ltlast/multop.hh" -#include "ltlast/unop.hh" #include "ltlvisit/randomltl.hh" -#include "ltlvisit/length.hh" #include "ltlvisit/simplify.hh" -#include "ltlenv/defaultenv.hh" #include "misc/random.hh" -#include "misc/hash.hh" #include "misc/optionmap.hh" const char argp_program_doc[] ="\ @@ -253,8 +248,8 @@ main(int argc, char** argv) opts.set("output", output); opts.set("tree_size_min", opt_tree_size.min); opts.set("tree_size_max", opt_tree_size.max); - opts.set("opt_wf", opt_wf); - opts.set("opt_seed", opt_seed); + opts.set("wf", opt_wf); + opts.set("seed", opt_seed); opts.set("simplification_level", simplification_level); return opts; }(), opt_pL, opt_pS, opt_pB); @@ -291,14 +286,13 @@ main(int argc, char** argv) default: error(2, 0, "internal error: unknown type of output"); } - destroy_atomic_prop_set(aprops); exit(0); } while (opt_formulas < 0 || opt_formulas--) { static int count = 0; - const spot::ltl::formula* f = rg.next(); + spot::ltl::formula f = rg.next(); if (!f) { error(2, 0, "failed to generate a new unique formula after %d " \ @@ -307,21 +301,17 @@ main(int argc, char** argv) else { output_formula_checked(f, 0, ++count); - f->destroy(); } }; } catch (const std::runtime_error& e) { - destroy_atomic_prop_set(aprops); error(2, 0, "%s", e.what()); } catch (const std::invalid_argument& e) { - destroy_atomic_prop_set(aprops); error(2, 0, "%s", e.what()); } - destroy_atomic_prop_set(aprops); return 0; } diff --git a/src/kripke/kripkeexplicit.cc b/src/kripke/kripkeexplicit.cc index 6f0f3f72c..acc473afb 100644 --- a/src/kripke/kripkeexplicit.cc +++ b/src/kripke/kripkeexplicit.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et +// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche et // Developpement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. @@ -260,11 +260,9 @@ namespace spot } - void kripke_explicit::add_condition(const ltl::formula* f, - std::string on_me) + void kripke_explicit::add_condition(ltl::formula f, std::string on_me) { add_conditions(formula_to_bdd(f, get_dict(), this), on_me); - f->destroy(); } diff --git a/src/kripke/kripkeexplicit.hh b/src/kripke/kripkeexplicit.hh index af4c68e25..cdbf8336d 100644 --- a/src/kripke/kripkeexplicit.hh +++ b/src/kripke/kripkeexplicit.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et -// Développement de l'Epita (LRDE) +// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche +// et Développement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -151,8 +151,7 @@ namespace spot /// /// \param f the formula to add. /// \param on_me the state where to add. - void add_condition(const ltl::formula* f, - std::string on_me); + void add_condition(ltl::formula f, std::string on_me); /// \brief Return map between states and their names. const std::map& diff --git a/src/kripkeparse/kripkeparse.yy b/src/kripkeparse/kripkeparse.yy index 6dd85891d..d7ff6b26a 100644 --- a/src/kripkeparse/kripkeparse.yy +++ b/src/kripkeparse/kripkeparse.yy @@ -48,7 +48,6 @@ typedef std::map formula_cache; { int token; std::string* str; - const spot::ltl::formula* f; std::list* list; } @@ -110,8 +109,8 @@ strident "," condition "," follow_list ";" if (i == fcache.end()) { parse_error_list pel; - const formula* f = spot::ltl::parse_infix_boolean(*$3, pel, - parse_environment); + formula f = spot::ltl::parse_infix_boolean(*$3, pel, + parse_environment); for (parse_error_list::iterator i = pel.begin(); i != pel.end(); ++i) { diff --git a/src/ltlast/Makefile.am b/src/ltlast/Makefile.am index 76586e592..5fc301805 100644 --- a/src/ltlast/Makefile.am +++ b/src/ltlast/Makefile.am @@ -1,6 +1,6 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2009, 2010, 2011, 2013, 2014 Laboratoire de Recherche -## et Développement de l'Epita (LRDE). +## Copyright (C) 2009, 2010, 2011, 2013, 2014, 2015 Laboratoire de +## Recherche et Développement de l'Epita (LRDE). ## Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), ## département Systèmes Répartis Coopératifs (SRC), Université Pierre ## et Marie Curie. @@ -26,24 +26,7 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) ltlastdir = $(pkgincludedir)/ltlast -ltlast_HEADERS = \ - allnodes.hh \ - atomic_prop.hh \ - binop.hh \ - bunop.hh \ - constant.hh \ - formula.hh \ - multop.hh \ - predecl.hh \ - unop.hh \ - visitor.hh +ltlast_HEADERS = formula.hh noinst_LTLIBRARIES = libltlast.la -libltlast_la_SOURCES = \ - atomic_prop.cc \ - binop.cc \ - bunop.cc \ - constant.cc \ - formula.cc \ - multop.cc \ - unop.cc +libltlast_la_SOURCES = formula.cc diff --git a/src/ltlast/allnodes.hh b/src/ltlast/allnodes.hh deleted file mode 100644 index 7a7bb8279..000000000 --- a/src/ltlast/allnodes.hh +++ /dev/null @@ -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 . - -/// \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" diff --git a/src/ltlast/atomic_prop.cc b/src/ltlast/atomic_prop.cc deleted file mode 100644 index 7e0bfdf83..000000000 --- a/src/ltlast/atomic_prop.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include "atomic_prop.hh" -#include "visitor.hh" -#include "misc/bareword.hh" -#include -#include -#include - -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; - } - - } -} diff --git a/src/ltlast/atomic_prop.hh b/src/ltlast/atomic_prop.hh deleted file mode 100644 index d9d89783e..000000000 --- a/src/ltlast/atomic_prop.hh +++ /dev/null @@ -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 . - -/// \file ltlast/atomic_prop.hh -/// \brief LTL atomic propositions -#pragma once - -#include "formula.hh" -#include -#include -#include -#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 key; - typedef std::map 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(f); - } - } -} diff --git a/src/ltlast/binop.cc b/src/ltlast/binop.cc deleted file mode 100644 index 60776a094..000000000 --- a/src/ltlast/binop.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include -#include -#include -#include "binop.hh" -#include "unop.hh" -#include "multop.hh" -#include "constant.hh" -#include "visitor.hh" -#include - -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; - } - - - } -} diff --git a/src/ltlast/binop.hh b/src/ltlast/binop.hh deleted file mode 100644 index 3489bf5cc..000000000 --- a/src/ltlast/binop.hh +++ /dev/null @@ -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 . - -/// \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 -#include -#include - -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 key; - typedef std::map 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(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); - } - } -} diff --git a/src/ltlast/bunop.cc b/src/ltlast/bunop.cc deleted file mode 100644 index 1026e22f2..000000000 --- a/src/ltlast/bunop.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include "bunop.hh" -#include "visitor.hh" -#include -#include -#include -#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; - } - - } -} diff --git a/src/ltlast/bunop.hh b/src/ltlast/bunop.hh deleted file mode 100644 index c8769b674..000000000 --- a/src/ltlast/bunop.hh +++ /dev/null @@ -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 . - -/// \file ltlast/bunop.hh -/// \brief Bounded Unary operators -#pragma once - -#include "formula.hh" -#include -#include -#include -#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 b[->i..j] using the Kleen star. - /// - /// b[->i..j] is implemented as - /// ((!b)[*];b)[*i..j]. - /// - /// 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. - /// - /// b[=i..j] is implemented as - /// ((!b)[*];b)[*i..j];(!b)[*]. - /// - /// \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 1[*]. - /// - /// 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 key; - typedef std::map 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(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; - } - - } -} diff --git a/src/ltlast/constant.cc b/src/ltlast/constant.cc deleted file mode 100644 index a19722877..000000000 --- a/src/ltlast/constant.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include "constant.hh" -#include "visitor.hh" -#include - -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(); - } - } -} diff --git a/src/ltlast/constant.hh b/src/ltlast/constant.hh deleted file mode 100644 index 5642d98a3..000000000 --- a/src/ltlast/constant.hh +++ /dev/null @@ -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 . - -/// \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(f); - } - } -} diff --git a/src/ltlast/formula.cc b/src/ltlast/formula.cc index 07ad14995..fab5ba360 100644 --- a/src/ltlast/formula.cc +++ b/src/ltlast/formula.cc @@ -1,9 +1,6 @@ // -*- 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. +// Copyright (C) 2015 Laboratoire de Recherche et Développement de +// l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -21,19 +18,1673 @@ // along with this program. If not, see . #include "config.h" +#include "misc/common.hh" #include "formula.hh" -#include "misc/hash.hh" -#include "misc/casts.hh" #include -#include "unop.hh" -#include "atomic_prop.hh" -#include +#include +#include +#include +#include +#include +#include "misc/bareword.hh" namespace spot { + namespace + { + typedef std::vector vec; + + // Compare two formulas, by looking at their operators and + // children. This does not use id for the top-level operator, + // because it is used to decide whether to reuse an equal existing + // formula. + struct formula_cmp + { + bool operator()(const ltl::fnode* l, const ltl::fnode* r) const + { + ltl::op opl = l->kind(); + ltl::op opr = r->kind(); + if (opl != opr) + return opl < opr; + + if (SPOT_UNLIKELY(opl == ltl::op::Star || opl == ltl::op::FStar)) + { + { + auto minl = l->min(); + auto minr = r->min(); + if (minl != minr) + return minl < minr; + } + { + auto maxl = l->max(); + auto maxr = r->max(); + if (maxl != maxr) + return maxl < maxr; + } + } + else + { + auto szl = l->size(); + auto szr = r->size(); + if (szl != szr) + return szl < szr; + } + + auto el = l->end(); + auto ir = r->begin(); + for (auto il = l->begin(); il != el; ++il, ++ir) + if (*il != *ir) + return (*il)->id() < (*ir)->id(); + + return false; + } + }; + + struct maps_t final + { + std::map name2ap; + std::map ap2name; + + std::set uniq; + }; + static maps_t m; + + static void + gather_bool(vec& v, ltl::op o) + { + // Gather all boolean terms. + vec b; + 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(), ltl::fnode::multop(o, std::move(b))); + } + } + namespace ltl { - size_t formula::max_serial = 0; + const fnode* fnode::unique(const fnode* f) + { + auto ires = m.uniq.emplace(f); + if (!ires.second) + { + //(*ires.first)->dump(std::cerr << "UNI: ") << '\n'; + for (auto c: *f) + c->destroy(); + delete f; + return (*ires.first)->clone(); + } + //f->dump(std::cerr << "INS: ") << '\n'; + return f; + } + + void + fnode::destroy_aux() const + { + if (SPOT_UNLIKELY(is(op::AP))) + { + auto i = m.ap2name.find(id()); + auto n = m.name2ap.erase(i->second); + assert(n == 1); + (void)n; + m.ap2name.erase(i); + } + else + { + auto n = m.uniq.erase(this); + assert(n == 1); + (void)n; + for (auto c: *this) + c->destroy(); + } + delete this; + } + + std::string fnode::kindstr() const + { + switch (op_) + { +#define C(x) \ + case op::x: \ + return #x; \ + break + C(False); + C(True); + C(EmptyWord); + C(AP); + C(Not); + C(X); + C(F); + C(G); + C(Closure); + C(NegClosure); + C(NegClosureMarked); + C(Xor); + C(Implies); + C(Equiv); + C(U); + C(R); + C(W); + C(M); + C(EConcat); + C(EConcatMarked); + C(UConcat); + C(Or); + C(OrRat); + C(And); + C(AndRat); + C(AndNLM); + C(Concat); + C(Fusion); + C(Star); + C(FStar); +#undef C + } + SPOT_UNREACHABLE(); + } + + const fnode* + fnode::multop(op o, 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) == nullptr) + { + // 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 ((*i)->is(o)) + { + unsigned ps = (*i)->size(); + for (unsigned n = 0; n < ps; ++n) + inlined.push_back((*i)->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 (o == op::Concat || o == op::Fusion) + inlined.push_back(*i); + ++i; + } + if (o == op::Concat || o == op::Fusion) + v.swap(inlined); + else + v.insert(v.end(), inlined.begin(), inlined.end()); + } + + if (o != op::Concat && o != op::Fusion) + std::sort(v.begin(), v.end(), formula_ptr_less_than_bool_first()); + + unsigned orig_size = v.size(); + + const fnode* neutral; + const fnode* neutral2; + const fnode* abs; + const fnode* abs2; + const fnode* weak_abs; + switch (o) + { + case op::And: + neutral = tt(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = nullptr; + break; + case op::AndRat: + neutral = one_star(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = eword(); + gather_bool(v, op::And); + break; + case op::AndNLM: + neutral = eword(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = tt(); + gather_bool(v, op::And); + break; + case op::Or: + neutral = ff(); + neutral2 = nullptr; + abs = tt(); + abs2 = nullptr; + weak_abs = nullptr; + break; + case op::OrRat: + neutral = ff(); + neutral2 = nullptr; + abs = one_star(); + abs2 = nullptr; + weak_abs = nullptr; + gather_bool(v, op::Or); + break; + case op::Concat: + neutral = eword(); + neutral2 = nullptr; + abs = ff(); + abs2 = nullptr; + weak_abs = nullptr; + + // - Concat(Exps1...,FExps2...,1[*],FExps3...,Exps4) = + // Concat(Exps1...,1[*],Exps4) + // If FExps2... and FExps3 all accept [*0]. + { + vec::iterator i = v.begin(); + const fnode* os = 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 op::Fusion: + neutral = tt(); + neutral2 = nullptr; + abs = ff(); + abs2 = eword(); + weak_abs = nullptr; + + // 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. + // FIXME: Investigate the removal of the temporary + // vector, by allowing range to be passed to + // multop() instead of entire containers. We + // should then able to do + // *first = multop(op::And, first, i); + // i = v.erase(first + 1, i); + vec b; + b.insert(b.begin(), first, i); + i = v.erase(first + 1, i); + *first = multop(op::And, b); + } + else + { + ++i; + } + } + } + + break; + + default: + neutral = nullptr; + neutral2 = nullptr; + abs = nullptr; + abs2 = nullptr; + weak_abs = nullptr; + 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 fnode* 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(); + return abs->clone(); + } + else + { + weak_abs_seen |= (*i == weak_abs); + if (o != op::Concat && o != op::Fusion) + // Don't remove duplicates + last = *i; + ++i; + } + } + + if (weak_abs_seen) + { + if (o == 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(); + } + if (acc_eword) + return weak_abs; + else + return abs; + } + else + { + // Similarly, a* & 1 & (c;d) = c;d + // a* & 1 & c* = 1 + assert(o == op::AndNLM); + vec tmp; + for (auto i: v) + { + 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 (o == op::Concat || o == 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] + op bop = o == op::Concat ? op::Star : op::FStar; + i = v.begin(); + while (i != v.end()) + { + vec::iterator fpos = i; + const fnode* f; + unsigned min; + unsigned max; + bool changed = false; + if ((*i)->is(bop)) + { + f = (*i)->nth(0); + min = (*i)->min(); + max = (*i)->max(); + } + else + { + f = *i; + min = max = 1; + } + + ++i; + while (i != v.end()) + { + const fnode* f2; + unsigned min2; + unsigned max2; + if ((*i)->is(bop)) + { + f2 = (*i)->nth(0); + if (f2 != f) + break; + min2 = (*i)->min(); + max2 = (*i)->max(); + } + else + { + f2 = *i; + if (f2 != f) + break; + min2 = max2 = 1; + } + if (min2 == unbounded()) + min = unbounded(); + else if (min != unbounded()) + min += min2; + if (max2 == unbounded()) + max = unbounded(); + else if (max != unbounded()) + max += max2; + (*i)->destroy(); + i = v.erase(i); + changed = true; + } + if (changed) + { + const fnode* newfs = bunop(bop, f->clone(), min, max); + (*fpos)->destroy(); + *fpos = newfs; + } + } + } + } + + vec::size_type s = v.size(); + if (s == 0) + { + assert(neutral != nullptr); + 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 fnode* res = v[0]; + if (o != op::Fusion || orig_size == 1 + || !res->accepts_eword()) + return res; + // If Fusion(f, ...) reduces to Fusion(f), emit Fusion(1,f). + // to ensure that [*0] is not accepted. + v.insert(v.begin(), tt()); + } + + + auto mem = operator new(sizeof(fnode) + + (v.size() - 1)*sizeof(*children)); + return unique(new(mem) fnode(o, v.begin(), v.end())); + } + + const fnode* + fnode::bunop(op o, const fnode* child, uint8_t min, uint8_t max) + { + assert(min <= max); + + const fnode* neutral = nullptr; + switch (o) + { + case op::Star: + neutral = eword(); + break; + case op::FStar: + neutral = tt(); + break; + default: + SPOT_UNREACHABLE(); + } + + + // common trivial simplifications + + // - [*0][*min..max] = [*0] + // - [*0][:*0..max] = 1 + // - [*0][:*min..max] = 0 if min > 0 + if (child->is_eword()) + switch (o) + { + case op::Star: + return neutral; + case op::FStar: + if (min == 0) + return neutral; + else + return ff(); + default: + SPOT_UNREACHABLE(); + } + + // - 0[*0..max] = [*0] + // - 0[*min..max] = 0 if min > 0 + // - b[:*0..max] = 1 + // - b[:*min..max] = 0 if min > 0 + if (child->is_false() + || (o == 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 (o == 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 (child->is(o)) + { + unsigned i = child->min(); + unsigned j = child->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 fnode* exp = child->nth(0); + 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; + } + } + } + + return unique(new fnode(o, child, min, max)); + } + + const fnode* + fnode::unop(op o, const fnode* f) + { + // Some trivial simplifications. + switch (o) + { + case op::F: + case op::G: + { + // F and G are idempotent. + if (f->is(o)) + return f; + + // F(0) = G(0) = 0 + // F(1) = G(1) = 1 + if (f->is_false() || f->is_true()) + return f; + + assert(!f->is_eword()); + } + break; + + case op::Not: + { + // !1 = 0 + if (f->is_true()) + return ff(); + // !0 = 1 + if (f->is_false()) + return tt(); + // ![*0] = 1[+] + if (f->is_eword()) + return bunop(op::Star, tt(), 1); + + auto fop = f->kind(); + // "Not" is an involution. + if (fop == o) + { + auto c = f->nth(0)->clone(); + f->destroy(); + return c; + } + // !Closure(Exp) = NegClosure(Exp) + if (fop == op::Closure) + { + const fnode* c = unop(op::NegClosure, f->nth(0)->clone()); + f->destroy(); + return c; + } + // !NegClosure(Exp) = Closure(Exp) + if (fop == op::NegClosure || fop == op::NegClosureMarked) + { + const fnode* c = unop(op::Closure, f->nth(0)->clone()); + f->destroy(); + return c; + } + break; + } + case op::X: + // X(1) = 1, X(0) = 0 + if (f->is_true() || f->is_false()) + return f; + assert(!f->is_eword()); + break; + + case op::Closure: + // {0} = 0, {1} = 1, {b} = b + if (f->is_boolean()) + return f; + // {[*0]} = 0 + if (f->is_eword()) + return ff(); + break; + + case op::NegClosure: + case op::NegClosureMarked: + // {1} = 0 + if (f->is_true()) + return ff(); + // {0} = 1, {[*0]} = 1 + if (f->is_false() || f->is_eword()) + return tt(); + // {b} = !b + if (f->is_boolean()) + return unop(op::Not, f); + break; + default: + SPOT_UNREACHABLE(); + } + + return unique(new fnode(o, {f})); + } + + const fnode* + fnode::binop(op o, const fnode* first, const fnode* 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 (o) + { + case op::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->is_true()) + return unop(op::Not, second); + if (first->is_false()) + return second; + if (first == second) + { + first->destroy(); + second->destroy(); + return ff(); + } + // We expect constants to appear first, because they are + // instantiated first. + assert(!second->is_constant()); + break; + case op::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->is_false()) + return unop(op::Not, second); + if (first->is_true()) + return second; + if (first == second) + { + first->destroy(); + second->destroy(); + return tt(); + } + // We expect constants to appear first, because they are + // instantiated first. + assert(!second->is_constant()); + break; + case op::Implies: + // - (1 => Exp) = Exp + // - (0 => Exp) = 1 + // - (Exp => 1) = 1 + // - (Exp => 0) = !Exp + // - (Exp => Exp) = 1 + if (first->is_true()) + return second; + if (first->is_false()) + { + second->destroy(); + return tt(); + } + if (second->is_true()) + { + first->destroy(); + return second; + } + if (second->is_false()) + return unop(op::Not, first); + if (first == second) + { + first->destroy(); + second->destroy(); + return tt(); + } + break; + case op::U: + // - (Exp U 1) = 1 + // - (Exp U 0) = 0 + // - (0 U Exp) = Exp + // - (Exp U Exp) = Exp + if (second->is_true() + || second->is_false() + || first->is_false() + || first == second) + { + first->destroy(); + return second; + } + break; + case op::W: + // - (Exp W 1) = 1 + // - (0 W Exp) = Exp + // - (1 W Exp) = 1 + // - (Exp W Exp) = Exp + if (second->is_true() + || first->is_false() + || first == second) + { + first->destroy(); + return second; + } + if (first->is_true()) + { + second->destroy(); + return first; + } + break; + case op::R: + // - (Exp R 1) = 1 + // - (Exp R 0) = 0 + // - (1 R Exp) = Exp + // - (Exp R Exp) = Exp + if (second->is_true() + || second->is_false() + || first->is_true() + || first == second) + { + first->destroy(); + return second; + } + break; + case op::M: + // - (Exp M 0) = 0 + // - (1 M Exp) = Exp + // - (0 M Exp) = 0 + // - (Exp M Exp) = Exp + if (second->is_false() + || first->is_true() + || first == second) + { + first->destroy(); + return second; + } + if (first->is_false()) + { + second->destroy(); + return first; + } + break; + case op::EConcat: + case op::EConcatMarked: + // - 0 <>-> Exp = 0 + // - 1 <>-> Exp = Exp + // - [*0] <>-> Exp = 0 + // - Exp <>-> 0 = 0 + // - boolExp <>-> Exp = boolExp & Exp + if (first->is_true()) + return second; + if (first->is_false() + || first->is_eword()) + { + second->destroy(); + return ff(); + } + if (second->is_false()) + { + first->destroy(); + return second; + } + if (first->is_boolean()) + return multop(op::And, {first, second}); + break; + case op::UConcat: + // - 0 []-> Exp = 1 + // - 1 []-> Exp = Exp + // - [*0] []-> Exp = 1 + // - Exp []-> 1 = 1 + // - boolExp []-> Exp = !boolExp | Exp + if (first->is_true()) + return second; + if (first->is_false() + || first->is_eword()) + { + second->destroy(); + return tt(); + } + if (second->is_true()) + { + first->destroy(); + return second; + } + if (first->is_boolean()) + return multop(op::Or, {unop(op::Not, first), second}); + break; + default: + SPOT_UNREACHABLE(); + } + + auto mem = operator new(sizeof(fnode) + sizeof(*children)); + return unique(new(mem) fnode(o, {first, second})); + } + + + const fnode* + fnode::ap(const std::string& name) + { + auto ires = m.name2ap.emplace(name, nullptr); + if (!ires.second) + return ires.first->second->clone(); + // Name the formula before creating it, because + // the constructor will call ap_name(). + m.ap2name.emplace(next_id_, name); + // next_id_ is incremented by setup_props(), called by the + // constructor of fnode + return ires.first->second = new fnode(op::AP, {}); + } + + const std::string& + fnode::ap_name() const + { + if (op_ != op::AP) + throw std::runtime_error("ap_name() called on non-AP formula"); + auto i = m.ap2name.find(id()); + assert(i != m.ap2name.end()); + return i->second; + } + + size_t fnode::next_id_ = 0U; + const fnode* fnode::ff_ = new fnode(op::False, {}); + const fnode* fnode::tt_ = new fnode(op::True, {}); + const fnode* fnode::ew_ = new fnode(op::EmptyWord, {}); + const fnode* fnode::one_star_ = nullptr; // Only built when necessary. + + void fnode::setup_props(op o) + { + op_ = o; + id_ = next_id_++; + // If the counter of formulae ever loops, we want to skip the + // first three values, because they are permanently associated + // to constants, and it is convenient to have constants + // smaller than all other formulas. + if (next_id_ == 0) + next_id_ = 3; + + switch (op_) + { + case op::False: + case op::True: + 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 op::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; + case op::AP: + 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. + const std::string& n = ap_name(); + std::string::const_iterator pos = n.begin(); + bool lbtap = (pos != n.end() && *pos++ == 'p'); + while (lbtap && pos != n.end()) + { + char l = *pos++; + lbtap = (l >= '0' && l <= '9'); + } + is_.lbt_atomic_props = lbtap; + is_.spin_atomic_props = lbtap || is_spin_ap(n.c_str()); + } + break; + case op::Not: + props = children[0]->props; + is_.not_marked = true; + is_.eventual = children[0]->is_universal(); + is_.universal = children[0]->is_eventual(); + is_.in_nenoform = (children[0]->is(op::AP)); + is_.sere_formula = is_.boolean; + + is_.syntactic_safety = children[0]->is_syntactic_guarantee(); + is_.syntactic_guarantee = children[0]->is_syntactic_safety(); + // is_.syntactic_obligation inherited from child + is_.syntactic_recurrence = children[0]->is_syntactic_persistence(); + is_.syntactic_persistence = children[0]->is_syntactic_recurrence(); + + is_.accepting_eword = false; + break; + case op::X: + props = children[0]->props; + 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 op::F: + props = children[0]->props; + 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 op::G: + props = children[0]->props; + 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 op::NegClosure: + case op::NegClosureMarked: + props = children[0]->props; + is_.not_marked = (op_ == 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(children[0]->is_sere_formula()); + assert(!children[0]->is_boolean()); + break; + case op::Closure: + props = children[0]->props; + 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(children[0]->is_sere_formula()); + assert(!children[0]->is_boolean()); + break; + case op::Xor: + case op::Equiv: + props = children[0]->props & children[1]->props; + 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 op::Implies: + props = children[0]->props & children[1]->props; + is_.eventual = false; + is_.universal = false; + is_.sere_formula = is_.boolean; + is_.sugar_free_boolean = false; + is_.in_nenoform = false; + is_.syntactic_safety = (children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_safety()); + is_.syntactic_guarantee = (children[0]->is_syntactic_safety() + && children[1]->is_syntactic_guarantee()); + // is_.syntactic_obligation inherited + is_.syntactic_persistence = children[0]->is_syntactic_recurrence() + && children[1]->is_syntactic_persistence(); + is_.syntactic_recurrence = children[0]->is_syntactic_persistence() + && children[1]->is_syntactic_recurrence(); + is_.accepting_eword = false; + break; + case op::EConcatMarked: + case op::EConcat: + props = children[0]->props & children[1]->props; + is_.not_marked = (op_ != op::EConcatMarked); + is_.ltl_formula = false; + is_.boolean = false; + is_.sere_formula = false; + is_.accepting_eword = false; + is_.psl_formula = true; + + is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); + is_.syntactic_persistence = children[1]->is_syntactic_persistence(); + if (children[0]->is_finite()) + { + is_.syntactic_safety = children[1]->is_syntactic_safety(); + is_.syntactic_obligation = children[1]->is_syntactic_obligation(); + is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); + } + else + { + is_.syntactic_safety = false; + is_.syntactic_obligation = children[1]->is_syntactic_guarantee(); + is_.syntactic_recurrence = children[1]->is_syntactic_guarantee(); + } + assert(children[0]->is_sere_formula()); + assert(children[1]->is_psl_formula()); + if (children[0]->is_boolean()) + is_.syntactic_si = false; + break; + case op::UConcat: + props = children[0]->props & children[1]->props; + 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 = children[1]->is_syntactic_safety(); + is_.syntactic_recurrence = children[1]->is_syntactic_recurrence(); + if (children[0]->is_finite()) + { + is_.syntactic_guarantee = children[1]->is_syntactic_guarantee(); + is_.syntactic_obligation = children[1]->is_syntactic_obligation(); + is_.syntactic_persistence = + children[1]->is_syntactic_persistence(); + } + else + { + is_.syntactic_guarantee = false; + is_.syntactic_obligation = children[1]->is_syntactic_safety(); + is_.syntactic_persistence = children[1]->is_syntactic_safety(); + } + assert(children[0]->is_sere_formula()); + assert(children[1]->is_psl_formula()); + if (children[0]->is_boolean()) + is_.syntactic_si = false; + break; + case op::U: + // 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 = children[0]->props & children[1]->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 + is_.not_marked = true; + // f U g is universal if g is eventual, or if f == 1. + is_.eventual = children[1]->is_eventual(); + is_.eventual |= children[0]->is_true(); + 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 + children[0]->is_syntactic_obligation() + && children[1]->is_syntactic_guarantee(); + is_.syntactic_recurrence = // Recurrence U Guarantee + children[0]->is_syntactic_recurrence() + && children[1]->is_syntactic_guarantee(); + // is_.syntactic_persistence = Persistence U Persistance + break; + case op::W: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // f W g is universal if f and g are, or if g == 0. + is_.universal |= children[1]->is_false(); + 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 + children[0]->is_syntactic_safety() + && children[1]->is_syntactic_obligation(); + // is_.syntactic_recurrence = Recurrence W Recurrence + is_.syntactic_persistence = // Safety W Persistance + children[0]->is_syntactic_safety() + && children[1]->is_syntactic_persistence(); + + break; + case op::R: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // g R f is universal if f is universal, or if g == 0. + is_.universal = children[1]->is_universal(); + is_.universal |= children[0]->is_false(); + 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 + children[0]->is_syntactic_obligation() + && children[1]->is_syntactic_safety(); + //is_.syntactic_recurrence = Recurrence R Recurrence + is_.syntactic_persistence = // Persistence R Safety + children[0]->is_syntactic_persistence() + && children[1]->is_syntactic_safety(); + + break; + case op::M: + // See comment for op::U. + props = children[0]->props & children[1]->props; + is_.not_marked = true; + // g M f is eventual if both g and f are eventual, or if f == 1. + is_.eventual |= children[1]->is_true(); + 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 + children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_obligation(); + is_.syntactic_recurrence = // Guarantee M Recurrence + children[0]->is_syntactic_guarantee() + && children[1]->is_syntactic_recurrence(); + // is_.syntactic_persistence = Persistence M Persistance + + break; + case op::Or: + { + props = children[0]->props; + unsigned s = size_; + bool ew = children[0]->accepts_eword(); + for (unsigned i = 1; i < s; ++i) + { + ew |= children[i]->accepts_eword(); + props &= children[i]->props; + } + is_.accepting_eword = ew; + break; + } + case op::OrRat: + { + props = children[0]->props; + unsigned s = size_; + 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 = children[0]->accepts_eword(); + for (unsigned i = 1; i < s; ++i) + { + ew |= children[i]->accepts_eword(); + syntactic_si &= children[i]->is_syntactic_stutter_invariant() + && !children[i]->is_boolean(); + props &= children[i]->props; + } + is_.accepting_eword = ew; + is_.syntactic_si = syntactic_si; + break; + } + case op::And: + { + props = children[0]->props; + unsigned s = size_; + for (unsigned i = 1; i < s; ++i) + props &= children[i]->props; + break; + } + case op::Fusion: + case op::Concat: + case op::AndNLM: + case op::AndRat: + { + props = children[0]->props; + unsigned s = size_; + 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 &= children[i]->is_syntactic_stutter_invariant() + && !children[i]->is_boolean(); + props &= children[i]->props; + } + is_.syntactic_si = syntactic_si; + if (op_ == op::Fusion) + is_.accepting_eword = false; + // 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_ == op::Concat) + { + unsigned sb = 0; // stared Boolean formulas seen + for (unsigned i = 0; i < s; ++i) + { + auto ci = children[i]; + if (ci->is_syntactic_stutter_invariant() + && !ci->is_boolean()) + continue; + if (ci->is(op::Star)) + { + sb += ci->nth(0)->is_boolean(); + if (sb > 1) + break; + } + else + { + sb = 0; + break; + } + } + is_.syntactic_si = sb == 1; + } + break; + } + + case op::Star: + case op::FStar: + { + props = children[0]->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 op::Star: + if (max_ == unbounded()) + { + is_.finite = false; + is_.syntactic_si = min_ == 1 && children[0]->is_boolean(); + } + else + { + is_.syntactic_si = false; + } + if (min_ == 0) + is_.accepting_eword = true; + break; + case op::FStar: + is_.accepting_eword = false; + is_.syntactic_si &= !children[0]->is_boolean(); + if (max_ == unbounded()) + is_.finite = false; + if (min_ == 0) + is_.syntactic_si = false; + break; + default: + SPOT_UNREACHABLE(); + } + } + break; + } + } + + + std::ostream& fnode::dump(std::ostream& os) const + { + os << kindstr() << "(@" << id_ << " #" << refs_; + if (op_ == op::Star || op_ == op::FStar) + { + os << ' ' << +min() << ".."; + auto m = max(); + if (m != unbounded()) + os << +m; + } + if (op_ == op::AP) + os << " \"" << ap_name() << '"'; + if (auto s = size()) + { + os << " ["; + for (auto c: *this) + { + c->dump(os); + if (--s) + os << ", "; + } + os << ']'; + } + return os << ')'; + } + + const fnode* fnode::all_but(unsigned i) const + { + switch (op o = kind()) + { + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + { + unsigned s = size(); + assert(s > 1); + vec v; + v.reserve(s - 1); + for (unsigned j = 0; j < s; ++j) + if (i != j) + v.push_back(nth(j)->clone()); + return multop(o, v); + } + default: + throw + std::runtime_error("all_but() is incompatible with this operator"); + } + SPOT_UNREACHABLE(); + } + + const fnode* fnode::boolean_operands(unsigned* width) const + { + unsigned s = boolean_count(); + if (width) + *width = s; + if (s == 0) + return nullptr; + if (s == 1) + return nth(0)->clone(); + vec v(children, children + s); + for (auto c: v) + c->clone(); + return multop(op_, v); + } + + bool fnode::instances_check() + { + unsigned cnt = 0; + for (auto i: m.uniq) + if (i->id() > 3 && i != one_star_) + { + if (!cnt++) + std::cerr << "*** m.uniq is not empty ***\n"; + i->dump(std::cerr) << std::endl; + } + return cnt == 0; + } + + formula formula::sugar_goto(const formula& b, uint8_t min, uint8_t max) + { + if (!b.is_boolean()) + throw + std::runtime_error("sugar_goto() called with non-Boolean argument"); + // b[->min..max] is implemented as ((!b)[*];b)[*min..max] + return Star(Concat({Star(Not(b)), b}), min, max); + } + + formula formula::sugar_equal(const formula& b, uint8_t min, uint8_t max) + { + if (!b.is_boolean()) + throw + std::runtime_error("sugar_equal() called with non-Boolean argument"); + + // b[=0..] = 1[*] + if (min == 0 && max == unbounded()) + return one_star(); + + // b[=min..max] is implemented as ((!b)[*];b)[*min..max];(!b)[*] + formula s = Star(Not(b)); + return Concat({Star(Concat({s, b}), min, max), s}); + } + + int atomic_prop_cmp(const fnode* f, const fnode* g) + { + return strverscmp(f->ap_name().c_str(), g->ap_name().c_str()); + } #define printprops \ proprint(is_boolean, "B", "Boolean formula"); \ @@ -62,11 +1713,11 @@ namespace spot std::list - list_formula_props(const formula* f) + list_formula_props(const formula& f) { std::list res; #define proprint(m, a, l) \ - if (f->m()) \ + if (f.m()) \ res.emplace_back(l); printprops; #undef proprint @@ -74,13 +1725,13 @@ namespace spot } std::ostream& - print_formula_props(std::ostream& out, const formula* f, bool abbr) + print_formula_props(std::ostream& out, const formula& f, bool abbr) { const char* comma = abbr ? "" : ", "; const char* sep = ""; #define proprint(m, a, l) \ - if (f->m()) \ + if (f.m()) \ { \ out << sep; out << (abbr ? a : l); \ sep = comma; \ @@ -90,21 +1741,5 @@ namespace spot return out; } - - const formula* get_literal(const formula* f) - { - const unop* g = is_Not(f); - if (g) - f = g->child(); - return is_atomic_prop(f); - } - - int atomic_prop_cmp(const formula* f, const formula* g) - { - const atomic_prop* a = down_cast(f); - const atomic_prop* b = down_cast(g); - return strverscmp(a->name().c_str(), b->name().c_str()); - } - } } diff --git a/src/ltlast/formula.hh b/src/ltlast/formula.hh index e1f8f702b..35bedab9f 100644 --- a/src/ltlast/formula.hh +++ b/src/ltlast/formula.hh @@ -1,7 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 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 +// Copyright (C) 2015 Laboratoire de Recherche et Développement de +// l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -19,114 +18,263 @@ // along with this program. If not, see . /// \file ltlast/formula.hh -/// \brief LTL formula interface +/// \brief LTL/PSL formula interface #pragma once #include "misc/common.hh" -#include +#include +#include +#include #include -#include "predecl.hh" +#include +#include +#include +#include +#include #include +#include +#include namespace spot { namespace ltl { - /// \defgroup ltl LTL formulae - /// - /// This module gathers types and definitions related to LTL formulae. + enum class op: uint8_t + { + False, + True, + EmptyWord, + AP, + // unary operators + Not, + X, + F, + G, + Closure, + NegClosure, + NegClosureMarked, + // binary operators + Xor, + Implies, + Equiv, + U, ///< until + R, ///< release (dual of until) + W, ///< weak until + M, ///< strong release (dual of weak until) + EConcat, ///< Seq + EConcatMarked, ///< Seq, Marked + UConcat, ///< Triggers + // n-ary operators + Or, ///< (omega-Rational) Or + OrRat, ///< Rational Or + And, ///< (omega-Rational) And + AndRat, ///< Rational And + AndNLM, ///< Non-Length-Matching Rational-And + Concat, + Fusion, + // star-like operators + Star, ///< Star + FStar, ///< Fustion Star + }; - /// \addtogroup ltl_essential Essential LTL types - /// \ingroup ltl - - /// \addtogroup ltl_ast LTL Abstract Syntax Tree - /// \ingroup ltl - - /// \addtogroup ltl_environment LTL environments - /// \ingroup ltl - /// LTL environment implementations. - - /// \addtogroup ltl_algorithm Algorithms for LTL formulae - /// \ingroup ltl - - /// \addtogroup ltl_io Input/Output of LTL formulae - /// \ingroup ltl_algorithm - - /// \addtogroup ltl_visitor Derivable visitors - /// \ingroup ltl_algorithm - - /// \addtogroup ltl_rewriting Rewriting LTL formulae - /// \ingroup ltl_algorithm - - /// \addtogroup ltl_misc Miscellaneous algorithms for LTL formulae - /// \ingroup ltl_algorithm - - - /// \ingroup ltl_essential - /// \ingroup ltl_ast - /// \brief An LTL formula. - /// - /// The only way you can work with a formula is to - /// build a spot::ltl::visitor or spot::ltl::const_visitor. - class SPOT_API formula +#ifndef SWIG + class SPOT_API fnode final { public: - /// Kind of a sub-formula - enum opkind { Constant, - AtomicProp, - UnOp, - BinOp, - MultOp, - BUnOp }; - - protected: - formula(opkind k) : serial_(max_serial++), kind_(k) - { - // If the counter of formulae ever loops, we want to skip the - // first three values, because they are permanently associated - // to constants, and it is convenient to have constants smaller - // than all other formulae. - if (max_serial == 0) - max_serial = 3; - } - - public: - /// Entry point for spot::ltl::visitor instances. - virtual void accept(visitor& v) const = 0; - - /// \brief clone this node - /// - /// This increments the reference counter of the node. - const formula* clone() const + const fnode* clone() const { ++refs_; return this; } - /// \brief Release this node void destroy() const { - // Delete if this is the last node, and it is not a constant. - if (!refs_) - { - if (kind() != Constant) - delete this; - } + // last reference to a node that is not a constant? + if (SPOT_UNLIKELY(!refs_ && id_ > 2)) + destroy_aux(); else - { - --refs_; - } + --refs_; } - /// Return a canonic representation of the formula - virtual std::string dump() const = 0; - - /// Return the kind of the top-level operator. - opkind kind() const + static constexpr uint8_t unbounded() { - return kind_; + return UINT8_MAX; } + static const fnode* ap(const std::string& name); + static const fnode* unop(op o, const fnode* f); + static const fnode* binop(op o, const fnode* f, const fnode* g); + static const fnode* multop(op o, std::vector l); + static const fnode* bunop(op o, const fnode* f, + uint8_t min, uint8_t max = unbounded()); + + op kind() const + { + return op_; + } + + std::string kindstr() const; + + bool is(op o) const + { + return op_ == o; + } + + bool is(op o1, op o2) const + { + return op_ == o1 || op_ == o2; + } + + bool is(std::initializer_list l) const + { + const fnode* n = this; + for (auto o: l) + { + if (!n->is(o)) + return false; + n = n->nth(0); + } + return true; + } + + /// \brief Remove operator \a o and return the child. + /// + /// This works only for unary operators. + const fnode* get_child_of(op o) const + { + if (op_ != o) + return nullptr; + assert(size_ == 1); + return nth(0); + } + + /// \brief Remove all operators in \a l and return the child. + /// + /// This works only for a list of unary operators. + /// For instance if \c f is a formula for XG(a U b), + /// then f.get_child_of({op::X, op::G}) + /// will return the subformula a U b. + const fnode* get_child_of(std::initializer_list l) const + { + auto c = this; + for (auto o: l) + { + c = c->get_child_of(o); + if (c == nullptr) + return c; + } + return c; + } + + uint8_t min() const + { + assert(op_ == op::FStar || op_ == op::Star); + return min_; + } + + uint8_t max() const + { + assert(op_ == op::FStar || op_ == op::Star); + return max_; + } + + uint8_t size() const + { + return size_; + } + + size_t id() const + { + return id_; + } + + const fnode*const* begin() const + { + return children; + } + + const fnode*const* end() const + { + return children + size(); + } + + const fnode* nth(unsigned i) const + { + assert(i < size()); + if (i >= size()) + throw std::runtime_error("access to non-existing child"); + return children[i]; + } + + static const fnode* ff() + { + return ff_; + } + + bool is_false() const + { + return op_ == op::False; + } + + static const fnode* tt() + { + return tt_; + } + + bool is_true() const + { + return op_ == op::True; + } + + static const fnode* eword() + { + return ew_; + } + + bool is_eword() const + { + return op_ == op::EmptyWord; + } + + bool is_constant() const + { + return op_ == op::False || op_ == op::True || op_ == op::EmptyWord; + } + + bool is_Kleene_star() const + { + if (op_ != op::Star) + return false; + return min_ == 0 && max_ == unbounded(); + } + + static const fnode* one_star() + { + if (!one_star_) + one_star_ = bunop(op::Star, tt(), 0); + return one_star_; + } + + const std::string& ap_name() const; + std::ostream& dump(std::ostream& os) const; + + const fnode* all_but(unsigned i) const; + + unsigned boolean_count() const + { + unsigned pos = 0; + unsigned s = size(); + while (pos < s && children[pos]->is_boolean()) + ++pos; + return pos; + } + + const fnode* boolean_operands(unsigned* width = nullptr) const; + + /// return true if the unicity map contains only the globally + /// pre-allocated formulas. + static bool instances_check(); + //////////////// // Properties // //////////////// @@ -134,13 +282,13 @@ namespace spot /// Whether the formula use only boolean operators. bool is_boolean() const { - return is.boolean; + return is_.boolean; } /// Whether the formula use only AND, OR, and NOT operators. bool is_sugar_free_boolean() const { - return is.sugar_free_boolean; + return is_.sugar_free_boolean; } /// \brief Whether the formula is in negative normal form. @@ -149,44 +297,44 @@ namespace spot /// occur only in front of atomic propositions. bool is_in_nenoform() const { - return is.in_nenoform; + return is_.in_nenoform; } /// Whether the formula is syntactically stutter_invariant bool is_syntactic_stutter_invariant() const { - return is.syntactic_si; + return is_.syntactic_si; } /// Whether the formula avoids the F and G operators. bool is_sugar_free_ltl() const { - return is.sugar_free_ltl; + return is_.sugar_free_ltl; } /// Whether the formula uses only LTL operators. bool is_ltl_formula() const { - return is.ltl_formula; + return is_.ltl_formula; } /// Whether the formula uses only PSL operators. bool is_psl_formula() const { - return is.psl_formula; + return is_.psl_formula; } /// Whether the formula uses only SERE operators. bool is_sere_formula() const { - return is.sere_formula; + return is_.sere_formula; } /// Whether a SERE describes a finite language, or an LTL /// formula uses no temporal operator but X. bool is_finite() const { - return is.finite; + return is_.finite; } /// \brief Whether the formula is purely eventual. @@ -211,7 +359,7 @@ namespace spot /// anything and still satisfies the formula. bool is_eventual() const { - return is.eventual; + return is_.eventual; } /// \brief Whether a formula is purely universal. @@ -236,86 +384,115 @@ namespace spot /// universal formula also satisfies the formula. bool is_universal() const { - return is.universal; + return is_.universal; } /// Whether a PSL/LTL formula is syntactic safety property. bool is_syntactic_safety() const { - return is.syntactic_safety; + return is_.syntactic_safety; } /// Whether a PSL/LTL formula is syntactic guarantee property. bool is_syntactic_guarantee() const { - return is.syntactic_guarantee; + return is_.syntactic_guarantee; } /// Whether a PSL/LTL formula is syntactic obligation property. bool is_syntactic_obligation() const { - return is.syntactic_obligation; + return is_.syntactic_obligation; } /// Whether a PSL/LTL formula is syntactic recurrence property. bool is_syntactic_recurrence() const { - return is.syntactic_recurrence; + return is_.syntactic_recurrence; } /// Whether a PSL/LTL formula is syntactic persistence property. bool is_syntactic_persistence() const { - return is.syntactic_persistence; + return is_.syntactic_persistence; } /// Whether the formula has an occurrence of EConcatMarked. bool is_marked() const { - return !is.not_marked; + return !is_.not_marked; } /// Whether the formula accepts [*0]. bool accepts_eword() const { - return is.accepting_eword; + return is_.accepting_eword; } bool has_lbt_atomic_props() const { - return is.lbt_atomic_props; + return is_.lbt_atomic_props; } bool has_spin_atomic_props() const { - return is.spin_atomic_props; + return is_.spin_atomic_props; } - /// The properties as a field of bits. For internal use. - unsigned get_props() const + private: + void setup_props(op o); + void destroy_aux() const; + + static const fnode* unique(const fnode*); + + // Destruction may only happen via destroy(). + ~fnode() = default; + // Disallow copies. + fnode(const fnode&) = delete; + fnode& operator=(const fnode&) = delete; + + + + template + fnode(op o, iter begin, iter end) { - return props; + size_t s = std::distance(begin, end); + if (s > (size_t) UINT16_MAX) + throw std::runtime_error("too many children for formula"); + size_ = s; + auto pos = children; + for (auto i = begin; i != end; ++i) + *pos++ = *i; + setup_props(o); } - /// Return a hash key for the formula. - size_t - hash() const - { - return serial_; - } - protected: - virtual ~formula() + fnode(op o, std::initializer_list l) + : fnode(o, l.begin(), l.end()) { } - /// \brief The hash key of this formula. - size_t serial_; + fnode(op o, const fnode* f, uint8_t min, uint8_t max) + { + size_ = 1; + children[0] = f; + min_ = min; + max_ = max; + setup_props(o); + } - // The number of actual references is refs_ + 1. - mutable unsigned refs_ = 0; - /// \brief Number of formulae created so far. - static size_t max_serial; - opkind kind_; + static const fnode* ff_; + static const fnode* tt_; + static const fnode* ew_; + static const fnode* one_star_; + + op op_; // operator + uint8_t min_; // range minimum (for star-like operators) + uint8_t max_; // range maximum; + //uint8_t unused_; + uint16_t size_; // number of children + mutable uint16_t refs_ = 0; // reference count - 1; + size_t id_; // Also used as hash. + static size_t next_id_; struct ltl_prop { @@ -357,95 +534,29 @@ namespace spot { // Use an unsigned for fast computation of all properties. unsigned props; - ltl_prop is; + ltl_prop is_; }; + + const fnode* children[1]; }; - - /// \brief Change \a f into a if it is equal to - /// !a or a. - /// - /// Return 0 otherwise. + /// Order two atomic propositions. SPOT_API - const formula* get_literal(const formula* f); + int atomic_prop_cmp(const fnode* f, const fnode* g); - /// Return true iff f is a literal. - inline - bool - is_literal(const formula* f) - { - return (f->kind() == formula::AtomicProp - // The only unary operator that is Boolean is Not, - // and if f is in nenoform, Not can only occur in - // front of an atomic proposition. So with this - // check we do not have to cast f to check what - // operator it is and the type of its child. - || (f->is_boolean() && f->is_in_nenoform() - && f->kind() == formula::UnOp)); - } - - - /// Compare two atomic propositions. - SPOT_API - int atomic_prop_cmp(const formula* f, const formula* g); - - - /// \ingroup ltl_essentials - /// \brief Strict Weak Ordering for const formula*. - /// - /// This is meant to be used as a comparison functor for - /// STL \c map whose key are of type const formula*. - /// - /// For instance here is how one could declare - /// a map of \c const::formula*. - /// \code - /// // Remember how many times each formula has been seen. - /// std::map seen; - /// \endcode - struct formula_ptr_less_than: - public std::binary_function - { - bool - operator()(const formula* left, const formula* right) const - { - assert(left); - assert(right); - if (left == right) - return false; - - size_t l = left->hash(); - size_t r = right->hash(); - if (l != r) - return l < r; - // Because the hash code assigned to each formula is the - // number of formulae constructed so far, it is very unlikely - // that we will ever reach a case were two different formulae - // have the same hash. This will happen only ever with have - // produced 256**sizeof(size_t) formulae (i.e. max_count has - // looped back to 0 and started over). In that case we can - // order two formulae by looking at their text representation. - // We could be more efficient and look at their AST, but it's - // not worth the burden. (Also ordering pointers is ruled out - // because it breaks the determinism of the implementation.) - return left->dump() < right->dump(); - } - }; - - /// \brief Strict Weak Ordering for const formula* - /// inside ltl::multop. + /// \brief Strict Weak Ordering for const fnode* + /// inside n-ary operators /// \ingroup ltl_essentials /// /// This is the comparison functor used by to order the - /// ltl::multop operands. It keeps Boolean formulae first in + /// n-ary operands. It keeps Boolean formulae first in /// order to speed up implication checks. /// /// Also keep literal alphabetically ordered. - struct formula_ptr_less_than_bool_first: - public std::binary_function + struct formula_ptr_less_than_bool_first { bool - operator()(const formula* left, const formula* right) const + operator()(const fnode* left, const fnode* right) const { assert(left); assert(right); @@ -460,15 +571,22 @@ namespace spot // We have two Boolean formulae if (lib) { - bool lconst = left->kind() == formula::Constant; - bool rconst = right->kind() == formula::Constant; - if (lconst != rconst) + bool lconst = left->is_constant(); + if (lconst != right->is_constant()) return lconst; if (!lconst) { + auto get_literal = [](const fnode* f) -> const fnode* + { + if (f->is(op::Not)) + f = f->nth(0); + if (f->is(op::AP)) + return f; + return nullptr; + }; // Literals should come first - const formula* litl = get_literal(left); - const formula* litr = get_literal(right); + const fnode* litl = get_literal(left); + const fnode* litr = get_literal(right); if (!litl != !litr) return litl; if (litl) @@ -481,8 +599,8 @@ namespace spot } } - size_t l = left->hash(); - size_t r = right->hash(); + size_t l = left->id(); + size_t r = right->id(); if (l != r) return l < r; // Because the hash code assigned to each formula is the @@ -491,23 +609,636 @@ namespace spot // have the same hash. This will happen only ever with have // produced 256**sizeof(size_t) formulae (i.e. max_count has // looped back to 0 and started over). In that case we can - // order two formulae by looking at their text representation. + // order two formulas by looking at their text representation. // We could be more efficient and look at their AST, but it's // not worth the burden. (Also ordering pointers is ruled out // because it breaks the determinism of the implementation.) - return left->dump() < right->dump(); + std::ostringstream old; + left->dump(old); + std::ostringstream ord; + right->dump(ord); + return old.str() < ord.str(); + } + }; + +#endif // SWIG + + class SPOT_API formula final + { + const fnode* ptr_; + public: + explicit formula(const fnode* f) noexcept + : ptr_(f) + { + } + + formula(std::nullptr_t) noexcept + : ptr_(nullptr) + { + } + + formula() noexcept + : ptr_(nullptr) + { + } + + formula(const formula& f) noexcept + : ptr_(f.ptr_) + { + if (ptr_) + ptr_->clone(); + } + + formula(formula&& f) noexcept + : ptr_(f.ptr_) + { + f.ptr_ = nullptr; + } + + ~formula() + { + if (ptr_) + ptr_->destroy(); + } + + const formula& operator=(std::nullptr_t) + { + this->~formula(); + ptr_ = nullptr; + return *this; + } + + const formula& operator=(const formula& f) + { + this->~formula(); + if ((ptr_ = f.ptr_)) + ptr_->clone(); + return *this; + } + + const formula& operator=(formula&& f) noexcept + { + std::swap(f.ptr_, ptr_); + return *this; + } + + bool operator<(const formula& other) const noexcept + { + return id() < other.id(); + } + + bool operator<=(const formula& other) const noexcept + { + return id() <= other.id(); + } + + bool operator>(const formula& other) const noexcept + { + return id() > other.id(); + } + + bool operator>=(const formula& other) const noexcept + { + return id() >= other.id(); + } + + bool operator==(const formula& other) const noexcept + { + return id() == other.id(); + } + + bool operator==(std::nullptr_t) const noexcept + { + return ptr_ == nullptr; + } + + bool operator!=(const formula& other) const noexcept + { + return id() != other.id(); + } + + bool operator!=(std::nullptr_t) const noexcept + { + return ptr_ != nullptr; + } + + ///////////////////////// + // Forwarded functions // + ///////////////////////// + + static constexpr uint8_t unbounded() + { + return fnode::unbounded(); + } + + static formula ap(const std::string& name) + { + return formula(fnode::ap(name)); + } + + static formula unop(op o, const formula& f) + { + return formula(fnode::unop(o, f.ptr_->clone())); + } + +#ifndef SWIG + static formula unop(op o, formula&& f) + { + return formula(fnode::unop(o, f.to_node_())); + } +#endif // !SWIG + +#ifdef SWIG +#define SPOT_DEF_UNOP(Name) \ + static formula Name(const formula& f) \ + { \ + return unop(op::Name, f); \ + } +#else // !SWIG +#define SPOT_DEF_UNOP(Name) \ + static formula Name(const formula& f) \ + { \ + return unop(op::Name, f); \ + } \ + static formula Name(formula&& f) \ + { \ + return unop(op::Name, std::move(f)); \ + } +#endif // !SWIG + SPOT_DEF_UNOP(Not); + SPOT_DEF_UNOP(X); + SPOT_DEF_UNOP(F); + SPOT_DEF_UNOP(G); + SPOT_DEF_UNOP(Closure); + SPOT_DEF_UNOP(NegClosure); + SPOT_DEF_UNOP(NegClosureMarked); +#undef SPOT_DEF_UNOP + + static formula binop(op o, const formula& f, const formula& g) + { + return formula(fnode::binop(o, f.ptr_->clone(), g.ptr_->clone())); + } + +#ifndef SWIG + static formula binop(op o, const formula& f, formula&& g) + { + return formula(fnode::binop(o, f.ptr_->clone(), g.to_node_())); + } + + static formula binop(op o, formula&& f, const formula& g) + { + return formula(fnode::binop(o, f.to_node_(), g.ptr_->clone())); + } + + static formula binop(op o, formula&& f, formula&& g) + { + return formula(fnode::binop(o, f.to_node_(), g.to_node_())); + } +#endif //SWIG + +#ifdef SWIG +#define SPOT_DEF_BINOP(Name) \ + static formula Name(const formula& f, const formula& g) \ + { \ + return binop(op::Name, f, g); \ + } +#else // !SWIG +#define SPOT_DEF_BINOP(Name) \ + static formula Name(const formula& f, const formula& g) \ + { \ + return binop(op::Name, f, g); \ + } \ + static formula Name(const formula& f, formula&& g) \ + { \ + return binop(op::Name, f, std::move(g)); \ + } \ + static formula Name(formula&& f, const formula& g) \ + { \ + return binop(op::Name, std::move(f), g); \ + } \ + static formula Name(formula&& f, formula&& g) \ + { \ + return binop(op::Name, std::move(f), std::move(g)); \ + } +#endif // !SWIG + SPOT_DEF_BINOP(Xor); + SPOT_DEF_BINOP(Implies); + SPOT_DEF_BINOP(Equiv); + SPOT_DEF_BINOP(U); + SPOT_DEF_BINOP(R); + SPOT_DEF_BINOP(W); + SPOT_DEF_BINOP(M); + SPOT_DEF_BINOP(EConcat); + SPOT_DEF_BINOP(EConcatMarked); + SPOT_DEF_BINOP(UConcat); +#undef SPOT_DEF_BINOP + + static formula multop(op o, const std::vector& l) + { + std::vector tmp; + tmp.reserve(l.size()); + for (auto f: l) + if (f.ptr_) + tmp.push_back(f.ptr_->clone()); + return formula(fnode::multop(o, std::move(tmp))); + } + +#ifndef SWIG + static formula multop(op o, std::vector&& l) + { + std::vector tmp; + tmp.reserve(l.size()); + for (auto f: l) + if (f.ptr_) + tmp.push_back(f.to_node_()); + return formula(fnode::multop(o, std::move(tmp))); + } +#endif // !SWIG + +#ifdef SWIG +#define SPOT_DEF_MULTOP(Name) \ + static formula Name(const std::vector& l) \ + { \ + return multop(op::Name, l); \ + } +#else // !SWIG +#define SPOT_DEF_MULTOP(Name) \ + static formula Name(const std::vector& l) \ + { \ + return multop(op::Name, l); \ + } \ + \ + static formula Name(std::vector&& l) \ + { \ + return multop(op::Name, std::move(l)); \ + } +#endif // !SWIG + SPOT_DEF_MULTOP(Or); + SPOT_DEF_MULTOP(OrRat); + SPOT_DEF_MULTOP(And); + SPOT_DEF_MULTOP(AndRat); + SPOT_DEF_MULTOP(AndNLM); + SPOT_DEF_MULTOP(Concat); + SPOT_DEF_MULTOP(Fusion); +#undef SPOT_DEF_MULTOP + + static formula bunop(op o, const formula& f, + uint8_t min = 0U, + uint8_t max = unbounded()) + { + return formula(fnode::bunop(o, f.ptr_->clone(), min, max)); + } + +#ifndef SWIG + static formula bunop(op o, formula&& f, + uint8_t min = 0U, + uint8_t max = unbounded()) + { + return formula(fnode::bunop(o, f.to_node_(), min, max)); + } +#endif // !SWIG + +#if SWIG +#define SPOT_DEF_BUNOP(Name) \ + static formula Name(const formula& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, f, min, max); \ + } +#else // !SWIG +#define SPOT_DEF_BUNOP(Name) \ + static formula Name(const formula& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, f, min, max); \ + } \ + static formula Name(formula&& f, \ + uint8_t min = 0U, \ + uint8_t max = unbounded()) \ + { \ + return bunop(op::Name, std::move(f), min, max); \ + } +#endif + SPOT_DEF_BUNOP(Star); + SPOT_DEF_BUNOP(FStar); +#undef SPOT_DEF_BUNOP + + static formula sugar_goto(const formula& b, uint8_t min, uint8_t max); + + static formula sugar_equal(const formula& b, uint8_t min, uint8_t max); + + operator bool() const + { + return ptr_ != nullptr; + } + +#ifndef SWIG + const fnode* to_node_() + { + auto tmp = ptr_; + ptr_ = nullptr; + return tmp; + } +#endif + + op kind() const + { + return ptr_->kind(); + } + + std::string kindstr() const + { + return ptr_->kindstr(); + } + + bool is(op o) const + { + return ptr_->is(o); + } + + bool is(op o1, op o2) const + { + return ptr_->is(o1, o2); + } + + bool is(std::initializer_list l) const + { + return ptr_->is(l); + } + + formula get_child_of(op o) const + { + auto f = ptr_->get_child_of(o); + if (f) + f->clone(); + return formula(f); + } + + formula get_child_of(std::initializer_list l) const + { + auto f = ptr_->get_child_of(l); + if (f) + f->clone(); + return formula(f); + } + + uint8_t min() const + { + return ptr_->min(); + } + + uint8_t max() const + { + return ptr_->max(); + } + + uint8_t size() const + { + return ptr_->size(); + } + + size_t id() const + { + return ptr_->id(); + } + + class SPOT_API formula_child_iterator final + { + const fnode*const* ptr_; + public: + formula_child_iterator() + : ptr_(nullptr) + { + } + + formula_child_iterator(const fnode*const* f) + : ptr_(f) + { + } + + bool operator==(formula_child_iterator o) + { + return ptr_ == o.ptr_; + } + + bool operator!=(formula_child_iterator o) + { + return ptr_ != o.ptr_; + } + + formula operator*() + { + return formula((*ptr_)->clone()); + } + + formula_child_iterator operator++() + { + ++ptr_; + return *this; + } + + formula_child_iterator operator++(int) + { + auto tmp = *this; + ++ptr_; + return tmp; + } + }; + + formula_child_iterator begin() const + { + return ptr_->begin(); + } + + formula_child_iterator end() const + { + return ptr_->end(); + } + + formula nth(unsigned i) const + { + return formula(ptr_->nth(i)->clone()); + } + + static formula ff() + { + return formula(fnode::ff()); + } + + bool is_false() const + { + return ptr_->is_false(); + } + + static formula tt() + { + return formula(fnode::tt()); + } + + bool is_true() const + { + return ptr_->is_true(); + } + + static formula eword() + { + return formula(fnode::eword()); + } + + bool is_eword() const + { + return ptr_->is_eword(); + } + + bool is_constant() const + { + return ptr_->is_constant(); + } + + bool is_Kleene_star() const + { + return ptr_->is_Kleene_star(); + } + + static formula one_star() + { + return formula(fnode::one_star()->clone()); + } + + bool is_literal() + { + return (is(op::AP) || + // If f is in nenoform, Not can only occur in front of + // an atomic proposition. So this way we do not have + // to check the type of the child. + (is(op::Not) && is_boolean() && is_in_nenoform())); + } + + const std::string& ap_name() const + { + return ptr_->ap_name(); + } + + std::ostream& dump(std::ostream& os) const + { + return ptr_->dump(os); + } + + formula all_but(unsigned i) const + { + return formula(ptr_->all_but(i)); + } + + unsigned boolean_count() const + { + return ptr_->boolean_count(); + } + + formula boolean_operands(unsigned* width = nullptr) const + { + return formula(ptr_->boolean_operands(width)); + } + +#define SPOT_DEF_PROP(Name) \ + bool Name() const \ + { \ + return ptr_->Name(); \ + } + SPOT_DEF_PROP(is_boolean); + SPOT_DEF_PROP(is_sugar_free_boolean); + SPOT_DEF_PROP(is_in_nenoform); + SPOT_DEF_PROP(is_syntactic_stutter_invariant); + SPOT_DEF_PROP(is_sugar_free_ltl); + SPOT_DEF_PROP(is_ltl_formula); + SPOT_DEF_PROP(is_psl_formula); + SPOT_DEF_PROP(is_sere_formula); + SPOT_DEF_PROP(is_finite); + SPOT_DEF_PROP(is_eventual); + SPOT_DEF_PROP(is_universal); + SPOT_DEF_PROP(is_syntactic_safety); + SPOT_DEF_PROP(is_syntactic_guarantee); + SPOT_DEF_PROP(is_syntactic_obligation); + SPOT_DEF_PROP(is_syntactic_recurrence); + SPOT_DEF_PROP(is_syntactic_persistence); + SPOT_DEF_PROP(is_marked); + SPOT_DEF_PROP(accepts_eword); + SPOT_DEF_PROP(has_lbt_atomic_props); + SPOT_DEF_PROP(has_spin_atomic_props); +#undef SPOT_DEF_PROP + + template + formula map(Trans trans) + { + switch (op o = kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + return *this; + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + return unop(o, trans(nth(0))); + 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 tmp = trans(nth(0)); + return binop(o, tmp, trans(nth(1))); + } + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + { + std::vector tmp; + tmp.reserve(size()); + for (auto f: *this) + tmp.push_back(trans(f)); + return multop(o, std::move(tmp)); + } + case op::Star: + case op::FStar: + return bunop(o, trans(nth(0)), min(), max()); + } + SPOT_UNREACHABLE(); + } + + template + void traverse(Func func) + { + if (func(*this)) + return; + for (auto f: *this) + f.traverse(func); } }; /// Print the properties of formula \a f on stream \a out. SPOT_API - std::ostream& print_formula_props(std::ostream& out, - const formula* f, + std::ostream& print_formula_props(std::ostream& out, const formula& f, bool abbreviated = false); /// List the properties of formula \a f. SPOT_API - std::list list_formula_props(const formula* f); + std::list list_formula_props(const formula& f); } } @@ -515,12 +1246,11 @@ namespace spot namespace std { template <> - struct hash + struct hash { - size_t operator()(const spot::ltl::formula* x) const noexcept + size_t operator()(const spot::ltl::formula& x) const noexcept { - assert(x); - return x->hash(); + return x.id(); } }; } diff --git a/src/ltlast/multop.cc b/src/ltlast/multop.cc deleted file mode 100644 index b632b7d3a..000000000 --- a/src/ltlast/multop.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include -#include -#include -#include -#include -#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; - } - - } -} diff --git a/src/ltlast/multop.hh b/src/ltlast/multop.hh deleted file mode 100644 index d94353703..000000000 --- a/src/ltlast/multop.hh +++ /dev/null @@ -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 . - -/// \file ltlast/multop.hh -/// \brief LTL multi-operand operators -#pragma once - -#include "formula.hh" -#include -#include -#include - -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 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 a|b|c|d and c - /// is child number 2, then calling f->all_but(2) will - /// return a new formula a|b|d. - const formula* all_but(unsigned n) const; - - /// \brief return the number of Boolean operands in the binop. - /// - /// For instance if \c f a|b|Xc|Gd, this - /// returns 2. - unsigned boolean_count() const; - - /// \brief return the Boolean part of the binop. - /// - /// For instance if \c f a|b|Xc|Gd, this - /// returns a|b. 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 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 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(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); - } - } -} diff --git a/src/ltlast/predecl.hh b/src/ltlast/predecl.hh deleted file mode 100644 index 83eecdd5c..000000000 --- a/src/ltlast/predecl.hh +++ /dev/null @@ -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 . - -/// \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; - } -} diff --git a/src/ltlast/unop.cc b/src/ltlast/unop.cc deleted file mode 100644 index 9473345c1..000000000 --- a/src/ltlast/unop.cc +++ /dev/null @@ -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 . - -#include "config.h" -#include "unop.hh" -#include "visitor.hh" -#include -#include -#include -#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; - } - - } -} diff --git a/src/ltlast/unop.hh b/src/ltlast/unop.hh deleted file mode 100644 index b54aa1154..000000000 --- a/src/ltlast/unop.hh +++ /dev/null @@ -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 . - -/// \file ltlast/unop.hh -/// \brief LTL unary operators -#pragma once - -#include -#include -#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 key; - typedef std::map 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(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; - } - } -} diff --git a/src/ltlast/visitor.hh b/src/ltlast/visitor.hh deleted file mode 100644 index 71c7f2caf..000000000 --- a/src/ltlast/visitor.hh +++ /dev/null @@ -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 . - -/// \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; - }; - } -} diff --git a/src/ltlenv/declenv.cc b/src/ltlenv/declenv.cc index ad278dc49..b1e96463b 100644 --- a/src/ltlenv/declenv.cc +++ b/src/ltlenv/declenv.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2012, 2014 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2009, 2012, 2014, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -31,28 +31,22 @@ namespace spot { } - declarative_environment::~declarative_environment() - { - for (prop_map::iterator i = props_.begin(); i != props_.end(); ++i) - i->second->destroy(); - } - bool declarative_environment::declare(const std::string& prop_str) { if (props_.find(prop_str) != props_.end()) return false; - props_[prop_str] = ltl::atomic_prop::instance(prop_str, *this); + props_[prop_str] = formula::ap(prop_str); return true; } - const formula* + formula declarative_environment::require(const std::string& prop_str) { prop_map::iterator i = props_.find(prop_str); if (i == props_.end()) - return 0; - return i->second->clone(); + return nullptr; + return i->second; } const std::string& diff --git a/src/ltlenv/declenv.hh b/src/ltlenv/declenv.hh index 45420a6e7..3fd64d4b9 100644 --- a/src/ltlenv/declenv.hh +++ b/src/ltlenv/declenv.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2012, 2013, 2014 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2009, 2012, 2013, 2014, 2015 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -25,7 +25,7 @@ #include "environment.hh" #include #include -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" namespace spot { @@ -41,18 +41,18 @@ namespace spot { public: declarative_environment(); - ~declarative_environment(); + ~declarative_environment() = default; /// Declare an atomic proposition. Return false iff the /// proposition was already declared. bool declare(const std::string& prop_str); - virtual const formula* require(const std::string& prop_str); + virtual formula require(const std::string& prop_str); /// Get the name of the environment. virtual const std::string& name() const; - typedef std::map prop_map; + typedef std::map prop_map; /// Get the map of atomic proposition known to this environment. const prop_map& get_prop_map() const; diff --git a/src/ltlenv/defaultenv.cc b/src/ltlenv/defaultenv.cc index 35bacba47..bbb5f0118 100644 --- a/src/ltlenv/defaultenv.cc +++ b/src/ltlenv/defaultenv.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -31,10 +31,10 @@ namespace spot { } - const atomic_prop* + formula default_environment::require(const std::string& s) { - return atomic_prop::instance(s, *this); + return formula::ap(s); } const std::string& diff --git a/src/ltlenv/defaultenv.hh b/src/ltlenv/defaultenv.hh index 71d1dab66..6324683c9 100644 --- a/src/ltlenv/defaultenv.hh +++ b/src/ltlenv/defaultenv.hh @@ -1,7 +1,6 @@ // -*- coding: utf-8 -*- // Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et -// Développement -// de l'Epita (LRDE). +// Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -24,7 +23,7 @@ #pragma once #include "environment.hh" -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" namespace spot { @@ -42,7 +41,7 @@ namespace spot { public: virtual ~default_environment(); - virtual const atomic_prop* require(const std::string& prop_str); + virtual formula require(const std::string& prop_str); virtual const std::string& name() const; /// Get the sole instance of spot::ltl::default_environment. diff --git a/src/ltlenv/environment.hh b/src/ltlenv/environment.hh index 9b75468bf..1692543a1 100644 --- a/src/ltlenv/environment.hh +++ b/src/ltlenv/environment.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 2012, 2014 Laboratoire de Recherche et +// Copyright (C) 2008, 2012, 2014, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -38,20 +38,14 @@ namespace spot /// /// Usually \a prop_str, is the name of an atomic proposition, /// and spot::ltl::require simply returns the associated - /// spot::ltl::atomic_prop. + /// spot::ltl::formula. /// /// Note this is not a \c const method. Some environments will /// "create" the atomic proposition when requested. /// - /// We return a spot::ltl::formula instead of an - /// spot::ltl::atomic_prop, because this - /// will allow nifty tricks (e.g., we could name formulae in an - /// environment, and let the parser build a larger tree from - /// these). - /// /// \return 0 iff \a prop_str is not part of the environment, /// or the associated spot::ltl::formula otherwise. - virtual const formula* require(const std::string& prop_str) = 0; + virtual formula require(const std::string& prop_str) = 0; /// Get the name of the environment. virtual const std::string& name() const = 0; diff --git a/src/ltlparse/ltlparse.yy b/src/ltlparse/ltlparse.yy index c16ad59ff..52c38990c 100644 --- a/src/ltlparse/ltlparse.yy +++ b/src/ltlparse/ltlparse.yy @@ -35,7 +35,7 @@ #include #include #include "public.hh" -#include "ltlast/allnodes.hh" +#include "ltlast/formula.hh" #include "ltlvisit/print.hh" struct minmax_t { unsigned min, max; }; @@ -43,11 +43,11 @@ %parse-param {spot::ltl::parse_error_list &error_list} %parse-param {spot::ltl::environment &parse_environment} -%parse-param {const spot::ltl::formula* &result} +%parse-param {spot::ltl::formula &result} %union { std::string* str; - const spot::ltl::formula* ltl; + const spot::ltl::fnode* ltl; unsigned num; minmax_t minmax; } @@ -67,7 +67,7 @@ using namespace spot::ltl; do \ { \ missing_right_op_msg(op, str); \ - res = constant::false_instance(); \ + res = fnode::ff(); \ } \ while (0); @@ -91,7 +91,7 @@ using namespace spot::ltl; enum parser_type { parser_ltl, parser_bool, parser_sere }; - static const formula* + static formula try_recursive_parse(const std::string& str, const spot::location& location, spot::ltl::environment& env, @@ -117,11 +117,11 @@ using namespace spot::ltl; if (str.empty()) { error_list.emplace_back(location, "unexpected empty block"); - return 0; + return nullptr; } spot::ltl::parse_error_list suberror; - const spot::ltl::formula* f = 0; + formula f; switch (type) { case parser_sere: @@ -138,9 +138,6 @@ using namespace spot::ltl; if (suberror.empty()) return f; - if (f) - f->destroy(); - f = env.require(str); if (!f) { @@ -240,72 +237,76 @@ using namespace spot::ltl; %destructor { $$->destroy(); } %printer { debug_stream() << *$$; } -%printer { spot::ltl::print_psl(debug_stream(), $$); } -%printer { spot::ltl::print_sere(debug_stream(), $$); } sere bracedsere +%printer { spot::ltl::print_psl(debug_stream(), formula($$)); } +%printer { spot::ltl::print_sere(debug_stream(), formula($$)); } sere bracedsere %printer { debug_stream() << $$; } %printer { debug_stream() << $$.min << ".." << $$.max; } %% result: START_LTL subformula END_OF_INPUT - { result = $2; + { + result = formula($2); YYACCEPT; } | START_LTL enderror { - result = 0; + result = nullptr; YYABORT; } | START_LTL subformula enderror { - result = $2; + result = formula($2); YYACCEPT; } | START_LTL emptyinput { YYABORT; } | START_BOOL boolformula END_OF_INPUT - { result = $2; + { + result = formula($2); YYACCEPT; } | START_BOOL enderror { - result = 0; + result = nullptr; YYABORT; } | START_BOOL boolformula enderror { - result = $2; + result = formula($2); YYACCEPT; } | START_BOOL emptyinput { YYABORT; } | START_SERE sere END_OF_INPUT - { result = $2; + { + result = formula($2); YYACCEPT; } | START_SERE enderror { - result = 0; + result = nullptr; YYABORT; } | START_SERE sere enderror { - result = $2; + result = formula($2); YYACCEPT; } | START_SERE emptyinput { YYABORT; } | START_LBT lbtformula END_OF_INPUT - { result = $2; + { + result = formula($2); YYACCEPT; } | START_LBT enderror { - result = 0; + result = nullptr; YYABORT; } | START_LBT lbtformula enderror { - result = $2; + result = formula($2); YYACCEPT; } | START_LBT emptyinput @@ -314,7 +315,7 @@ result: START_LTL subformula END_OF_INPUT emptyinput: END_OF_INPUT { error_list.emplace_back(@$, "empty input"); - result = 0; + result = nullptr; } enderror: error END_OF_INPUT @@ -331,11 +332,11 @@ error_opt: | error sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE { $$.min = $1; $$.max = $3; } | OP_SQBKT_NUM OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE - { $$.min = $1; $$.max = bunop::unbounded; } + { $$.min = $1; $$.max = fnode::unbounded(); } | OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE { $$.min = 0U; $$.max = $2; } | OP_SQBKT_SEP_opt OP_SQBKT_CLOSE - { $$.min = 0U; $$.max = bunop::unbounded; } + { $$.min = 0U; $$.max = fnode::unbounded(); } | OP_SQBKT_NUM OP_SQBKT_CLOSE { $$.min = $$.max = $1; } @@ -343,11 +344,11 @@ sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE gotoargs: OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE { $$.min = $2; $$.max = $4; } | OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE - { $$.min = $2; $$.max = bunop::unbounded; } + { $$.min = $2; $$.max = fnode::unbounded(); } | OP_GOTO_OPEN OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE { $$.min = 1U; $$.max = $3; } | OP_GOTO_OPEN OP_SQBKT_SEP_unbounded OP_SQBKT_CLOSE - { $$.min = 1U; $$.max = bunop::unbounded; } + { $$.min = 1U; $$.max = fnode::unbounded(); } | OP_GOTO_OPEN OP_SQBKT_CLOSE { $$.min = $$.max = 1U; } | OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_CLOSE @@ -363,28 +364,28 @@ gotoargs: OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE kleen_star: OP_STAR | OP_BSTAR starargs: kleen_star - { $$.min = 0U; $$.max = bunop::unbounded; } + { $$.min = 0U; $$.max = fnode::unbounded(); } | OP_PLUS - { $$.min = 1U; $$.max = bunop::unbounded; } + { $$.min = 1U; $$.max = fnode::unbounded(); } | OP_STAR_OPEN sqbracketargs { $$ = $2; } | OP_STAR_OPEN error OP_SQBKT_CLOSE { error_list.emplace_back(@$, "treating this star block as [*]"); - $$.min = 0U; $$.max = bunop::unbounded; } + $$.min = 0U; $$.max = fnode::unbounded(); } | OP_STAR_OPEN error_opt END_OF_INPUT { error_list.emplace_back(@$, "missing closing bracket for star"); $$.min = $$.max = 0U; } fstarargs: OP_BFSTAR - { $$.min = 0U; $$.max = bunop::unbounded; } + { $$.min = 0U; $$.max = fnode::unbounded(); } | OP_FPLUS - { $$.min = 1U; $$.max = bunop::unbounded; } + { $$.min = 1U; $$.max = fnode::unbounded(); } | OP_FSTAR_OPEN sqbracketargs { $$ = $2; } | OP_FSTAR_OPEN error OP_SQBKT_CLOSE { error_list.emplace_back (@$, "treating this fusion-star block as [:*]"); - $$.min = 0U; $$.max = bunop::unbounded; } + $$.min = 0U; $$.max = fnode::unbounded(); } | OP_FSTAR_OPEN error_opt END_OF_INPUT { error_list.emplace_back (@$, "missing closing bracket for fusion-star"); @@ -394,19 +395,16 @@ equalargs: OP_EQUAL_OPEN sqbracketargs { $$ = $2; } | OP_EQUAL_OPEN error OP_SQBKT_CLOSE { error_list.emplace_back(@$, "treating this equal block as [*]"); - $$.min = 0U; $$.max = bunop::unbounded; } + $$.min = 0U; $$.max = fnode::unbounded(); } | OP_EQUAL_OPEN error_opt END_OF_INPUT { error_list. emplace_back(@$, "missing closing bracket for equal operator"); $$.min = $$.max = 0U; } -/* The reason we use `constant::false_instance()' for error recovery - is that it isn't reference counted. (Hence it can't leak references.) */ - booleanatom: ATOMIC_PROP { - $$ = parse_environment.require(*$1); + $$ = parse_environment.require(*$1).to_node_(); if (! $$) { std::string s = "unknown atomic proposition `"; @@ -423,7 +421,7 @@ booleanatom: ATOMIC_PROP } | ATOMIC_PROP OP_POST_POS { - $$ = parse_environment.require(*$1); + $$ = parse_environment.require(*$1).to_node_(); if (! $$) { std::string s = "unknown atomic proposition `"; @@ -440,7 +438,7 @@ booleanatom: ATOMIC_PROP } | ATOMIC_PROP OP_POST_NEG { - $$ = parse_environment.require(*$1); + $$ = parse_environment.require(*$1).to_node_(); if (! $$) { std::string s = "unknown atomic proposition `"; @@ -454,19 +452,19 @@ booleanatom: ATOMIC_PROP } else delete $1; - $$ = unop::instance(unop::Not, $$); + $$ = fnode::unop(op::Not, $$); } | CONST_TRUE - { $$ = constant::true_instance(); } + { $$ = fnode::tt(); } | CONST_FALSE - { $$ = constant::false_instance(); } + { $$ = fnode::ff(); } sere: booleanatom | OP_NOT sere { if ($2->is_boolean()) { - $$ = unop::instance(unop::Not, $2); + $$ = fnode::unop(op::Not, $2); } else { @@ -475,14 +473,16 @@ sere: booleanatom "be applied to a Boolean expression"); error_list.emplace_back(@$, "treating this block as false"); $2->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | bracedsere | PAR_BLOCK { - $$ = try_recursive_parse(*$1, @1, parse_environment, - debug_level(), parser_sere, error_list); + $$ = + try_recursive_parse(*$1, @1, parse_environment, + debug_level(), parser_sere, error_list) + .to_node_(); delete $1; if (!$$) YYERROR; @@ -493,7 +493,7 @@ sere: booleanatom { error_list. emplace_back(@$, "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | PAR_OPEN sere END_OF_INPUT { error_list.emplace_back(@1 + @2, "missing closing parenthesis"); @@ -503,28 +503,28 @@ sere: booleanatom { error_list.emplace_back(@$, "missing closing parenthesis, " "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | sere OP_AND sere - { $$ = multop::instance(multop::AndRat, $1, $3); } + { $$ = fnode::multop(op::AndRat, {$1, $3}); } | sere OP_AND error { missing_right_binop($$, $1, @2, "length-matching and operator"); } | sere OP_SHORT_AND sere - { $$ = multop::instance(multop::AndNLM, $1, $3); } + { $$ = fnode::multop(op::AndNLM, {$1, $3}); } | sere OP_SHORT_AND error { missing_right_binop($$, $1, @2, "non-length-matching and operator"); } | sere OP_OR sere - { $$ = multop::instance(multop::OrRat, $1, $3); } + { $$ = fnode::multop(op::OrRat, {$1, $3}); } | sere OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | sere OP_CONCAT sere - { $$ = multop::instance(multop::Concat, $1, $3); } + { $$ = fnode::multop(op::Concat, {$1, $3}); } | sere OP_CONCAT error { missing_right_binop($$, $1, @2, "concat operator"); } | sere OP_FUSION sere - { $$ = multop::instance(multop::Fusion, $1, $3); } + { $$ = fnode::multop(op::Fusion, {$1, $3}); } | sere OP_FUSION error { missing_right_binop($$, $1, @2, "fusion operator"); } | starargs @@ -534,8 +534,7 @@ sere: booleanatom error_list.emplace_back(@1, "reversed range"); std::swap($1.max, $1.min); } - $$ = bunop::instance(bunop::Star, constant::true_instance(), - $1.min, $1.max); + $$ = fnode::bunop(op::Star, fnode::tt(), $1.min, $1.max); } | sere starargs { @@ -544,7 +543,7 @@ sere: booleanatom error_list.emplace_back(@2, "reversed range"); std::swap($2.max, $2.min); } - $$ = bunop::instance(bunop::Star, $1, $2.min, $2.max); + $$ = fnode::bunop(op::Star, $1, $2.min, $2.max); } | sere fstarargs { @@ -553,7 +552,7 @@ sere: booleanatom error_list.emplace_back(@2, "reversed range"); std::swap($2.max, $2.min); } - $$ = bunop::instance(bunop::FStar, $1, $2.min, $2.max); + $$ = fnode::bunop(op::FStar, $1, $2.min, $2.max); } | sere equalargs { @@ -564,7 +563,8 @@ sere: booleanatom } if ($1->is_boolean()) { - $$ = bunop::sugar_equal($1, $2.min, $2.max); + $$ = formula::sugar_equal(formula($1), + $2.min, $2.max).to_node_(); } else { @@ -574,7 +574,7 @@ sere: booleanatom error_list.emplace_back(@$, "treating this block as false"); $1->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | sere gotoargs @@ -586,7 +586,8 @@ sere: booleanatom } if ($1->is_boolean()) { - $$ = bunop::sugar_goto($1, $2.min, $2.max); + $$ = formula::sugar_goto(formula($1), + $2.min, $2.max).to_node_(); } else { @@ -596,14 +597,14 @@ sere: booleanatom error_list.emplace_back(@$, "treating this block as false"); $1->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | sere OP_XOR sere { if ($1->is_boolean() && $3->is_boolean()) { - $$ = binop::instance(binop::Xor, $1, $3); + $$ = fnode::binop(op::Xor, $1, $3); } else { @@ -622,7 +623,7 @@ sere: booleanatom error_list.emplace_back(@$, "treating this block as false"); $1->destroy(); $3->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | sere OP_XOR error @@ -631,7 +632,7 @@ sere: booleanatom { if ($1->is_boolean()) { - $$ = binop::instance(binop::Implies, $1, $3); + $$ = fnode::binop(op::Implies, $1, $3); } else { @@ -644,7 +645,7 @@ sere: booleanatom error_list.emplace_back(@$, "treating this block as false"); $1->destroy(); $3->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | sere OP_IMPLIES error @@ -653,7 +654,7 @@ sere: booleanatom { if ($1->is_boolean() && $3->is_boolean()) { - $$ = binop::instance(binop::Equiv, $1, $3); + $$ = fnode::binop(op::Equiv, $1, $3); } else { @@ -672,7 +673,7 @@ sere: booleanatom error_list.emplace_back(@$, "treating this block as false"); $1->destroy(); $3->destroy(); - $$ = constant::false_instance(); + $$ = fnode::ff(); } } | sere OP_EQUIV error @@ -687,7 +688,7 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE | BRACE_OPEN error BRACE_CLOSE { error_list.emplace_back(@$, "treating this brace block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | BRACE_OPEN sere END_OF_INPUT { error_list.emplace_back(@1 + @2, @@ -703,13 +704,13 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE { error_list.emplace_back(@$, "missing closing brace, " "treating this brace block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | BRA_BLOCK { $$ = try_recursive_parse(*$1, @1, parse_environment, debug_level(), - parser_sere, error_list); + parser_sere, error_list).to_node_(); delete $1; if (!$$) YYERROR; @@ -718,7 +719,8 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE parenthesedsubformula: PAR_BLOCK { $$ = try_recursive_parse(*$1, @1, parse_environment, - debug_level(), parser_ltl, error_list); + debug_level(), parser_ltl, error_list) + .to_node_(); delete $1; if (!$$) YYERROR; @@ -732,7 +734,7 @@ parenthesedsubformula: PAR_BLOCK | PAR_OPEN error PAR_CLOSE { error_list.emplace_back(@$, "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | PAR_OPEN subformula END_OF_INPUT { error_list.emplace_back(@1 + @2, "missing closing parenthesis"); @@ -747,7 +749,7 @@ parenthesedsubformula: PAR_BLOCK { error_list.emplace_back(@$, "missing closing parenthesis, " "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } @@ -755,7 +757,8 @@ boolformula: booleanatom | PAR_BLOCK { $$ = try_recursive_parse(*$1, @1, parse_environment, - debug_level(), parser_bool, error_list); + debug_level(), parser_bool, error_list) + .to_node_(); delete $1; if (!$$) YYERROR; @@ -769,7 +772,7 @@ boolformula: booleanatom | PAR_OPEN error PAR_CLOSE { error_list.emplace_back(@$, "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | PAR_OPEN boolformula END_OF_INPUT { error_list.emplace_back(@1 + @2, @@ -785,156 +788,153 @@ boolformula: booleanatom { error_list.emplace_back(@$, "missing closing parenthesis, " "treating this parenthetical block as false"); - $$ = constant::false_instance(); + $$ = fnode::ff(); } | boolformula OP_AND boolformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | boolformula OP_AND error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_SHORT_AND boolformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | boolformula OP_SHORT_AND error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_STAR boolformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | boolformula OP_STAR error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_OR boolformula - { $$ = multop::instance(multop::Or, $1, $3); } + { $$ = fnode::multop(op::Or, {$1, $3}); } | boolformula OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | boolformula OP_XOR boolformula - { $$ = binop::instance(binop::Xor, $1, $3); } + { $$ = fnode::binop(op::Xor, $1, $3); } | boolformula OP_XOR error { missing_right_binop($$, $1, @2, "xor operator"); } | boolformula OP_IMPLIES boolformula - { $$ = binop::instance(binop::Implies, $1, $3); } + { $$ = fnode::binop(op::Implies, $1, $3); } | boolformula OP_IMPLIES error { missing_right_binop($$, $1, @2, "implication operator"); } | boolformula OP_EQUIV boolformula - { $$ = binop::instance(binop::Equiv, $1, $3); } + { $$ = fnode::binop(op::Equiv, $1, $3); } | boolformula OP_EQUIV error { missing_right_binop($$, $1, @2, "equivalent operator"); } | OP_NOT boolformula - { $$ = unop::instance(unop::Not, $2); } + { $$ = fnode::unop(op::Not, $2); } | OP_NOT error { missing_right_op($$, @1, "not operator"); } subformula: booleanatom | parenthesedsubformula | subformula OP_AND subformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | subformula OP_AND error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_SHORT_AND subformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | subformula OP_SHORT_AND error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_STAR subformula - { $$ = multop::instance(multop::And, $1, $3); } + { $$ = fnode::multop(op::And, {$1, $3}); } | subformula OP_STAR error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_OR subformula - { $$ = multop::instance(multop::Or, $1, $3); } + { $$ = fnode::multop(op::Or, {$1, $3}); } | subformula OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | subformula OP_XOR subformula - { $$ = binop::instance(binop::Xor, $1, $3); } + { $$ = fnode::binop(op::Xor, $1, $3); } | subformula OP_XOR error { missing_right_binop($$, $1, @2, "xor operator"); } | subformula OP_IMPLIES subformula - { $$ = binop::instance(binop::Implies, $1, $3); } + { $$ = fnode::binop(op::Implies, $1, $3); } | subformula OP_IMPLIES error { missing_right_binop($$, $1, @2, "implication operator"); } | subformula OP_EQUIV subformula - { $$ = binop::instance(binop::Equiv, $1, $3); } + { $$ = fnode::binop(op::Equiv, $1, $3); } | subformula OP_EQUIV error { missing_right_binop($$, $1, @2, "equivalent operator"); } | subformula OP_U subformula - { $$ = binop::instance(binop::U, $1, $3); } + { $$ = fnode::binop(op::U, $1, $3); } | subformula OP_U error { missing_right_binop($$, $1, @2, "until operator"); } | subformula OP_R subformula - { $$ = binop::instance(binop::R, $1, $3); } + { $$ = fnode::binop(op::R, $1, $3); } | subformula OP_R error { missing_right_binop($$, $1, @2, "release operator"); } | subformula OP_W subformula - { $$ = binop::instance(binop::W, $1, $3); } + { $$ = fnode::binop(op::W, $1, $3); } | subformula OP_W error { missing_right_binop($$, $1, @2, "weak until operator"); } | subformula OP_M subformula - { $$ = binop::instance(binop::M, $1, $3); } + { $$ = fnode::binop(op::M, $1, $3); } | subformula OP_M error { missing_right_binop($$, $1, @2, "strong release operator"); } | OP_F subformula - { $$ = unop::instance(unop::F, $2); } + { $$ = fnode::unop(op::F, $2); } | OP_F error { missing_right_op($$, @1, "sometimes operator"); } | OP_G subformula - { $$ = unop::instance(unop::G, $2); } + { $$ = fnode::unop(op::G, $2); } | OP_G error { missing_right_op($$, @1, "always operator"); } | OP_X subformula - { $$ = unop::instance(unop::X, $2); } + { $$ = fnode::unop(op::X, $2); } | OP_X error { missing_right_op($$, @1, "next operator"); } | OP_NOT subformula - { $$ = unop::instance(unop::Not, $2); } + { $$ = fnode::unop(op::Not, $2); } | OP_NOT error { missing_right_op($$, @1, "not operator"); } | bracedsere - { $$ = unop::instance(unop::Closure, $1); } + { $$ = fnode::unop(op::Closure, $1); } | bracedsere OP_UCONCAT subformula - { $$ = binop::instance(binop::UConcat, $1, $3); } + { $$ = fnode::binop(op::UConcat, $1, $3); } | bracedsere parenthesedsubformula - { $$ = binop::instance(binop::UConcat, $1, $2); } + { $$ = fnode::binop(op::UConcat, $1, $2); } | bracedsere OP_UCONCAT error { missing_right_binop_hard($$, $1, @2, "universal overlapping concat operator"); } | bracedsere OP_ECONCAT subformula - { $$ = binop::instance(binop::EConcat, $1, $3); } + { $$ = fnode::binop(op::EConcat, $1, $3); } | bracedsere OP_ECONCAT error { missing_right_binop_hard($$, $1, @2, "existential overlapping concat operator"); } | bracedsere OP_UCONCAT_NONO subformula /* {SERE}[]=>EXP = {SERE;1}[]->EXP */ - { $$ = binop::instance(binop::UConcat, - multop::instance(multop::Concat, $1, - constant::true_instance()), $3); - } + { $$ = fnode::binop(op::UConcat, + fnode::multop(op::Concat, {$1, fnode::tt()}), + $3); } | bracedsere OP_UCONCAT_NONO error { missing_right_binop_hard($$, $1, @2, "universal non-overlapping concat operator"); } | bracedsere OP_ECONCAT_NONO subformula /* {SERE}<>=>EXP = {SERE;1}<>->EXP */ - { $$ = binop::instance(binop::EConcat, - multop::instance(multop::Concat, $1, - constant::true_instance()), $3); - } + { $$ = fnode::binop(op::EConcat, + fnode::multop(op::Concat, {$1, fnode::tt()}), + $3); } | bracedsere OP_ECONCAT_NONO error { missing_right_binop_hard($$, $1, @2, "existential non-overlapping concat operator"); } | BRACE_OPEN sere BRACE_BANG_CLOSE /* {SERE}! = {SERE} <>-> 1 */ - { $$ = binop::instance(binop::EConcat, $2, - constant::true_instance()); } + { $$ = fnode::binop(op::EConcat, $2, fnode::tt()); } | BRA_BANG_BLOCK { $$ = try_recursive_parse(*$1, @1, parse_environment, - debug_level(), parser_sere, error_list); + debug_level(), parser_sere, error_list) + .to_node_(); delete $1; if (!$$) YYERROR; - $$ = binop::instance(binop::EConcat, $$, - constant::true_instance()); + $$ = fnode::binop(op::EConcat, $$, fnode::tt()); } lbtformula: ATOMIC_PROP { - $$ = parse_environment.require(*$1); + $$ = parse_environment.require(*$1).to_node_(); if (! $$) { std::string s = "atomic proposition `"; @@ -950,37 +950,37 @@ lbtformula: ATOMIC_PROP delete $1; } | '!' lbtformula - { $$ = unop::instance(unop::Not, $2); } + { $$ = fnode::unop(op::Not, $2); } | '&' lbtformula lbtformula - { $$ = multop::instance(multop::And, $2, $3); } + { $$ = fnode::multop(op::And, {$2, $3}); } | '|' lbtformula lbtformula - { $$ = multop::instance(multop::Or, $2, $3); } + { $$ = fnode::multop(op::Or, {$2, $3}); } | '^' lbtformula lbtformula - { $$ = binop::instance(binop::Xor, $2, $3); } + { $$ = fnode::binop(op::Xor, $2, $3); } | 'i' lbtformula lbtformula - { $$ = binop::instance(binop::Implies, $2, $3); } + { $$ = fnode::binop(op::Implies, $2, $3); } | 'e' lbtformula lbtformula - { $$ = binop::instance(binop::Equiv, $2, $3); } + { $$ = fnode::binop(op::Equiv, $2, $3); } | 'X' lbtformula - { $$ = unop::instance(unop::X, $2); } + { $$ = fnode::unop(op::X, $2); } | 'F' lbtformula - { $$ = unop::instance(unop::F, $2); } + { $$ = fnode::unop(op::F, $2); } | 'G' lbtformula - { $$ = unop::instance(unop::G, $2); } + { $$ = fnode::unop(op::G, $2); } | 'U' lbtformula lbtformula - { $$ = binop::instance(binop::U, $2, $3); } + { $$ = fnode::binop(op::U, $2, $3); } | 'V' lbtformula lbtformula - { $$ = binop::instance(binop::R, $2, $3); } + { $$ = fnode::binop(op::R, $2, $3); } | 'R' lbtformula lbtformula - { $$ = binop::instance(binop::R, $2, $3); } + { $$ = fnode::binop(op::R, $2, $3); } | 'W' lbtformula lbtformula - { $$ = binop::instance(binop::W, $2, $3); } + { $$ = fnode::binop(op::W, $2, $3); } | 'M' lbtformula lbtformula - { $$ = binop::instance(binop::M, $2, $3); } + { $$ = fnode::binop(op::M, $2, $3); } | 't' - { $$ = constant::true_instance(); } + { $$ = fnode::tt(); } | 'f' - { $$ = constant::false_instance(); } + { $$ = fnode::ff(); } ; %% @@ -995,13 +995,13 @@ namespace spot { namespace ltl { - const formula* + formula parse_infix_psl(const std::string& ltl_string, parse_error_list& error_list, environment& env, bool debug, bool lenient) { - const formula* result = 0; + formula result = nullptr; flex_set_buffer(ltl_string, ltlyy::parser::token::START_LTL, lenient); @@ -1012,13 +1012,13 @@ namespace spot return result; } - const formula* + formula parse_infix_boolean(const std::string& ltl_string, parse_error_list& error_list, environment& env, bool debug, bool lenient) { - const formula* result = 0; + formula result = nullptr; flex_set_buffer(ltl_string, ltlyy::parser::token::START_BOOL, lenient); @@ -1029,13 +1029,13 @@ namespace spot return result; } - const formula* + formula parse_prefix_ltl(const std::string& ltl_string, parse_error_list& error_list, environment& env, bool debug) { - const formula* result = 0; + formula result = nullptr; flex_set_buffer(ltl_string, ltlyy::parser::token::START_LBT, false); @@ -1046,14 +1046,14 @@ namespace spot return result; } - const formula* + formula parse_infix_sere(const std::string& sere_string, parse_error_list& error_list, environment& env, bool debug, bool lenient) { - const formula* result = 0; + formula result = nullptr; flex_set_buffer(sere_string, ltlyy::parser::token::START_SERE, lenient); @@ -1064,16 +1064,16 @@ namespace spot return result; } - const formula* + formula parse_formula(const std::string& ltl_string, environment& env) { parse_error_list pel; - const formula* f = parse_infix_psl(ltl_string, pel, env); + formula f = parse_infix_psl(ltl_string, pel, env); std::ostringstream s; if (format_parse_errors(s, ltl_string, pel)) { parse_error_list pel2; - const formula* g = parse_prefix_ltl(ltl_string, pel2, env); + formula g = parse_prefix_ltl(ltl_string, pel2, env); if (pel2.empty()) return g; else diff --git a/src/ltlparse/public.hh b/src/ltlparse/public.hh index 9e14dd374..0a7705a5d 100644 --- a/src/ltlparse/public.hh +++ b/src/ltlparse/public.hh @@ -56,8 +56,8 @@ namespace spot /// \param lenient When true, parenthesized blocks that cannot be /// parsed as subformulas will be considered as /// atomic propositions. - /// \return A pointer to the formula built from \a ltl_string, or - /// 0 if the input was unparsable. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. /// /// Note that the parser usually tries to recover from errors. It can /// return a non zero value even if it encountered error during the @@ -66,12 +66,12 @@ namespace spot /// /// \warning This function is not reentrant. SPOT_API - const formula* parse_infix_psl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + formula parse_infix_psl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); /// \brief Build a Boolean formula from a string. /// \param ltl_string The string to parse. @@ -82,8 +82,8 @@ namespace spot /// \param lenient When true, parenthesized blocks that cannot be /// parsed as subformulas will be considered as /// atomic propositions. - /// \return A pointer to the formula built from \a ltl_string, or - /// 0 if the input was unparsable. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. /// /// Note that the parser usually tries to recover from errors. It can /// return a non zero value even if it encountered error during the @@ -92,12 +92,12 @@ namespace spot /// /// \warning This function is not reentrant. SPOT_API - const formula* parse_infix_boolean(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + formula parse_infix_boolean(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); /// \brief Build a formula from an LTL string in LBT's format. /// \param ltl_string The string to parse. @@ -105,8 +105,8 @@ namespace spot /// parse errors that occured during parsing. /// \param env The environment into which parsing should take place. /// \param debug When true, causes the parser to trace its execution. - /// \return A pointer to the formula built from \a ltl_string, or - /// 0 if the input was unparsable. + /// \return A formula built from \a ltl_string, or + /// formula(nullptr) if the input was unparsable. /// /// Note that the parser usually tries to recover from errors. It can /// return an non zero value even if it encountered error during the @@ -119,11 +119,11 @@ namespace spot /// /// \warning This function is not reentrant. SPOT_API - const formula* parse_prefix_ltl(const std::string& ltl_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false); + formula parse_prefix_ltl(const std::string& ltl_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false); /// \brief A simple wrapper to parse_infix_psl() and parse_prefix_ltl(). /// @@ -131,7 +131,7 @@ namespace spot /// parse_infix_psl(); if this fails it tries parse_prefix_ltl(); /// and if both fails it returns the errors of the first call to /// parse_infix_psl() as a parse_error exception. - SPOT_API const formula* + SPOT_API formula parse_formula(const std::string& ltl_string, environment& env = default_environment::instance()); @@ -144,8 +144,8 @@ namespace spot /// \param lenient When true, parenthesized blocks that cannot be /// parsed as subformulas will be considered as /// atomic propositions. - /// \return A pointer to the formula built from \a sere_string, or - /// 0 if the input was unparsable. + /// \return A formula built from \a sere_string, or + /// formula(0) if the input was unparsable. /// /// Note that the parser usually tries to recover from errors. It can /// return an non zero value even if it encountered error during the @@ -154,12 +154,12 @@ namespace spot /// /// \warning This function is not reentrant. SPOT_API - const formula* parse_infix_sere(const std::string& sere_string, - parse_error_list& error_list, - environment& env = - default_environment::instance(), - bool debug = false, - bool lenient = false); + formula parse_infix_sere(const std::string& sere_string, + parse_error_list& error_list, + environment& env = + default_environment::instance(), + bool debug = false, + bool lenient = false); /// \brief Format diagnostics produced by spot::ltl::parse /// or spot::ltl::ratexp @@ -181,11 +181,10 @@ namespace spot /// \brief Fix location of diagnostics assuming the input is utf8. /// - /// The spot::ltl::parse() and spot::ltl::parse_sere() function - /// return a parse_error_list that contain locations specified at - /// the byte level. Although these parser recognize some - /// utf8 characters they only work byte by byte and will report - /// positions by counting byte. + /// The different parser functions return a parse_error_list that + /// contain locations specified at the byte level. Although these + /// parser recognize some utf8 characters they only work byte by + /// byte and will report positions by counting byte. /// /// This function fixes the positions returned by the parser to /// look correct when the string is interpreted as a utf8-encoded diff --git a/src/ltlvisit/Makefile.am b/src/ltlvisit/Makefile.am index e1d6f5e3d..abfd0d09e 100644 --- a/src/ltlvisit/Makefile.am +++ b/src/ltlvisit/Makefile.am @@ -28,15 +28,12 @@ ltlvisitdir = $(pkgincludedir)/ltlvisit ltlvisit_HEADERS = \ apcollect.hh \ contain.hh \ - clone.hh \ dot.hh \ - dump.hh \ exclusive.hh \ length.hh \ mutation.hh \ nenoform.hh \ print.hh \ - postfix.hh \ randomltl.hh \ relabel.hh \ remove_x.hh \ @@ -49,9 +46,7 @@ noinst_LTLIBRARIES = libltlvisit.la libltlvisit_la_SOURCES = \ apcollect.cc \ contain.cc \ - clone.cc \ dot.cc \ - dump.cc \ exclusive.cc \ length.cc \ mark.cc \ @@ -59,7 +54,6 @@ libltlvisit_la_SOURCES = \ mutation.cc \ nenoform.cc \ print.cc \ - postfix.cc \ randomltl.cc \ relabel.cc \ remove_x.cc \ diff --git a/src/ltlvisit/apcollect.cc b/src/ltlvisit/apcollect.cc index d1c092d7d..1316172f6 100644 --- a/src/ltlvisit/apcollect.cc +++ b/src/ltlvisit/apcollect.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement +// Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -21,7 +21,6 @@ // along with this program. If not, see . #include "apcollect.hh" -#include "ltlvisit/postfix.hh" #include "twa/twa.hh" #include "twa/bdddict.hh" @@ -29,70 +28,40 @@ namespace spot { namespace ltl { - namespace - { - class atomic_prop_collector : public spot::ltl::postfix_visitor - { - public: - atomic_prop_collector(atomic_prop_set* s) - : postfix_visitor(), sap(s) - { - } - - virtual ~atomic_prop_collector() - { - } - - virtual void doit(const spot::ltl::atomic_prop* ap) - { - sap->insert(ap); - } - - private: - atomic_prop_set* sap; - }; - } - atomic_prop_set create_atomic_prop_set(unsigned n) { atomic_prop_set res; - auto& e = spot::ltl::default_environment::instance(); for (unsigned i = 0; i < n; ++i) { std::ostringstream p; p << 'p' << i; - res.insert(e.require(p.str())); + res.insert(formula::ap(p.str())); } return res; } - void destroy_atomic_prop_set(atomic_prop_set& aprops) - { - atomic_prop_set::const_iterator i = aprops.begin(); - while (i != aprops.end()) - (*(i++))->destroy(); - } - - atomic_prop_set* - atomic_prop_collect(const formula* f, atomic_prop_set* s) + atomic_prop_collect(formula f, atomic_prop_set* s) { if (!s) s = new atomic_prop_set; - atomic_prop_collector v(s); - f->accept(v); + f.traverse([&](const formula& f) + { + if (f.is(op::AP)) + s->insert(f); + return false; + }); return s; } bdd - atomic_prop_collect_as_bdd(const formula* f, const twa_ptr& a) + atomic_prop_collect_as_bdd(formula f, const twa_ptr& a) { spot::ltl::atomic_prop_set aps; atomic_prop_collect(f, &aps); bdd res = bddtrue; - for (atomic_prop_set::const_iterator i = aps.begin(); - i != aps.end(); ++i) - res &= bdd_ithvar(a->register_ap(*i)); + for (auto f: aps) + res &= bdd_ithvar(a->register_ap(f)); return res; } } diff --git a/src/ltlvisit/apcollect.hh b/src/ltlvisit/apcollect.hh index dab6052c1..8c32e8dcc 100644 --- a/src/ltlvisit/apcollect.hh +++ b/src/ltlvisit/apcollect.hh @@ -22,7 +22,7 @@ #pragma once -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" #include #include #include "twa/fwd.hh" @@ -35,18 +35,12 @@ namespace spot /// @{ /// Set of atomic propositions. - typedef std::set atomic_prop_set; + typedef std::set atomic_prop_set; /// \brief construct an atomic_prop_set with n propositions SPOT_API atomic_prop_set create_atomic_prop_set(unsigned n); - /// \brief Destroy all the atomic propositions in an - /// atomic_prop_set. - SPOT_API void - destroy_atomic_prop_set(atomic_prop_set& aprops); - /// \brief Return the set of atomic propositions occurring in a formula. /// /// \param f the formula to inspect @@ -55,10 +49,8 @@ namespace spot /// \return A pointer to the supplied set, \c s, augmented with /// atomic propositions occurring in \c f; or a newly allocated /// set containing all these atomic propositions if \c s is 0. - /// The atomic propositions inserted into \a s are not cloned, so - /// they are only valid as long as \a f is. SPOT_API atomic_prop_set* - atomic_prop_collect(const formula* f, atomic_prop_set* s = 0); + atomic_prop_collect(formula f, atomic_prop_set* s = nullptr); /// \brief Return the set of atomic propositions occurring in a /// formula, as a BDD. @@ -67,8 +59,7 @@ namespace spot /// \param a that automaton that should register the BDD variables used. /// \return A conjunction the atomic propositions. SPOT_API bdd - atomic_prop_collect_as_bdd(const formula* f, - const twa_ptr& a); + atomic_prop_collect_as_bdd(formula f, const twa_ptr& a); /// @} } diff --git a/src/ltlvisit/clone.cc b/src/ltlvisit/clone.cc deleted file mode 100644 index 74650a831..000000000 --- a/src/ltlvisit/clone.cc +++ /dev/null @@ -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 . - -#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_; - } - } -} diff --git a/src/ltlvisit/clone.hh b/src/ltlvisit/clone.hh deleted file mode 100644 index 9918287fa..000000000 --- a/src/ltlvisit/clone.hh +++ /dev/null @@ -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 . - -#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_; - }; - } -} diff --git a/src/ltlvisit/contain.cc b/src/ltlvisit/contain.cc index 0b3e87fa7..48465fd01 100644 --- a/src/ltlvisit/contain.cc +++ b/src/ltlvisit/contain.cc @@ -22,10 +22,7 @@ #include "contain.hh" #include "simplify.hh" -#include "ltlast/unop.hh" -#include "ltlast/binop.hh" -#include "ltlast/multop.hh" -#include "ltlast/constant.hh" +#include "ltlast/formula.hh" #include "twaalgos/product.hh" namespace spot @@ -50,13 +47,7 @@ namespace spot void language_containment_checker::clear() { - while (!translated_.empty()) - { - trans_map::iterator i = translated_.begin(); - const formula* f = i->first; - translated_.erase(i); - f->destroy(); - } + translated_.clear(); } bool @@ -75,31 +66,26 @@ namespace spot // Check whether L(l) is a subset of L(g). bool - language_containment_checker::contained(const formula* l, - const formula* g) + language_containment_checker::contained(formula l, + formula g) { if (l == g) return true; record_* rl = register_formula_(l); - const formula* ng = unop::instance(unop::Not, g->clone()); - record_* rng = register_formula_(ng); - ng->destroy(); + record_* rng = register_formula_(formula::Not(g)); return incompatible_(rl, rng); } // Check whether L(!l) is a subset of L(g). bool - language_containment_checker::neg_contained(const formula* l, - const formula* g) + language_containment_checker::neg_contained(formula l, + formula g) { if (l == g) return false; - const formula* nl = unop::instance(unop::Not, l->clone()); + formula nl = formula::Not(l); record_* rnl = register_formula_(nl); - const formula* ng = unop::instance(unop::Not, g->clone()); - record_* rng = register_formula_(ng); - nl->destroy(); - ng->destroy(); + record_* rng = register_formula_(formula::Not(g)); if (nl == g) return true; return incompatible_(rnl, rng); @@ -107,8 +93,8 @@ namespace spot // Check whether L(l) is a subset of L(!g). bool - language_containment_checker::contained_neg(const formula* l, - const formula* g) + language_containment_checker::contained_neg(formula l, + formula g) { if (l == g) return false; @@ -119,13 +105,13 @@ namespace spot // Check whether L(l) = L(g). bool - language_containment_checker::equal(const formula* l, const formula* g) + language_containment_checker::equal(formula l, formula g) { return contained(l, g) && contained(g, l); } language_containment_checker::record_* - language_containment_checker::register_formula_(const formula* f) + language_containment_checker::register_formula_(formula f) { trans_map::iterator i = translated_.find(f); if (i != translated_.end()) @@ -133,17 +119,17 @@ namespace spot auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_, branching_postponement_, fair_loop_approx_); - record_& r = translated_[f->clone()]; + record_& r = translated_[f]; r.translation = e; return &r; } - const formula* - reduce_tau03(const formula* f, bool stronger) + formula + reduce_tau03(formula f, bool stronger) { - if (!f->is_psl_formula()) - return f->clone(); + if (!f.is_psl_formula()) + return f; ltl_simplifier_options opt(false, false, false, true, stronger); diff --git a/src/ltlvisit/contain.hh b/src/ltlvisit/contain.hh index 993771020..f4803c95c 100644 --- a/src/ltlvisit/contain.hh +++ b/src/ltlvisit/contain.hh @@ -40,7 +40,7 @@ namespace spot typedef std::map incomp_map; incomp_map incompatible; }; - typedef std::unordered_map trans_map; + typedef std::unordered_map trans_map; public: /// This class uses spot::ltl_to_tgba_fm to translate LTL /// formulae. See that function for the meaning of these options. @@ -55,19 +55,19 @@ namespace spot void clear(); /// Check whether L(l) is a subset of L(g). - bool contained(const formula* l, const formula* g); + bool contained(formula l, formula g); /// Check whether L(!l) is a subset of L(g). - bool neg_contained(const formula* l, const formula* g); + bool neg_contained(formula l, formula g); /// Check whether L(l) is a subset of L(!g). - bool contained_neg(const formula* l, const formula* g); + bool contained_neg(formula l, formula g); /// Check whether L(l) = L(g). - bool equal(const formula* l, const formula* g); + bool equal(formula l, formula g); protected: bool incompatible_(record_* l, record_* g); - record_* register_formula_(const formula* f); + record_* register_formula_(formula f); /* Translation options */ bdd_dict_ptr dict_; diff --git a/src/ltlvisit/dot.cc b/src/ltlvisit/dot.cc index c2684da74..1b5d12cb0 100644 --- a/src/ltlvisit/dot.cc +++ b/src/ltlvisit/dot.cc @@ -20,10 +20,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "misc/hash.hh" #include "dot.hh" -#include "ltlast/visitor.hh" -#include "ltlast/allnodes.hh" +#include "ltlast/formula.hh" +#include #include #include @@ -33,160 +32,106 @@ namespace spot { namespace { - class dotty_visitor: public visitor + struct dot_printer final { - public: - typedef std::unordered_map> map; - dotty_visitor(std::ostream& os, map& m) - : os_(os), father_(-1), node_(m), sinks_(new std::ostringstream) - { - } + std::ostream& os_; + std::unordered_map node_; + std::ostringstream* sinks_; - virtual - ~dotty_visitor() - { - } + dot_printer(std::ostream& os, formula f) + : os_(os), sinks_(new std::ostringstream) + { + os_ << "digraph G {\n"; + rec(f); + os_ << " subgraph atoms {\n rank=sink;\n" + << sinks_->str() << " }\n}\n"; + } - void - visit(const atomic_prop* ap) + ~dot_printer() { - draw_node_(ap, ap->name(), true); - } - - void - visit(const constant* c) - { - draw_node_(c, c->val_name(), true); - } - - void - visit(const bunop* so) - { - if (draw_node_(so, so->format())) - { - childnum = 0; - so->child()->accept(*this); - } - } - - void - visit(const binop* bo) - { - if (draw_node_(bo, bo->op_name())) - { - childnum = -1; - dotty_visitor v(*this); - bo->first()->accept(v); - childnum = -2; - bo->second()->accept(*this); - } - } - - void - visit(const unop* uo) - { - if (draw_node_(uo, uo->op_name())) - { - childnum = 0; - uo->child()->accept(*this); - } - } - - void - visit(const multop* mo) - { - if (!draw_node_(mo, mo->op_name())) - return; - childnum = 0; - unsigned max = mo->size(); - multop::type op = mo->op(); - bool update_childnum = (op == multop::Fusion || - op == multop::Concat); - - for (unsigned n = 0; n < max; ++n) - { - if (update_childnum) - ++childnum; - dotty_visitor v(*this); - mo->nth(n)->accept(v); - } - } - - void finish() - { - os_ << (" subgraph atoms {\n" - " rank=sink;\n") - << sinks_->str() << " }\n"; delete sinks_; } - int childnum; - - private: - std::ostream& os_; - int father_; - map& node_; - std::ostringstream* sinks_; - - bool - draw_node_(const formula* f, const std::string& str, bool sink = false) + int rec(formula f) { - map::iterator i = node_.find(f); - int node; - bool node_exists = false; - if (i != node_.end()) - { - node = i->second; - node_exists = true; - } + auto i = node_.emplace(f, node_.size()); + int src = i.first->second; + if (!i.second) + return src; + + op o = f.kind(); + std::string str = (o == op::AP) ? f.ap_name() : f.kindstr(); + + if (o == op::AP || f.is_constant()) + *sinks_ << " " << src << " [label=\"" + << str << "\", shape=box];\n"; else + os_ << " " << src << " [label=\"" << str << "\"];\n"; + + int childnum = 0; + switch (o) { - node = node_.size(); - node_[f] = node; + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Star: + case op::FStar: + childnum = 0; // No number for children + break; + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + childnum = -2; // L and R markers + break; + case op::Concat: + case op::Fusion: + childnum = 1; // Numbered children + break; } - // the link - if (father_ >= 0) + + for (auto c: f) { - os_ << " " << father_ << " -> " << node; + os_ << " " << src << " -> " << rec(c); if (childnum > 0) os_ << " [taillabel=\"" << childnum << "\"]"; - if (childnum == -1) + if (childnum == -2) os_ << " [taillabel=\"L\"]"; - else if (childnum == -2) + else if (childnum == -1) os_ << " [taillabel=\"R\"]"; os_ << ";\n"; + ++childnum; } - father_ = node; - // the node - if (node_exists) - return false; - - if (!sink) - { - os_ << " " << node << " [label=\"" << str << "\"];"; - } - else - { - *sinks_ << " " << node - << " [label=\"" << str << "\", shape=box];\n"; - } - return true; + return src; } }; } std::ostream& - print_dot_psl(std::ostream& os, const formula* f) + print_dot_psl(std::ostream& os, formula f) { - dotty_visitor::map m; - dotty_visitor v(os, m); - os << "digraph G {\n"; - f->accept(v); - v.finish(); - os << '}' << std::endl; + dot_printer p(os, f); return os; } - } } diff --git a/src/ltlvisit/dot.hh b/src/ltlvisit/dot.hh index 095e13cbd..0e52d1807 100644 --- a/src/ltlvisit/dot.hh +++ b/src/ltlvisit/dot.hh @@ -23,7 +23,6 @@ #pragma once #include -#include namespace spot { @@ -37,6 +36,6 @@ namespace spot /// \c dot is part of the GraphViz package /// http://www.graphviz.org/ SPOT_API - std::ostream& print_dot_psl(std::ostream& os, const formula* f); + std::ostream& print_dot_psl(std::ostream& os, formula f); } } diff --git a/src/ltlvisit/dump.cc b/src/ltlvisit/dump.cc deleted file mode 100644 index 24e67e37a..000000000 --- a/src/ltlvisit/dump.cc +++ /dev/null @@ -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 . - -#include "dump.hh" -#include "ltlast/visitor.hh" -#include "ltlast/allnodes.hh" -#include - -namespace spot -{ - namespace ltl - { - std::ostream& - dump(std::ostream& os, const formula* f) - { - os << f->dump(); - return os; - } - - } -} diff --git a/src/ltlvisit/dump.hh b/src/ltlvisit/dump.hh deleted file mode 100644 index ea60bab8d..000000000 --- a/src/ltlvisit/dump.hh +++ /dev/null @@ -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 . - -#pragma once - -#include "ltlast/formula.hh" -#include - -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); - } -} diff --git a/src/ltlvisit/exclusive.cc b/src/ltlvisit/exclusive.cc index 69f26d8eb..58ca2d03a 100644 --- a/src/ltlvisit/exclusive.cc +++ b/src/ltlvisit/exclusive.cc @@ -18,10 +18,6 @@ // along with this program. If not, see . #include "exclusive.hh" -#include "ltlenv/defaultenv.hh" -#include "ltlast/multop.hh" -#include "ltlast/unop.hh" -#include "ltlast/constant.hh" #include "twaalgos/mask.hh" #include "misc/casts.hh" #include "misc/minato.hh" @@ -29,20 +25,12 @@ namespace spot { - exclusive_ap::~exclusive_ap() - { - for (auto& g: groups) - for (auto ap: g) - ap->destroy(); - } - namespace { - static const std::vector + static const std::vector split_aps(const char* arg) { - auto& env = spot::ltl::default_environment::instance(); - std::vector group; + std::vector group; auto start = arg; while (*start) { @@ -73,8 +61,7 @@ namespace spot throw std::invalid_argument(s); } std::string ap(start, end - start); - auto* t = env.require(ap); - group.push_back(down_cast(t)); + group.emplace_back(ltl::formula::ap(ap)); do ++end; while (*end == ' ' || *end == '\t'); @@ -99,8 +86,7 @@ namespace spot while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t')) --rend; std::string ap(start, rend - start); - auto* t = env.require(ap); - group.push_back(down_cast(t)); + group.emplace_back(ltl::formula::ap(ap)); if (*end == ',') start = end + 1; else @@ -116,29 +102,27 @@ namespace spot add_group(split_aps(ap_csv)); } - void exclusive_ap::add_group(std::vector ap) + void exclusive_ap::add_group(std::vector ap) { groups.push_back(ap); } namespace { - const ltl::formula* - nand(const ltl::formula* lhs, const ltl::formula* rhs) + ltl::formula + nand(ltl::formula lhs, ltl::formula rhs) { - auto f = ltl::multop::instance(ltl::multop::And, - lhs->clone(), rhs->clone()); - return ltl::unop::instance(ltl::unop::Not, f); + return ltl::formula::Not(ltl::formula::And({lhs, rhs})); } } - const ltl::formula* - exclusive_ap::constrain(const ltl::formula* f) const + ltl::formula + exclusive_ap::constrain(ltl::formula f) const { - spot::ltl::atomic_prop_set* s = atomic_prop_collect(f); + auto* s = atomic_prop_collect(f); - std::vector group; - ltl::multop::vec* v = new ltl::multop::vec; + std::vector group; + std::vector v; for (auto& g: groups) { @@ -151,14 +135,11 @@ namespace spot unsigned s = group.size(); for (unsigned j = 0; j < s; ++j) for (unsigned k = j + 1; k < s; ++k) - v->push_back(nand(group[j], group[k])); + v.push_back(nand(group[j], group[k])); }; delete s; - - auto* c = ltl::unop::instance(ltl::unop::G, - ltl::multop::instance(ltl::multop::And, v)); - return ltl::multop::instance(ltl::multop::And, f->clone(), c); + return ltl::formula::And({f, ltl::formula::G(ltl::formula::And(v))}); } twa_graph_ptr exclusive_ap::constrain(const_twa_graph_ptr aut, diff --git a/src/ltlvisit/exclusive.hh b/src/ltlvisit/exclusive.hh index 31c2bd4bf..bb6ca06da 100644 --- a/src/ltlvisit/exclusive.hh +++ b/src/ltlvisit/exclusive.hh @@ -20,19 +20,17 @@ #pragma once #include -#include "ltlast/atomic_prop.hh" #include "ltlast/formula.hh" #include "twa/twagraph.hh" namespace spot { - class SPOT_API exclusive_ap + class SPOT_API exclusive_ap final { - std::vector> groups; + std::vector> groups; public: - ~exclusive_ap(); #ifndef SWIG - void add_group(std::vector ap); + void add_group(std::vector ap); #endif void add_group(const char* ap_csv); @@ -41,7 +39,7 @@ namespace spot return groups.empty(); } - const ltl::formula* constrain(const ltl::formula* f) const; + ltl::formula constrain(ltl::formula f) const; twa_graph_ptr constrain(const_twa_graph_ptr aut, bool simplify_guards = false) const; }; diff --git a/src/ltlvisit/length.cc b/src/ltlvisit/length.cc index b093f3595..ab769aa41 100644 --- a/src/ltlvisit/length.cc +++ b/src/ltlvisit/length.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2012, 2014 Laboratoire de Recherche et +// Copyright (C) 2010, 2012, 2014, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -21,111 +21,59 @@ // along with this program. If not, see . #include "length.hh" -#include "ltlvisit/postfix.hh" -#include "ltlast/multop.hh" -#include "ltlast/unop.hh" +#include "ltlast/formula.hh" namespace spot { namespace ltl { - namespace + int + length(formula f) { - class length_visitor: public postfix_visitor - { - public: - length_visitor() - : result_(0) - { - } - - int - result() const - { - return result_; - } - - virtual void - visit(const multop* mo) - { - unsigned s = mo->size(); - for (unsigned i = 0; i < s; ++i) - mo->nth(i)->accept(*this); - // "a & b & c" should count for 5, even though it is - // stored as And(a,b,c). - result_ += s - 1; - } - - virtual void - doit_default(const formula*) - { - ++result_; - } - - protected: - int result_; // size of the formula - }; - - class length_boolone_visitor: public length_visitor - { - - virtual void - visit(const unop* uo) - { - ++result_; - // Boolean formulas have length one. - if (!uo->is_boolean()) - uo->child()->accept(*this); - } - - virtual void - visit(const multop* mo) - { - // Boolean formulas have length one. - if (mo->is_boolean()) - { - ++result_; - return; - } - - unsigned s = mo->size(); - unsigned bool_seen = 0; - for (unsigned i = 0; i < s; ++i) - { - const formula* f = mo->nth(i); - // Ignore all Boolean children. We only want to count them once. - if (f->is_boolean()) - ++bool_seen; - else - f->accept(*this); - } - - // "a & b & c" should count for 5, even though it is stored - // as And(a,b,c). So add the number of operators here (it - // is either s - 1 or s - bool_seen depending on whether - // Boolean children were encountered). If Boolean were - // seen, also add one (!!bool_seen) to account for the - // "global" Boolean term. - result_ += s - !bool_seen - bool_seen + !!bool_seen; - } - - }; + int len = 0; + f.traverse([&len](const formula& x) + { + auto s = x.size(); + if (s > 1) + len += s - 1; + else + ++len; + return false; + }); + return len; } int - length(const formula* f) + length_boolone(formula f) { - length_visitor v; - f->accept(v); - return v.result(); - } - - int - length_boolone(const formula* f) - { - length_boolone_visitor v; - f->accept(v); - return v.result(); + int len = 0; + f.traverse([&len](const formula& x) + { + if (x.is_boolean()) + { + ++len; + return true; + } + auto s = x.size(); + if (s > 2) + { + int b = 0; + for (const auto& y: x) + if (y.is_boolean()) + ++b; + len += s - b * 2 + 1; + } + else if (s > 1) + { + len += s - 1; + } + else + { + ++len; + } + return false; + }); + return len; } } diff --git a/src/ltlvisit/length.hh b/src/ltlvisit/length.hh index b509c9c1b..cc194c740 100644 --- a/src/ltlvisit/length.hh +++ b/src/ltlvisit/length.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013 Laboratoire de Recherche et Developement de -// l'Epita (LRDE). +// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -33,15 +33,14 @@ namespace spot /// /// The length of a formula is the number of atomic propositions, /// constants, and operators (logical and temporal) occurring in - /// the formula. spot::ltl::multop instances with n arguments - /// count for n-1; for instance a | b | c has length - /// 5, even if there is only as single | node - /// internally. + /// the formula. n-ary operators count for n-1; for instance + /// a | b | c has length 5, even if there is only as + /// single | node internally. /// /// If squash_boolean is set, all Boolean formulae are assumed /// to have length one. SPOT_API - int length(const formula* f); + int length(formula f); /// \ingroup ltl_misc /// \brief Compute the length of a formula, squashing Boolean formulae @@ -49,6 +48,6 @@ namespace spot /// This is similar to spot::ltl::length(), except all Boolean /// formulae are assumed to have length one. SPOT_API - int length_boolone(const formula* f); + int length_boolone(formula f); } } diff --git a/src/ltlvisit/mark.cc b/src/ltlvisit/mark.cc index 8afd03552..2df7ef966 100644 --- a/src/ltlvisit/mark.cc +++ b/src/ltlvisit/mark.cc @@ -18,344 +18,176 @@ // along with this program. If not, see . #include "mark.hh" -#include "ltlast/allnodes.hh" #include #include #include #include -#include "misc/casts.hh" namespace spot { namespace ltl { - namespace - { - class simplify_mark_visitor : public visitor - { - const formula* result_; - mark_tools* tools_; - - public: - simplify_mark_visitor(mark_tools* t) - : tools_(t) - { - } - - ~simplify_mark_visitor() - { - } - - const formula* - result() - { - return result_; - } - - void - visit(const atomic_prop* ao) - { - result_ = ao->clone(); - } - - void - visit(const constant* c) - { - result_ = c->clone(); - } - - void - visit(const bunop* bo) - { - result_ = bo->clone(); - } - - void - visit(const unop* uo) - { - result_ = uo->clone(); - } - - void - visit(const multop* mo) - { - unsigned mos = mo->size(); - multop::vec* res = new multop::vec; - switch (mo->op()) - { - case multop::OrRat: - case multop::AndNLM: - case multop::AndRat: - case multop::Concat: - case multop::Fusion: - SPOT_UNIMPLEMENTED(); - case multop::Or: - for (unsigned i = 0; i < mos; ++i) - res->push_back(recurse(mo->nth(i))); - break; - case multop::And: - { - typedef std::set > pset; - pset empairs; - typedef std::set sset; - sset nmset; - typedef std::vector unbinop; - unbinop elist; - typedef std::vector ununop; - ununop nlist; - - for (unsigned i = 0; i < mos; ++i) - { - const formula* f = mo->nth(i); - if (const binop* bo = is_binop(f)) - { - switch (bo->op()) - { - case binop::EConcatMarked: - empairs.emplace(bo->first(), bo->second()); - // fall through - case binop::Xor: - case binop::Implies: - case binop::Equiv: - case binop::U: - case binop::W: - case binop::M: - case binop::R: - case binop::UConcat: - res->push_back(recurse(f)); - break; - case binop::EConcat: - elist.push_back(bo); - break; - } - } - if (const unop* uo = is_unop(f)) - { - switch (uo->op()) - { - case unop::NegClosureMarked: - nmset.insert(uo->child()); - // fall through - case unop::Not: - case unop::X: - case unop::F: - case unop::G: - case unop::Closure: - res->push_back(recurse(f)); - break; - case unop::NegClosure: - nlist.push_back(uo); - break; - } - } - else - { - res->push_back(recurse(f)); - } - } - // Keep only the non-marked EConcat for which we - // have not seen a similar EConcatMarked. - for (unbinop::const_iterator i = elist.begin(); - i != elist.end(); ++i) - if (empairs.find(std::make_pair((*i)->first(), - (*i)->second())) - == empairs.end()) - res->push_back((*i)->clone()); - // Keep only the non-marked NegClosure for which we - // have not seen a similar NegClosureMarked. - for (ununop::const_iterator i = nlist.begin(); - i != nlist.end(); ++i) - if (nmset.find((*i)->child()) == nmset.end()) - res->push_back((*i)->clone()); - } - } - result_ = multop::instance(mo->op(), res); - } - - void - visit(const binop* bo) - { - result_ = bo->clone(); - } - - const formula* - recurse(const formula* f) - { - return tools_->simplify_mark(f); - } - }; - - - class mark_visitor : public visitor - { - const formula* result_; - mark_tools* tools_; - - public: - mark_visitor(mark_tools* t) - : tools_(t) - { - } - ~mark_visitor() - { - } - - const formula* - result() - { - return result_; - } - - void - visit(const atomic_prop* ap) - { - result_ = ap->clone(); - } - - void - visit(const constant* c) - { - result_ = c->clone(); - } - - void - visit(const bunop* bo) - { - result_ = bo->clone(); - } - - void - visit(const unop* uo) - { - switch (uo->op()) - { - case unop::Not: - case unop::X: - case unop::F: - case unop::G: - case unop::Closure: - case unop::NegClosureMarked: - result_ = uo->clone(); - return; - case unop::NegClosure: - result_ = unop::instance(unop::NegClosureMarked, - uo->child()->clone()); - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const multop* mo) - { - multop::vec* res = new multop::vec; - unsigned mos = mo->size(); - for (unsigned i = 0; i < mos; ++i) - res->push_back(recurse(mo->nth(i))); - result_ = multop::instance(mo->op(), res); - } - - void - visit(const binop* bo) - { - switch (bo->op()) - { - case binop::Xor: - case binop::Implies: - case binop::Equiv: - SPOT_UNIMPLEMENTED(); - case binop::U: - case binop::W: - case binop::M: - case binop::R: - case binop::UConcat: - case binop::EConcatMarked: - result_ = bo->clone(); - return; - case binop::EConcat: - { - const formula* f1 = bo->first()->clone(); - const formula* f2 = bo->second()->clone(); - result_ = binop::instance(binop::EConcatMarked, f1, f2); - return; - } - } - SPOT_UNREACHABLE(); - } - - const formula* - recurse(const formula* f) - { - return tools_->mark_concat_ops(f); - } - }; - - } - - mark_tools::mark_tools() - : simpvisitor_(new simplify_mark_visitor(this)), - markvisitor_(new mark_visitor(this)) - { - } - - - mark_tools::~mark_tools() - { - delete simpvisitor_; - delete markvisitor_; - { - f2f_map::iterator i = simpmark_.begin(); - f2f_map::iterator end = simpmark_.end(); - while (i != end) - { - f2f_map::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - { - f2f_map::iterator i = markops_.begin(); - f2f_map::iterator end = markops_.end(); - while (i != end) - { - f2f_map::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - } - - const formula* - mark_tools::mark_concat_ops(const formula* f) + formula + mark_tools::mark_concat_ops(formula f) { f2f_map::iterator i = markops_.find(f); if (i != markops_.end()) - return i->second->clone(); + return i->second; - f->accept(*markvisitor_); + ltl::formula res; + switch (f.kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosureMarked: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Star: + case op::FStar: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcatMarked: + case op::UConcat: + case op::Concat: + case op::Fusion: + res = f; + break; + case op::NegClosure: + res = ltl::formula::NegClosureMarked(f.nth(0)); + break; + case op::EConcat: + res = ltl::formula::EConcatMarked(f.nth(0), f.nth(1)); + break; + case op::Or: + case op::And: + res = f.map([this](formula f) + { + return this->mark_concat_ops(f); + }); + break; + case op::Xor: + case op::Implies: + case op::Equiv: + SPOT_UNIMPLEMENTED(); + } - const formula* r = down_cast(markvisitor_)->result(); - markops_[f->clone()] = r->clone(); - return r; + markops_[f] = res; + return res; } - const formula* - mark_tools::simplify_mark(const formula* f) + formula + mark_tools::simplify_mark(formula f) { - if (!f->is_marked()) - return f->clone(); + if (!f.is_marked()) + return f; f2f_map::iterator i = simpmark_.find(f); if (i != simpmark_.end()) - return i->second->clone(); + return i->second; - f->accept(*simpvisitor_); + auto recurse = [this](formula f) + { + return this->simplify_mark(f); + }; - const formula* r = - down_cast(simpvisitor_)->result(); - simpmark_[f->clone()] = r->clone(); - return r; + ltl::formula res; + switch (f.kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + res = f; + break; + case op::Or: + res = f.map(recurse); + break; + case op::And: + { + std::set> empairs; + std::set nmset; + std::vector elist; + std::vector nlist; + std::vector 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; } } diff --git a/src/ltlvisit/mark.hh b/src/ltlvisit/mark.hh index 0b3f1213b..0baf0eb3c 100644 --- a/src/ltlvisit/mark.hh +++ b/src/ltlvisit/mark.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2011, 2012, 2013 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2010, 2011, 2012, 2013, 2015 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -20,34 +20,27 @@ #pragma once #include "ltlast/formula.hh" -#include "ltlast/visitor.hh" #include "misc/hash.hh" namespace spot { namespace ltl { - class mark_tools + class mark_tools final { public: /// \ingroup ltl_rewriting /// \brief Mark operators NegClosure and EConcat. /// /// \param f The formula to rewrite. - const formula* mark_concat_ops(const formula* f); + formula mark_concat_ops(formula f); - const formula* simplify_mark(const formula* f); - - mark_tools(); - ~mark_tools(); + formula simplify_mark(formula f); private: - typedef std::unordered_map> f2f_map; + typedef std::unordered_map f2f_map; f2f_map simpmark_; f2f_map markops_; - visitor* simpvisitor_; - visitor* markvisitor_; }; } diff --git a/src/ltlvisit/mutation.cc b/src/ltlvisit/mutation.cc index f582b4857..7b48e322e 100644 --- a/src/ltlvisit/mutation.cc +++ b/src/ltlvisit/mutation.cc @@ -17,28 +17,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include +#include #include -#include "ltlast/allnodes.hh" #include "ltlvisit/apcollect.hh" -#include "ltlvisit/clone.hh" #include "ltlvisit/mutation.hh" #include "ltlvisit/length.hh" -#include "misc/hash.hh" -#define Implies_(x, y) \ - spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y)) -#define And_(x, y) \ - spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y)) -#define AndRat_(x, y) \ - spot::ltl::multop::instance(spot::ltl::multop::AndRat, (x), (y)) -#define AndNLM_(x) \ - spot::ltl::multop::instance(spot::ltl::multop::AndNLM, (x)) -#define Concat_(x, y) \ - spot::ltl::multop::instance(spot::ltl::multop::Concat, (x), (y)) -#define Not_(x) \ - spot::ltl::unop::instance(spot::ltl::unop::Not, (x)) +#define And_(x, y) formula::And({(x), (y)}) +#define AndRat_(x, y) formula::AndRat({(x), (y)}) +#define AndNLM_(x) formula::AndNLM(x) +#define Concat_(x, y) formula::Concat({(x), (y)}) +#define Not_(x) formula::Not(x) namespace spot { @@ -46,355 +36,307 @@ namespace spot { namespace { - class replace_visitor final : public clone_visitor + formula substitute_ap(formula f, formula ap_src, formula ap_dst) { - public: - void visit(const atomic_prop* ap) - { - if (ap == ap1_) - result_ = ap2_->clone(); - else - result_ = ap->clone(); - } + return f.map([&](formula f) + { + if (f == ap_src) + return ap_dst; + else + return substitute_ap(f, ap_src, ap_dst); + }); + } - const formula* - replace(const formula* f, - const atomic_prop* ap1, - const atomic_prop* ap2) - { - ap1_ = ap1; - ap2_ = ap2; - return recurse(f); - } - - private: - const atomic_prop* ap1_; - const atomic_prop* ap2_; - }; - - typedef std::vector vec; - class mutation_visitor final : public clone_visitor + typedef std::vector vec; + class mutator final { + int mutation_counter_ = 0; + formula f_; + unsigned opts_; public: - mutation_visitor(const formula* f, unsigned opts) : f_(f), opts_(opts) + mutator(formula f, unsigned opts) : f_(f), opts_(opts) { } - void visit(const atomic_prop* ap) + formula mutate(formula f) { - result_ = 0; - if (opts_ & Mut_Ap2Const) - { - if (mutation_counter_-- == 0) - result_ = constant::true_instance(); - if (mutation_counter_-- == 0) - result_ = constant::false_instance(); - } - if (!result_) - result_ = ap->clone(); - } - - void visit(const unop* uo) - { - result_ = 0; - if (opts_ & Mut_Remove_Ops) - { - if ((uo->op() == unop::G - || uo->op() == unop::F - || uo->op() == unop::X - || uo->op() == unop::Not) - && mutation_counter_-- == 0) - result_ = uo->child()->clone(); - } - if (!result_) - { - if (mutation_counter_ < 0) - result_ = uo->clone(); - else - { - result_ = unop::instance(uo->op(), recurse(uo->child())); - } - } - } - - void visit(const binop* bo) - { - const formula* first = bo->first(); - const formula* second = bo->second(); - result_ = 0; - auto op = bo->op(); - bool left_is_sere = op == binop::EConcat - || op == binop::EConcatMarked - || op == binop::UConcat; - - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + auto recurse = [this](formula f) { - if (!left_is_sere) - result_ = first->clone(); - else if (op == binop::UConcat) - result_ = unop::instance(unop::NegClosure, first->clone()); - else // EConcat or EConcatMarked - result_ = unop::instance(unop::Closure, first->clone()); - } - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) - result_ = second->clone(); - if (opts_ & Mut_Rewrite_Ops) - { - switch (op) - { - case binop::U: - if (mutation_counter_-- == 0) - result_ = binop::instance(binop::W, first->clone(), - second->clone()); - break; - case binop::M: - if (mutation_counter_-- == 0) - result_ = binop::instance(binop::R, first->clone(), - second->clone()); - if (mutation_counter_-- == 0) - result_ = binop::instance(binop::U, second->clone(), - first->clone()); - break; - case binop::R: - if (mutation_counter_-- == 0) - result_ = binop::instance(binop::W, second->clone(), - first->clone()); - break; - default: - break; - } - } - if (opts_ & Mut_Split_Ops) - { - switch (op) - { - case binop::Equiv: - if (mutation_counter_-- == 0) - result_ = Implies_(first->clone(), second->clone()); - if (mutation_counter_-- == 0) - result_ = Implies_(second->clone(), first->clone()); - if (mutation_counter_-- == 0) - result_ = And_(first->clone(), second->clone()); - if (mutation_counter_-- == 0) - { - // Negate the two argument sequentially (in this - // case right before left, otherwise different - // compilers will make different choices. - auto right = Not_(second->clone()); - result_ = And_(Not_(first->clone()), right); - } - break; - case binop::Xor: - if (mutation_counter_-- == 0) - result_ = And_(first->clone(), Not_(second->clone())); - if (mutation_counter_-- == 0) - result_ = And_(Not_(first->clone()), second->clone()); - break; - default: - break; - } - } - if (!result_) - { - if (mutation_counter_ < 0) - { - result_ = bo->clone(); - } - else - { - // For historical reasons, we evaluate the right - // side before the left one. The other order would - // be OK as well but require changing the test - // suite. Evaluating both sides during the call to - // instance() is incorrect, because each compiler - // could decide of a different order. - auto right = recurse(second); - result_ = binop::instance(op, recurse(first), right); - } - } - } + return this->mutate(f); + }; - void visit(const bunop* bu) - { - const formula* c = bu->child()->clone(); - result_ = nullptr; - auto op = bu->op(); - - if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) - result_ = c; - if (opts_ & Mut_Simplify_Bounds) - { - auto min = bu->min(); - auto max = bu->max(); - if (min > 0) + switch (f.kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + return f; + case op::AP: + if (opts_ & Mut_Ap2Const) { if (mutation_counter_-- == 0) - result_ = bunop::instance(op, c, min - 1, max); + return formula::tt(); if (mutation_counter_-- == 0) - result_ = bunop::instance(op, c, 0, max); - } - if (max != bunop::unbounded) - { - if (max > min && mutation_counter_-- == 0) - result_ = bunop::instance(op, c, min, max - 1); - if (mutation_counter_-- == 0) - result_ = bunop::instance(op, c, min, bunop::unbounded); - } - } - if (!result_) - { - c->destroy(); - if (mutation_counter_ < 0) - result_ = bu->clone(); - else - result_ = bunop::instance(op, recurse(c), bu->min(), bu->max()); - } - } - - void visit(const multop* mo) - { - int mos = mo->size(); - int i; - result_ = 0; - - if (opts_ & Mut_Remove_Multop_Operands) - { - for (i = 0; i < mos; ++i) - if (mutation_counter_-- == 0) - result_ = mo->all_but(i); - } - - if (opts_ & Mut_Split_Ops && mo->op() == multop::AndNLM) - { - if (mutation_counter_ >= 0 && mutation_counter_ < 2 * (mos - 1)) - { - vec* v1 = new vec(); - vec* v2 = new vec(); - v1->push_back(mo->nth(0)->clone()); - bool reverse = false; - i = 1; - while (i < mos) - { - if (mutation_counter_-- == 0) - break; - if (mutation_counter_-- == 0) - { - reverse = true; - break; - } - v1->push_back(mo->nth(i++)->clone()); - } - for (; i < mos; ++i) - v2->push_back(mo->nth(i)->clone()); - const formula* tstar = - bunop::instance(bunop::Star, constant::true_instance(), - 0, - bunop::unbounded); - const formula* first = AndNLM_(v1); - const formula* second = AndNLM_(v2); - if (!reverse) - result_ = AndRat_(Concat_(first, tstar), second); - else - result_ = AndRat_(Concat_(second, tstar), first); + return formula::ff(); } + return f; + case op::Not: + case op::X: + case op::F: + case op::G: + if ((opts_ & Mut_Remove_Ops) + && mutation_counter_-- == 0) + return f.nth(0); + // fall through + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + if (mutation_counter_ < 0) + return f; else - mutation_counter_ -= 2 * (mos - 1); - } + return f.map(recurse); + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + { + int mos = f.size(); + if (opts_ & Mut_Remove_Multop_Operands) + { + for (int i = 0; i < mos; ++i) + if (mutation_counter_-- == 0) + return f.all_but(i); + } - if (!result_) - { - if (mutation_counter_ < 0) - result_ = mo->clone(); - else - { - vec* v = new vec(); - for (i = 0; i < mos; ++i) - v->push_back(recurse(mo->nth(i))); - result_ = multop::instance(mo->op(), v); - } - } + if (opts_ & Mut_Split_Ops && f.is(op::AndNLM)) + { + if (mutation_counter_ >= 0 + && mutation_counter_ < 2 * (mos - 1)) + { + vec v1; + vec v2; + v1.push_back(f.nth(0)); + bool reverse = false; + int i = 1; + while (i < mos) + { + if (mutation_counter_-- == 0) + break; + if (mutation_counter_-- == 0) + { + reverse = true; + break; + } + v1.push_back(f.nth(i++)); + } + for (; i < mos; ++i) + v2.push_back(f.nth(i)); + formula first = AndNLM_(v1); + formula second = AndNLM_(v2); + formula ost = formula::one_star(); + if (!reverse) + return AndRat_(Concat_(first, ost), second); + else + return AndRat_(Concat_(second, ost), first); + } + else + { + mutation_counter_ -= 2 * (mos - 1); + } + } + + if (mutation_counter_ < 0) + return f; + else + return f.map(recurse); + } + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + { + formula first = f.nth(0); + formula second = f.nth(1); + op o = f.kind(); + bool left_is_sere = o == op::EConcat + || o == op::EConcatMarked + || o == op::UConcat; + + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + { + if (!left_is_sere) + return first; + else if (o == op::UConcat) + return formula::NegClosure(first); + else // EConcat or EConcatMarked + return formula::Closure(first); + } + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + return second; + if (opts_ & Mut_Rewrite_Ops) + { + switch (o) + { + case op::U: + if (mutation_counter_-- == 0) + return formula::W(first, second); + break; + case op::M: + if (mutation_counter_-- == 0) + return formula::R(first, second); + if (mutation_counter_-- == 0) + return formula::U(second, first); + break; + case op::R: + if (mutation_counter_-- == 0) + return formula::W(second, first); + break; + default: + break; + } + } + if (opts_ & Mut_Split_Ops) + { + switch (o) + { + case op::Equiv: + if (mutation_counter_-- == 0) + return formula::Implies(first, second); + if (mutation_counter_-- == 0) + return formula::Implies(second, first); + if (mutation_counter_-- == 0) + return formula::And({first, second}); + if (mutation_counter_-- == 0) + { + // Negate the two argument sequentially (in this + // case right before left, otherwise different + // compilers will make different choices. + auto right = formula::Not(second); + return formula::And({formula::Not(first), right}); + } + break; + case op::Xor: + if (mutation_counter_-- == 0) + return formula::And({first, formula::Not(second)}); + if (mutation_counter_-- == 0) + return formula::And({formula::Not(first), second}); + break; + default: + break; + } + } + if (mutation_counter_ < 0) + return f; + else + return f.map(recurse); + } + case op::Star: + case op::FStar: + { + formula c = f.nth(0); + op o = f.kind(); + if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) + return c; + if (opts_ & Mut_Simplify_Bounds) + { + auto min = f.min(); + auto max = f.max(); + if (min > 0) + { + if (mutation_counter_-- == 0) + return formula::bunop(o, c, min - 1, max); + if (mutation_counter_-- == 0) + return formula::bunop(o, c, 0, max); + } + if (max != formula::unbounded()) + { + if (max > min && mutation_counter_-- == 0) + return formula::bunop(o, c, min, max - 1); + if (mutation_counter_-- == 0) + return formula::bunop(o, c, min, + formula::unbounded()); + } + } + if (mutation_counter_ < 0) + return f; + else + return f.map(recurse); + } + } + SPOT_UNREACHABLE(); } - const formula* - recurse(const formula* f) - { - f->accept(*this); - return result_; - } - - const formula* + formula get_mutation(int n) { mutation_counter_ = n; - const formula* mut = recurse(f_); + formula mut = mutate(f_); if (mut == f_) - { - mut->destroy(); - return 0; - } + return nullptr; return mut; } - private: - const formula* f_; - int mutation_counter_ = 0; - unsigned opts_; }; bool - formula_length_less_than(const formula* left, const formula* right) + formula_length_less_than(formula left, formula right) { - assert(left); - assert(right); + assert(left != nullptr); + assert(right != nullptr); if (left == right) return false; - return length(left) < length(right); + auto ll = length(left); + auto lr = length(right); + if (ll < lr) + return true; + if (ll > lr) + return false; + return left < right; } - typedef std::set fset_t; + typedef std::set fset_t; void - single_mutation_rec(const formula* f, fset_t& mutations, unsigned opts, + single_mutation_rec(formula f, fset_t& mutations, unsigned opts, unsigned& n, unsigned m) { if (m == 0) { if (mutations.insert(f).second) - { - f->clone(); - --n; - } + --n; } else { - const formula* mut(nullptr); + formula mut; int i = 0; - mutation_visitor mv(f, opts); - while (n > 0 && (mut = mv.get_mutation(i++))) - { - single_mutation_rec(mut, mutations, opts, n, m - 1); - mut->destroy(); - } + mutator mv(f, opts); + while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr)) + single_mutation_rec(mut, mutations, opts, n, m - 1); } } void - replace_ap_rec(const formula* f, fset_t& mutations, unsigned opts, + replace_ap_rec(formula f, fset_t& mutations, unsigned opts, unsigned& n, unsigned m) { if (m == 0) { if (mutations.insert(f).second) - { - f->clone(); - --n; - } + --n; } else { if (!n) return; - replace_visitor rv; auto aps = std::unique_ptr(atomic_prop_collect(f)); for (auto ap1: *aps) @@ -402,9 +344,8 @@ namespace spot { if (ap1 == ap2) continue; - auto mut = rv.replace(f, ap1, ap2); + auto mut = substitute_ap(f, ap1, ap2); replace_ap_rec(mut, mutations, opts, n, m - 1); - mut->destroy(); if (!n) return; } @@ -412,8 +353,8 @@ namespace spot } } - std::vector - mutate(const formula* f, unsigned opts, unsigned max_output, + std::vector + mutate(formula f, unsigned opts, unsigned max_output, unsigned mutation_count, bool sort) { fset_t mutations; diff --git a/src/ltlvisit/mutation.hh b/src/ltlvisit/mutation.hh index 29e27eb0e..aeb09d4f5 100644 --- a/src/ltlvisit/mutation.hh +++ b/src/ltlvisit/mutation.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement +// de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -20,7 +20,6 @@ #pragma once #include "ltlast/formula.hh" -#include #include namespace spot @@ -40,10 +39,10 @@ namespace spot }; SPOT_API - std::vector mutate(const formula* f, - unsigned opts = Mut_All, - unsigned max_output = -1U, - unsigned mutation_count = 1, - bool sort = true); + std::vector mutate(formula f, + unsigned opts = Mut_All, + unsigned max_output = -1U, + unsigned mutation_count = 1, + bool sort = true); } } diff --git a/src/ltlvisit/nenoform.cc b/src/ltlvisit/nenoform.cc index bf1bcc7fb..a2163a82b 100644 --- a/src/ltlvisit/nenoform.cc +++ b/src/ltlvisit/nenoform.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2010, 2011, 2012, 2013 Laboratoire de Recherche -// et Développement de l'Epita (LRDE). +// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -27,11 +27,11 @@ namespace spot { namespace ltl { - const formula* - negative_normal_form(const formula* f, bool negated) + formula + negative_normal_form(formula f, bool negated) { - if (!negated && f->is_in_nenoform()) - return f->clone(); + if (!negated && f.is_in_nenoform()) + return f; ltl_simplifier s; return s.negative_normal_form(f, negated); diff --git a/src/ltlvisit/nenoform.hh b/src/ltlvisit/nenoform.hh index 2948e53be..d57e1ae55 100644 --- a/src/ltlvisit/nenoform.hh +++ b/src/ltlvisit/nenoform.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2012, 2013 Laboratoire de Recherche et +// Copyright (C) 2011, 2012, 2013, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -39,11 +39,11 @@ namespace spot /// \c !f /// /// Note that this will not remove abbreviated operators. If you - /// want to remove abbreviations, call spot::ltl::unabbreviate_logic - /// or spot::ltl::unabbreviate_ltl first. (Calling these functions - /// after spot::ltl::negative_normal_form would likely produce a - /// formula which is not in negative normal form.) - SPOT_API const formula* - negative_normal_form(const formula* f, bool negated = false); + /// want to remove abbreviations, call spot::ltl::unabbreviate + /// first. (Calling this function after + /// spot::ltl::negative_normal_form would likely produce a formula + /// which is not in negative normal form.) + SPOT_API formula + negative_normal_form(formula f, bool negated = false); } } diff --git a/src/ltlvisit/postfix.cc b/src/ltlvisit/postfix.cc deleted file mode 100644 index 4fe5f3c13..000000000 --- a/src/ltlvisit/postfix.cc +++ /dev/null @@ -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 . - -#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. - } - } -} diff --git a/src/ltlvisit/postfix.hh b/src/ltlvisit/postfix.hh deleted file mode 100644 index 07b9fa97c..000000000 --- a/src/ltlvisit/postfix.hh +++ /dev/null @@ -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 . - -#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); - }; - } -} diff --git a/src/ltlvisit/print.cc b/src/ltlvisit/print.cc index 2fd91e4d8..97ac7788b 100644 --- a/src/ltlvisit/print.cc +++ b/src/ltlvisit/print.cc @@ -25,8 +25,6 @@ #include #include #include -#include "ltlast/allnodes.hh" -#include "ltlast/visitor.hh" #include "unabbrev.hh" #include "print.hh" #include "misc/escape.hh" @@ -338,32 +336,27 @@ namespace spot } // If the formula has the form (!b)[*], return b. - static - const formula* - strip_star_not(const formula* f) + static formula + strip_star_not(formula f) { - if (const bunop* s = is_Star(f)) - if (const unop* n = is_Not(s->child())) - return n->child(); - return 0; + return f.get_child_of({op::Star, op::Not}); } - // If the formula as position i in multop mo has the form + // If the formula at position i in mo has the form // (!b)[*];b with b being a Boolean formula, return b. - static - const formula* - match_goto(const multop *mo, unsigned i) + static formula + match_goto(formula mo, unsigned i) { - assert(i + 1 < mo->size()); - const formula* b = strip_star_not(mo->nth(i)); - if (!b || !b->is_boolean()) - return 0; - if (mo->nth(i + 1) == b) + assert(i + 1 < mo.size()); + formula b = strip_star_not(mo.nth(i)); + if (b == nullptr || !b.is_boolean()) + return nullptr; + if (mo.nth(i + 1) == b) return b; - return 0; + return nullptr; } - class to_string_visitor: public visitor + class to_string_visitor final { public: to_string_visitor(std::ostream& os, @@ -376,11 +369,6 @@ namespace spot { } - virtual - ~to_string_visitor() - { - } - void openp() const { @@ -406,503 +394,458 @@ namespace spot } void - visit(const atomic_prop* ap) + visit(formula f) { - std::string str = ap->name(); - if (full_parent_) - os_ << '('; - if (!is_bare_word(str.c_str())) - { - // Spin 6 supports atomic propositions such as (a == 0) - // as long as they are enclosed in parentheses. - if (kw_ == sclatex_kw || kw_ == latex_kw) - escape_latex(os_ << "``\\mathit{", str) << "}\\textrm{''}"; - else if (kw_ != spin_kw) - escape_str(os_ << '"', str) << '"'; - else if (!full_parent_) - os_ << '(' << str << ')'; - else - os_ << str; - } - else - { - if (kw_ == latex_kw || kw_ == sclatex_kw) - { - size_t s = str.size(); - while (str[s - 1] >= '0' && str[s - 1] <= '9') - { - --s; - assert(s != 0); // bare words cannot start with digits - } - if (s > 1) - os_ << "\\mathit{"; - escape_latex(os_, str.substr(0, s)); - if (s > 1) - os_ << '}'; - if (s != str.size()) - os_ << "_{" - << str.substr(s) - << '}'; - } - else - { - os_ << str; - } - } - if (kw_ == wring_kw) - os_ << "=1"; - if (full_parent_) - os_ << ')'; - } + bool top_level = top_level_; + top_level_ = false; - void - visit(const constant* c) - { - if (full_parent_) + auto s = f.size(); + bool want_par = (full_parent_ || s > 1) && !top_level; + if (want_par) openp(); - switch (c->val()) + + auto emit_bunop_child = [this](formula b) { - case constant::False: + // b[*] is OK, no need to print {b}[*]. However we want + // braces for {!b}[*], the only unary operator that can + // be nested with Star/FStar. + // If full_parent_ is set, we do NOT emit those extra + // braces, because they are already output for the node + // below. + bool need_parent = (!full_parent_ && b.is(op::Not)); + if (need_parent) + openp(); + this->visit(b); + if (need_parent) + closep(); + }; + + op o = f.kind(); + switch (o) + { + case op::False: emit(KFalse); break; - case constant::True: + case op::True: emit(KTrue); break; - case constant::EmptyWord: + case op::EmptyWord: emit(KEmptyWord); break; - } - if (full_parent_) - closep(); - } - - void - visit(const binop* bo) - { - bool top_level = top_level_; - top_level_ = false; - if (!top_level) - openp(); - - bool onelast = false; - - switch (bo->op()) - { - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - in_ratexp_ = true; - openp(); - top_level_ = true; + case op::AP: { - const multop* m = is_multop(bo->first(), multop::Concat); - if (m) + const std::string& str = f.ap_name(); + if (!is_bare_word(str.c_str())) { - unsigned s = m->size(); - if (m->nth(s - 1) == constant::true_instance()) + // Spin 6 supports atomic propositions such as (a == 0) + // as long as they are enclosed in parentheses. + if (kw_ == sclatex_kw || kw_ == latex_kw) + escape_latex(os_ << "``\\mathit{", str) + << "}\\textrm{''}"; + else if (kw_ != spin_kw) + escape_str(os_ << '"', str) << '"'; + else if (!full_parent_) + os_ << '(' << str << ')'; + else + os_ << str; + } + else + { + if (kw_ == latex_kw || kw_ == sclatex_kw) { - const formula* tmp = m->all_but(s - 1); - tmp->accept(*this); - tmp->destroy(); - onelast = true; - break; + size_t s = str.size(); + while (str[s - 1] >= '0' && str[s - 1] <= '9') + { + --s; + // bare words cannot start with digits + assert(s != 0); + } + if (s > 1) + os_ << "\\mathit{"; + escape_latex(os_, str.substr(0, s)); + if (s > 1) + os_ << '}'; + if (s != str.size()) + os_ << "_{" + << str.substr(s) + << '}'; + } + else + { + os_ << str; } } + if (kw_ == wring_kw) + os_ << "=1"; + } - // fall through - default: - bo->first()->accept(*this); break; - } - - switch (bo->op()) - { - case binop::Xor: - emit(KXor); - break; - case binop::Implies: - emit(KImplies); - break; - case binop::Equiv: - emit(KEquiv); - break; - case binop::U: - emit(KU); - break; - case binop::R: - emit(KR); - break; - case binop::W: - emit(KW); - break; - case binop::M: - emit(KM); - break; - case binop::UConcat: - closep(); - emit(onelast ? KTriggersNext : KTriggers); - in_ratexp_ = false; - top_level_ = false; - break; - case binop::EConcat: - emit(KCloseSERE); - if (bo->second() == constant::true_instance()) - { - os_ << '!'; - in_ratexp_ = false; - goto second_done; - } - emit(onelast ? KSeqNext : KSeq); - in_ratexp_ = false; - top_level_ = false; - break; - case binop::EConcatMarked: - os_ << '}'; - emit(onelast ? KSeqMarkedNext : KSeqMarked); - in_ratexp_ = false; - top_level_ = false; - break; - } - - bo->second()->accept(*this); - second_done: - if (!top_level) - closep(); - } - - void - emit_bunop_child(const formula* b) - { - // b[*] is OK, no need to print {b}[*]. However want braces - // for {!b}[*], the only unary operator that can be nested - // with [*] or any other BUnOp like [->i..j] or [=i..j]. - formula::opkind ck = b->kind(); - bool need_parent = (full_parent_ - || ck == formula::UnOp - || ck == formula::BinOp - || ck == formula::MultOp); - if (need_parent) - openp(); - b->accept(*this); - if (need_parent) - closep(); - } - - void - visit(const bunop* bo) - { - const formula* c = bo->child(); - enum { Star, FStar, Goto } sugar = Star; - unsigned default_min = 0; - unsigned default_max = bunop::unbounded; - - bunop::type op = bo->op(); - // Abbreviate "1[*]" as "[*]". - if (c != constant::true_instance() || op != bunop::Star) - { - switch (op) - { - case bunop::Star: - // Is this a Goto? - if (const multop* mo = is_Concat(c)) - { - unsigned s = mo->size(); - if (s == 2) - if (const formula* b = match_goto(mo, 0)) + case op::Not: + { + formula c = f.nth(0); + if (c.is(op::AP)) + { + // If we negate a single letter in UTF-8, use a + // combining overline. + if (!full_parent_ && kw_ == utf8_kw) + { + auto& name = c.ap_name(); + if (name.size() == 1 && is_bare_word(name.c_str())) { - c = b; - sugar = Goto; + os_ << name << "̅"; + break; } - } - break; - case bunop::FStar: - sugar = FStar; - break; - } - - emit_bunop_child(c); - } - - unsigned min = bo->min(); - unsigned max = bo->max(); - switch (sugar) - { - case Star: - if (min == 1 && max == bunop::unbounded) - { - emit(KPlusBunop); - return; - } - emit(KStarBunop); - break; - case FStar: - if (min == 1 && max == bunop::unbounded) - { - emit(KFPlusBunop); - return; - } - emit(KFStarBunop); - break; - case Goto: - emit(KGotoBunop); - default_min = 1; - default_max = 1; - break; - } - - // Beware that the default parameters of the Goto operator are - // not the same as Star or Equal: - // - // [->] = [->1..1] - // [->..] = [->1..unbounded] - // [*] = [*0..unbounded] - // [*..] = [*0..unbounded] - // [=] = [=0..unbounded] - // [=..] = [=0..unbounded] - // - // Strictly speaking [=] is not specified by PSL, and anyway we - // automatically rewrite Exp[=0..unbounded] as - // Exp[*0..unbounded], so we should never have to print [=] - // here. - // - // Also - // [*..] = [*0..unbounded] - - if (min != default_min || max != default_max) - { - // Always print the min_, even when it is equal to - // default_min, this way we avoid ambiguities (like - // when reading [*..3] near [->..2]) - os_ << min; - if (min != max) - { - os_ << ".."; - if (max != bunop::unbounded) - os_ << max; - } - } - emit(KCloseBunop); - } - - void - visit(const unop* uo) - { - top_level_ = false; - // The parser treats F0, F1, G0, G1, X0, and X1 as atomic - // propositions. So make sure we output F(0), G(1), etc. - bool need_parent = (uo->child()->kind() == formula::Constant); - bool top_level = top_level_; - bool overline = false; - - if (full_parent_) - { - need_parent = false; // These will be printed by each subformula - - if (!top_level) - openp(); - } - - switch (uo->op()) - { - case unop::Not: - need_parent = false; - // If we negate a single letter in UTF-8, use a - // combining overline. - if (!full_parent_ && kw_ == utf8_kw) - if (const ltl::atomic_prop* ap = is_atomic_prop(uo->child())) - if (ap->name().size() == 1 - && is_bare_word(ap->name().c_str())) - { - overline = true; - break; - } - // If we negate an atomic proposition for Wring, - // output prop=0. - if (kw_ == wring_kw) - if (const ltl::atomic_prop* ap = is_atomic_prop(uo->child())) - if (is_bare_word(ap->name().c_str())) - { - os_ << ap->name() << "=0"; - goto skiprec; - } - emit(KNot); - break; - case unop::X: + } + // If we negate an atomic proposition for Wring, + // output prop=0. + if (kw_ == wring_kw) + { + auto& name = c.ap_name(); + if (is_bare_word(name.c_str())) + { + os_ << name << "=0"; + break; + } + } + } + emit(KNot); + visit(c); + break; + } + case op::X: emit(KX); + visit(f.nth(0)); break; - case unop::F: + case op::F: emit(KF); + visit(f.nth(0)); break; - case unop::G: + case op::G: emit(KG); + visit(f.nth(0)); break; - case unop::Closure: + case op::NegClosure: + case op::NegClosureMarked: + emit(KNot); + if (o == op::NegClosureMarked) + os_ << (kw_ == utf8_kw ? "̃": "+"); + // Fall through + case op::Closure: os_ << '{'; in_ratexp_ = true; top_level_ = true; - break; - case unop::NegClosure: - emit(KNot); - os_ << '{'; - in_ratexp_ = true; - top_level_ = true; - break; - case unop::NegClosureMarked: - emit(KNot); - os_ << (kw_ == utf8_kw ? "̃{": "+{"); - in_ratexp_ = true; - top_level_ = true; - break; - } - - if (need_parent) - openp(); - uo->child()->accept(*this); - if (need_parent) - closep(); - - switch (uo->op()) - { - case unop::Closure: - case unop::NegClosure: + visit(f.nth(0)); os_ << '}'; in_ratexp_ = false; top_level_ = false; break; - default: + case op::Xor: + visit(f.nth(0)); + emit(KXor); + visit(f.nth(1)); break; - } - - skiprec: - - if (full_parent_ && !top_level) - closep(); - else if (overline) - // The following string contains only the character U+0305 - // (a combining overline). It looks better than U+0304 (a - // combining overbar). - os_ << "̅"; - } - - void - resugar_concat(const multop* mo) - { - unsigned max = mo->size(); - - for (unsigned i = 0; i < max; ++i) - { - if (i > 0) - emit(KConcat); - if (i + 1 < max) - { - // Try to match (!b)[*];b - const formula* b = match_goto(mo, i); - if (b) - { - emit_bunop_child(b); - - // Wait... maybe we are looking at (!b)[*];b;(!b)[*] - // in which case it's b[=1]. - if (i + 2 < max && mo->nth(i) == mo->nth(i + 2)) - { - emit(KEqualBunop); - os_ << '1'; - emit(KCloseBunop); - i += 2; - } - else - { - emit(KGotoBunop); - emit(KCloseBunop); - ++i; - } - continue; - } - // Try to match ((!b)[*];b)[*i..j];(!b)[*] - if (const bunop* s = is_Star(mo->nth(i))) - if (const formula* b2 = strip_star_not(mo->nth(i + 1))) - if (const multop* sc = is_Concat(s->child())) - if (const formula* b1 = match_goto(sc, 0)) - if (b1 == b2) - { - emit_bunop_child(b1); - emit(KEqualBunop); - unsigned min = s->min(); - os_ << min; - unsigned max = s->max(); - if (max != min) - { - os_ << ".."; - if (max != bunop::unbounded) - os_ << max; - } - emit(KCloseBunop); - ++i; - continue; - } - } - mo->nth(i)->accept(*this); - } - } - - - void - visit(const multop* mo) - { - bool top_level = top_level_; - top_level_ = false; - if (!top_level) - openp(); - multop::type op = mo->op(); - - // Handle the concatenation separately, because we want to - // "resugar" some patterns. - if (op == multop::Concat) - { - resugar_concat(mo); - if (!top_level) + case op::Implies: + visit(f.nth(0)); + emit(KImplies); + visit(f.nth(1)); + break; + case op::Equiv: + visit(f.nth(0)); + emit(KEquiv); + visit(f.nth(1)); + break; + case op::U: + visit(f.nth(0)); + emit(KU); + visit(f.nth(1)); + break; + case op::R: + visit(f.nth(0)); + emit(KR); + visit(f.nth(1)); + break; + case op::W: + visit(f.nth(0)); + emit(KW); + visit(f.nth(1)); + break; + case op::M: + visit(f.nth(0)); + emit(KM); + visit(f.nth(1)); + break; + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + { + in_ratexp_ = true; + openp(); + top_level_ = true; + formula left = f.nth(0); + formula right = f.nth(1); + unsigned last = left.size() - 1; + bool onelast = false; + if (left.is(op::Concat) && left.nth(last).is(op::True)) + { + visit(left.all_but(last)); + onelast = true; + } + else + { + visit(left); + } + top_level_ = false; closep(); - return; - } + in_ratexp_ = false; + if (o == op::UConcat) + { + emit(onelast ? KTriggersNext : KTriggers); + visit(right); + } + else if (o == op::EConcatMarked) + { + emit(onelast ? KSeqMarkedNext : KSeqMarked); + visit(right); + } + else if (o == op::EConcat) + { + if (f.nth(1).is(op::True)) + { + os_ << '!'; + // No recursion on right. + } + else + { + emit(onelast ? KSeqNext : KSeq); + visit(right); + } + } + else + { + SPOT_UNREACHABLE(); + } + } + break; + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Fusion: + { + visit(f.nth(0)); + keyword k = KFalse; // Initialize to something to please GCC. + switch (o) + { + case op::Or: + k = KOr; + break; + case op::OrRat: + k = KOrRat; + break; + case op::And: + k = in_ratexp_ ? KAndRat : KAnd; + break; + case op::AndRat: + k = KAndRat; + break; + case op::AndNLM: + k = KAndNLM; + break; + case op::Fusion: + k = KFusion; + break; + default: + SPOT_UNREACHABLE(); + } + assert(k != KFalse); - mo->nth(0)->accept(*this); - keyword k = KFalse; // Initialize to something to please GCC. - switch (op) - { - case multop::Or: - k = KOr; - break; - case multop::OrRat: - k = KOrRat; - break; - case multop::And: - k = in_ratexp_ ? KAndRat : KAnd; - break; - case multop::AndRat: - k = KAndRat; - break; - case multop::AndNLM: - k = KAndNLM; - break; - case multop::Concat: - // Handled by resugar_concat. - SPOT_UNREACHABLE(); - break; - case multop::Fusion: - k = KFusion; - break; - } - assert(k != KFalse); + unsigned max = f.size(); + for (unsigned n = 1; n < max; ++n) + { + emit(k); + visit(f.nth(n)); + } + break; + } + case op::Concat: + { + unsigned max = f.size(); - unsigned max = mo->size(); - for (unsigned n = 1; n < max; ++n) - { - emit(k); - mo->nth(n)->accept(*this); + for (unsigned i = 0; i < max; ++i) + { + if (i > 0) + emit(KConcat); + if (i + 1 < max) + { + // Try to match (!b)[*];b + formula b = match_goto(f, i); + if (b != nullptr) + { + emit_bunop_child(b); + + // Wait... maybe we are looking at (!b)[*];b;(!b)[*] + // in which case it's b[=1]. + if (i + 2 < max && f.nth(i) == f.nth(i + 2)) + { + emit(KEqualBunop); + os_ << '1'; + emit(KCloseBunop); + i += 2; + } + else + { + emit(KGotoBunop); + emit(KCloseBunop); + ++i; + } + continue; + } + // Try to match ((!b)[*];b)[*i..j];(!b)[*] + formula fi = f.nth(i); + if (fi.is(op::Star)) + { + if (formula b2 = strip_star_not(f.nth(i + 1))) + { + formula fic = fi.nth(0); + if (fic.is(op::Concat)) + if (formula b1 = match_goto(fic, 0)) + if (b1 == b2) + { + emit_bunop_child(b1); + emit(KEqualBunop); + unsigned min = fi.min(); + os_ << min; + unsigned max = fi.max(); + if (max != min) + { + os_ << ".."; + if (max != formula::unbounded()) + os_ << max; + } + emit(KCloseBunop); + ++i; + continue; + } + } + } + } + visit(f.nth(i)); + } + break; + } + case op::Star: + case op::FStar: + { + formula c = f.nth(0); + enum { Star, FStar, Goto } sugar = Star; + unsigned default_min = 0; + unsigned default_max = formula::unbounded(); + + // Abbreviate "1[*]" as "[*]". + if (!c.is(op::True) || o != op::Star) + { + if (o == op::Star) + { + // Is this a Goto? + if (c.is(op::Concat)) + { + unsigned s = c.size(); + if (s == 2) + if (formula b = match_goto(c, 0)) + { + c = b; + sugar = Goto; + } + } + } + else if (o == op::FStar) + { + sugar = FStar; + } + else + { + SPOT_UNREACHABLE(); + } + emit_bunop_child(c); + } + + unsigned min = f.min(); + unsigned max = f.max(); + bool range = true; + switch (sugar) + { + case Star: + if (min == 1 && max == formula::unbounded()) + { + range = false; + emit(KPlusBunop); + } + else + { + emit(KStarBunop); + } + break; + case FStar: + if (min == 1 && max == formula::unbounded()) + { + range = false; + emit(KFPlusBunop); + } + else + { + emit(KFStarBunop); + } + break; + case Goto: + emit(KGotoBunop); + default_min = 1; + default_max = 1; + break; + } + + // Beware that the default parameters of the Goto operator are + // not the same as Star or Equal: + // + // [->] = [->1..1] + // [->..] = [->1..unbounded] + // [*] = [*0..unbounded] + // [*..] = [*0..unbounded] + // [=] = [=0..unbounded] + // [=..] = [=0..unbounded] + // + // Strictly speaking [=] is not specified by PSL, and anyway we + // automatically rewrite Exp[=0..unbounded] as + // Exp[*0..unbounded], so we should never have to print [=] + // here. + // + // Also + // [*..] = [*0..unbounded] + + if (range) + { + if (min != default_min || max != default_max) + { + // Always print the min_, even when it is equal to + // default_min, this way we avoid ambiguities (like + // when reading [*..3] near [->..2]) + os_ << min; + if (min != max) + { + os_ << ".."; + if (max != formula::unbounded()) + os_ << max; + } + } + emit(KCloseBunop); + } + } + break; } - if (!top_level) + if (want_par) closep(); } + protected: std::ostream& os_; bool top_level_; @@ -913,16 +856,16 @@ namespace spot std::ostream& - printer_(std::ostream& os, const formula* f, bool full_parent, + printer_(std::ostream& os, formula f, bool full_parent, bool ratexp, const char** kw) { to_string_visitor v(os, full_parent, ratexp, kw); - f->accept(v); + v.visit(f); return os; } std::string - str_(const formula* f, bool full_parent, bool ratexp, const char** kw) + str_(formula f, bool full_parent, bool ratexp, const char** kw) { std::ostringstream os; printer_(os, f, full_parent, ratexp, kw); @@ -932,67 +875,65 @@ namespace spot } // anonymous std::ostream& - print_psl(std::ostream& os, const formula* f, bool full_parent) + print_psl(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, false, spot_kw); } std::string - str_psl(const formula* f, bool full_parent) + str_psl(formula f, bool full_parent) { return str_(f, full_parent, false, spot_kw); } std::ostream& - print_sere(std::ostream& os, const formula* f, bool full_parent) + print_sere(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, true, spot_kw); } std::string - str_sere(const formula* f, bool full_parent) + str_sere(formula f, bool full_parent) { return str_(f, full_parent, false, spot_kw); } std::ostream& - print_utf8_psl(std::ostream& os, const formula* f, bool full_parent) + print_utf8_psl(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, false, utf8_kw); } std::string - str_utf8_psl(const formula* f, bool full_parent) + str_utf8_psl(formula f, bool full_parent) { return str_(f, full_parent, false, utf8_kw); } std::ostream& - print_utf8_sere(std::ostream& os, const formula* f, bool full_parent) + print_utf8_sere(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, true, utf8_kw); } std::string - str_utf8_sere(const formula* f, bool full_parent) + str_utf8_sere(formula f, bool full_parent) { return str_(f, full_parent, false, utf8_kw); } std::ostream& - print_spin_ltl(std::ostream& os, const formula* f, bool full_parent) + print_spin_ltl(std::ostream& os, formula f, bool full_parent) { - f = unabbreviate(f, "^MW"); to_string_visitor v(os, full_parent, false, spin_kw); - f->accept(v); - f->destroy(); + v.visit(unabbreviate(f, "^MW")); return os; } std::string - str_spin_ltl(const formula* f, bool full_parent) + str_spin_ltl(formula f, bool full_parent) { std::ostringstream os; print_spin_ltl(os, f, full_parent); @@ -1000,17 +941,15 @@ namespace spot } std::ostream& - print_wring_ltl(std::ostream& os, const formula* f) + print_wring_ltl(std::ostream& os, formula f) { - f = unabbreviate(f, "MW"); to_string_visitor v(os, true, false, wring_kw); - f->accept(v); - f->destroy(); + v.visit(unabbreviate(f, "MW")); return os; } std::string - str_wring_ltl(const formula* f) + str_wring_ltl(formula f) { std::ostringstream os; print_wring_ltl(os, f); @@ -1018,50 +957,50 @@ namespace spot } std::ostream& - print_latex_psl(std::ostream& os, const formula* f, bool full_parent) + print_latex_psl(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, false, latex_kw); } std::string - str_latex_psl(const formula* f, bool full_parent) + str_latex_psl(formula f, bool full_parent) { return str_(f, full_parent, false, latex_kw); } std::ostream& - print_latex_sere(std::ostream& os, const formula* f, bool full_parent) + print_latex_sere(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, true, latex_kw); } std::string - str_latex_sere(const formula* f, bool full_parent) + str_latex_sere(formula f, bool full_parent) { return str_(f, full_parent, true, latex_kw); } std::ostream& - print_sclatex_psl(std::ostream& os, const formula* f, bool full_parent) + print_sclatex_psl(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, false, sclatex_kw); } std::string - str_sclatex_psl(const formula* f, bool full_parent) + str_sclatex_psl(formula f, bool full_parent) { return str_(f, full_parent, false, sclatex_kw); } std::ostream& - print_sclatex_sere(std::ostream& os, const formula* f, bool full_parent) + print_sclatex_sere(std::ostream& os, formula f, bool full_parent) { return printer_(os, f, full_parent, true, sclatex_kw); } std::string - str_sclatex_sere(const formula* f, bool full_parent) + str_sclatex_sere(formula f, bool full_parent) { return str_(f, full_parent, true, sclatex_kw); } @@ -1080,7 +1019,7 @@ namespace spot return true; } - class lbt_visitor: public visitor + class lbt_visitor final { protected: std::ostream& os_; @@ -1092,164 +1031,109 @@ namespace spot { } - void blank() + void + visit(formula f) { if (first_) first_ = false; else os_ << ' '; - } - virtual - ~lbt_visitor() - { - } - - void - visit(const atomic_prop* ap) - { - blank(); - std::string str = ap->name(); - if (!is_pnum(str.c_str())) - escape_str(os_ << '"', str) << '"'; - else - os_ << str; - } - - void - visit(const constant* c) - { - blank(); - switch (c->val()) + op o = f.kind(); + switch (o) { - case constant::False: + case op::False: os_ << 'f'; break; - case constant::True: + case op::True: os_ << 't'; break; - case constant::EmptyWord: - SPOT_UNIMPLEMENTED(); - break; - } - } - - void - visit(const binop* bo) - { - blank(); - switch (bo->op()) - { - case binop::Xor: - os_ << '^'; - break; - case binop::Implies: - os_ << 'i'; - break; - case binop::Equiv: - os_ << 'e'; - break; - case binop::U: - os_ << 'U'; - break; - case binop::R: - os_ << 'V'; - break; - case binop::W: - os_ << 'W'; - break; - case binop::M: - os_ << 'M'; - break; - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - SPOT_UNIMPLEMENTED(); - break; - } - bo->first()->accept(*this); - bo->second()->accept(*this); - } - - void - visit(const bunop*) - { - SPOT_UNIMPLEMENTED(); - } - - void - visit(const unop* uo) - { - blank(); - switch (uo->op()) - { - case unop::Not: + case op::AP: + { + const std::string& str = f.ap_name(); + if (!is_pnum(str.c_str())) + escape_str(os_ << '"', str) << '"'; + else + os_ << str; + break; + } + case op::Not: os_ << '!'; break; - case unop::X: + case op::X: os_ << 'X'; break; - case unop::F: + case op::F: os_ << 'F'; break; - case unop::G: + case op::G: os_ << 'G'; break; - case unop::Closure: - case unop::NegClosure: - case unop::NegClosureMarked: + case op::Xor: + os_ << '^'; + break; + case op::Implies: + os_ << 'i'; + break; + case op::Equiv: + os_ << 'e'; + break; + case op::U: + os_ << 'U'; + break; + case op::R: + os_ << 'V'; + break; + case op::W: + os_ << 'W'; + break; + case op::M: + os_ << 'M'; + break; + case op::Or: + for (unsigned i = f.size() - 1; i != 0; --i) + os_ << "| "; + first_ = true; + break; + case op::And: + for (unsigned i = f.size() - 1; i != 0; --i) + os_ << "& "; + first_ = true; + break; + case op::EmptyWord: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: SPOT_UNIMPLEMENTED(); - break; } - uo->child()->accept(*this); - } - - void - visit(const multop* mo) - { - char o = 0; - switch (mo->op()) - { - case multop::Or: - o = '|'; - break; - case multop::And: - o = '&'; - break; - case multop::OrRat: - case multop::AndRat: - case multop::AndNLM: - case multop::Concat: - case multop::Fusion: - SPOT_UNIMPLEMENTED(); - break; - } - - unsigned n = mo->size(); - for (unsigned i = n - 1; i != 0; --i) - { - blank(); - os_ << o; - } - - for (unsigned i = 0; i < n; ++i) - mo->nth(i)->accept(*this); + for (auto c: f) + visit(c); } }; } // anonymous std::ostream& - print_lbt_ltl(std::ostream& os, const formula* f) + print_lbt_ltl(std::ostream& os, formula f) { - assert(f->is_ltl_formula()); + assert(f.is_ltl_formula()); lbt_visitor v(os); - f->accept(v); + v.visit(f); return os; } std::string - str_lbt_ltl(const formula* f) + str_lbt_ltl(formula f) { std::ostringstream os; print_lbt_ltl(os, f); diff --git a/src/ltlvisit/print.hh b/src/ltlvisit/print.hh index cad3fefb5..e52d177fa 100644 --- a/src/ltlvisit/print.hh +++ b/src/ltlvisit/print.hh @@ -38,14 +38,14 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_psl(std::ostream& os, const formula* f, bool full_parent = false); + print_psl(std::ostream& os, formula f, bool full_parent = false); /// \brief Convert a PSL formula into a string which is parsable /// \param f The formula to translate. /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_psl(const formula* f, bool full_parent = false); + str_psl(formula f, bool full_parent = false); /// \brief Output a PSL formula as an utf-8 string which is parsable. /// \param os The stream where it should be output. @@ -53,7 +53,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_utf8_psl(std::ostream& os, const formula* f, + print_utf8_psl(std::ostream& os, formula f, bool full_parent = false); /// \brief Convert a PSL formula into a utf-8 string which is parsable @@ -61,7 +61,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_utf8_psl(const formula* f, bool full_parent = false); + str_utf8_psl(formula f, bool full_parent = false); /// \brief Output a SERE formula as a string which is parsable. /// \param f The formula to translate. @@ -69,14 +69,14 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_sere(std::ostream& os, const formula* f, bool full_parent = false); + print_sere(std::ostream& os, formula f, bool full_parent = false); /// \brief Convert a SERE formula into a string which is parsable /// \param f The formula to translate. /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_sere(const formula* f, bool full_parent = false); + str_sere(formula f, bool full_parent = false); /// \brief Output a SERE formula as a utf-8 string which is parsable. /// \param os The stream where it should be output. @@ -84,7 +84,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_utf8_sere(std::ostream& os, const formula* f, + print_utf8_sere(std::ostream& os, formula f, bool full_parent = false); /// \brief Convert a SERE formula into a string which is parsable @@ -92,7 +92,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_utf8_sere(const formula* f, bool full_parent = false); + str_utf8_sere(formula f, bool full_parent = false); /// \brief Output an LTL formula as a string parsable by Spin. /// \param os The stream where it should be output. @@ -100,7 +100,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_spin_ltl(std::ostream& os, const formula* f, + print_spin_ltl(std::ostream& os, formula f, bool full_parent = false); /// \brief Convert an LTL formula into a string parsable by Spin. @@ -108,18 +108,18 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_spin_ltl(const formula* f, bool full_parent = false); + str_spin_ltl(formula f, bool full_parent = false); /// \brief Output an LTL formula as a string parsable by Wring. /// \param os The stream where it should be output. /// \param f The formula to translate. SPOT_API std::ostream& - print_wring_ltl(std::ostream& os, const formula* f); + print_wring_ltl(std::ostream& os, formula f); /// \brief Convert a formula into a string parsable by Wring /// \param f The formula to translate. SPOT_API std::string - str_wring_ltl(const formula* f); + str_wring_ltl(formula f); /// \brief Output a PSL formula as a LaTeX string. /// \param os The stream where it should be output. @@ -127,7 +127,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_latex_psl(std::ostream& os, const formula* f, + print_latex_psl(std::ostream& os, formula f, bool full_parent = false); /// \brief Output a formula as a LaTeX string which is parsable. @@ -136,7 +136,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_latex_psl(const formula* f, bool full_parent = false); + str_latex_psl(formula f, bool full_parent = false); /// \brief Output a SERE formula as a LaTeX string. /// \param os The stream where it should be output. @@ -144,7 +144,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_latex_sere(std::ostream& os, const formula* f, + print_latex_sere(std::ostream& os, formula f, bool full_parent = false); /// \brief Output a SERE formula as a LaTeX string which is parsable. @@ -153,7 +153,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_latex_sere(const formula* f, bool full_parent = false); + str_latex_sere(formula f, bool full_parent = false); /// \brief Output a PSL formula as a self-contained LaTeX string. /// @@ -163,7 +163,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_sclatex_psl(std::ostream& os, const formula* f, + print_sclatex_psl(std::ostream& os, formula f, bool full_parent = false); /// \brief Output a PSL formula as a self-contained LaTeX string. @@ -173,7 +173,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_sclatex_psl(const formula* f, bool full_parent = false); + str_sclatex_psl(formula f, bool full_parent = false); /// \brief Output a SERE formula as a self-contained LaTeX string. /// @@ -183,7 +183,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::ostream& - print_sclatex_sere(std::ostream& os, const formula* f, + print_sclatex_sere(std::ostream& os, formula f, bool full_parent = false); /// \brief Output a SERE formula as a self-contained LaTeX string. @@ -193,7 +193,7 @@ namespace spot /// \param full_parent Whether or not the string should by fully /// parenthesized. SPOT_API std::string - str_sclatex_sere(const formula* f, bool full_parent = false); + str_sclatex_sere(formula f, bool full_parent = false); /// \brief Output an LTL formula as a string in LBT's format. /// @@ -206,7 +206,7 @@ namespace spot /// \param f The formula to translate. /// \param os The stream where it should be output. SPOT_API std::ostream& - print_lbt_ltl(std::ostream& os, const formula* f); + print_lbt_ltl(std::ostream& os, formula f); /// \brief Output an LTL formula as a string in LBT's format. /// @@ -218,7 +218,7 @@ namespace spot /// /// \param f The formula to translate. SPOT_API std::string - str_lbt_ltl(const formula* f); + str_lbt_ltl(formula f); /// @} } } diff --git a/src/ltlvisit/randomltl.cc b/src/ltlvisit/randomltl.cc index 0baba161b..d3d95a063 100644 --- a/src/ltlvisit/randomltl.cc +++ b/src/ltlvisit/randomltl.cc @@ -23,7 +23,6 @@ #include #include #include "randomltl.hh" -#include "ltlast/allnodes.hh" #include "misc/random.hh" #include #include @@ -37,25 +36,41 @@ namespace spot { namespace { - static const formula* + static formula ap_builder(const random_formula* rl, int n) { assert(n == 1); (void) n; atomic_prop_set::const_iterator i = rl->ap()->begin(); std::advance(i, mrand(rl->ap()->size())); - return (*i)->clone(); + return *i; } - static const formula* + static formula true_builder(const random_formula*, int n) { assert(n == 1); (void) n; - return constant::true_instance(); + return formula::tt(); } - static const formula* + static formula + false_builder(const random_formula*, int n) + { + assert(n == 1); + (void) n; + return formula::ff(); + } + + static formula + eword_builder(const random_formula*, int n) + { + assert(n == 1); + (void) n; + return formula::eword(); + } + + static formula boolform_builder(const random_formula* rl, int n) { assert(n >= 1); @@ -63,40 +78,24 @@ namespace spot return rs->rb.generate(n); } - static const formula* - false_builder(const random_formula*, int n) - { - assert(n == 1); - (void) n; - return constant::false_instance(); - } - - static const formula* - eword_builder(const random_formula*, int n) - { - assert(n == 1); - (void) n; - return constant::empty_word_instance(); - } - - template - static const formula* + template + static formula unop_builder(const random_formula* rl, int n) { assert(n >= 2); - return unop::instance(Op, rl->generate(n - 1)); + return formula::unop(Op, rl->generate(n - 1)); } - static const formula* + static formula closure_builder(const random_formula* rl, int n) { assert(n >= 2); const random_psl* rp = static_cast(rl); - return unop::instance(unop::Closure, rp->rs.generate(n - 1)); + return formula::Closure(rp->rs.generate(n - 1)); } - template - static const formula* + template + static formula binop_builder(const random_formula* rl, int n) { assert(n >= 3); @@ -109,11 +108,11 @@ namespace spot // discovering that clang would perform the nested calls from // left to right. auto right = rl->generate(n - l); - return binop::instance(Op, rl->generate(l), right); + return formula::binop(Op, rl->generate(l), right); } - template - static const formula* + template + static formula binop_SERELTL_builder(const random_formula* rl, int n) { assert(n >= 3); @@ -122,41 +121,41 @@ namespace spot int l = rrand(1, n - 1); // See comment in binop_builder. auto right = rl->generate(n - l); - return binop::instance(Op, rp->rs.generate(l), right); + return formula::binop(Op, rp->rs.generate(l), right); } - template - static const formula* + template + static formula bunop_unbounded_builder(const random_formula* rl, int n) { assert(n >= 2); - return bunop::instance(Op, rl->generate(n - 1)); + return formula::bunop(Op, rl->generate(n - 1)); } - template - static const formula* + template + static formula bunop_bounded_builder(const random_formula* rl, int n) { assert(n >= 2); int min = rrand(0, 2); int max = rrand(min, 3); - return bunop::instance(Op, rl->generate(n - 1), min, max); + return formula::bunop(Op, rl->generate(n - 1), min, max); } - template - static const formula* + template + static formula bunop_bool_bounded_builder(const random_formula* rl, int n) { assert(n >= 2); int min = rrand(0, 2); int max = rrand(min, 3); const random_sere* rp = static_cast(rl); - return bunop::instance(Op, rp->rb.generate(n - 1), min, max); + return formula::bunop(Op, rp->rb.generate(n - 1), min, max); } - template - static const formula* + template + static formula multop_builder(const random_formula* rl, int n) { assert(n >= 3); @@ -164,7 +163,7 @@ namespace spot int l = rrand(1, n - 1); // See comment in binop_builder. auto right = rl->generate(n - l); - return multop::instance(Op, rl->generate(l), right); + return formula::multop(Op, {rl->generate(l), right}); } } // anonymous @@ -208,7 +207,7 @@ namespace spot assert(total_2_and_more_ >= total_2_); } - const formula* + formula random_formula::generate(int n) const { assert(n > 0); @@ -319,15 +318,15 @@ namespace spot proba_2_ = proba_ + 1; proba_2_or_more_ = proba_ + 1; proba_[1].setup("boolform", 1, boolform_builder); - proba_[2].setup("star", 2, bunop_unbounded_builder); - proba_[3].setup("star_b", 2, bunop_bounded_builder); - proba_[4].setup("fstar", 2, bunop_unbounded_builder); - proba_[5].setup("fstar_b", 2, bunop_bounded_builder); - proba_[6].setup("and", 3, multop_builder); - proba_[7].setup("andNLM", 3, multop_builder); - proba_[8].setup("or", 3, multop_builder); - proba_[9].setup("concat", 3, multop_builder); - proba_[10].setup("fusion", 3, multop_builder); + proba_[2].setup("star", 2, bunop_unbounded_builder); + proba_[3].setup("star_b", 2, bunop_bounded_builder); + proba_[4].setup("fstar", 2, bunop_unbounded_builder); + proba_[5].setup("fstar_b", 2, bunop_bounded_builder); + proba_[6].setup("and", 3, multop_builder); + proba_[7].setup("andNLM", 3, multop_builder); + proba_[8].setup("or", 3, multop_builder); + proba_[9].setup("concat", 3, multop_builder); + proba_[10].setup("fusion", 3, multop_builder); update_sums(); } @@ -341,12 +340,12 @@ namespace spot proba_[1].setup("false", 1, false_builder); proba_[2].setup("true", 1, true_builder); proba_2_or_more_ = proba_2_ = proba_ + 3; - proba_[3].setup("not", 2, unop_builder); - proba_[4].setup("equiv", 3, binop_builder); - proba_[5].setup("implies", 3, binop_builder); - proba_[6].setup("xor", 3, binop_builder); - proba_[7].setup("and", 3, multop_builder); - proba_[8].setup("or", 3, multop_builder); + proba_[3].setup("not", 2, unop_builder); + proba_[4].setup("equiv", 3, binop_builder); + proba_[5].setup("implies", 3, binop_builder); + proba_[6].setup("xor", 3, binop_builder); + proba_[7].setup("and", 3, multop_builder); + proba_[8].setup("or", 3, multop_builder); update_sums(); } @@ -360,19 +359,19 @@ namespace spot proba_[1].setup("false", 1, false_builder); proba_[2].setup("true", 1, true_builder); proba_2_or_more_ = proba_2_ = proba_ + 3; - proba_[3].setup("not", 2, unop_builder); - proba_[4].setup("F", 2, unop_builder); - proba_[5].setup("G", 2, unop_builder); - proba_[6].setup("X", 2, unop_builder); - proba_[7].setup("equiv", 3, binop_builder); - proba_[8].setup("implies", 3, binop_builder); - proba_[9].setup("xor", 3, binop_builder); - proba_[10].setup("R", 3, binop_builder); - proba_[11].setup("U", 3, binop_builder); - proba_[12].setup("W", 3, binop_builder); - proba_[13].setup("M", 3, binop_builder); - proba_[14].setup("and", 3, multop_builder); - proba_[15].setup("or", 3, multop_builder); + proba_[3].setup("not", 2, unop_builder); + proba_[4].setup("F", 2, unop_builder); + proba_[5].setup("G", 2, unop_builder); + proba_[6].setup("X", 2, unop_builder); + proba_[7].setup("equiv", 3, binop_builder); + proba_[8].setup("implies", 3, binop_builder); + proba_[9].setup("xor", 3, binop_builder); + proba_[10].setup("R", 3, binop_builder); + proba_[11].setup("U", 3, binop_builder); + proba_[12].setup("W", 3, binop_builder); + proba_[13].setup("M", 3, binop_builder); + proba_[14].setup("and", 3, multop_builder); + proba_[15].setup("or", 3, multop_builder); } random_ltl::random_ltl(const atomic_prop_set* ap) @@ -399,8 +398,8 @@ namespace spot ((proba_ + 16) - (proba_ + 7)) * sizeof(*proba_)); proba_[7].setup("Closure", 2, closure_builder); - proba_[17].setup("EConcat", 3, binop_SERELTL_builder); - proba_[18].setup("UConcat", 3, binop_SERELTL_builder); + proba_[17].setup("EConcat", 3, binop_SERELTL_builder); + proba_[18].setup("UConcat", 3, binop_SERELTL_builder); update_sums(); } @@ -490,16 +489,13 @@ namespace spot randltlgenerator::~randltlgenerator() { delete rf_; - // Cleanup the unicity table. - for (auto i: unique_set_) - i->destroy(); } - const formula* randltlgenerator::next() + formula randltlgenerator::next() { unsigned trials = MAX_TRIALS; bool ignore; - const formula* f = nullptr; + formula f = nullptr; do { ignore = false; @@ -512,29 +508,14 @@ namespace spot { atomic_prop_set s = aprops_; remove_some_props(s); - f = multop::instance(multop::And, - f, GF_n()); + f = formula::And({f, GF_n()}); } if (opt_simpl_level_) - { - const spot::ltl::formula* tmp = simpl_.simplify(f); - f->destroy(); - f = tmp; - } + f = simpl_.simplify(f); - if (opt_unique_) - { - if (unique_set_.insert(f).second) - { - f->clone(); - } - else - { - ignore = true; - f->destroy(); - } - } + if (opt_unique_ && !unique_set_.insert(f).second) + ignore = true; } while (ignore && --trials); if (trials <= 0) return nullptr; @@ -557,17 +538,15 @@ namespace spot } // GF(p_1) & GF(p_2) & ... & GF(p_n) - const formula* + formula randltlgenerator::GF_n() { - const formula* res = 0; + formula res = nullptr; for (auto v: aprops_) { - const formula* f = - unop::instance(unop::F, v->clone()); - f = unop::instance(unop::G, f); + formula f = formula::G(formula::F(v)); if (res) - res = multop::instance(multop::And, f, res); + res = formula::And({f, res}); else res = f; } diff --git a/src/ltlvisit/randomltl.hh b/src/ltlvisit/randomltl.hh index 37774196c..4bfa19f31 100644 --- a/src/ltlvisit/randomltl.hh +++ b/src/ltlvisit/randomltl.hh @@ -70,7 +70,7 @@ namespace spot /// n, because some simple simplifications are performed by the /// AST. (For instance the formula a | a is /// automatically reduced to a by spot::ltl::multop.) - const formula* generate(int n) const; + formula generate(int n) const; /// \brief Print the priorities of each operator, constants, /// and atomic propositions. @@ -93,7 +93,7 @@ namespace spot const char* name; int min_n; double proba; - typedef const formula* (*builder)(const random_formula* rl, int n); + typedef formula (*builder)(const random_formula* rl, int n); builder build; void setup(const char* name, int min_n, builder build); }; @@ -304,9 +304,7 @@ namespace spot class SPOT_API randltlgenerator { - typedef - std::unordered_set> fset_t; + typedef std::unordered_set fset_t; public: @@ -322,7 +320,7 @@ namespace spot ~randltlgenerator(); - const spot::ltl::formula* next(); + formula next(); void dump_ltl_priorities(std::ostream& os); void dump_bool_priorities(std::ostream& os); @@ -331,7 +329,7 @@ namespace spot void dump_sere_bool_priorities(std::ostream& os); void remove_some_props(atomic_prop_set& s); - const formula* GF_n(); + formula GF_n(); private: fset_t unique_set_; diff --git a/src/ltlvisit/relabel.cc b/src/ltlvisit/relabel.cc index 52fb3a185..5787d708c 100644 --- a/src/ltlvisit/relabel.cc +++ b/src/ltlvisit/relabel.cc @@ -19,10 +19,7 @@ #include "relabel.hh" #include -#include "clone.hh" #include "misc/hash.hh" -#include "ltlenv/defaultenv.hh" -#include "ltlast/allnodes.hh" #include #include #include @@ -41,7 +38,7 @@ namespace spot { struct ap_generator { - virtual const formula* next() = 0; + virtual formula next() = 0; virtual ~ap_generator() {} }; @@ -53,11 +50,11 @@ namespace spot { } - const formula* next() + formula next() { std::ostringstream s; s << 'p' << nn++; - return default_environment::instance().require(s.str()); + return formula::ap(s.str()); } }; @@ -71,7 +68,7 @@ namespace spot unsigned nn; - const formula* next() + formula next() { std::string s; unsigned n = nn++; @@ -81,16 +78,15 @@ namespace spot n /= 26; } while (n); - - return default_environment::instance().require(s); + return formula::ap(s); } }; - class relabeler: public clone_visitor + class relabeler { public: - typedef std::unordered_map map; + typedef std::unordered_map map; map newname; ap_generator* gen; relabeling_map* oldnames; @@ -105,29 +101,33 @@ namespace spot delete gen; } - const formula* rename(const formula* old) + formula rename(formula old) { auto r = newname.emplace(old, nullptr); if (!r.second) { - return r.first->second->clone(); + return r.first->second; } else { - const formula* res; - r.first->second = res = gen->next(); + formula res = gen->next(); + r.first->second = res; if (oldnames) - (*oldnames)[res] = old->clone(); + (*oldnames)[res] = old; return res; } } - using clone_visitor::visit; - - void - visit(const atomic_prop* ap) + formula + visit(formula f) { - result_ = rename(ap); + if (f.is(op::AP)) + return rename(f); + else + return f.map([this](formula f) + { + return this->visit(f); + }); } }; @@ -135,9 +135,8 @@ namespace spot } - const formula* - relabel(const formula* f, relabeling_style style, - relabeling_map* m) + formula + relabel(formula f, relabeling_style style, relabeling_map* m) { ap_generator* gen = 0; switch (style) @@ -149,8 +148,9 @@ namespace spot gen = new abc_generator; break; } - relabeler rel(gen, m); - return rel.recurse(f); + + relabeler r(gen, m); + return r.visit(f); } ////////////////////////////////////////////////////////////////////// @@ -227,16 +227,16 @@ namespace spot // stop a, b, and !c, producing (p0&p1)U(p1&p2). namespace { - typedef std::vector succ_vec; - typedef std::map fgraph; + typedef std::vector succ_vec; + typedef std::map fgraph; // Convert the formula's syntax tree into an undirected graph // labeled by subformulas. - class formula_to_fgraph: public visitor + class formula_to_fgraph final { public: fgraph& g; - std::stack s; + std::stack s; formula_to_fgraph(fgraph& g): g(g) @@ -248,104 +248,63 @@ namespace spot } void - visit(const atomic_prop*) + visit(formula f) { - } + { + // Connect to parent + auto in = g.emplace(f, succ_vec()); + if (!s.empty()) + { + formula top = s.top(); + in.first->second.push_back(top); + g[top].push_back(f); + if (!in.second) + return; + } + else + { + assert(in.second); + } + } + s.push(f); - void - visit(const constant*) - { - } - - void - visit(const bunop* bo) - { - recurse(bo->child()); - } - - void - visit(const unop* uo) - { - recurse(uo->child()); - } - - void - visit(const binop* bo) - { - const formula* l = bo->first(); - recurse(l); - const formula* r = bo->second(); - recurse(r); - // Link operands of Boolean operators. - if (bo->is_boolean()) - { - g[l].push_back(r); - g[r].push_back(l); - } - } - - void - visit(const multop* mo) - { - unsigned mos = mo->size(); - - /// If we have a formula like (a & b & Xc), consider - /// it as ((a & b) & Xc) in the graph to isolate the - /// Boolean operands as a single node. + unsigned sz = f.size(); unsigned i = 0; - const formula* b = mo->is_boolean() ? 0 : mo->boolean_operands(&i); - if (b) + if (sz > 2 && !f.is_boolean()) { - recurse(b); - b->destroy(); + /// If we have a formula like (a & b & Xc), consider + /// it as ((a & b) & Xc) in the graph to isolate the + /// Boolean operands as a single node. + formula b = f.boolean_operands(&i); + if (b) + visit(b); } - for (; i < mos; ++i) - recurse(mo->nth(i)); - // For Boolean nodes, connect all children in a loop. This - // way the node can only be a cut-point if it separates all - // children from the reset of the graph (not only one). - if (mo->is_boolean()) + for (; i < sz; ++i) + visit(f.nth(i)); + if (sz > 1 && f.is_boolean()) { - const formula* pred = mo->nth(0); - for (i = 1; i < mos; ++i) + // For Boolean nodes, connect all children in a + // loop. This way the node can only be a cut-point + // if it separates all children from the reset of + // the graph (not only one). + formula pred = f.nth(0); + for (i = 1; i < sz; ++i) { - const formula* next = mo->nth(i); - // Note that we only add an edge in one direction, - // because we are building a cycle between all - // children anyway. + formula next = f.nth(i); + // Note that we only add an edge in one + // direction, because we are building a cycle + // between all children anyway. g[pred].push_back(next); pred = next; } - g[pred].push_back(mo->nth(0)); + g[pred].push_back(f.nth(0)); } - } - - void - recurse(const formula* f) - { - auto i = g.emplace(f, succ_vec()); - if (!s.empty()) - { - const formula* top = s.top(); - i.first->second.push_back(top); - g[top].push_back(f); - if (!i.second) - return; - } - else - { - assert(i.second); - } - f->clone(); - s.push(f); - f->accept(*this); s.pop(); } - }; - typedef std::set fset; + typedef std::set fset; struct data_entry // for each node of the graph { unsigned num; // serial number, in pre-order @@ -355,11 +314,11 @@ namespace spot { } }; - typedef std::unordered_map fmap_t; + typedef std::unordered_map fmap_t; struct stack_entry { - const formula* grand_parent; - const formula* parent; // current node + formula grand_parent; + formula parent; // current node succ_vec::const_iterator current_child; succ_vec::const_iterator last_child; }; @@ -377,7 +336,7 @@ namespace spot // cut-point, but since we only return Boolean cut-points it's // OK: if the top-most formula is Boolean we want to replace it // as a whole). - void cut_points(const fgraph& g, fset& c, const formula* start) + void cut_points(const fgraph& g, fset& c, formula start) { stack_t s; @@ -397,7 +356,7 @@ namespace spot { // Skip the edge if it is just the reverse of the one // we took. - const formula* child = *e.current_child; + formula child = *e.current_child; if (child == e.grand_parent) { ++e.current_child; @@ -428,15 +387,15 @@ namespace spot } else { - const formula* grand_parent = e.grand_parent; - const formula* parent = e.parent; + formula grand_parent = e.grand_parent; + formula parent = e.parent; s.pop(); if (!s.empty()) { data_entry& dparent = data[parent]; data_entry& dgrand_parent = data[grand_parent]; if (dparent.low >= dgrand_parent.num // cut-point - && grand_parent->is_boolean()) + && grand_parent.is_boolean()) c.insert(grand_parent); if (dparent.low < dgrand_parent.low) dgrand_parent.low = dparent.low; @@ -450,7 +409,6 @@ namespace spot { public: fset& c; - bse_relabeler(ap_generator* gen, fset& c, relabeling_map* m) : relabeler(gen, m), c(c) @@ -459,57 +417,51 @@ namespace spot using relabeler::visit; - void - visit(const multop* mo) + formula + visit(formula f) { - unsigned mos = mo->size(); + if (f.is(op::AP) || (c.find(f) != c.end())) + return rename(f); + unsigned sz = f.size(); + if (sz <= 2) + return f.map([this](formula f) + { + return visit(f); + }); + + unsigned i = 0; + std::vector res; /// If we have a formula like (a & b & Xc), consider /// it as ((a & b) & Xc) in the graph to isolate the /// Boolean operands as a single node. - unsigned i = 0; - const formula* b = mo->is_boolean() ? 0 : mo->boolean_operands(&i); - multop::vec* res = new multop::vec; + formula b = f.boolean_operands(&i); if (b) { - res->reserve(mos - i + 1); - res->push_back(recurse(b)); - b->destroy(); + res.reserve(sz - i + 1); + res.push_back(visit(b)); } else { - res->reserve(mos); + res.reserve(sz); } - for (; i < mos; ++i) - res->push_back(recurse(mo->nth(i))); - result_ = multop::instance(mo->op(), res); - } - - const formula* - recurse(const formula* f) - { - fset::const_iterator it = c.find(f); - if (it != c.end()) - result_ = rename(f); - else - f->accept(*this); - return result_; + for (; i < sz; ++i) + res.push_back(visit(f.nth(i))); + return formula::multop(f.kind(), res); } }; - } - const formula* - relabel_bse(const formula* f, relabeling_style style, - relabeling_map* m) + formula + relabel_bse(formula f, relabeling_style style, relabeling_map* m) { fgraph g; // Build the graph g from the formula f. { formula_to_fgraph conv(g); - conv.recurse(f); + conv.visit(f); } // Compute its cut-points @@ -529,18 +481,7 @@ namespace spot break; } bse_relabeler rel(gen, c, m); - f = rel.recurse(f); - - // Cleanup. - fgraph::const_iterator i = g.begin(); - while (i != g.end()) - { - const formula* f = i->first; - ++i; - f->destroy(); - } - - return f; + return rel.visit(f); } } } diff --git a/src/ltlvisit/relabel.hh b/src/ltlvisit/relabel.hh index 17be94e43..98411d4da 100644 --- a/src/ltlvisit/relabel.hh +++ b/src/ltlvisit/relabel.hh @@ -29,25 +29,7 @@ namespace spot { enum relabeling_style { Abc, Pnn }; - - struct relabeling_map: public std::map - { - void clear() - { - iterator i = begin(); - while (i != end()) - i++->second->destroy(); - this->std::map::clear(); - } - - ~relabeling_map() - { - clear(); - } - }; + typedef std::map relabeling_map; /// \ingroup ltl_rewriting /// \brief Relabel the atomic propositions in a formula. @@ -55,8 +37,8 @@ namespace spot /// If \a m is non-null, it is filled with correspondence /// between the new names (keys) and the old names (values). SPOT_API - const formula* relabel(const formula* f, relabeling_style style, - relabeling_map* m = 0); + formula relabel(formula f, relabeling_style style, + relabeling_map* m = 0); /// \ingroup ltl_rewriting @@ -66,7 +48,7 @@ namespace spot /// If \a m is non-null, it is filled with correspondence /// between the new names (keys) and the old names (values). SPOT_API - const formula* relabel_bse(const formula* f, relabeling_style style, - relabeling_map* m = 0); + formula relabel_bse(formula f, relabeling_style style, + relabeling_map* m = 0); } } diff --git a/src/ltlvisit/remove_x.cc b/src/ltlvisit/remove_x.cc index 774468e92..eec440c13 100644 --- a/src/ltlvisit/remove_x.cc +++ b/src/ltlvisit/remove_x.cc @@ -17,9 +17,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "ltlast/allnodes.hh" #include "ltlvisit/simplify.hh" -#include "ltlvisit/clone.hh" #include "ltlvisit/apcollect.hh" #include "ltlvisit/remove_x.hh" @@ -29,104 +27,76 @@ namespace spot { namespace { - -#define AND(x, y) multop::instance(multop::And, (x), (y)) -#define OR(x, y) multop::instance(multop::Or, (x), (y)) -#define NOT(x) unop::instance(unop::Not, (x)) -#define G(x) unop::instance(unop::G, (x)) -#define U(x, y) binop::instance(binop::U, (x), (y)) - - class remove_x_visitor final : public clone_visitor + static formula + remove_x_rec(formula f, atomic_prop_set& aps) { - typedef clone_visitor super; - atomic_prop_set aps; + if (f.is_syntactic_stutter_invariant()) + return f; - public: - remove_x_visitor(const formula* f) - { - atomic_prop_collect(f, &aps); - } + auto rec = [&aps](formula f) + { + return remove_x_rec(f, aps); + }; - virtual - ~remove_x_visitor() - { - } + if (!f.is(op::X)) + return f.map(rec); - using super::visit; - void visit(const unop* uo) - { - const formula* c = recurse(uo->child()); + formula c = rec(f.nth(0)); - unop::type op = uo->op(); - if (op != unop::X) - { - result_ = unop::instance(op, c); - return; - } - multop::vec* vo = new multop::vec; - for (atomic_prop_set::const_iterator i = aps.begin(); - i != aps.end(); ++i) - { - // First line - multop::vec* va1 = new multop::vec; - const formula* npi = NOT((*i)->clone()); - va1->push_back((*i)->clone()); - va1->push_back(U((*i)->clone(), AND(npi, c->clone()))); - for (atomic_prop_set::const_iterator j = aps.begin(); - j != aps.end(); ++j) - if (*j != *i) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = U(NOT((*j)->clone()), npi->clone()); - va1->push_back(OR(U((*j)->clone(), npi->clone()), tmp)); - } - vo->push_back(multop::instance(multop::And, va1)); - // Second line - multop::vec* va2 = new multop::vec; - va2->push_back(npi->clone()); - va2->push_back(U(npi->clone(), AND((*i)->clone(), c->clone()))); - for (atomic_prop_set::const_iterator j = aps.begin(); - j != aps.end(); ++j) - if (*j != *i) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = U(NOT((*j)->clone()), (*i)->clone()); - va2->push_back(OR(U((*j)->clone(), (*i)->clone()), tmp)); - } - vo->push_back(multop::instance(multop::And, va2)); - } - const formula* l12 = multop::instance(multop::Or, vo); - // Third line - multop::vec* va3 = new multop::vec; - for (atomic_prop_set::const_iterator i = aps.begin(); - i != aps.end(); ++i) - { - // make sure the arguments of OR are created in a - // deterministic order - auto tmp = G(NOT((*i)->clone())); - va3->push_back(OR(G((*i)->clone()), tmp)); - } - result_ = OR(l12, AND(multop::instance(multop::And, va3), c)); - return; - } - - virtual const formula* recurse(const formula* f) - { - if (f->is_syntactic_stutter_invariant()) - return f->clone(); - f->accept(*this); - return this->result(); - } - }; + std::vector vo; + for (auto i: aps) + { + // First line + std::vector va1; + formula npi = formula::Not(i); + va1.push_back(i); + va1.push_back(formula::U(i, formula::And({npi, c}))); + for (auto j: aps) + if (j != i) + { + // make sure the arguments of OR are created in a + // deterministic order + auto tmp = formula::U(formula::Not(j), npi); + va1.push_back(formula::Or({formula::U(j, npi), tmp})); + } + vo.push_back(formula::And(va1)); + // Second line + std::vector 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 va3; + for (auto i: aps) + { + // make sure the arguments of OR are created in a + // deterministic order + auto tmp = formula::G(formula::Not(i)); + va3.push_back(formula::Or({formula::G(i), tmp})); + } + va3.push_back(c); + vo.push_back(formula::And(va3)); + return formula::Or(vo); + } } - const formula* remove_x(const formula* f) + formula remove_x(formula f) { - remove_x_visitor v(f); - return v.recurse(f); + if (f.is_syntactic_stutter_invariant()) + return f; + atomic_prop_set aps; + atomic_prop_collect(f, &aps); + return remove_x_rec(f, aps); } } } diff --git a/src/ltlvisit/remove_x.hh b/src/ltlvisit/remove_x.hh index 5e52c560a..e347f4466 100644 --- a/src/ltlvisit/remove_x.hh +++ b/src/ltlvisit/remove_x.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013 Laboratoire de Recherche et Developpement de -// l'Epita (LRDE). +// Copyright (C) 2013, 2015 Laboratoire de Recherche et Developpement +// de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -19,14 +19,12 @@ #pragma once -#include "misc/common.hh" +#include "ltlast/formula.hh" namespace spot { namespace ltl { - class formula; - /// \brief Rewrite a stutter-insensitive formula \a f without /// using the X operator. /// @@ -46,6 +44,6 @@ namespace spot } \endverbatim */ SPOT_API - const formula* remove_x(const formula* f); + formula remove_x(formula f); } } diff --git a/src/ltlvisit/simpfg.cc b/src/ltlvisit/simpfg.cc index 0afbbd39a..cc9fb818b 100644 --- a/src/ltlvisit/simpfg.cc +++ b/src/ltlvisit/simpfg.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2012, 2014 Laboratoire de Recherche et +// Copyright (C) 2010, 2012, 2014, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -20,87 +20,28 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "ltlast/allnodes.hh" -#include "ltlvisit/clone.hh" #include "simpfg.hh" -#include namespace spot { namespace ltl { - - simplify_f_g_visitor::simplify_f_g_visitor() + formula simplify_f_g(formula p) { + // 1 U p = Fp + if (p.is(op::U) && p.nth(0).is_true()) + return formula::F(p.nth(1)); + // 0 R p = Gp + if (p.is(op::R) && p.nth(0).is_false()) + return formula::G(p.nth(1)); + // p W 0 = Gp + if (p.is(op::W) && p.nth(1).is_false()) + return formula::G(p.nth(0)); + // p M 1 = Fp + if (p.is(op::M) && p.nth(1).is_true()) + return formula::F(p.nth(0)); + return p.map(simplify_f_g); } - simplify_f_g_visitor::~simplify_f_g_visitor() - { - } - - void - simplify_f_g_visitor::visit(const binop* bo) - { - const formula* f1 = recurse(bo->first()); - const formula* f2 = recurse(bo->second()); - binop::type op = bo->op(); - - switch (op) - { - case binop::Xor: - case binop::Implies: - case binop::Equiv: - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - result_ = binop::instance(op, f1, f2); - return; - /* true U f2 == F(f2) */ - case binop::U: - if (f1 == constant::true_instance()) - result_ = unop::instance(unop::F, f2); - else - result_ = binop::instance(binop::U, f1, f2); - return; - /* false R f2 == G(f2) */ - case binop::R: - if (f1 == constant::false_instance()) - result_ = unop::instance(unop::G, f2); - else - result_ = binop::instance(binop::R, f1, f2); - return; - /* f1 W false == G(f1) */ - case binop::W: - if (f2 == constant::false_instance()) - result_ = unop::instance(unop::G, f1); - else - result_ = binop::instance(binop::W, f1, f2); - return; - /* f1 M true == F(f1) */ - case binop::M: - if (f2 == constant::true_instance()) - result_ = unop::instance(unop::F, f1); - else - result_ = binop::instance(binop::M, f1, f2); - return; - } - SPOT_UNREACHABLE(); - } - - const formula* - simplify_f_g_visitor::recurse(const formula* f) - { - return simplify_f_g(f); - } - - const formula* - simplify_f_g(const formula* f) - { - if (f->is_boolean()) - return f->clone(); - simplify_f_g_visitor v; - f->accept(v); - return v.result(); - } } } diff --git a/src/ltlvisit/simpfg.hh b/src/ltlvisit/simpfg.hh index 20bc65e19..e3094b140 100644 --- a/src/ltlvisit/simpfg.hh +++ b/src/ltlvisit/simpfg.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2012, 2013 Laboratoire de Recherche et +// Copyright (C) 2010, 2012, 2013, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -22,36 +22,12 @@ #pragma once -#include "clone.hh" +#include "ltlast/formula.hh" namespace spot { namespace ltl { - /// \ingroup ltl_visitor - /// \brief Replace true U f and false R g by - /// F f and G g. - /// - /// Perform the following rewriting (from left to right): - /// - /// - true U a = F a - /// - a M true = F a - /// - false R a = G a - /// - a W false = G a - /// - class SPOT_API simplify_f_g_visitor : public clone_visitor - { - typedef clone_visitor super; - public: - simplify_f_g_visitor(); - virtual ~simplify_f_g_visitor(); - - using super::visit; - void visit(const binop* bo); - - virtual const formula* recurse(const formula* f); - }; - /// \ingroup ltl_rewriting /// \brief Replace true U f and false R g by /// F f and G g. @@ -63,6 +39,6 @@ namespace spot /// - false R a = G a /// - a W false = G a /// - SPOT_API const formula* simplify_f_g(const formula* f); + SPOT_API formula simplify_f_g(formula f); } } diff --git a/src/ltlvisit/simplify.cc b/src/ltlvisit/simplify.cc index 0330e373d..de11222e7 100644 --- a/src/ltlvisit/simplify.cc +++ b/src/ltlvisit/simplify.cc @@ -18,7 +18,7 @@ // along with this program. If not, see . #include -// #define TRACE +//#define TRACE #ifdef TRACE #define trace std::cerr #else @@ -26,29 +26,26 @@ #endif #include "simplify.hh" -#include "misc/hash.hh" -#include "ltlast/allnodes.hh" -#include "ltlast/visitor.hh" +#include #include "ltlvisit/contain.hh" #include "ltlvisit/print.hh" #include "ltlvisit/snf.hh" #include "twa/formula2bdd.hh" #include +#include namespace spot { namespace ltl { - + typedef std::vector vec; // The name of this class is public, but not its contents. class ltl_simplifier_cache { - typedef std::unordered_map> f2f_map; - typedef std::unordered_map> f2b_map; - typedef std::pair pairf; + typedef std::unordered_map f2f_map; + typedef std::unordered_map f2b_map; + typedef std::pair pairf; typedef std::map syntimpl_cache_t; public: bdd_dict_ptr dict; @@ -57,76 +54,6 @@ namespace spot ~ltl_simplifier_cache() { - { - f2f_map::iterator i = simplified_.begin(); - f2f_map::iterator end = simplified_.end(); - while (i != end) - { - f2f_map::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - { - f2f_map::iterator i = nenoform_.begin(); - f2f_map::iterator end = nenoform_.end(); - while (i != end) - { - f2f_map::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - { - f2b_map::iterator i = as_bdd_.begin(); - f2b_map::iterator end = as_bdd_.end(); - while (i != end) - { - f2b_map::iterator old = i++; - old->first->destroy(); - } - } - { - syntimpl_cache_t::iterator i = syntimpl_.begin(); - syntimpl_cache_t::iterator end = syntimpl_.end(); - while (i != end) - { - syntimpl_cache_t::iterator old = i++; - old->first.first->destroy(); - old->first.second->destroy(); - } - } - { - snf_cache::iterator i = snf_cache_.begin(); - snf_cache::iterator end = snf_cache_.end(); - while (i != end) - { - snf_cache::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - { - snf_cache::iterator i = snfb_cache_.begin(); - snf_cache::iterator end = snfb_cache_.end(); - while (i != end) - { - snf_cache::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - { - f2f_map::iterator i = bool_isop_.begin(); - f2f_map::iterator end = bool_isop_.end(); - while (i != end) - { - f2f_map::iterator old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - dict->unregister_all_my_variables(this); } @@ -157,19 +84,12 @@ namespace spot void clear_as_bdd_cache() { - f2b_map::iterator i = as_bdd_.begin(); - f2b_map::iterator end = as_bdd_.end(); - while (i != end) - { - f2b_map::iterator old = i++; - old->first->destroy(); - } as_bdd_.clear(); } // Convert a Boolean formula into a BDD for easier comparison. bdd - as_bdd(const formula* f) + as_bdd(formula f) { // Lookup the result in case it has already been computed. f2b_map::const_iterator it = as_bdd_.find(f); @@ -178,107 +98,71 @@ namespace spot bdd result = bddfalse; - switch (f->kind()) + switch (f.kind()) { - case formula::Constant: - if (f == constant::true_instance()) - result = bddtrue; - else if (f == constant::false_instance()) - result = bddfalse; - else - SPOT_UNIMPLEMENTED(); + case op::True: + result = bddtrue; break; - case formula::AtomicProp: + case op::False: + result = bddfalse; + break; + case op::AP: result = bdd_ithvar(dict->register_proposition(f, this)); break; - case formula::UnOp: - { - const unop* uo = static_cast(f); - assert(uo->op() == unop::Not); - result = !as_bdd(uo->child()); - break; - } - case formula::BinOp: - { - const binop* bo = static_cast(f); - int op = 0; - switch (bo->op()) - { - case binop::Xor: - op = bddop_xor; - break; - case binop::Implies: - op = bddop_imp; - break; - case binop::Equiv: - op = bddop_biimp; - break; - default: - SPOT_UNIMPLEMENTED(); - } - result = bdd_apply(as_bdd(bo->first()), as_bdd(bo->second()), op); - break; - } - case formula::MultOp: - { - const multop* mo = static_cast(f); - switch (mo->op()) - { - case multop::And: - { - result = bddtrue; - unsigned s = mo->size(); - for (unsigned n = 0; n < s; ++n) - result &= as_bdd(mo->nth(n)); - break; - } - case multop::Or: - { - result = bddfalse; - unsigned s = mo->size(); - for (unsigned n = 0; n < s; ++n) - result |= as_bdd(mo->nth(n)); - break; - } - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: - case multop::Concat: - case multop::Fusion: - SPOT_UNIMPLEMENTED(); - break; - } - break; - } - case formula::BUnOp: - SPOT_UNIMPLEMENTED(); + case op::Not: + result = !as_bdd(f.nth(0)); break; + case op::Xor: + result = bdd_apply(as_bdd(f.nth(0)), as_bdd(f.nth(1)), bddop_xor); + break; + case op::Implies: + result = bdd_apply(as_bdd(f.nth(0)), as_bdd(f.nth(1)), bddop_imp); + break; + case op::Equiv: + result = bdd_apply(as_bdd(f.nth(0)), as_bdd(f.nth(1)), bddop_biimp); + break; + case op::And: + { + result = bddtrue; + for (auto c: f) + result &= as_bdd(c); + break; + } + case op::Or: + { + result = bddfalse; + for (auto c: f) + result |= as_bdd(c); + break; + } + default: + SPOT_UNIMPLEMENTED(); } // Cache the result before returning. - as_bdd_[f->clone()] = result; + as_bdd_[f] = result; return result; } - const formula* - lookup_nenoform(const formula* f) + formula + lookup_nenoform(formula f) { f2f_map::const_iterator i = nenoform_.find(f); if (i == nenoform_.end()) - return 0; - return i->second->clone(); + return nullptr; + return i->second; } void - cache_nenoform(const formula* orig, const formula* nenoform) + cache_nenoform(formula orig, formula nenoform) { - nenoform_[orig->clone()] = nenoform->clone(); + nenoform_[orig] = nenoform; } // Return true iff the option set (syntactic implication // or containment checks) allow to prove that f1 => f2. bool - implication(const formula* f1, const formula* f2) + implication(formula f1, formula f2) { trace << "[->] does " << str_psl(f1) << " implies " << str_psl(f2) << " ?" << std::endl; @@ -294,15 +178,15 @@ namespace spot // Return true if f1 => f2 syntactically bool - syntactic_implication(const formula* f1, const formula* f2); + syntactic_implication(formula f1, formula f2); bool - syntactic_implication_aux(const formula* f1, const formula* f2); + syntactic_implication_aux(formula f1, formula f2); // Return true if f1 => f2 bool - contained(const formula* f1, const formula* f2) + contained(formula f1, formula f2) { - if (!f1->is_psl_formula() || !f2->is_psl_formula()) + if (!f1.is_psl_formula() || !f2.is_psl_formula()) return false; return lcc.contained(f1, f2); } @@ -310,13 +194,13 @@ namespace spot // If right==false, true if !f1 => f2, false otherwise. // If right==true, true if f1 => !f2, false otherwise. bool - syntactic_implication_neg(const formula* f1, const formula* f2, + syntactic_implication_neg(formula f1, formula f2, bool right); // Return true if f1 => !f2 - bool contained_neg(const formula* f1, const formula* f2) + bool contained_neg(formula f1, formula f2) { - if (!f1->is_psl_formula() || !f2->is_psl_formula()) + if (!f1.is_psl_formula() || !f2.is_psl_formula()) return false; trace << "[CN] Does (" << str_psl(f1) << ") implies !(" << str_psl(f2) << ") ?" << std::endl; @@ -333,9 +217,9 @@ namespace spot } // Return true if f1 => !f2 - bool neg_contained(const formula* f1, const formula* f2) + bool neg_contained(formula f1, formula f2) { - if (!f1->is_psl_formula() || !f2->is_psl_formula()) + if (!f1.is_psl_formula() || !f2.is_psl_formula()) return false; trace << "[NC] Does (" << str_psl(f1) << ") implies !(" << str_psl(f2) << ") ?" << std::endl; @@ -356,7 +240,7 @@ namespace spot // - !f1 => f2 (case where right=false) // - f1 => !f2 (case where right=true) bool - implication_neg(const formula* f1, const formula* f2, bool right) + implication_neg(formula f1, formula f2, bool right) { trace << "[IN] Does " << (right ? "(" : "!(") << str_psl(f1) << ") implies " @@ -376,44 +260,44 @@ namespace spot } } - const formula* - lookup_simplified(const formula* f) + formula + lookup_simplified(formula f) { f2f_map::const_iterator i = simplified_.find(f); if (i == simplified_.end()) return 0; - return i->second->clone(); + return i->second; } void - cache_simplified(const formula* orig, const formula* simplified) + cache_simplified(formula orig, formula simplified) { - simplified_[orig->clone()] = simplified->clone(); + simplified_[orig] = simplified; } - const formula* - star_normal_form(const formula* f) + formula + star_normal_form(formula f) { return ltl::star_normal_form(f, &snf_cache_); } - const formula* - star_normal_form_bounded(const formula* f) + formula + star_normal_form_bounded(formula f) { return ltl::star_normal_form_bounded(f, &snfb_cache_); } - const formula* - boolean_to_isop(const formula* f) + formula + boolean_to_isop(formula f) { f2f_map::const_iterator it = bool_isop_.find(f); if (it != bool_isop_.end()) - return it->second->clone(); + return it->second; - assert(f->is_boolean()); - const formula* res = bdd_to_formula(as_bdd(f), dict); - bool_isop_[f->clone()] = res->clone(); + assert(f.is_boolean()); + formula res = bdd_to_formula(as_bdd(f), dict); + bool_isop_[f] = res; return res; } @@ -432,310 +316,223 @@ namespace spot { ////////////////////////////////////////////////////////////////////// // - // NEGATIVE_NORMAL_FORM_VISITOR + // NEGATIVE_NORMAL_FORM // ////////////////////////////////////////////////////////////////////// - // Forward declaration. - const formula* - nenoform_recursively(const formula* f, - bool negated, - ltl_simplifier_cache* c); + formula + nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c); - class negative_normal_form_visitor: public visitor - { - public: - negative_normal_form_visitor(bool negated, ltl_simplifier_cache* c) - : negated_(negated), cache_(c) - { - } - - virtual - ~negative_normal_form_visitor() - { - } - - const formula* result() const - { - return result_; - } - - void - visit(const atomic_prop* ap) - { - const formula* f = ap->clone(); - if (negated_) - result_ = unop::instance(unop::Not, f); - else - result_ = f; - } - - void - visit(const constant* c) - { - // Negation of constants is taken care of in the constructor - // of unop::Not, so these cases should be caught by - // nenoform_recursively(). - assert(!negated_); - result_ = c; - return; - } - - void - visit(const unop* uo) - { - const formula* f = uo->child(); - unop::type op = uo->op(); - switch (op) - { - case unop::Not: - // "Not"s should be caught by nenoform_recursively(). - SPOT_UNREACHABLE(); - case unop::X: - /* !Xa == X!a */ - result_ = unop::instance(unop::X, recurse(f)); - return; - case unop::F: - /* !Fa == G!a */ - result_ = unop::instance(negated_ ? unop::G : unop::F, - recurse(f)); - return; - case unop::G: - /* !Ga == F!a */ - result_ = unop::instance(negated_ ? unop::F : unop::G, - recurse(f)); - return; - case unop::Closure: - result_ = unop::instance(negated_ ? - unop::NegClosure : unop::Closure, - recurse_(f, false)); - return; - case unop::NegClosure: - case unop::NegClosureMarked: - result_ = unop::instance(negated_ ? - unop::Closure : op, - recurse_(f, false)); - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const bunop* bo) - { - // !(a*) should never occur. - assert(!negated_); - result_ = bunop::instance(bo->op(), recurse_(bo->child(), false), - bo->min(), bo->max()); - } - - const formula* equiv_or_xor(bool equiv, - const formula* f1, - const formula* f2) - { - if (equiv) - { - // Rewrite a<=>b as (a&b)|(!a&!b) - auto recurse_f1_true = recurse_(f1, true); - auto recurse_f1_false = recurse_(f1, false); - auto recurse_f2_true = recurse_(f2, true); - auto recurse_f2_false = recurse_(f2, false); - auto left = multop::instance(multop::And, - recurse_f1_false, - recurse_f2_false); - auto right = multop::instance(multop::And, - recurse_f1_true, - recurse_f2_true); - return multop::instance(multop::Or, left, right); - } - else - { - // Rewrite a^b as (a&!b)|(!a&b) - auto recurse_f1_true = recurse_(f1, true); - auto recurse_f1_false = recurse_(f1, false); - auto recurse_f2_true = recurse_(f2, true); - auto recurse_f2_false = recurse_(f2, false); - auto left = multop::instance(multop::And, - recurse_f1_false, - recurse_f2_true); - auto right = multop::instance(multop::And, - recurse_f1_true, - recurse_f2_false); - return multop::instance(multop::Or, left, right); - } - } - - void - visit(const binop* bo) - { - const formula* f1 = bo->first(); - const formula* f2 = bo->second(); - switch (bo->op()) - { - case binop::Xor: - // !(a ^ b) == a <=> b - result_ = equiv_or_xor(negated_, f1, f2); - return; - case binop::Equiv: - // !(a <=> b) == a ^ b - result_ = equiv_or_xor(!negated_, f1, f2); - return; - case binop::Implies: - if (negated_) - // !(a => b) == a & !b - result_ = multop::instance(multop::And, - recurse_(f1, false), - recurse_(f2, true)); - else // a => b == !a | b - result_ = multop::instance(multop::Or, - recurse_(f1, true), - recurse_(f2, false)); - return; - case binop::U: - // !(a U b) == !a R !b - result_ = binop::instance(negated_ ? binop::R : binop::U, - recurse(f1), recurse(f2)); - return; - case binop::R: - // !(a R b) == !a U !b - result_ = binop::instance(negated_ ? binop::U : binop::R, - recurse(f1), recurse(f2)); - return; - case binop::W: - // !(a W b) == !a M !b - result_ = binop::instance(negated_ ? binop::M : binop::W, - recurse(f1), recurse(f2)); - return; - case binop::M: - // !(a M b) == !a W !b - result_ = binop::instance(negated_ ? binop::W : binop::M, - recurse(f1), recurse(f2)); - return; - case binop::UConcat: - // !(a []-> b) == a<>-> !b - result_ = binop::instance(negated_ ? - binop::EConcat : binop::UConcat, - recurse_(f1, false), recurse(f2)); - return; - case binop::EConcat: - // !(a <>-> b) == a[]-> !b - result_ = binop::instance(negated_ ? - binop::UConcat : binop::EConcat, - recurse_(f1, false), recurse(f2)); - return; - case binop::EConcatMarked: - // !(a <>-> b) == a[]-> !b - result_ = binop::instance(negated_ ? - binop::UConcat : - binop::EConcatMarked, - recurse_(f1, false), recurse(f2)); - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const multop* mo) - { - multop::type op = mo->op(); - /* !(a & b & c) == !a | !b | !c */ - /* !(a | b | c) == !a & !b & !c */ - if (negated_) - switch (op) - { - case multop::And: - op = multop::Or; - break; - case multop::Or: - op = multop::And; - break; - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::OrRat: - case multop::AndRat: - break; - } - multop::vec* res = new multop::vec; - unsigned mos = mo->size(); - switch (op) - { - case multop::And: - case multop::Or: - { - for (unsigned i = 0; i < mos; ++i) - res->push_back(recurse(mo->nth(i))); - result_ = multop::instance(op, res); - break; - } - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: - { - for (unsigned i = 0; i < mos; ++i) - res->push_back(recurse_(mo->nth(i), false)); - result_ = multop::instance(op, res); - assert(!negated_); - } - } - } - - const formula* - recurse_(const formula* f, bool negated) - { - return nenoform_recursively(f, negated, cache_); - } - - const formula* - recurse(const formula* f) - { - return recurse_(f, negated_); - } - - protected: - const formula* result_; - bool negated_; - ltl_simplifier_cache* cache_; - }; - - - const formula* - nenoform_recursively(const formula* f, - bool negated, + formula equiv_or_xor(bool equiv, formula f1, formula f2, ltl_simplifier_cache* c) { - if (const unop* uo = is_Not(f)) + auto rec = [c](formula f, bool negated) { - negated = !negated; - f = uo->child(); - } + return nenoform_rec(f, negated, c); + }; - const formula* key = f; - if (negated) - key = unop::instance(unop::Not, f->clone()); - const formula* result = c->lookup_nenoform(key); - if (result) - goto done; - - if (key->is_in_nenoform() - || (c->options.nenoform_stop_on_boolean && key->is_boolean())) + if (equiv) { - result = key->clone(); + // Rewrite a<=>b as (a&b)|(!a&!b) + auto recurse_f1_true = rec(f1, true); + auto recurse_f1_false = rec(f1, false); + auto recurse_f2_true = rec(f2, true); + auto recurse_f2_false = rec(f2, false); + auto left = formula::And({recurse_f1_false, recurse_f2_false}); + auto right = formula::And({recurse_f1_true, recurse_f2_true}); + return formula::Or({left, right}); } else { - negative_normal_form_visitor v(negated, c); - f->accept(v); - result = v.result(); + // Rewrite a^b as (a&!b)|(!a&b) + auto recurse_f1_true = rec(f1, true); + auto recurse_f1_false = rec(f1, false); + auto recurse_f2_true = rec(f2, true); + auto recurse_f2_false = rec(f2, false); + auto left = formula::And({recurse_f1_false, recurse_f2_true}); + auto right = formula::And({recurse_f1_true, recurse_f2_false}); + return formula::Or({left, right}); + } + } + + formula + nenoform_rec(formula f, bool negated, ltl_simplifier_cache* c) + { + if (f.is(op::Not)) + { + negated = !negated; + f = f.nth(0); + } + + formula key = f; + if (negated) + key = formula::Not(f); + formula result = c->lookup_nenoform(key); + if (result) + return result; + + if (key.is_in_nenoform() + || (c->options.nenoform_stop_on_boolean && key.is_boolean())) + { + result = key; + } + else + { + auto rec = [c](formula f, bool neg) + { + return nenoform_rec(f, neg, c); + }; + + switch (op o = f.kind()) + { + case op::False: + case op::True: + // Negation of constants is taken care of in the + // constructor of unop::Not, so these cases should be + // caught by nenoform_recursively(). + assert(!negated); + result = f; + break; + case op::AP: + result = negated ? formula::Not(f) : f; + break; + case op::X: + // !Xa == X!a + result = formula::X(rec(f.nth(0), negated)); + break; + case op::F: + // !Fa == G!a + result = formula::unop(negated ? op::G : op::F, + rec(f.nth(0), negated)); + break; + case op::G: + // !Ga == F!a + result = formula::unop(negated ? op::F : op::G, + rec(f.nth(0), negated)); + break; + case op::Closure: + result = formula::unop(negated ? + op::NegClosure : op::Closure, + rec(f.nth(0), false)); + break; + case op::NegClosure: + case op::NegClosureMarked: + result = formula::unop(negated ? op::Closure : o, + rec(f.nth(0), false)); + break; + + case op::Implies: + if (negated) + // !(a => b) == a & !b + { + auto f2 = rec(f.nth(1), true); + result = formula::And({rec(f.nth(0), false), f2}); + } + else // a => b == !a | b + { + auto f2 = rec(f.nth(1), false); + result = formula::Or({rec(f.nth(0), true), f2}); + } + break; + case op::Xor: + { + // !(a ^ b) == a <=> b + result = equiv_or_xor(negated, f.nth(0), f.nth(1), c); + break; + } + case op::Equiv: + { + // !(a <=> b) == a ^ b + result = equiv_or_xor(!negated, f.nth(0), f.nth(1), c); + break; + } + case op::U: + { + // !(a U b) == !a R !b + auto f1 = rec(f.nth(0), negated); + auto f2 = rec(f.nth(1), negated); + result = formula::binop(negated ? op::R : op::U, f1, f2); + break; + } + case op::R: + { + // !(a R b) == !a U !b + auto f1 = rec(f.nth(0), negated); + auto f2 = rec(f.nth(1), negated); + result = formula::binop(negated ? op::U : op::R, f1, f2); + break; + } + case op::W: + { + // !(a W b) == !a M !b + auto f1 = rec(f.nth(0), negated); + auto f2 = rec(f.nth(1), negated); + result = formula::binop(negated ? op::M : op::W, f1, f2); + break; + } + case op::M: + { + // !(a M b) == !a W !b + auto f1 = rec(f.nth(0), negated); + auto f2 = rec(f.nth(1), negated); + result = formula::binop(negated ? op::W : op::M, f1, f2); + break; + } + case op::Or: + case op::And: + { + unsigned mos = f.size(); + vec v; + for (unsigned i = 0; i < mos; ++i) + v.push_back(rec(f.nth(i), negated)); + op on = o; + if (negated) + on = o == op::Or ? op::And : op::Or; + result = formula::multop(on, v); + break; + } + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + // !(a*) etc. should never occur. + { + assert(!negated); + result = f.map([c](formula f) + { + return nenoform_rec(f, false, c); + }); + break; + } + case op::EConcat: + case op::EConcatMarked: + { + // !(a <>-> b) == a[]-> !b + auto f1 = f.nth(0); + auto f2 = f.nth(1); + result = formula::binop(negated ? op::UConcat : o, + rec(f1, false), rec(f2, negated)); + break; + } + case op::UConcat: + { + // !(a []-> b) == a<>-> !b + auto f1 = f.nth(0); + auto f2 = f.nth(1); + result = formula::binop(negated ? op::EConcat : op::UConcat, + rec(f1, false), rec(f2, negated)); + break; + } + case op::EmptyWord: + case op::Not: + SPOT_UNREACHABLE(); + } } c->cache_nenoform(key, result); - done: - if (negated) - key->destroy(); - return result; } @@ -746,108 +543,97 @@ namespace spot ////////////////////////////////////////////////////////////////////// // Forward declaration. - const formula* - simplify_recursively(const formula* f, ltl_simplifier_cache* c); - - + formula + simplify_recursively(formula f, ltl_simplifier_cache* c); // X(a) R b or X(a) M b // This returns a. - const formula* - is_XRM(const formula* f) + formula + is_XRM(formula f) { - const binop* bo = is_binop(f, binop::R, binop::M); - if (!bo) - return 0; - const unop* uo = is_X(bo->first()); - if (!uo) - return 0; - return uo->child(); + if (!f.is(op::R, op::M)) + return nullptr; + auto left = f.nth(0); + if (!left.is(op::X)) + return nullptr; + return left.nth(0); } // X(a) W b or X(a) U b // This returns a. - const formula* - is_XWU(const formula* f) + formula + is_XWU(formula f) { - const binop* bo = is_binop(f, binop::W, binop::U); - if (!bo) - return 0; - const unop* uo = is_X(bo->first()); - if (!uo) - return 0; - return uo->child(); + if (!f.is(op::W, op::U)) + return nullptr; + auto left = f.nth(0); + if (!left.is(op::X)) + return nullptr; + return left.nth(0); } // b & X(b W a) or b & X(b U a) // This returns (b W a) or (b U a). - const binop* - is_bXbWU(const formula* f) + formula + is_bXbWU(formula f) { - const multop* mo = is_multop(f, multop::And); - if (!mo) - return 0; - unsigned s = mo->size(); + if (!f.is(op::And)) + return nullptr; + unsigned s = f.size(); for (unsigned pos = 0; pos < s; ++pos) { - const unop* u = is_X(mo->nth(pos)); - if (!u) + auto p = f.nth(pos); + if (!(p.is(op::X))) continue; - const binop* bo = is_binop(u->child(), binop::U, binop::W); - if (!bo) + auto c = p.nth(0); + if (!c.is(op::U, op::W)) continue; - const formula* b = mo->all_but(pos); - bool result = (b == bo->first()); - b->destroy(); - if (result) - return bo; + formula b = f.all_but(pos); + if (b == c.nth(0)) + return c; } - return 0; + return nullptr; } // b | X(b R a) or b | X(b M a) // This returns (b R a) or (b M a). - const binop* - is_bXbRM(const formula* f) + formula + is_bXbRM(formula f) { - const multop* mo = is_multop(f, multop::Or); - if (!mo) - return 0; - unsigned s = mo->size(); + if (!f.is(op::Or)) + return nullptr; + unsigned s = f.size(); for (unsigned pos = 0; pos < s; ++pos) { - const unop* u = is_X(mo->nth(pos)); - if (!u) + auto p = f.nth(pos); + if (!(p.is(op::X))) continue; - const binop* bo = is_binop(u->child(), binop::R, binop::M); - if (!bo) + auto c = p.nth(0); + if (!c.is(op::R, op::M)) continue; - const formula* b = mo->all_but(pos); - bool result = (b == bo->first()); - b->destroy(); - if (result) - return bo; + formula b = f.all_but(pos); + if (b == c.nth(0)) + return c; } - return 0; + return nullptr; } - const formula* - unop_multop(unop::type uop, multop::type mop, multop::vec* v) + formula + unop_multop(op uop, op mop, vec v) { - return unop::instance(uop, multop::instance(mop, v)); + return formula::unop(uop, formula::multop(mop, v)); } - const formula* - unop_unop_multop(unop::type uop1, unop::type uop2, multop::type mop, - multop::vec* v) + formula + unop_unop_multop(op uop1, op uop2, op mop, vec v) { - return unop::instance(uop1, unop_multop(uop2, mop, v)); + return formula::unop(uop1, unop_multop(uop2, mop, v)); } - const formula* - unop_unop(unop::type uop1, unop::type uop2, const formula* f) + formula + unop_unop(op uop1, op uop2, formula f) { - return unop::instance(uop1, unop::instance(uop2, f)); + return formula::unop(uop1, formula::unop(uop2, f)); } struct mospliter @@ -869,107 +655,115 @@ namespace spot Split_Bool = (1 << 14) }; - void init() + private: + mospliter(unsigned split, ltl_simplifier_cache* cache) + : split_(split), c_(cache), + res_GF{(split_ & Split_GF) ? new vec : nullptr}, + res_FG{(split_ & Split_FG) ? new vec : nullptr}, + res_F{(split_ & Split_F) ? new vec : nullptr}, + res_G{(split_ & Split_G) ? new vec : nullptr}, + res_X{(split_ & Strip_X) ? new vec : nullptr}, + res_U_or_W{(split_ & Split_U_or_W) ? new vec : nullptr}, + res_R_or_M{(split_ & Split_R_or_M) ? new vec : nullptr}, + res_Event{(split_ & Split_Event) ? new vec : nullptr}, + res_Univ{(split_ & Split_Univ) ? new vec : nullptr}, + res_EventUniv{(split_ & Split_EventUniv) ? new vec : nullptr}, + res_Bool{(split_ & Split_Bool) ? new vec : nullptr}, + res_other{new vec} { - res_GF = (split_ & Split_GF) ? new multop::vec : 0; - res_FG = (split_ & Split_FG) ? new multop::vec : 0; - res_F = (split_ & Split_F) ? new multop::vec : 0; - res_G = (split_ & Split_G) ? new multop::vec : 0; - res_X = (split_ & Strip_X) ? new multop::vec : 0; - res_U_or_W = (split_ & Split_U_or_W) ? new multop::vec : 0; - res_R_or_M = (split_ & Split_R_or_M) ? new multop::vec : 0; - res_EventUniv = (split_ & Split_EventUniv) ? new multop::vec : 0; - res_Event = (split_ & Split_Event) ? new multop::vec : 0; - res_Univ = (split_ & Split_Univ) ? new multop::vec : 0; - res_Bool = (split_ & Split_Bool) ? new multop::vec : 0; - res_other = new multop::vec; } - void process(const formula* f) + public: + mospliter(unsigned split, vec v, ltl_simplifier_cache* cache) + : mospliter(split, cache) { - bool e = f->is_eventual(); - bool u = f->is_universal(); - bool eu = res_EventUniv && e & u && c_->options.favor_event_univ; - switch (f->kind()) + for (auto f: v) { - case formula::UnOp: - { - const unop* uo = static_cast(f); - const formula* c = uo->child(); - switch (uo->op()) - { - case unop::X: - if (res_X && !eu) - { - res_X->push_back(c->clone()); - return; - } - break; - case unop::F: - if (res_FG && u) - if (const unop* cc = is_G(c)) - { - res_FG->push_back(((split_ & Strip_FG) == Strip_FG - ? cc->child() : f)->clone()); - return; - } - if (res_F && !eu) - { - res_F->push_back(((split_ & Strip_F) == Strip_F - ? c : f)->clone()); - return; - } - break; - case unop::G: - if (res_GF && e) - if (const unop* cc = is_F(c)) - { - res_GF->push_back(((split_ & Strip_GF) == Strip_GF - ? cc->child() : f)->clone()); - return; - } - if (res_G && !eu) - { - res_G->push_back(((split_ & Strip_G) == Strip_G - ? c : f)->clone()); - return; - } - break; - default: - break; - } - } + if (f) // skip null pointers left by previous simplifications + process(f); + } + } + + mospliter(unsigned split, formula mo, + ltl_simplifier_cache* cache) + : mospliter(split, cache) + { + unsigned mos = mo.size(); + for (unsigned i = 0; i < mos; ++i) + { + formula f = simplify_recursively(mo.nth(i), cache); + process(f); + } + } + + void process(formula f) + { + bool e = f.is_eventual(); + bool u = f.is_universal(); + bool eu = res_EventUniv && e & u && c_->options.favor_event_univ; + switch (f.kind()) + { + case op::X: + if (res_X && !eu) + { + res_X->push_back(f.nth(0)); + return; + } break; - case formula::BinOp: + case op::F: { - const binop* bo = static_cast(f); - switch (bo->op()) + formula c = f.nth(0); + if (res_FG && u && c.is(op::G)) { - case binop::U: - case binop::W: - if (res_U_or_W) - { - res_U_or_W->push_back(bo->clone()); - return; - } - break; - case binop::R: - case binop::M: - if (res_R_or_M) - { - res_R_or_M->push_back(bo->clone()); - return; - } - break; - default: - break; + res_FG->push_back(((split_ & Strip_FG) == Strip_FG + ? c.nth(0) : f)); + return; } + if (res_F && !eu) + { + res_F->push_back(((split_ & Strip_F) == Strip_F + ? c : f)); + return; + } + break; } + case op::G: + { + formula c = f.nth(0); + if (res_GF && e && c.is(op::F)) + { + res_GF->push_back(((split_ & Strip_GF) == Strip_GF + ? c.nth(0) : f)); + return; + } + if (res_G && !eu) + { + res_G->push_back(((split_ & Strip_G) == Strip_G + ? c : f)); + return; + } + break; + } + case op::U: + case op::W: + if (res_U_or_W) + { + res_U_or_W->push_back(f); + return; + } + break; + case op::R: + case op::M: + if (res_R_or_M) + { + res_R_or_M->push_back(f); + return; + } break; default: - if (res_Bool && f->is_boolean()) + if (res_Bool && f.is_boolean()) { - res_Bool->push_back(f->clone()); + res_Bool->push_back(f); return; } break; @@ -978,71 +772,40 @@ namespace spot { if (res_EventUniv && e && u) { - res_EventUniv->push_back(f->clone()); + res_EventUniv->push_back(f); return; } if (res_Event && e) { - res_Event->push_back(f->clone()); + res_Event->push_back(f); return; } if (res_Univ && u) { - res_Univ->push_back(f->clone()); + res_Univ->push_back(f); return; } } - - res_other->push_back(f->clone()); + res_other->push_back(f); } - mospliter(unsigned split, multop::vec* v, ltl_simplifier_cache* cache) - : split_(split), c_(cache) - { - init(); - for (auto f: *v) - { - if (f) // skip null pointers left by previous simplifications - { - process(f); - f->destroy(); - } - } - delete v; - } - - mospliter(unsigned split, const multop* mo, - ltl_simplifier_cache* cache) - : split_(split), c_(cache) - { - init(); - unsigned mos = mo->size(); - for (unsigned i = 0; i < mos; ++i) - { - const formula* f = simplify_recursively(mo->nth(i), cache); - process(f); - f->destroy(); - } - mo->destroy(); - } - - multop::vec* res_GF; - multop::vec* res_FG; - multop::vec* res_F; - multop::vec* res_G; - multop::vec* res_X; - multop::vec* res_U_or_W; - multop::vec* res_R_or_M; - multop::vec* res_Event; - multop::vec* res_Univ; - multop::vec* res_EventUniv; - multop::vec* res_Bool; - multop::vec* res_other; unsigned split_; ltl_simplifier_cache* c_; + std::unique_ptr res_GF; + std::unique_ptr res_FG; + std::unique_ptr res_F; + std::unique_ptr res_G; + std::unique_ptr res_X; + std::unique_ptr res_U_or_W; + std::unique_ptr res_R_or_M; + std::unique_ptr res_Event; + std::unique_ptr res_Univ; + std::unique_ptr res_EventUniv; + std::unique_ptr res_Bool; + std::unique_ptr res_other; }; - class simplify_visitor: public visitor + class simplify_visitor final { public: @@ -1051,858 +814,641 @@ namespace spot { } - virtual ~simplify_visitor() - { - } - - const formula* - result() const - { - return result_; - } - - void - visit(const atomic_prop* ap) - { - result_ = ap->clone(); - } - - void - visit(const constant* c) - { - result_ = c; - } - - void - visit(const bunop* bo) - { - bunop::type op = bo->op(); - unsigned min = bo->min(); - const formula* h = recurse(bo->child()); - switch (op) - { - case bunop::Star: - if (h->accepts_eword()) - min = 0; - if (min == 0) - { - const formula* s = - bo->max() == bunop::unbounded ? - c_->star_normal_form(h) : - c_->star_normal_form_bounded(h); - h->destroy(); - h = s; - } - result_ = bunop::instance(op, h, min, bo->max()); - break; - case bunop::FStar: - result_ = bunop::instance(op, h, min, bo->max()); - break; - } - } - // if !neg build c&X(c&X(...&X(tail))) with n occurences of c // if neg build !c|X(!c|X(...|X(tail))). - const formula* - dup_b_x_tail(bool neg, const formula* c, - const formula* tail, unsigned n) + formula + dup_b_x_tail(bool neg, formula c, formula tail, unsigned n) { - c->clone(); - multop::type mop; + op mop; if (neg) { - c = unop::instance(unop::Not, c); - mop = multop::Or; + c = formula::Not(c); + mop = op::Or; } else { - mop = multop::And; + mop = op::And; } while (n--) - { - tail = unop::instance(unop::X, tail); - tail = // b&X(tail) or !b|X(tail) - multop::instance(mop, c->clone(), tail); - } - c->destroy(); + tail = // b&X(tail) or !b|X(tail) + formula::multop(mop, {c, formula::X(tail)}); return tail; } - void - visit(const unop* uo) + formula + visit(formula f) { - result_ = recurse(uo->child()); + formula result = f; - unop::type op = uo->op(); - switch (op) + auto recurse = [this](formula f) { - case unop::Not: - break; + return simplify_recursively(f, c_); + }; - case unop::X: - // X(constant) = constant is a trivial identity, but if - // the constant has been constructed by recurse() this - // identity has not been applied. - if (is_constant(result_)) - return; + f = f.map(recurse); - // Xf = f if f is both eventual and universal. - if (result_->is_universal() && result_->is_eventual()) + switch (op o = f.kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + case op::Not: + case op::FStar: + return f; + case op::X: { - if (opt_.event_univ) - return; - // If EventUniv simplification is disabled, use - // only the following basic rewriting rules: - // XGF(f) = GF(f) and XFG(f) = FG(f) - // The former comes from Somenzi&Bloem (CAV'00). - // It's not clear why they do not list the second. - if (opt_.reduce_basics && - (is_GF(result_) || is_FG(result_))) - return; + formula c = f.nth(0); + // Xf = f if f is both eventual and universal. + if (c.is_universal() && c.is_eventual()) + { + if (opt_.event_univ) + return c; + // If EventUniv simplification is disabled, use + // only the following basic rewriting rules: + // XGF(f) = GF(f) and XFG(f) = FG(f) + // The former comes from Somenzi&Bloem (CAV'00). + // It's not clear why they do not list the second. + if (opt_.reduce_basics && + (c.is({op::G, op::F}) || c.is({op::F, op::G}))) + return c; + } + + // If Xa = a, keep only a. + if (opt_.containment_checks_stronger + && c_->lcc.equal(f, c)) + return c; + + // X(f1 & GF(f2)) = X(f1) & GF(f2) + // X(f1 | GF(f2)) = X(f1) | GF(f2) + // X(f1 & FG(f2)) = X(f1) & FG(f2) + // X(f1 | FG(f2)) = X(f1) | FG(f2) + // + // The above usually make more sense when reversed (see + // them in the And and Or rewritings), except when we + // try to maximaze the size of subformula that do not + // have EventUniv formulae. + if (opt_.favor_event_univ) + if (c.is(op::Or, op::And)) + { + mospliter s(mospliter::Split_EventUniv, c, c_); + op oc = c.kind(); + s.res_EventUniv-> + push_back(unop_multop(op::X, oc, + std::move(*s.res_other))); + formula result = + formula::multop(oc, + std::move(*s.res_EventUniv)); + if (result != f) + result = recurse(result); + return result; + } + return f; } - - - // If Xa = a, keep only a. - if (opt_.containment_checks_stronger - && c_->lcc.equal(result_, uo)) - return; - - // X(f1 & GF(f2)) = X(f1) & GF(f2) - // X(f1 | GF(f2)) = X(f1) | GF(f2) - // X(f1 & FG(f2)) = X(f1) & FG(f2) - // X(f1 | FG(f2)) = X(f1) | FG(f2) - // - // The above usually make more sense when reversed (see - // them in the And and Or rewritings), except when we - // try to maximaze the size of subformula that do not - // have EventUniv formulae. - if (opt_.favor_event_univ) - if (const multop* mo = is_multop(result_, - multop::Or, multop::And)) - { - mospliter s(mospliter::Split_EventUniv, mo, c_); - multop::type op = mo->op(); - s.res_EventUniv->push_back(unop_multop(unop::X, op, - s.res_other)); - result_ = multop::instance(op, s.res_EventUniv); - if (result_ != uo) - result_ = recurse_destroy(result_); - return; - } - break; - - case unop::F: - // F(constant) = constant is a trivial identity, but if - // the constant has been constructed by recurse() this - // identity has not been applied. - if (is_constant(result_)) - return; - - // If f is a pure eventuality formula then F(f)=f. - if (opt_.event_univ && result_->is_eventual()) - return; - - if (opt_.reduce_basics) + case op::F: { - // F(a U b) = F(b) - const binop* bo = is_U(result_); - if (bo) - { - const formula* r = - unop::instance(unop::F, bo->second()->clone()); - bo->destroy(); - result_ = recurse_destroy(r); - return; - } + formula c = f.nth(0); + // If f is a pure eventuality formula then F(f)=f. + if (opt_.event_univ && c.is_eventual()) + return c; - // F(a M b) = F(a & b) - bo = is_M(result_); - if (bo) + if (opt_.reduce_basics) { - const formula* r = - unop::instance(unop::F, - multop::instance(multop::And, - bo->first()->clone(), - bo->second()->clone())); - bo->destroy(); - result_ = recurse_destroy(r); - return; - } + // F(a U b) = F(b) + if (c.is(op::U)) + return recurse(formula::F(c.nth(1))); - // FX(a) = XF(a) - if (const unop* u = is_X(result_)) - { - const formula* res = - unop_unop(unop::X, unop::F, u->child()->clone()); - u->destroy(); + // F(a M b) = F(a & b) + if (c.is(op::M)) + return recurse(unop_multop(op::F, op::And, + {c.nth(0), c.nth(1)})); + + // FX(a) = XF(a) // FXX(a) = XXF(a) ... // FXG(a) = XFG(a) = FG(a) ... - result_ = recurse_destroy(res); - return; - } + if (c.is(op::X)) + return recurse(unop_unop(op::X, op::F, c.nth(0))); - // FG(a & Xb) = FG(a & b) - // FG(a & Gb) = FG(a & b) - if (const unop* g = is_G(result_)) - if (const multop* m = is_And(g->child())) - if (!m->is_boolean()) + // FG(a & Xb) = FG(a & b) + // FG(a & Gb) = FG(a & b) + if (c.is({op::G, op::And})) { - m->clone(); - mospliter s(mospliter::Strip_G | mospliter::Strip_X, - m, c_); - if (!s.res_G->empty() || !s.res_X->empty()) + formula m = c.nth(0); + if (!m.is_boolean()) { - result_->destroy(); - s.res_other->insert(s.res_other->begin(), - s.res_G->begin(), - s.res_G->end()); - delete s.res_G; - s.res_other->insert(s.res_other->begin(), - s.res_X->begin(), - s.res_X->end()); - delete s.res_X; - const formula* in = - multop::instance(multop::And, s.res_other); - result_ = - recurse_destroy(unop_unop(unop::F, unop::G, - in)); - return; - } - else - { - for (auto f: *s.res_other) - if (f) - f->destroy(); - delete s.res_other; - delete s.res_G; - delete s.res_X; - // and continue... + formula out = m.map([](formula f) + { + if (f.is(op::X, op::G)) + return f.nth(0); + return f; + }); + if (out != m) + return recurse(unop_unop(op::F, op::G, out)); } } - } + } + // if Fa => a, keep a. + if (opt_.containment_checks_stronger + && c_->lcc.contained(f, c)) + return c; - // if Fa => a, keep a. - if (opt_.containment_checks_stronger - && c_->lcc.contained(uo, result_)) - return; - - // Disabled by default: - // F(f1 & GF(f2)) = F(f1) & GF(f2) - // - // As is, these two formulae are translated into - // equivalent Büchi automata so the rewriting is - // useless. - // - // However when taken in a larger formula such as F(f1 - // & GF(f2)) | F(a & GF(b)), this rewriting used to - // produce (F(f1) & GF(f2)) | (F(a) & GF(b)), missing - // the opportunity to apply the F(E1)|F(E2) = F(E1|E2) - // rule which really helps the translation. F((f1 & - // GF(f2)) | (a & GF(b))) is indeed easier to translate. - // - // So we do not consider this rewriting rule by default. - // However if favor_event_univ is set, we want to move - // the GF out of the F. - if (opt_.favor_event_univ) - // F(f1&f2&FG(f3)&FG(f4)&f5&f6) = - // F(f1&f2) & FG(f3&f4) & f5 & f6 - // if f5 and f6 are both eventual and universal. - if (const multop* mo = is_And(result_)) - { - mo->clone(); - mospliter s(mospliter::Strip_FG | - mospliter::Split_EventUniv, - mo, c_); - s.res_EventUniv-> - push_back(unop_multop(unop::F, multop::And, - s.res_other)); - s.res_EventUniv-> - push_back(unop_unop_multop(unop::F, unop::G, - multop::And, s.res_FG)); - result_ = multop::instance(multop::And, s.res_EventUniv); - if (result_ != uo) - { - mo->destroy(); - result_ = recurse_destroy(result_); - return; - } - else - { - // Revert to the previous value of result_, - // for the next simplification. - result_->destroy(); - result_ = mo; - } - } - // If u3 and u4 are universal formulae and h is not: - // F(f1 | f2 | Fu3 | u4 | FGg | Fh) - // = F(f1 | f2 | u3 | u4 | Gg | h) - // or - // F(f1 | f2 | Fu3 | u4 | FGg | Fh) - // = F(f1 | f2 | h) | F(u3 | u4 | Gg) - // depending on whether favor_event_univ is set. - if (const multop* mo = is_Or(result_)) - { - mo->clone(); - int w = mospliter::Strip_F; + // Disabled by default: + // F(f1 & GF(f2)) = F(f1) & GF(f2) + // + // As is, these two formulae are translated into + // equivalent Büchi automata so the rewriting is + // useless. + // + // However when taken in a larger formula such + // as F(f1 & GF(f2)) | F(a & GF(b)), this + // rewriting used to produce (F(f1) & GF(f2)) | + // (F(a) & GF(b)), missing the opportunity to + // apply the F(E1)|F(E2) = F(E1|E2) rule which + // really helps the translation. F((f1 & GF(f2)) + // | (a & GF(b))) is indeed easier to translate. + // + // So we do not consider this rewriting rule by + // default. However if favor_event_univ is set, + // we want to move the GF out of the F. if (opt_.favor_event_univ) - w |= mospliter::Split_Univ; - mospliter s(w, mo, c_); - s.res_other->insert(s.res_other->end(), - s.res_F->begin(), s.res_F->end()); - delete s.res_F; - result_ = unop_multop(unop::F, multop::Or, s.res_other); - if (s.res_Univ) + // F(f1&f2&FG(f3)&FG(f4)&f5&f6) = + // F(f1&f2) & FG(f3&f4) & f5 & f6 + // if f5 and f6 are both eventual and universal. + if (c.is(op::And)) + { + mospliter s(mospliter::Strip_FG | + mospliter::Split_EventUniv, + c, c_); + s.res_EventUniv-> + push_back(unop_multop(op::F, op::And, + std::move(*s.res_other))); + s.res_EventUniv-> + push_back(unop_unop_multop(op::F, op::G, op::And, + std::move(*s.res_FG))); + formula res = + formula::And(std::move(*s.res_EventUniv)); + if (res != f) + return recurse(res); + } + // If u3 and u4 are universal formulae and h is not: + // F(f1 | f2 | Fu3 | u4 | FGg | Fh) + // = F(f1 | f2 | u3 | u4 | Gg | h) + // or + // F(f1 | f2 | Fu3 | u4 | FGg | Fh) + // = F(f1 | f2 | h) | F(u3 | u4 | Gg) + // depending on whether favor_event_univ is set. + if (c.is(op::Or)) { - // Strip any F. - for (auto& f: *s.res_Univ) - if (const unop* u = is_F(f)) - { - f = u->child()->clone(); - u->destroy(); - } - const formula* fu = - unop_multop(unop::F, multop::Or, s.res_Univ); - result_ = multop::instance(multop::Or, result_, fu); - } - if (result_ != uo) - { - mo->destroy(); - result_ = recurse_destroy(result_); - return; - } - else - { - // Revert to the previous value of result_, - // for the next simplification. - result_->destroy(); - result_ = mo; + int w = mospliter::Strip_F; + if (opt_.favor_event_univ) + w |= mospliter::Split_Univ; + mospliter s(w, c, c_); + s.res_other->insert(s.res_other->end(), + s.res_F->begin(), s.res_F->end()); + formula res = unop_multop(op::F, op::Or, + std::move(*s.res_other)); + if (s.res_Univ) + { + // Strip any F. + for (auto& g: *s.res_Univ) + if (g.is(op::F)) + g = g.nth(0); + formula fu = unop_multop(op::F, op::Or, + std::move(*s.res_Univ)); + res = formula::Or({res, fu}); + } + if (res != f) + return recurse(res); } } - break; - - case unop::G: - // G(constant) = constant is a trivial identity, but if - // the constant has been constructed by recurse() this - // identity has not been applied. - if (is_constant(result_)) - return; - - // If f is a pure universality formula then G(f)=f. - if (opt_.event_univ && result_->is_universal()) - return; - - if (opt_.reduce_basics) + return f; + case op::G: { - // G(a R b) = G(b) - const binop* bo = is_R(result_); - if (bo) - { - const formula* r = - unop::instance(unop::G, bo->second()->clone()); - bo->destroy(); - result_ = recurse_destroy(r); - return; - } + formula c = f.nth(0); + // If f is a pure universality formula then G(f)=f. + if (opt_.event_univ && c.is_universal()) + return c; - // G(a W b) = G(a | b) - bo = is_W(result_); - if (bo) + if (opt_.reduce_basics) { - const formula* r = - unop::instance(unop::G, - multop::instance(multop::Or, - bo->first()->clone(), - bo->second()->clone())); - bo->destroy(); - result_ = recurse_destroy(r); - return; - } + // G(a R b) = G(b) + if (c.is(op::R)) + return recurse(formula::G(c.nth(1))); - // GX(a) = XG(a) - if (const unop* u = is_X(result_)) - { - const formula* res = - unop_unop(unop::X, unop::G, u->child()->clone()); - u->destroy(); + // G(a W b) = G(a | b) + if (c.is(op::W)) + return recurse(unop_multop(op::G, op::Or, + {c.nth(0), c.nth(1)})); + + // GX(a) = XG(a) // GXX(a) = XXG(a) ... // GXF(a) = XGF(a) = GF(a) ... - result_ = recurse_destroy(res); - return; - } + if (c.is(op::X)) + return recurse(unop_unop(op::X, op::G, c.nth(0))); - // G(f1|f2|GF(f3)|GF(f4)|f5|f6) = - // G(f1|f2) | GF(f3|f4) | f5 | f6 - // if f5 and f6 are both eventual and universal. - if (const multop* mo = is_Or(result_)) - { - mo->clone(); - mospliter s(mospliter::Strip_GF | - mospliter::Split_EventUniv, - mo, c_); - s.res_EventUniv-> - push_back(unop_multop(unop::G, multop::Or, - s.res_other)); - s.res_EventUniv-> - push_back(unop_unop_multop(unop::G, unop::F, - multop::Or, s.res_GF)); - result_ = multop::instance(multop::Or, - s.res_EventUniv); - if (result_ != uo) + // G(f1|f2|GF(f3)|GF(f4)|f5|f6) = + // G(f1|f2) | GF(f3|f4) | f5 | f6 + // if f5 and f6 are both eventual and universal. + if (c.is(op::Or)) { - mo->destroy(); - result_ = recurse_destroy(result_); - return; - } - else - { - // Revert to the previous value of result_, - // for the next simplification. - result_->destroy(); - result_ = mo; - } - } - // If e3 and e4 are eventual formulae and h is not: - // G(f1 & f2 & Ge3 & e4 & GFg & Gh) - // = G(f1 & f2 & e3 & e4 & Fg & h) - // or - // G(f1 & f2 & Ge3 & e4 & GFg & Gh) - // = G(f1 & f2 & h) & G(e3 & e4 & Fg) - // depending on whether favor_event_univ is set. - else if (const multop* mo = is_And(result_)) - { - mo->clone(); - int w = mospliter::Strip_G; - if (opt_.favor_event_univ) - w |= mospliter::Split_Event; - mospliter s(w, mo, c_); - s.res_other->insert(s.res_other->end(), - s.res_G->begin(), s.res_G->end()); - delete s.res_G; - result_ = unop_multop(unop::G, multop::And, s.res_other); - if (s.res_Event) - { - // Strip any G. - for (auto& f: *s.res_Event) - if (const unop* u = is_G(f)) - { - f = u->child()->clone(); - u->destroy(); - } - const formula* ge = - unop_multop(unop::G, multop::And, s.res_Event); - result_ = multop::instance(multop::And, result_, ge); - } - if (result_ != uo) - { - mo->destroy(); - result_ = recurse_destroy(result_); - return; - } - else - { - // Revert to the previous value of result_, - // for the next simplification. - result_->destroy(); - result_ = mo; - } - } + mospliter s(mospliter::Strip_GF | + mospliter::Split_EventUniv, + c, c_); + s.res_EventUniv-> + push_back(unop_multop(op::G, op::Or, + std::move(*s.res_other))); + s.res_EventUniv-> + push_back(unop_unop_multop(op::G, op::F, op::Or, + std::move(*s.res_GF))); + formula res = + formula::Or(std::move(*s.res_EventUniv)); - // GF(a | Xb) = GF(a | b) - // GF(a | Fb) = GF(a | b) - if (const unop* f = is_F(result_)) - if (const multop* m = is_Or(f->child())) - if (!m->is_boolean()) + if (res != f) + return recurse(res); + } + // If e3 and e4 are eventual formulae and h is not: + // G(f1 & f2 & Ge3 & e4 & GFg & Gh) + // = G(f1 & f2 & e3 & e4 & Fg & h) + // or + // G(f1 & f2 & Ge3 & e4 & GFg & Gh) + // = G(f1 & f2 & h) & G(e3 & e4 & Fg) + // depending on whether favor_event_univ is set. + else if (c.is(op::And)) { - m->clone(); - mospliter s(mospliter::Strip_F | mospliter::Strip_X, - m, c_); - if (!s.res_F->empty() || !s.res_X->empty()) + int w = mospliter::Strip_G; + if (opt_.favor_event_univ) + w |= mospliter::Split_Event; + mospliter s(w, c, c_); + s.res_other->insert(s.res_other->end(), + s.res_G->begin(), s.res_G->end()); + formula res = unop_multop(op::G, op::And, + std::move(*s.res_other)); + + if (s.res_Event) { - result_->destroy(); - s.res_other->insert(s.res_other->begin(), - s.res_F->begin(), - s.res_F->end()); - delete s.res_F; - s.res_other->insert(s.res_other->begin(), - s.res_X->begin(), - s.res_X->end()); - delete s.res_X; - const formula* in = - multop::instance(multop::Or, s.res_other); - result_ = - recurse_destroy(unop_unop(unop::G, unop::F, - in)); - return; + // Strip any G. + for (auto& g: *s.res_Event) + if (g.is(op::G)) + g = g.nth(0); + formula ge = + unop_multop(op::G, op::And, + std::move(*s.res_Event)); + res = formula::And({res, ge}); } - else + if (res != f) + return recurse(res); + } + + // GF(a | Xb) = GF(a | b) + // GF(a | Fb) = GF(a | b) + if (c.is({op::F, op::Or})) + { + formula m = c.nth(0); + if (!m.is_boolean()) { - for (auto f: *s.res_other) - if (f) - f->destroy(); - delete s.res_other; - delete s.res_F; - delete s.res_X; - // and continue... + formula out = m.map([](formula f) + { + if (f.is(op::X, op::F)) + return f.nth(0); + return f; + }); + if (out != m) + return recurse(unop_unop(op::G, op::F, out)); } } + } + // if a => Ga, keep a. + if (opt_.containment_checks_stronger + && c_->lcc.contained(c, f)) + return c; } - // if a => Ga, keep a. - if (opt_.containment_checks_stronger - && c_->lcc.contained(result_, uo)) - return; - break; - case unop::Closure: - case unop::NegClosure: - case unop::NegClosureMarked: - // {e[*]} = {e} - // !{e[*]} = !{e} - if (result_->accepts_eword()) - if (const bunop* bo = is_Star(result_)) - { - result_ = - recurse_destroy(unop::instance(op, - bo->child()->clone())); - bo->destroy(); - return; - } - if (!opt_.reduce_size_strictly) - if (const multop* mo = is_OrRat(result_)) - { - // {a₁|a₂} = {a₁}| {a₂} - // !{a₁|a₂} = !{a₁}&!{a₂} - unsigned s = mo->size(); - multop::vec* v = new multop::vec; - for (unsigned n = 0; n < s; ++n) - v->push_back(unop::instance(op, mo->nth(n)->clone())); - mo->destroy(); - result_ = - recurse_destroy(multop::instance(op == unop::Closure - ? multop::Or - : multop::And, v)); - return; - } - if (const multop* mo = is_Concat(result_)) + return f; + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: { - if (mo->accepts_eword()) - { - if (opt_.reduce_size_strictly) - break; - // If all terms accept the empty word, we have - // {e₁;e₂;e₃} = {e₁}|{e₂}|{e₃} - // !{e₁;e₂;e₃} = !{e₁}&!{e₂}&!{e₃} - multop::vec* v = new multop::vec; - unsigned end = mo->size(); - v->reserve(end); - for (unsigned i = 0; i < end; ++i) - v->push_back(unop::instance(op, mo->nth(i)->clone())); - mo->destroy(); - result_ = multop::instance(op == unop::Closure ? - multop::Or : multop::And, v); - result_ = recurse_destroy(result_); - return; - } + formula c = f.nth(0); + // {e[*]} = {e} + // !{e[*]} = !{e} + if (c.accepts_eword() && c.is(op::Star)) + return recurse(formula::unop(o, c.nth(0))); - // Some term does not accept the empty word. - unsigned end = mo->size() - 1; - // {b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} - // = b₁&X(b₂&X({e₁;f₁;e₂;f₂})) - // !{b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} - // = !b₁|X(!b₂|X(!{e₁;f₁;e₂;f₂})) - // if e denotes a term that accepts [*0] - // and b denotes a Boolean formula. - while (mo->nth(end)->accepts_eword()) - --end; - unsigned start = 0; - while (start <= end) - { - const formula* r = mo->nth(start); - if (r->is_boolean() && !opt_.reduce_size_strictly) - ++start; - else - break; - } - unsigned s = end + 1 - start; - if (s != mo->size()) - { - bool doneg = op != unop::Closure; - const formula* tail; - if (s > 0) - { - multop::vec* v = new multop::vec; - v->reserve(s); - for (unsigned n = start; n <= end; ++n) - v->push_back(mo->nth(n)->clone()); - tail = multop::instance(multop::Concat, v); - tail = unop::instance(op, tail); - } - else - { - if (doneg) - tail = constant::false_instance(); - else - tail = constant::true_instance(); - } - - for (unsigned n = start; n > 0;) - { - --n; - const formula* e = mo->nth(n); - // {b;f} = b & X{f} - // !{b;f} = !b | X!{f} - if (e->is_boolean()) - { - tail = unop::instance(unop::X, tail); - e->clone(); - if (doneg) - tail = - multop::instance(multop::Or, - unop::instance(unop::Not, e), - tail); - else - tail = - multop::instance(multop::And, e, tail); - } - } - mo->destroy(); - result_ = recurse_destroy(tail); - return; - } - - // {b[*i..j];c} = b&X(b&X(... b&X{b[*0..j-i];c})) - // !{b[*i..j];c} = !b&X(!b&X(... !b&X!{b[*0..j-i];c})) if (!opt_.reduce_size_strictly) - if (const bunop* s = is_Star(mo->nth(0))) + if (c.is(op::OrRat)) { - const formula* c = s->child(); - unsigned min = s->min(); - if (c->is_boolean() && min > 0) - { - unsigned max = s->max(); - if (max != bunop::unbounded) - max -= min; - unsigned ss = mo->size(); - multop::vec* v = new multop::vec; - v->reserve(ss); - v->push_back(bunop::instance(bunop::Star, - c->clone(), - 0, max)); - for (unsigned n = 1; n < ss; ++n) - v->push_back(mo->nth(n)->clone()); - const formula* tail = - multop::instance(multop::Concat, v); - tail = // {b[*0..j-i]} or !{b[*0..j-i]} - unop::instance(op, tail); - tail = - dup_b_x_tail(op != unop::Closure, - c, tail, min); - mo->destroy(); - result_ = recurse_destroy(tail); - return; - } - } - } - // {b[*i..j]} = b&X(b&X(... b)) with i occurences of b - // !{b[*i..j]} = !b&X(!b&X(... !b)) - if (!opt_.reduce_size_strictly) - if (const bunop* s = is_Star(result_)) - { - const formula* c = s->child(); - if (c->is_boolean()) - { - unsigned min = s->min(); - assert(min > 0); - const formula* tail; - if (op == unop::Closure) - tail = - dup_b_x_tail(false, - c, constant::true_instance(), min); - else - tail = - dup_b_x_tail(true, - c, constant::false_instance(), min); - result_->destroy(); - result_ = recurse_destroy(tail); - return; + // {a₁|a₂} = {a₁}| {a₂} + // !{a₁|a₂} = !{a₁}&!{a₂} + unsigned s = c.size(); + vec v; + for (unsigned n = 0; n < s; ++n) + v.push_back(formula::unop(o, c.nth(n))); + return recurse(formula::multop(o == op::Closure + ? op::Or : op::And, v)); } - } - break; + if (c.is(op::Concat)) + { + if (c.accepts_eword()) + { + if (opt_.reduce_size_strictly) + return f; + // If all terms accept the empty word, we have + // {e₁;e₂;e₃} = {e₁}|{e₂}|{e₃} + // !{e₁;e₂;e₃} = !{e₁}&!{e₂}&!{e₃} + vec v; + unsigned end = c.size(); + v.reserve(end); + for (unsigned i = 0; i < end; ++i) + v.push_back(formula::unop(o, c.nth(i))); + return recurse(formula::multop(o == op::Closure ? + op::Or : op::And, v)); + } + + // Some term does not accept the empty word. + unsigned end = c.size() - 1; + // {b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} + // = b₁&X(b₂&X({e₁;f₁;e₂;f₂})) + // !{b₁;b₂;e₁;f₁;e₂;f₂;e₂;e₃;e₄} + // = !b₁|X(!b₂|X(!{e₁;f₁;e₂;f₂})) + // if e denotes a term that accepts [*0] + // and b denotes a Boolean formula. + while (c.nth(end).accepts_eword()) + --end; + unsigned start = 0; + while (start <= end) + { + formula r = c.nth(start); + if (r.is_boolean() && !opt_.reduce_size_strictly) + ++start; + else + break; + } + unsigned s = end + 1 - start; + if (s != c.size()) + { + bool doneg = o != op::Closure; + formula tail; + if (s > 0) + { + vec v; + v.reserve(s); + for (unsigned n = start; n <= end; ++n) + v.push_back(c.nth(n)); + tail = formula::Concat(v); + tail = formula::unop(o, tail); + } + else + { + tail = doneg ? formula::ff() : formula::tt(); + } + + for (unsigned n = start; n > 0;) + { + --n; + formula e = c.nth(n); + // {b;f} = b & X{f} + // !{b;f} = !b | X!{f} + if (e.is_boolean()) + { + tail = formula::X(tail); + if (doneg) + tail = formula::Or({formula::Not(e), tail}); + else + tail = formula::And({e, tail}); + } + } + return recurse(tail); + } + + // {b[*i..j];c} = b&X(b&X(... b&X{b[*0..j-i];c})) + // !{b[*i..j];c} = !b&X(!b&X(... !b&X!{b[*0..j-i];c})) + if (!opt_.reduce_size_strictly) + if (c.nth(0).is(op::Star)) + { + formula s = c.nth(0); + formula sc = s.nth(0); + unsigned min = s.min(); + if (sc.is_boolean() && min > 0) + { + unsigned max = s.max(); + if (max != formula::unbounded()) + max -= min; + unsigned ss = c.size(); + vec v; + v.reserve(ss); + v.push_back(formula::Star(sc, 0, max)); + for (unsigned n = 1; n < ss; ++n) + v.push_back(c.nth(n)); + formula tail = formula::Concat(v); + tail = // {b[*0..j-i]} or !{b[*0..j-i]} + formula::unop(o, tail); + tail = + dup_b_x_tail(o != op::Closure, + sc, tail, min); + return recurse(tail); + } + } + } + // {b[*i..j]} = b&X(b&X(... b)) with i occurences of b + // !{b[*i..j]} = !b&X(!b&X(... !b)) + if (!opt_.reduce_size_strictly) + if (c.is(op::Star)) + { + formula cs = c.nth(0); + if (cs.is_boolean()) + { + unsigned min = c.min(); + assert(min > 0); + formula tail; + if (o == op::Closure) + tail = dup_b_x_tail(false, cs, + formula::tt(), min); + else + tail = dup_b_x_tail(true, cs, + formula::ff(), min); + return recurse(tail); + } + } + return f; + } + 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: + return visit_binop(f); + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + return visit_multop(f); + case op::Fusion: + return f; + case op::Star: + { + formula h = f.nth(0); + auto min = f.min(); + if (h.accepts_eword()) + min = 0; + if (min == 0) + h = f.max() == formula::unbounded() + ? c_->star_normal_form(h) + : c_->star_normal_form_bounded(h); + return formula::Star(h, min, f.max()); + } } - result_ = unop::instance(op, result_); + SPOT_UNREACHABLE(); } - // Return true iff reduction occurred. - bool - reduce_sere_ltl(binop::type bindop, const formula* a, const formula* b) + formula reduce_sere_ltl(formula orig) { - // All this function is documented assuming bindop == - // UConcat, but by changing the following variable it can - // perform the rules for EConcat as well. - unop::type op_g; - binop::type op_w; - binop::type op_r; - multop::type op_and; - bool doneg; - if (bindop == binop::UConcat) + op bindop = orig.kind(); + formula a = orig.nth(0); + formula b = orig.nth(1); + + auto recurse = [this](formula f) { - op_g = unop::G; - op_w = binop::W; - op_r = binop::R; - op_and = multop::And; + return simplify_recursively(f, c_); + }; + + // All this function is documented assuming bindop == + // UConcat, but by changing the following variables it can + // perform the rules for EConcat as well. + op op_g; + op op_w; + op op_r; + op op_and; + bool doneg; + if (bindop == op::UConcat) + { + op_g = op::G; + op_w = op::W; + op_r = op::R; + op_and = op::And; doneg = true; } else // EConcat & EConcatMarked { - op_g = unop::F; - op_w = binop::M; - op_r = binop::U; - op_and = multop::Or; + op_g = op::F; + op_w = op::M; + op_r = op::U; + op_and = op::Or; doneg = false; } if (!opt_.reduce_basics) - return false; - if (const bunop* bu = is_Star(a)) + return orig; + if (a.is(op::Star)) { // {[*]}[]->b = Gb - if (a == bunop::one_star()) - { - a->destroy(); - result_ = recurse_destroy(unop::instance(op_g, b)); - return true; - } - const formula* s = bu->child(); - unsigned min = bu->min(); - unsigned max = bu->max(); + if (a == formula::one_star()) + return recurse(formula::unop(op_g, b)); + + formula s = a.nth(0); + unsigned min = a.min(); + unsigned max = a.max(); // {s[*]}[]->b = b W !s if s is Boolean. // {s[+]}[]->b = b W !s if s is Boolean. - if (s->is_boolean() && max == bunop::unbounded && min <= 1) + if (s.is_boolean() && max == formula::unbounded() && min <= 1) { - const formula* ns = // !s - doneg ? unop::instance(unop::Not, s->clone()) : s->clone(); - result_ = // b W !s - binop::instance(op_w, b, ns); - bu->destroy(); - result_ = recurse_destroy(result_); - return true; + formula ns = doneg ? formula::Not(s) : s; + // b W !s + return recurse(formula::binop(op_w, b, ns)); } if (opt_.reduce_size_strictly) - return false; + return orig; // {s[*i..j]}[]->b = {s;s;...;s[*1..j-i+1]}[]->b // = {s}[]->X({s}[]->X(...[]->X({s[*1..j-i+1]}[]->b))) // if i>0 and s does not accept the empty word - if (min == 0 || s->accepts_eword()) - return false; + if (min == 0 || s.accepts_eword()) + return orig; --min; - if (max != bunop::unbounded) + if (max != formula::unbounded()) max -= min; // j-i+1 // Don't rewrite s[1..]. if (min == 0) - return false; - const formula* tail = // {s[*1..j-i]}[]->b - binop::instance(bindop, - bunop::instance(bunop::Star, - s->clone(), 1, max), - b); + return orig; + formula tail = // {s[*1..j-i]}[]->b + formula::binop(bindop, formula::Star(s, 1, max), b); for (unsigned n = 0; n < min; ++n) tail = // {s}[]->X(tail) - binop::instance(bindop, - s->clone(), - unop::instance(unop::X, tail)); - result_ = tail; - bu->destroy(); - result_ = recurse_destroy(result_); - return true; + formula::binop(bindop, s, formula::X(tail)); + return recurse(tail); } - else if (const multop* mo = is_Concat(a)) + else if (a.is(op::Concat)) { - unsigned s = mo->size() - 1; - const formula* last = mo->nth(s); + unsigned s = a.size() - 1; + formula last = a.nth(s); // {r;[*]}[]->b = {r}[]->Gb - if (last == bunop::one_star()) - { - result_ = - binop::instance(bindop, - mo->all_but(s), unop::instance(op_g, b)); - mo->destroy(); - result_ = recurse_destroy(result_); - return true; - } + if (last == formula::one_star()) + return recurse(formula::binop(bindop, a.all_but(s), + formula::unop(op_g, b))); - const formula* first = mo->nth(0); + formula first = a.nth(0); // {[*];r}[]->b = G({r}[]->b) - if (first == bunop::one_star()) - { - result_ = - unop::instance(op_g, - binop::instance(bindop, mo->all_but(0), b)); - mo->destroy(); - result_ = recurse_destroy(result_); - return true; - } + if (first == formula::one_star()) + return recurse(formula::unop(op_g, + formula::binop(bindop, + a.all_but(0), b))); if (opt_.reduce_size_strictly) - return false; + return orig; // {r;s[*]}[]->b = {r}[]->(b & X(b W !s)) // if s is Boolean and r does not accept [*0]; - if (const bunop* l = is_KleenStar(last)) // l = s[*] - if (l->child()->is_boolean()) + if (last.is_Kleene_star()) // l = s[*] + if (last.nth(0).is_boolean()) { - const formula* r = mo->all_but(s); - if (!r->accepts_eword()) + formula r = a.all_but(s); + if (!r.accepts_eword()) { - const formula* ns = // !s - doneg - ? unop::instance(unop::Not, l->child()->clone()) - : l->child()->clone(); - const formula* w = // b W !s - binop::instance(op_w, b->clone(), ns); - const formula* x = // X(b W !s) - unop::instance(unop::X, w); - const formula* d = // b & X(b W !s) - multop::instance(op_and, b, x); - result_ = // {r}[]->(b & X(b W !s)) - binop::instance(bindop, r, d); - mo->destroy(); - result_ = recurse_destroy(result_); - return true; + formula ns = // !s + doneg ? formula::Not(last.nth(0)) : last.nth(0); + formula w = // b W !s + formula::binop(op_w, b, ns); + formula x = // X(b W !s) + formula::X(w); + formula d = // b & X(b W !s) + formula::multop(op_and, {b, x}); + // {r}[]->(b & X(b W !s)) + return recurse(formula::binop(bindop, r, d)); } - r->destroy(); } // {s[*];r}[]->b = !s R ({r}[]->b) // if s is Boolean and r does not accept [*0]; - if (const bunop* l = is_KleenStar(first)) - if (l->child()->is_boolean()) + if (first.is_Kleene_star()) + if (first.nth(0).is_boolean()) { - const formula* r = mo->all_but(0); - if (!r->accepts_eword()) + formula r = a.all_but(0); + if (!r.accepts_eword()) { - const formula* ns = // !s + formula ns = // !s doneg - ? unop::instance(unop::Not, l->child()->clone()) - : l->child()->clone(); - const formula* u = // {r}[]->b - binop::instance(bindop, r, b); - result_ = // !s R ({r}[]->b) - binop::instance(op_r, ns, u); - mo->destroy(); - result_ = recurse_destroy(result_); - return true; + ? formula::Not(first.nth(0)) + : first.nth(0); + formula u = // {r}[]->b + formula::binop(bindop, r, b); + // !s R ({r}[]->b) + return recurse(formula::binop(op_r, ns, u)); } - r->destroy(); } // {r₁;r₂;r₃}[]->b = {r₁}[]->X({r₂}[]->X({r₃}[]->b)) // if r₁, r₂, r₃ do not accept [*0]. - if (!mo->accepts_eword()) + if (!a.accepts_eword()) { unsigned count = 0; for (unsigned n = 0; n <= s; ++n) - count += !mo->nth(n)->accepts_eword(); + count += !a.nth(n).accepts_eword(); assert(count > 0); if (count == 1) - return false; + return orig; // Let e denote a term that accepts [*0] // and let f denote a term that do not. // A formula such as {e₁;f₁;e₂;e₃;f₂;e₄}[]->b @@ -1916,140 +1462,102 @@ namespace spot // We compute the r formulas from the right // (i.e., r₂ before r₁.) - multop::vec* r = new multop::vec; + vec r; do - r->insert(r->begin(), mo->nth(--pos)->clone()); - while (r->front()->accepts_eword()); - const formula* tail = // {r₂}[]->b - binop::instance(bindop, - multop::instance(multop::Concat, r), - b); + r.insert(r.begin(), a.nth(--pos)); + while (r.front().accepts_eword()); + formula tail = // {r₂}[]->b + formula::binop(bindop, formula::Concat(r), b); while (--count) { - multop::vec* r = new multop::vec; + vec r; do - r->insert(r->begin(), mo->nth(--pos)->clone()); - while (r->front()->accepts_eword()); + r.insert(r.begin(), a.nth(--pos)); + while (r.front().accepts_eword()); // If it's the last block, take all leading // formulae as well. if (count == 1) while (pos > 0) { - r->insert(r->begin(), mo->nth(--pos)->clone()); - assert(r->front()->accepts_eword()); + r.insert(r.begin(), a.nth(--pos)); + assert(r.front().accepts_eword()); } tail = // X({r₂}[]->b) - unop::instance(unop::X, tail); + formula::X(tail); tail = // {r₁}[]->X({r₂}[]->b) - binop::instance(bindop, - multop::instance(multop::Concat, r), - tail); + formula::binop(bindop, formula::Concat(r), tail); } - mo->destroy(); - result_ = recurse_destroy(tail); - return true; + return recurse(tail); } } else if (opt_.reduce_size_strictly) { - return false; + return orig; } - else if (const multop* mo = is_Fusion(a)) + else if (a.is(op::Fusion)) { // {r₁:r₂:r₃}[]->b = {r₁}[]->({r₂}[]->({r₃}[]->b)) - unsigned s = mo->size(); - const formula* tail = b; + unsigned s = a.size(); + formula tail = b; do { --s; - tail = binop::instance(bindop, - mo->nth(s)->clone(), tail); + tail = formula::binop(bindop, a.nth(s), tail); } while (s != 0); - mo->destroy(); - result_ = recurse_destroy(tail); - return true; + return recurse(tail); } - else if (const multop* mo = is_OrRat(a)) + else if (a.is(op::OrRat)) { // {r₁|r₂|r₃}[]->b = ({r₁}[]->b)&({r₂}[]->b)&({r₃}[]->b) - unsigned s = mo->size(); - multop::vec* v = new multop::vec; + unsigned s = a.size(); + vec v; for (unsigned n = 0; n < s; ++n) - { - const formula* x = // {r₁}[]->b - binop::instance(bindop, - mo->nth(n)->clone(), b->clone()); - v->push_back(x); - } - mo->destroy(); - b->destroy(); - result_ = recurse_destroy(multop::instance(op_and, v)); - return true; + // {r₁}[]->b + v.push_back(formula::binop(bindop, a.nth(n), b)); + return recurse(formula::multop(op_and, v)); } - return false; + return orig; } - void - visit(const binop* bo) + formula + visit_binop(formula bo) { - binop::type op = bo->op(); - - const formula* b = recurse(bo->second()); - + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; + op o = bo.kind(); + formula b = bo.nth(1); if (opt_.event_univ) { trace << "bo: trying eventuniv rules" << std::endl; /* If b is a pure eventuality formula then a U b = b. If b is a pure universality formula a R b = b. */ - if ((b->is_eventual() && (op == binop::U)) - || (b->is_universal() && (op == binop::R))) - { - result_ = b; - return; - } + if ((b.is_eventual() && bo.is(op::U)) + || (b.is_universal() && bo.is(op::R))) + return b; } - const formula* a = recurse(bo->first()); - + formula a = bo.nth(0); if (opt_.event_univ) { /* If a is a pure eventuality formula then a M b = a & b. - If a is a pure universality formula a W b = a|b. */ - if (a->is_eventual() && (op == binop::M)) - { - result_ = - recurse_destroy(multop::instance(multop::And, a, b)); - return; - } - if (a->is_universal() && (op == binop::W)) - { - result_ = - recurse_destroy(multop::instance(multop::Or, a, b)); - return; - } + If a is a pure universality formula a W b = a | b. */ + if (a.is_eventual() && bo.is(op::M)) + return recurse(formula::And({a, b})); + if (a.is_universal() && bo.is(op::W)) + return recurse(formula::Or({a, b})); // e₁ W e₂ = Ge₁ | e₂ // u₁ M u₂ = Fu₁ & u₂ if (!opt_.reduce_size_strictly) { - if (op == binop::W && a->is_eventual() && b->is_eventual()) - { - result_ = - recurse_destroy(multop::instance - (multop::Or, - unop::instance(unop::G, a), b)); - return; - } - if (op == binop::M && a->is_universal() && b->is_universal()) - { - result_ = - recurse_destroy(multop::instance - (multop::And, - unop::instance(unop::F, a), b)); - return; - } + if (bo.is(op::W) && a.is_eventual() && b.is_eventual()) + return recurse(formula::Or({formula::G(a), b})); + if (bo.is(op::M) && a.is_universal() && b.is_universal()) + return recurse(formula::And({formula::F(a), b})); } // In the following rewritings we assume that @@ -2064,83 +1572,57 @@ namespace spot // (a M (b&u)) = (a M b)&u if (opt_.favor_event_univ) { - if (op == binop::U || op == binop::W) - if (const multop* mo = is_Or(b)) + if (bo.is(op::U, op::W)) + if (b.is(op::Or)) { - b->clone(); - mospliter s(mospliter::Split_Event, mo, c_); - const formula* b2 = - multop::instance(multop::Or, s.res_other); + mospliter s(mospliter::Split_Event, b, c_); + formula b2 = formula::Or(std::move(*s.res_other)); if (b2 != b) { - b->destroy(); - s.res_Event->push_back(binop::instance(op, a, b2)); - result_ = - recurse_destroy(multop::instance(multop::Or, - s.res_Event)); - return; + s.res_Event->push_back(formula::binop(o, a, b2)); + return recurse + (formula::Or(std::move(*s.res_Event))); } - b2->destroy(); - delete s.res_Event; } - if (op == binop::U) - if (const multop* mo = is_And(b)) + if (bo.is(op::U)) + if (b.is(op::And)) { - b->clone(); - mospliter s(mospliter::Split_EventUniv, mo, c_); - const formula* b2 = - multop::instance(multop::And, s.res_other); + mospliter s(mospliter::Split_EventUniv, b, c_); + formula b2 = formula::And(std::move(*s.res_other)); if (b2 != b) { - b->destroy(); - s.res_EventUniv->push_back(binop::instance(op, - a, b2)); - result_ = recurse_destroy - (multop::instance(multop::And, s.res_EventUniv)); - return; + s.res_EventUniv->push_back(formula::binop(o, + a, b2)); + return recurse + (formula::And(std::move(*s.res_EventUniv))); } - b2->destroy(); - delete s.res_Event; } - if (op == binop::M) - if (const multop* mo = is_And(a)) + if (bo.is(op::M)) + if (a.is(op::And)) { - a->clone(); - mospliter s(mospliter::Split_EventUniv, mo, c_); - const formula* a2 = - multop::instance(multop::And, s.res_other); + mospliter s(mospliter::Split_EventUniv, a, c_); + formula a2 = formula::And(std::move(*s.res_other)); if (a2 != a) { - a->destroy(); - s.res_EventUniv->push_back(binop::instance(op, - a2, b)); - result_ = recurse_destroy - (multop::instance(multop::And, s.res_EventUniv)); - return; + s.res_EventUniv->push_back(formula::binop(o, + a2, b)); + return recurse + (formula::And(std::move(*s.res_EventUniv))); } - a2->destroy(); - delete s.res_EventUniv; } - if (op == binop::R || op == binop::M) - if (const multop* mo = is_And(b)) + if (bo.is(op::R, op::M)) + if (b.is(op::And)) { - b->clone(); - mospliter s(mospliter::Split_Univ, mo, c_); - const formula* b2 = - multop::instance(multop::And, s.res_other); + mospliter s(mospliter::Split_Univ, b, c_); + formula b2 = formula::And(std::move(*s.res_other)); if (b2 != b) { - b->destroy(); - s.res_Univ->push_back(binop::instance(op, a, b2)); - result_ = recurse_destroy - (multop::instance(multop::And, s.res_Univ)); - return; + s.res_Univ->push_back(formula::binop(o, a, b2)); + return recurse + (formula::And(std::move(*s.res_Univ))); } - b2->destroy(); - delete s.res_Univ; } } - trace << "bo: no eventuniv rule matched" << std::endl; } @@ -2148,323 +1630,131 @@ namespace spot if (opt_.synt_impl | opt_.containment_checks) { trace << "bo: trying inclusion-based rules" << std::endl; - switch (op) + switch (o) { - case binop::Xor: - case binop::Equiv: - case binop::Implies: + case op::Xor: + case op::Equiv: + case op::Implies: SPOT_UNIMPLEMENTED(); - return; - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: break; - case binop::U: + case op::U: // if a => b, then a U b = b // if (a U b) => b, then a U b = b (for stronger containment) if (c_->implication(a, b) || (opt_.containment_checks_stronger && c_->contained(bo, b))) - { - a->destroy(); - result_ = b; - return; - } + return b; // if !a => b, then a U b = Fb if (c_->implication_neg(a, b, false)) - { - a->destroy(); - result_ = - recurse_destroy(unop::instance(unop::F, b)); - return; - } + return formula::F(b); // if a => b, then a U (b U c) = (b U c) // if a => b, then a U (b W c) = (b W c) + if (b.is(op::U, op::W) && c_->implication(a, b.nth(0))) + return b; // if b => a, then a U (b U c) = (a U c) + if (b.is(op::U) && c_->implication(b.nth(0), a)) + return recurse(formula::U(a, b.nth(1))); // if a => c, then a U (b R (c U d)) = (b R (c U d)) // if a => c, then a U (b R (c W d)) = (b R (c W d)) // if a => c, then a U (b M (c U d)) = (b M (c U d)) // if a => c, then a U (b M (c W d)) = (b M (c W d)) - if (const binop* bo = is_binop(b)) - { - // if a => b, then a U (b U c) = (b U c) - // if a => b, then a U (b W c) = (b W c) - if ((bo->op() == binop::U || bo->op() == binop::W) - && c_->implication(a, bo->first())) - { - a->destroy(); - result_ = b; - return; - } - // if b => a, then a U (b U c) = (a U c) - if (bo->op() == binop::U - && c_->implication(bo->first(), a)) - { - result_ = recurse_destroy - (binop::instance(binop::U, - a, bo->second()->clone())); - b->destroy(); - return; - } - // if a => c, then a U (b R (c U d)) = (b R (c U d)) - // if a => c, then a U (b R (c W d)) = (b R (c W d)) - // if a => c, then a U (b M (c U d)) = (b M (c U d)) - // if a => c, then a U (b M (c W d)) = (b M (c W d)) - if ((bo->op() == binop::R || bo->op() == binop::M) - && bo->second()->kind() == formula::BinOp) - { - const binop* cd = - static_cast(bo->second()); - if ((cd->op() == binop::U || cd->op() == binop::W) - && c_->implication(a, cd->first())) - { - a->destroy(); - result_ = b; - return; - } - } - } + if (b.is(op::R, op::M) && b.nth(1).is(op::U, op::W) + && c_->implication(a, b.nth(1).nth(0))) + return b; // if a => b, then (a U c) U b = c U b // if a => b, then (a W c) U b = c U b + if (a.is(op::U, op::W) && c_->implication(a.nth(0), b)) + return recurse(formula::U(a.nth(1), b)); // if c => b, then (a U c) U b = (a U c) | b - if (const binop* bo = is_binop(a)) - { - if ((bo->op() == binop::U || bo->op() == binop::W) - && c_->implication(bo->first(), b)) - { - result_ = recurse_destroy - (binop::instance(binop::U, - bo->second()->clone(), - b)); - a->destroy(); - return; - } - else if ((bo->op() == binop::U) - && c_->implication(bo->second(), b)) - { - result_ = recurse_destroy - (multop::instance(multop::Or, a, b)); - return; - } - } + if (a.is(op::U) && c_->implication(a.nth(1), b)) + return recurse(formula::Or({a, b})); break; - case binop::R: + case op::R: // if b => a, then a R b = b if (c_->implication(b, a)) - { - a->destroy(); - result_ = b; - return; - } + return b; // if b => !a, then a R b = Gb if (c_->implication_neg(b, a, true)) - { - a->destroy(); - result_ = recurse_destroy(unop::instance(unop::G, b)); - return; - } - if (b->kind() == formula::BinOp) - { - // if b => a, then a R (b R c) = b R c - // if b => a, then a R (b M c) = b M c - const binop* bo = static_cast(b); - if ((bo->op() == binop::R || bo->op() == binop::M) - && c_->implication(bo->first(), a)) - { - a->destroy(); - result_ = b; - return; - } - - // if a => b, then a R (b R c) = a R c - if (bo->op() == binop::R - && c_->implication(a, bo->first())) - { - result_ = recurse_destroy - (binop::instance(binop::R, a, - bo->second()->clone())); - b->destroy(); - return; - } - } - + return recurse(formula::G(b)); + // if b => a, then a R (b R c) = b R c + // if b => a, then a R (b M c) = b M c + if (b.is(op::R, op::M) && c_->implication(b.nth(0), a)) + return b; + // if a => b, then a R (b R c) = a R c + if (b.is(op::R) && c_->implication(a, b.nth(0))) + return recurse(formula::R(a, b.nth(1))); // if b => a, then (a R c) R b = c R b // if b => a, then (a M c) R b = c R b // if c => b, then (a R c) R b = (a & c) R b // if c => b, then (a M c) R b = (a & c) R b - if (const binop* bo = is_binop(a)) + if (a.is(op::R, op::M)) { - if (bo->op() == binop::M || bo->op() == binop::R) + if (c_->implication(b, a.nth(0))) + return recurse(formula::R(a.nth(1), b)); + if (c_->implication(a.nth(1), b)) { - if (c_->implication(b, bo->first())) - { - result_ = recurse_destroy - (binop::instance(binop::R, - bo->second()->clone(), - b)); - a->destroy(); - return; - } - else if (c_->implication(bo->second(), b)) - { - const formula* ac = - multop::instance(multop::And, - bo->first()->clone(), - bo->second()->clone()); - a->destroy(); - result_ = recurse_destroy - (binop::instance(binop::R, ac, b)); - return; - } + formula ac = formula::And({a.nth(0), a.nth(1)}); + return recurse(formula::R(ac, b)); } } - break; - case binop::W: + case op::W: // if a => b, then a W b = b // if a W b => b, then a W b = b (for stronger containment) if (c_->implication(a, b) || (opt_.containment_checks_stronger && c_->contained(bo, b))) - { - a->destroy(); - result_ = b; - return; - } + return b; // if !a => b then a W b = 1 if (c_->implication_neg(a, b, false)) - { - a->destroy(); - b->destroy(); - result_ = constant::true_instance(); - return; - } - // if a => b, then a W (b W c) = (b W c) + return formula::tt(); + // if a => b, then a W (b W c) = b W c // (Beware: even if a => b we do not have a W (b U c) = b U c) - // if b => a, then a W (b U c) = (a W c) - // if b => a, then a W (b W c) = (a W c) - if (b->kind() == formula::BinOp) - { - const binop* bo = static_cast(b); - // if a => b, then a W (b W c) = (b W c) - if (bo->op() == binop::W - && c_->implication(a, bo->first())) - { - a->destroy(); - result_ = b; - return; - } - // if b => a, then a W (b U c) = (a W c) - // if b => a, then a W (b W c) = (a W c) - if ((bo->op() == binop::U || bo->op() == binop::W) - && c_->implication(bo->first(), a)) - { - result_ = recurse_destroy - (binop::instance(binop::W, - a, bo->second()->clone())); - b->destroy(); - return; - } - } + if (b.is(op::W) && c_->implication(a, b.nth(0))) + return b; + // if b => a, then a W (b U c) = a W c + // if b => a, then a W (b W c) = a W c + if (b.is(op::U, op::W) && c_->implication(b.nth(0), a)) + return recurse(formula::W(a, b.nth(1))); // if a => b, then (a U c) W b = c W b // if a => b, then (a W c) W b = c W b + if (a.is(op::U, op::W) && c_->implication(a.nth(0), b)) + return recurse(formula::W(a.nth(1), b)); // if c => b, then (a W c) W b = (a W c) | b // if c => b, then (a U c) W b = (a U c) | b - if (const binop* bo = is_binop(a)) - { - if ((bo->op() == binop::U || bo->op() == binop::W)) - { - if (c_->implication(bo->first(), b)) - { - result_ = recurse_destroy - (binop::instance(binop::W, - bo->second()->clone(), - b)); - a->destroy(); - return; - } - else if (c_->implication(bo->second(), b)) - { - result_ = recurse_destroy - (multop::instance(multop::Or, a, b)); - return; - } - } - } + if (a.is(op::U, op::W) && c_->implication(a.nth(1), b)) + return recurse(formula::Or({a, b})); break; - case binop::M: + case op::M: // if b => a, then a M b = b if (c_->implication(b, a)) - { - a->destroy(); - result_ = b; - return; - } + return b; // if b => !a, then a M b = 0 if (c_->implication_neg(b, a, true)) - { - a->destroy(); - b->destroy(); - result_ = constant::false_instance(); - return; - } - if (b->kind() == formula::BinOp) - { - // if b => a, then a M (b M c) = b M c - const binop* bo = static_cast(b); - if (bo->op() == binop::M - && c_->implication(bo->first(), a)) - { - result_ = b; - a->destroy(); - return; - } - - // if a => b, then a M (b M c) = a M c - // if a => b, then a M (b R c) = a M c - if ((bo->op() == binop::M || bo->op() == binop::R) - && c_->implication(a, bo->first())) - { - b->destroy(); - result_ = recurse_destroy - (binop::instance(binop::M, a, - bo->second()->clone())); - return; - } - } - + return formula::ff(); + // if b => a, then a M (b M c) = b M c + if (b.is(op::M) && c_->implication(b.nth(0), a)) + return b; + // if a => b, then a M (b M c) = a M c + // if a => b, then a M (b R c) = a M c + if (b.is(op::M, op::R) && c_->implication(a, b.nth(0))) + return recurse(formula::M(a, b.nth(1))); // if b => a, then (a R c) M b = c M b // if b => a, then (a M c) M b = c M b + if (a.is(op::R, op::M) && c_->implication(b, a.nth(0))) + return recurse(formula::M(a.nth(1), b)); // if c => b, then (a M c) M b = (a & c) M b - if (const binop* bo = is_binop(a)) - { - if ((bo->op() == binop::M || bo->op() == binop::R) - && c_->implication(b, bo->first())) - { - result_ = recurse_destroy - (binop::instance(binop::M, - bo->second()->clone(), - b)); - a->destroy(); - return; - } - else if ((bo->op() == binop::M) - && c_->implication(bo->second(), b)) - { - const formula* ac = - multop::instance(multop::And, - bo->first()->clone(), - bo->second()->clone()); - a->destroy(); - result_ = recurse_destroy - (binop::instance(binop::M, ac, b)); - return; - } - } + if (a.is(op::M) && c_->implication(a.nth(1), b)) + return + recurse(formula::M(formula::And({a.nth(0), a.nth(1)}), + b)); + break; + + default: break; } trace << "bo: no inclusion-based rules matched" << std::endl; @@ -2473,373 +1763,183 @@ namespace spot if (!opt_.reduce_basics) { trace << "bo: basic reductions disabled" << std::endl; - result_ = binop::instance(op, a, b); - return; + return bo; } trace << "bo: trying basic reductions" << std::endl; // Rewrite U,R,W,M as F or G when possible. - switch (op) + // true U b == F(b) + if (bo.is(op::U) && a.is(op::True)) + return recurse(formula::F(b)); + // false R b == G(b) + if (bo.is(op::R) && a.is(op::False)) + return recurse(formula::G(b)); + // a W false == G(a) + if (bo.is(op::W) && b.is(op::False)) + return recurse(formula::G(a)); + // a M true == F(a) + if (bo.is(op::M) && b.is(op::True)) + return recurse(formula::F(a)); + + if (bo.is(op::W, op::M) || bo.is(op::U, op::R)) { - case binop::U: - // true U b == F(b) - if (a == constant::true_instance()) + // X(a) U X(b) = X(a U b) + // X(a) R X(b) = X(a R b) + // X(a) W X(b) = X(a W b) + // X(a) M X(b) = X(a M b) + if (a.is(op::X) && b.is(op::X)) + return recurse(formula::X(formula::binop(o, + a.nth(0), b.nth(0)))); + + if (bo.is(op::U, op::W)) { - result_ = recurse_destroy(unop::instance(unop::F, b)); - return; + // a U Ga = Ga + // a W Ga = Ga + if (b.is(op::G) && a == b.nth(0)) + return b; + // a U (b | c | G(a)) = a W (b | c) + // a W (b | c | G(a)) = a W (b | c) + if (b.is(op::Or)) + for (int i = 0, s = b.size(); i < s; ++i) + { + formula c = b.nth(i); + if (c.is(op::G) && c.nth(0) == a) + return recurse(formula::W(a, b.all_but(i))); + } + // a U (b & a & c) == (b & c) M a + // a W (b & a & c) == (b & c) R a + if (b.is(op::And)) + for (int i = 0, s = b.size(); i < s; ++i) + if (b.nth(i) == a) + return recurse(formula::binop(o == op::U ? + op::M : op::R, + b.all_but(i), a)); + // If b is Boolean: + // (Xc) U b = b | X(b M c) + // (Xc) W b = b | X(b R c) + if (!opt_.reduce_size_strictly + && a.is(op::X) && b.is_boolean()) + { + formula x = formula::X(formula::binop(o == op::U ? + op::M : op::R, + b, a.nth(0))); + return recurse(formula::Or({b, x})); + } } - break; - case binop::R: - // false R b == G(b) - if (a == constant::false_instance()) + else if (bo.is(op::M, op::R)) { - result_ = recurse_destroy(unop::instance(unop::G, b)); - return; + // a R Fa = Fa + // a M Fa = Fa + if (b.is(op::F) && b.nth(0) == a) + return b; + + // a R (b & c & F(a)) = a M (b & c) + // a M (b & c & F(a)) = a M (b & c) + if (b.is(op::And)) + for (int i = 0, s = b.size(); i < s; ++i) + { + formula c = b.nth(i); + if (c.is(op::F) && c.nth(0) == a) + return recurse(formula::M(a, b.all_but(i))); + } + // a M (b | a | c) == (b | c) U a + // a R (b | a | c) == (b | c) W a + if (b.is(op::Or)) + for (int i = 0, s = b.size(); i < s; ++i) + if (b.nth(i) == a) + return recurse(formula::binop(o == op::M ? + op::U : op::W, + b.all_but(i), a)); + // If b is Boolean: + // (Xc) R b = b & X(b W c) + // (Xc) M b = b & X(b U c) + if (!opt_.reduce_size_strictly + && a.is(op::X) && b.is_boolean()) + { + formula x = + formula::X(formula::binop(o == op::M ? op::U : op::W, + b, a.nth(0))); + return recurse(formula::And({b, x})); + } } - break; - case binop::W: - // a W false == G(a) - if (b == constant::false_instance()) - { - result_ = recurse_destroy(unop::instance(unop::G, a)); - return; - } - break; - case binop::M: - // a M true == F(a) - if (b == constant::true_instance()) - { - result_ = recurse_destroy(unop::instance(unop::F, a)); - return; - } - break; - default: - break; } - - switch (op) - { - case binop::W: - case binop::M: - case binop::U: - case binop::R: - { - // These are trivial identities: - // a U false = false - // a U true = true - // a R false = false - // a R true = true - // a W true = true - // a M false = false - if (is_constant(b)) - { - result_ = b; - a->destroy(); - return; - } - - const unop* fu1 = is_unop(a); - const unop* fu2 = is_unop(b); - - // X(a) U X(b) = X(a U b) - // X(a) R X(b) = X(a R b) - // X(a) W X(b) = X(a W b) - // X(a) M X(b) = X(a M b) - if (fu1 && fu2 - && fu1->op() == unop::X - && fu2->op() == unop::X) - { - const formula* bin = - binop::instance(op, - fu1->child()->clone(), - fu2->child()->clone()); - a->destroy(); - b->destroy(); - result_ = recurse_destroy(unop::instance(unop::X, bin)); - return; - } - - if (op == binop::U || op == binop::W) - { - // a U Ga = Ga - // a W Ga = Ga - if (fu2 && fu2->op() == unop::G && fu2->child() == a) - { - a->destroy(); - result_ = b; - return; - } - - // a U (b | c | G(a)) = a W (b | c) - // a W (b | c | G(a)) = a W (b | c) - // a U (a & b & c) = (b & c) M a - // a W (a & b & c) = (b & c) R a - if (const multop* fm2 = is_multop(b)) - { - multop::type bt = fm2->op(); - // a U (b | c | G(a)) = a W (b | c) - // a W (b | c | G(a)) = a W (b | c) - if (bt == multop::Or) - { - int s = fm2->size(); - for (int i = 0; i < s; ++i) - { - const unop* c = is_G(fm2->nth(i)); - if (!c || c->child() != a) - continue; - result_ = - recurse_destroy(binop::instance - (binop::W, a, - fm2->all_but(i))); - b->destroy(); - return; - } - } - // a U (b & a & c) == (b & c) M a - // a W (b & a & c) == (b & c) R a - if (bt == multop::And) - { - int s = fm2->size(); - for (int i = 0; i < s; ++i) - { - if (fm2->nth(i) != a) - continue; - result_ = recurse_destroy(binop::instance - (op == binop::U ? binop::M : binop::R, - fm2->all_but(i), a)); - b->destroy(); - return; - } - } - } - // If b is Boolean: - // (Xc) U b = b | X(b M c) - // (Xc) W b = b | X(b R c) - if (!opt_.reduce_size_strictly - && fu1 && fu1->op() == unop::X && b->is_boolean()) - { - const formula* c = fu1->child()->clone(); - fu1->destroy(); - const formula* x = - unop::instance(unop::X, - binop::instance(op == binop::U - ? binop::M - : binop::R, - b->clone(), c)); - result_ = - recurse_destroy(multop::instance(multop::Or, b, x)); - return; - } - } - else if (op == binop::M || op == binop::R) - { - // a R Fa = Fa - // a M Fa = Fa - if (fu2 && fu2->op() == unop::F && fu2->child() == a) - { - a->destroy(); - result_ = b; - return; - } - - // a R (b & c & F(a)) = a M (b & c) - // a M (b & c & F(a)) = a M (b & c) - // a M (b | a | c) == (b | c) U a - // a R (b | a | c) == (b | c) W a - if (const multop* fm2 = is_multop(b)) - { - multop::type bt = fm2->op(); - - // a R (b & c & F(a)) = a M (b & c) - // a M (b & c & F(a)) = a M (b & c) - if (bt == multop::And) - { - int s = fm2->size(); - for (int i = 0; i < s; ++i) - { - const unop* c = is_F(fm2->nth(i)); - if (!c || c->child() != a) - continue; - result_ = - recurse_destroy(binop::instance - (binop::M, a, - fm2->all_but(i))); - b->destroy(); - return; - } - } - // a M (b | a | c) == (b | c) U a - // a R (b | a | c) == (b | c) W a - if (bt == multop::Or) - { - int s = fm2->size(); - for (int i = 0; i < s; ++i) - { - if (fm2->nth(i) != a) - continue; - result_ = recurse_destroy(binop::instance - (op == binop::M ? binop::U : binop::W, - fm2->all_but(i), a)); - b->destroy(); - return; - } - } - } - // If b is Boolean: - // (Xc) R b = b & X(b W c) - // (Xc) M b = b & X(b U c) - if (!opt_.reduce_size_strictly - && fu1 && fu1->op() == unop::X && b->is_boolean()) - { - const formula* c = fu1->child()->clone(); - fu1->destroy(); - const formula* x = - unop::instance(unop::X, - binop::instance(op == binop::M - ? binop::U - : binop::W, - b->clone(), c)); - result_ = - recurse_destroy(multop::instance(multop::And, b, x)); - return; - } - } - } - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - if (reduce_sere_ltl(op, a, b)) - return; - else - break; - case binop::Xor: - case binop::Equiv: - case binop::Implies: - // No simplification... Yet? - break; - } - - result_ = binop::instance(op, a, b); + if (bo.is(op::UConcat) || bo.is(op::EConcat, op::EConcatMarked)) + return reduce_sere_ltl(bo); + return bo; } - void - visit(const multop* mo) + formula + visit_multop(formula mo) { - unsigned mos = mo->size(); - multop::vec* res = new multop::vec; + auto recurse = [this](formula f) + { + return simplify_recursively(f, c_); + }; - for (unsigned i = 0; i < mos; ++i) - res->push_back(recurse(mo->nth(i))); - - multop::type op = mo->op(); + unsigned mos = mo.size(); if ((opt_.synt_impl | opt_.containment_checks) - && (op != multop::AndRat) - && (op != multop::AndNLM) - && (op != multop::OrRat) - && (op != multop::Concat) - && (op != multop::Fusion)) - { - bool is_and = op == multop::And; - constant* neutral = is_and - ? constant::false_instance() : constant::true_instance(); + && mo.is(op::Or, op::And)) + for (unsigned i = 0; i < mos; ++i) + { + formula fi = mo.nth(i); + formula fo = mo.all_but(i); + // if fi => fo, then fi | fo = fo + // if fo => fi, then fi & fo = fo + if ((mo.is(op::Or) && c_->implication(fi, fo)) + || (mo.is(op::And) && c_->implication(fo, fi))) + return recurse(fo); + // if fi => !fo, then fi & fo = false + // if fo => !fi, then fi & fo = false + // if !fi => fo, then fi | fo = true + // if !fo => fi, then fi | fo = true + bool is_and = mo.is(op::And); + if (c_->implication_neg(fi, fo, is_and) + || c_->implication_neg(fo, fi, is_and)) + return recurse(is_and ? formula::ff() : formula::tt()); + } - result_ = multop::instance(op, res); - const multop* check = is_multop(result_, op); - if (!check) - return; - - unsigned s = check->size(); - unsigned i = 0; - res = new multop::vec; - res->reserve(s); - while (i < s) - { - const formula* fi = check->nth(i); - const formula* fo = check->all_but(i); - // if fi => fo, then fi | fo = fo - // if fo => fi, then fi & fo = fo - if ((op == multop::Or && c_->implication(fi, fo)) - || (op == multop::And && c_->implication(fo, fi))) - { - check->destroy(); - check = is_multop(fo, op); - if (!check) - { - result_ = fo; - for (unsigned j = 0; j < i; ++j) - (*res)[j]->destroy(); - delete res; - return; - } - --s; - } - // if fi => !fo, then fi & fo = false - // if fo => !fi, then fi & fo = false - // if !fi => fo, then fi | fo = true - // if !fo => fi, then fi | fo = true - else if (c_->implication_neg(fi, fo, is_and) - || c_->implication_neg(fo, fi, is_and)) - { - fo->destroy(); - check->destroy(); - result_ = neutral; - for (unsigned j = 0; j < i; ++j) - (*res)[j]->destroy(); - delete res; - return; - } - else - { - fo->destroy(); - res->push_back(fi->clone()); - ++i; - } - } - check->destroy(); - } - - assert(!res->empty()); + vec res; + res.reserve(mos); + for (auto f: mo) + res.push_back(f); + op o = mo.kind(); // basics reduction do not concern Boolean formulas, // so don't waste time trying to apply them. - if (opt_.reduce_basics && !mo->is_boolean()) + if (opt_.reduce_basics && !mo.is_boolean()) { - switch (op) + switch (o) { - case multop::And: - assert(!mo->is_sere_formula()); + case op::And: + assert(!mo.is_sere_formula()); { // a & X(G(a&b...) & c...) = Ga & X(G(b...) & c...) // a & (Xa W b) = b R a // a & (Xa U b) = b M a // a & (b | X(b R a)) = b R a // a & (b | X(b M a)) = b M a - if (!mo->is_syntactic_stutter_invariant()) // Skip if no X. + if (!mo.is_syntactic_stutter_invariant()) // Skip if no X. { - typedef std::unordered_set> fset_t; - typedef std::unordered_map, - ptr_hash> fmap_t; + typedef std::unordered_set fset_t; + typedef std::unordered_map> fmap_t; fset_t xgset; // XG(...) fset_t xset; // X(...) fmap_t wuset; // (X...)W(...) or (X...)U(...) - unsigned s = res->size(); - std::vector tokill(s); + std::vector tokill(mos); // Make a pass to search for subterms // of the form XGa or X(... & G(...&a&...) & ...) - for (unsigned n = 0; n < s; ++n) + for (unsigned n = 0; n < mos; ++n) { - if (!(*res)[n]) + if (!res[n]) continue; - if ((*res)[n]->is_syntactic_stutter_invariant()) + if (res[n].is_syntactic_stutter_invariant()) continue; - const formula* xarg = is_XWU((*res)[n]); - if (xarg) + if (formula xarg = is_XWU(res[n])) { wuset[xarg].insert(n); continue; @@ -2849,74 +1949,51 @@ namespace spot // - X(...) // - b | X(b R ...) // - b | X(b M ...) - - const binop* barg = is_bXbRM((*res)[n]); - if (barg) + if (formula barg = is_bXbRM(res[n])) { - wuset[barg->second()].insert(n); + wuset[barg.nth(1)].insert(n); continue; } - const unop* uo = is_X((*res)[n]); - if (!uo) + if (!res[n].is(op::X)) continue; - const formula* c = uo->child(); - const multop* a; - const unop* g; - if ((g = is_G(c))) + formula c = res[n].nth(0); + auto handle_G = [&xgset](formula c) { -#define HANDLE_G const multop* a2; \ - if ((a2 = is_And(g->child()))) \ - { \ - unsigned y = a2->size(); \ - for (unsigned n = 0; n < y; ++n) \ - { \ - const formula* sub = a2->nth(n); \ - if (xgset.insert(sub).second) \ - sub->clone(); \ - } \ - } \ - else \ - { \ - const formula* sub = g->child(); \ - if (xgset.insert(sub).second) \ - sub->clone(); \ - } - HANDLE_G; + formula a2 = c.nth(0); + if (a2.is(op::And)) + for (auto c: a2) + xgset.insert(c); + else + xgset.insert(a2); + }; + + if (c.is(op::G)) + { + handle_G(c); } - else if ((a = is_And(c))) + else if (c.is(op::And)) { - unsigned z = a->size(); - for (unsigned m = 0; m < z; ++m) - { - const formula* x = a->nth(m); - if ((g = is_G(x))) - { - HANDLE_G; - } - else - { - if (xset.insert(x).second) - x->clone(); - } - } + for (auto cc: c) + if (cc.is(op::G)) + handle_G(cc); + else + xset.insert(cc); } else { - if (xset.insert(c).second) - c->clone(); + xset.insert(c); } - (*res)[n]->destroy(); - (*res)[n] = 0; + res[n] = 0; } // Make a second pass to check if the "a" // terms can be used to simplify "Xa W b", // "Xa U b", "b | X(b R a)", or "b | X(b M a)". - multop::vec resorig(*res); - for (unsigned n = 0; n < s; ++n) + vec resorig(res); + for (unsigned n = 0; n < mos; ++n) { - const formula* x = resorig[n]; + formula x = resorig[n]; if (!x) continue; fmap_t::const_iterator gs = wuset.find(x); @@ -2925,18 +2002,16 @@ namespace spot for (unsigned pos: gs->second) { - const binop* wu = is_binop(resorig[pos]); - if (wu) + formula wu = resorig[pos]; + if (wu.is(op::W, op::U)) { // a & (Xa W b) = b R a // a & (Xa U b) = b M a - binop::type t = (wu->op() == binop::U) - ? binop::M : binop::R; - const unop* xa = - down_cast(wu->first()); - const formula* a = xa->child()->clone(); - const formula* b = wu->second()->clone(); - (*res)[pos] = binop::instance(t, b, a); + op t = wu.is(op::U) ? op::M : op::R; + assert(wu.nth(0).is(op::X)); + formula a = wu.nth(0).nth(0); + formula b = wu.nth(1); + res[pos] = formula::binop(t, b, a); } else { @@ -2944,61 +2019,49 @@ namespace spot // a & (b | X(b M a)) = b M a wu = is_bXbRM(resorig[pos]); assert(wu); - wu->clone(); - (*res)[pos] = wu; + res[pos] = wu; } // Remember to kill "a". tokill[n] = true; } } - for (unsigned n = 0; n < s; ++n) - if (resorig[n] != (*res)[n]) - resorig[n]->destroy(); - // Make third pass to search for terms 'a' // that also appears as 'XGa'. Replace them // by 'Ga' and delete XGa. - for (unsigned n = 0; n < s; ++n) + for (unsigned n = 0; n < mos; ++n) { - const formula* x = (*res)[n]; + formula x = res[n]; if (!x) continue; fset_t::const_iterator g = xgset.find(x); if (g != xgset.end()) { // x can appear only once. - const formula* gf = *g; + formula gf = *g; xgset.erase(g); - gf->destroy(); - (*res)[n] = unop::instance(unop::G, x); + res[n] = formula::G(x); } else if (tokill[n]) { - (*res)[n]->destroy(); - (*res)[n] = 0; + res[n] = 0; } } - multop::vec* xv = new multop::vec; - size_t xgs = xgset.size(); - xv->reserve(xset.size() + 1); + vec xv; + unsigned xgs = xgset.size(); + xv.reserve(xset.size() + 1); if (xgs > 0) { - multop::vec* xgv = new multop::vec; - xgv->reserve(xgs); - fset_t::iterator i; + vec xgv; + xgv.reserve(xgs); for (auto f: xgset) - xgv->push_back(f); - const formula* gv = - multop::instance(multop::And, xgv); - xv->push_back(unop::instance(unop::G, gv)); + xgv.push_back(f); + xv.emplace_back(unop_multop(op::G, op::And, xgv)); } for (auto f: xset) - xv->push_back(f); - const formula* av = - multop::instance(multop::And, xv); - res->push_back(unop::instance(unop::X, av)); + xv.emplace_back(f); + res.push_back(unop_multop(op::X, op::And, xv)); } // Gather all operands by type. @@ -3012,10 +2075,8 @@ namespace spot res, c_); // FG(a) & FG(b) = FG(a & b) - const formula* allFG = - unop_unop_multop(unop::F, unop::G, multop::And, - s.res_FG); - + formula allFG = unop_unop_multop(op::F, op::G, op::And, + std::move(*s.res_FG)); // Xa & Xb = X(a & b) // Xa & Xb & FG(c) = X(a & b & FG(c)) // For Universal&Eventual formulae f1...fn we also have: @@ -3023,7 +2084,7 @@ namespace spot if (!s.res_X->empty() && !opt_.favor_event_univ) { s.res_X->push_back(allFG); - allFG = 0; + allFG = nullptr; s.res_X->insert(s.res_X->begin(), s.res_EventUniv->begin(), s.res_EventUniv->end()); @@ -3036,47 +2097,45 @@ namespace spot // This gathers eventual&universal formulae // under the same term. { - multop::vec* eu = new multop::vec; + vec eu; bool seen_g = false; for (auto f: *s.res_EventUniv) { - if (f->is_eventual() && f->is_universal()) + if (f.is_eventual() && f.is_universal()) { - if (const unop* g = is_G(f)) + if (f.is(op::G)) { seen_g = true; - eu->push_back(g->child()->clone()); - g->destroy(); + eu.push_back(f.nth(0)); } else { - eu->push_back(f); + eu.push_back(f); } } else - s.res_other->push_back(f); + { + s.res_other->push_back(f); + } } if (seen_g) { - eu->push_back(allFG); - allFG = 0; - s.res_other->push_back(unop_multop(unop::G, - multop::And, + eu.push_back(allFG); + allFG = nullptr; + s.res_other->push_back(unop_multop(op::G, op::And, eu)); } else { s.res_other->insert(s.res_other->end(), - eu->begin(), eu->end()); - delete eu; + eu.begin(), eu.end()); } } - delete s.res_EventUniv; // Xa & Xb & f1...fn = X(a & b & f1...fn) - // is built at the end of this multop::And case. + // is built at the end of this op::And case. // G(a) & G(b) = G(a & b) - // is built at the end of this multop::And case. + // is built at the end of this op::And case. // The following three loops perform these rewritings: // (a U b) & (c U b) = (a & c) U b @@ -3089,9 +2148,7 @@ namespace spot // F(a) & (a M b) = a M b // F(b) & (a W b) = a U b // F(b) & (a U b) = a U b - typedef std::unordered_map> fmap_t; + typedef std::unordered_map fmap_t; fmap_t uwmap; // associates "b" to "a U b" or "a W b" fmap_t rmmap; // associates "a" to "a R b" or "a M b" // (a U b) & (c U b) = (a & c) U b @@ -3100,9 +2157,8 @@ namespace spot for (auto i = s.res_U_or_W->begin(); i != s.res_U_or_W->end(); ++i) { - const binop* bo = static_cast(*i); - const formula* b = bo->second(); - fmap_t::iterator j = uwmap.find(b); + formula b = i->nth(1); + auto j = uwmap.find(b); if (j == uwmap.end()) { // First occurrence. @@ -3110,21 +2166,14 @@ namespace spot continue; } // We already have one occurrence. Merge them. - const binop* old = - static_cast(*j->second); - binop::type op = binop::W; - if (bo->op() == binop::U - || old->op() == binop::U) - op = binop::U; - const formula* fst_arg = - multop::instance(multop::And, - old->first()->clone(), - bo->first()->clone()); - *j->second = binop::instance(op, fst_arg, b->clone()); - assert((*j->second)->kind() == formula::BinOp); - *i = 0; - old->destroy(); - bo->destroy(); + formula old = *j->second; + op o = op::W; + if (i->is(op::U) || old.is(op::U)) + o = op::U; + formula fst_arg = formula::And({old.nth(0), i->nth(0)}); + *j->second = formula::binop(o, fst_arg, b); + assert(j->second->is(o)); + *i = nullptr; } // (a R b) & (a R c) = a R (b & c) // (a R b) & (a M c) = a M (b & c) @@ -3132,9 +2181,8 @@ namespace spot for (auto i = s.res_R_or_M->begin(); i != s.res_R_or_M->end(); ++i) { - const binop* bo = static_cast(*i); - const formula* a = bo->first(); - fmap_t::iterator j = rmmap.find(a); + formula a = i->nth(0); + auto j = rmmap.find(a); if (j == rmmap.end()) { // First occurrence. @@ -3142,72 +2190,48 @@ namespace spot continue; } // We already have one occurrence. Merge them. - const binop* old = - static_cast(*j->second); - binop::type op = binop::R; - if (bo->op() == binop::M - || old->op() == binop::M) - op = binop::M; - const formula* snd_arg = - multop::instance(multop::And, - old->second()->clone(), - bo->second()->clone()); - *j->second = binop::instance(op, a->clone(), snd_arg); - assert((*j->second)->kind() == formula::BinOp); - *i = 0; - old->destroy(); - bo->destroy(); + formula old = *j->second; + op o = op::R; + if (i->is(op::M) || old.is(op::M)) + o = op::M; + formula snd_arg = formula::And({old.nth(1), i->nth(1)}); + *j->second = formula::binop(o, a, snd_arg); + assert(j->second->is(o)); + *i = nullptr; } - // F(a) & (a R b) = a M b - // F(a) & (a M b) = a M b // F(b) & (a W b) = a U b // F(b) & (a U b) = a U b - for (auto i = s.res_F->begin(); - i != s.res_F->end(); ++i) + // F(a) & (a R b) = a M b + // F(a) & (a M b) = a M b + for (auto& f: *s.res_F) { bool superfluous = false; - const unop* uo = static_cast(*i); - const formula* c = uo->child(); + formula c = f.nth(0); fmap_t::iterator j = uwmap.find(c); if (j != uwmap.end()) { superfluous = true; - const binop* bo = - static_cast(*j->second); - if (bo->op() == binop::W) + formula bo = *j->second; + if (bo.is(op::W)) { - *j->second = - binop::instance(binop::U, - bo->first()->clone(), - bo->second()->clone()); - assert((*j->second)->kind() - == formula::BinOp); - bo->destroy(); + *j->second = formula::U(bo.nth(0), bo.nth(1)); + assert(j->second->is(op::U)); } } j = rmmap.find(c); if (j != rmmap.end()) { superfluous = true; - const binop* bo = - static_cast(*j->second); - if (bo->op() == binop::R) + formula bo = *j->second; + if (bo.is(op::R)) { - *j->second = - binop::instance(binop::M, - bo->first()->clone(), - bo->second()->clone()); - assert((*j->second)->kind() - == formula::BinOp); - bo->destroy(); + *j->second = formula::M(bo.nth(0), bo.nth(1)); + assert(j->second->is(op::M)); } } if (superfluous) - { - (*i)->destroy(); - *i = 0; - } + f = nullptr; } s.res_other->reserve(s.res_other->size() @@ -3218,15 +2242,12 @@ namespace spot s.res_other->insert(s.res_other->end(), s.res_F->begin(), s.res_F->end()); - delete s.res_F; s.res_other->insert(s.res_other->end(), s.res_U_or_W->begin(), s.res_U_or_W->end()); - delete s.res_U_or_W; s.res_other->insert(s.res_other->end(), s.res_R_or_M->begin(), s.res_R_or_M->end()); - delete s.res_R_or_M; // Those "G" formulae that are eventual can be // postponed inside the X term if there is one. @@ -3235,341 +2256,234 @@ namespace spot // Xa&Xb&GFc&GFd&Ge as X(a&b&G(Fc&Fd))&Ge if (!s.res_X->empty() && !opt_.favor_event_univ) { - multop::vec* event = new multop::vec; + vec event; for (auto& f: *s.res_G) - if (f->is_eventual()) + if (f.is_eventual()) { - event->push_back(f); - f = 0; // Remove it from res_G. + event.push_back(f); + f = nullptr; // Remove it from res_G. } - s.res_X->push_back(unop_multop(unop::G, - multop::And, event)); + s.res_X->push_back(unop_multop(op::G, op::And, + std::move(event))); } // G(a) & G(b) & ... = G(a & b & ...) - const formula* allG = - unop_multop(unop::G, multop::And, s.res_G); + formula allG = unop_multop(op::G, op::And, + std::move(*s.res_G)); // Xa & Xb & ... = X(a & b & ...) - const formula* allX = - unop_multop(unop::X, multop::And, s.res_X); - + formula allX = unop_multop(op::X, op::And, + std::move(*s.res_X)); s.res_other->push_back(allX); s.res_other->push_back(allG); s.res_other->push_back(allFG); - result_ = multop::instance(multop::And, s.res_other); + formula r = formula::And(std::move(*s.res_other)); // If we altered the formula in some way, process // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; + if (r != mo) + return recurse(r); + return r; } - break; - case multop::AndRat: + case op::AndRat: { mospliter s(mospliter::Split_Bool, res, c_); if (!s.res_Bool->empty()) { // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 - const formula* b = - multop::instance(multop::And, s.res_Bool); + formula b = formula::And(std::move(*s.res_Bool)); - multop::vec* ares = new multop::vec; + vec ares; for (auto& f: *s.res_other) - switch (f->kind()) + switch (f.kind()) { - case formula::BUnOp: + case op::Star: + // b && r[*i..j] = b & r if i<=1<=j + // = 0 otherwise + if (f.min() > 1 || f.max() < 1) + return formula::ff(); + ares.push_back(f.nth(0)); + f = nullptr; + break; + case op::Fusion: + // b && {r1:..:rn} = b && r1 && .. && rn + for (auto ri: f) + ares.push_back(ri); + f = nullptr; + break; + case op::Concat: + // b && {r1;...;rn} = + // - b && ri if there is only one ri + // that does not accept [*0] + // - b && (r1|...|rn) if all ri + // do not accept [*0] + // - 0 if more than one ri accept [*0] { - const bunop* r = down_cast(f); - // b && r[*i..j] = b & r if i<=1<=j - // = 0 otherwise - switch (r->op()) + formula ri = nullptr; + unsigned nonempty = 0; + unsigned rs = f.size(); + for (unsigned j = 0; j < rs; ++j) { - case bunop::Star: - if (r->min() > 1 || r->max() < 1) - goto returnfalse; - ares->push_back(r->child()->clone()); - r->destroy(); - f = 0; - break; - case bunop::FStar: - goto common; + formula jf = f.nth(j); + if (!jf.accepts_eword()) + { + ri = jf; + ++nonempty; + } } - break; - } - case formula::MultOp: - { - const multop* r = down_cast(f); - unsigned rs = r->size(); - switch (r->op()) + if (nonempty == 1) { - case multop::Fusion: - //b && {r1:..:rn} = b && r1 && .. && rn - for (unsigned j = 0; j < rs; ++j) - ares->push_back(r->nth(j)->clone()); - r->destroy(); - f = 0; - break; - case multop::Concat: - // b && {r1;...;rn} = - // - b && ri if there is only one ri - // that does not accept [*0] - // - b && (r1|...|rn) if all ri - // do not accept [*0] - // - 0 if more than one ri accept [*0] - { - const formula* ri = 0; - unsigned nonempty = 0; - for (unsigned j = 0; j < rs; ++j) - { - const formula* jf = r->nth(j); - if (!jf->accepts_eword()) - { - ri = jf; - ++nonempty; - } - } - if (nonempty == 1) - { - ares->push_back(ri->clone()); - } - else if (nonempty == 0) - { - multop::vec* sum = new multop::vec; - for (unsigned j = 0; j < rs; ++j) - sum->push_back(r->nth(j) - ->clone()); - const formula* sumf = - multop::instance(multop::OrRat, - sum); - ares->push_back(sumf); - } - else - { - goto returnfalse; - } - r->destroy(); - f = 0; - break; - } - default: - goto common; + ares.push_back(ri); } + else if (nonempty == 0) + { + vec sum; + for (auto j: f) + sum.push_back(j); + ares.emplace_back(formula::OrRat(sum)); + } + else + { + return formula::ff(); + } + f = nullptr; break; } default: - common: - ares->push_back(f); - f = 0; + ares.push_back(f); + f = nullptr; break; } - delete s.res_other; - ares->push_back(b); - result_ = multop::instance(multop::AndRat, ares); + ares.push_back(b); + auto r = formula::AndRat(std::move(ares)); // If we altered the formula in some way, process // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; - returnfalse: - b->destroy(); - for (auto f: *s.res_other) - if (f) - f->destroy(); - delete s.res_other; - for (auto f: *ares) - f->destroy(); - delete ares; - result_ = constant::false_instance(); - return; + if (r != mo) + return recurse(r); + return r; } - else + // No Boolean as argument of &&. + + // Look for occurrences of {b;r} or {b:r}. We have + // {b1;r1}&&{b2;r2} = {b1&&b2};{r1&&r2} + // head1 tail1 + // {b1:r1}&&{b2:r2} = {b1&&b2}:{r1&&r2} + // head2 tail2 + vec head1; + vec tail1; + vec head2; + vec tail2; + for (auto& i: *s.res_other) { - // No Boolean as argument of &&. - delete s.res_Bool; - - // Look for occurrences of {b;r} or {b:r}. We have - // {b1;r1}&&{b2;r2} = {b1&&b2};{r1&&r2} - // head1 tail1 - // {b1:r1}&&{b2:r2} = {b1&&b2}:{r1&&r2} - // head2 tail2 - - multop::vec* head1 = new multop::vec; - multop::vec* tail1 = new multop::vec; - multop::vec* head2 = new multop::vec; - multop::vec* tail2 = new multop::vec; - for (auto& i: *s.res_other) + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + formula h = i.nth(0); + if (!h.is_boolean()) + continue; + if (i.is(op::Concat)) { - if (!i) - continue; - if (i->kind() != formula::MultOp) - continue; - const multop* f = down_cast(i); - const formula* h = f->nth(0); - if (!h->is_boolean()) - continue; - multop::type op = f->op(); - if (op == multop::Concat) - { - head1->push_back(h->clone()); - tail1->push_back(f->all_but(0)); - i->destroy(); - i = 0; - } - else if (op == multop::Fusion) - { - head2->push_back(h->clone()); - tail2->push_back(f->all_but(0)); - i->destroy(); - i = 0; - } - else - { - continue; - } + head1.push_back(h); + tail1.push_back(i.all_but(0)); } - if (!head1->empty()) + else // op::Fusion { - const formula* h = - multop::instance(multop::And, head1); - const formula* t = - multop::instance(multop::AndRat, tail1); - const formula* f = - multop::instance(multop::Concat, h, t); - s.res_other->push_back(f); + head2.push_back(h); + tail2.push_back(i.all_but(0)); } - else - { - delete head1; - delete tail1; - } - if (!head2->empty()) - { - const formula* h = - multop::instance(multop::And, head2); - const formula* t = - multop::instance(multop::AndRat, tail2); - const formula* f = - multop::instance(multop::Fusion, h, t); - s.res_other->push_back(f); - } - else - { - delete head2; - delete tail2; - } - - // {r1;b1}&&{r2;b2} = {r1&&r2};{b1∧b2} - // head3 tail3 - // {r1:b1}&&{r2:b2} = {r1&&r2}:{b1∧b2} - // head4 tail4 - multop::vec* head3 = new multop::vec; - multop::vec* tail3 = new multop::vec; - multop::vec* head4 = new multop::vec; - multop::vec* tail4 = new multop::vec; - for (auto& i: *s.res_other) - { - if (!i) - continue; - if (i->kind() != formula::MultOp) - continue; - const multop* f = down_cast(i); - unsigned s = f->size() - 1; - const formula* t = f->nth(s); - if (!t->is_boolean()) - continue; - multop::type op = f->op(); - if (op == multop::Concat) - { - tail3->push_back(t->clone()); - head3->push_back(f->all_but(s)); - i->destroy(); - i = 0; - } - else if (op == multop::Fusion) - { - tail4->push_back(t->clone()); - head4->push_back(f->all_but(s)); - i->destroy(); - i = 0; - } - else - { - continue; - } - } - if (!head3->empty()) - { - const formula* h = - multop::instance(multop::AndRat, head3); - const formula* t = - multop::instance(multop::And, tail3); - const formula* f = - multop::instance(multop::Concat, h, t); - s.res_other->push_back(f); - } - else - { - delete head3; - delete tail3; - } - if (!head4->empty()) - { - const formula* h = - multop::instance(multop::AndRat, head4); - const formula* t = - multop::instance(multop::And, tail4); - const formula* f = - multop::instance(multop::Fusion, h, t); - s.res_other->push_back(f); - } - else - { - delete head4; - delete tail4; - } - - result_ = - multop::instance(multop::AndRat, s.res_other); - // If we altered the formula in some way, process - // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; + i = nullptr; } + if (!head1.empty()) + { + formula h = formula::And(std::move(head1)); + formula t = formula::AndRat(std::move(tail1)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head2.empty()) + { + formula h = formula::And(std::move(head2)); + formula t = formula::AndRat(std::move(tail2)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + // {r1;b1}&&{r2;b2} = {r1&&r2};{b1∧b2} + // head3 tail3 + // {r1:b1}&&{r2:b2} = {r1&&r2}:{b1∧b2} + // head4 tail4 + vec head3; + vec tail3; + vec head4; + vec tail4; + for (auto& i: *s.res_other) + { + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + unsigned s = i.size() - 1; + formula t = i.nth(s); + if (!t.is_boolean()) + continue; + if (i.is(op::Concat)) + { + tail3.push_back(t); + head3.push_back(i.all_but(s)); + } + else // op::Fusion + { + tail4.push_back(t); + head4.push_back(i.all_but(s)); + } + i = nullptr; + } + if (!head3.empty()) + { + formula h = formula::AndRat(std::move(head3)); + formula t = formula::And(std::move(tail3)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head4.empty()) + { + formula h = formula::AndRat(std::move(head4)); + formula t = formula::And(std::move(tail4)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + auto r = formula::AndRat(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; } - case multop::Or: + case op::Or: { // a | X(F(a) | c...) = Fa | X(c...) // a | (Xa R b) = b W a // a | (Xa M b) = b U a // a | (b & X(b W a)) = b W a // a | (b & X(b U a)) = b U a - if (!mo->is_syntactic_stutter_invariant()) // Skip if no X + if (!mo.is_syntactic_stutter_invariant()) // Skip if no X { - typedef std::unordered_set> fset_t; - typedef std::unordered_map, - ptr_hash> fmap_t; + typedef std::unordered_set fset_t; + typedef std::unordered_map> fmap_t; fset_t xfset; // XF(...) fset_t xset; // X(...) fmap_t rmset; // (X...)R(...) or (X...)M(...) or // b & X(b W ...) or b & X(b U ...) - unsigned s = res->size(); - std::vector tokill(s); + std::vector tokill(mos); // Make a pass to search for subterms // of the form XFa or X(... | F(...|a|...) | ...) - for (unsigned n = 0; n < s; ++n) + for (unsigned n = 0; n < mos; ++n) { - if (!(*res)[n]) + if (!res[n]) continue; - if ((*res)[n]->is_syntactic_stutter_invariant()) + if (res[n].is_syntactic_stutter_invariant()) continue; - const formula* xarg = is_XRM((*res)[n]); - if (xarg) + if (formula xarg = is_XRM(res[n])) { rmset[xarg].insert(n); continue; @@ -3579,74 +2493,52 @@ namespace spot // - X(...) // - b & X(b W ...) // - b & X(b U ...) - - const binop* barg = is_bXbWU((*res)[n]); - if (barg) + if (formula barg = is_bXbWU(res[n])) { - rmset[barg->second()].insert(n); + rmset[barg.nth(1)].insert(n); continue; } - const unop* uo = is_X((*res)[n]); - if (!uo) + if (!res[n].is(op::X)) continue; - const formula* c = uo->child(); - const multop* o; - const unop* f; - if ((f = is_F(c))) + formula c = res[n].nth(0); + + auto handle_F = [&xfset](formula c) { -#define HANDLE_F const multop* o2; \ - if ((o2 = is_Or(f->child()))) \ - { \ - unsigned y = o2->size(); \ - for (unsigned n = 0; n < y; ++n) \ - { \ - const formula* sub = o2->nth(n); \ - if (xfset.insert(sub).second) \ - sub->clone(); \ - } \ - } \ - else \ - { \ - const formula* sub = f->child(); \ - if (xfset.insert(sub).second) \ - sub->clone(); \ - } - HANDLE_F; + formula a2 = c.nth(0); + if (a2.is(op::Or)) + for (auto c: a2) + xfset.insert(c); + else + xfset.insert(a2); + }; + + if (c.is(op::F)) + { + handle_F(c); } - else if ((o = is_Or(c))) + else if (c.is(op::Or)) { - unsigned z = o->size(); - for (unsigned m = 0; m < z; ++m) - { - const formula* x = o->nth(m); - if ((f = is_F(x))) - { - HANDLE_F; - } - else - { - if (xset.insert(x).second) - x->clone(); - } - } + for (auto cc: c) + if (cc.is(op::F)) + handle_F(cc); + else + xset.insert(cc); } else { - if (xset.insert(c).second) - c->clone(); + xset.insert(c); } - (*res)[n]->destroy(); - (*res)[n] = 0; + res[n] = nullptr; } // Make a second pass to check if we can // remove all instance of XF(a). unsigned allofthem = xfset.size(); - multop::vec resorig(*res); - for (unsigned n = 0; n < s; ++n) + vec resorig(res); + for (unsigned n = 0; n < mos; ++n) { - const formula* x = resorig[n]; + formula x = resorig[n]; if (!x) continue; fset_t::const_iterator f = xfset.find(x); @@ -3661,18 +2553,16 @@ namespace spot continue; for (unsigned pos: gs->second) { - const binop* rm = is_binop(resorig[pos]); - if (rm) + formula rm = resorig[pos]; + if (rm.is(op::M, op::R)) { // a | (Xa R b) = b W a // a | (Xa M b) = b U a - binop::type t = (rm->op() == binop::M) - ? binop::U : binop::W; - const unop* xa = - down_cast(rm->first()); - const formula* a = xa->child()->clone(); - const formula* b = rm->second()->clone(); - (*res)[pos] = binop::instance(t, b, a); + op t = rm.is(op::M) ? op::U : op::W; + assert(rm.nth(0).is(op::X)); + formula a = rm.nth(0).nth(0); + formula b = rm.nth(1); + res[pos] = formula::binop(t, b, a); } else { @@ -3680,70 +2570,57 @@ namespace spot // a | (b & X(b U a)) = b U a rm = is_bXbWU(resorig[pos]); assert(rm); - rm->clone(); - (*res)[pos] = rm; + res[pos] = rm; } // Remember to kill "a". tokill[n] = true; } } - for (unsigned n = 0; n < s; ++n) - if (resorig[n] != (*res)[n]) - resorig[n]->destroy(); - - // If we can remove all of them... if (allofthem == 0) // Make third pass to search for terms 'a' // that also appears as 'XFa'. Replace them // by 'Fa' and delete XFa. - for (unsigned n = 0; n < s; ++n) + for (unsigned n = 0; n < mos; ++n) { - const formula* x = (*res)[n]; + formula x = res[n]; if (!x) continue; fset_t::const_iterator f = xfset.find(x); if (f != xfset.end()) { // x can appear only once. - const formula* ff = *f; + formula ff = *f; xfset.erase(f); - ff->destroy(); - (*res)[n] = unop::instance(unop::F, x); + res[n] = formula::F(x); // We don't need to kill "a" anymore. tokill[n] = false; } } // Kill any remaining "a", used to simplify Xa R b // or Xa M b. - for (unsigned n = 0; n < s; ++n) - if (tokill[n] && (*res)[n]) - { - (*res)[n]->destroy(); - (*res)[n] = 0; - } + for (unsigned n = 0; n < mos; ++n) + if (tokill[n] && res[n]) + res[n] = nullptr; // Now rebuild the formula that remains. - multop::vec* xv = new multop::vec; + vec xv; size_t xfs = xfset.size(); - xv->reserve(xset.size() + 1); + xv.reserve(xset.size() + 1); if (xfs > 0) { // Group all XF(a)|XF(b|c|...)|... as XF(a|b|c|...) - multop::vec* xfv = new multop::vec; - xfv->reserve(xfs); + vec xfv; + xfv.reserve(xfs); for (auto f: xfset) - xfv->push_back(f); - const formula* fv = - multop::instance(multop::Or, xfv); - xv->push_back(unop::instance(unop::F, fv)); + xfv.push_back(f); + xv.push_back(unop_multop(op::F, op::Or, xfv)); } // Also gather the remaining Xa | X(b|c) as X(b|c). for (auto f: xset) - xv->push_back(f); - const formula* ov = multop::instance(multop::Or, xv); - res->push_back(unop::instance(unop::X, ov)); + xv.push_back(f); + res.push_back(unop_multop(op::X, op::Or, xv)); } // Gather all operand by type. @@ -3756,8 +2633,9 @@ namespace spot mospliter::Split_EventUniv, res, c_); // GF(a) | GF(b) = GF(a | b) - const formula* allGF = - unop_unop_multop(unop::G, unop::F, multop::Or, s.res_GF); + formula allGF = unop_unop_multop(op::G, op::F, op::Or, + std::move(*s.res_GF)); + // Xa | Xb = X(a | b) // Xa | Xb | GF(c) = X(a | b | GF(c)) // For Universal&Eventual formula f1...fn we also have: @@ -3765,7 +2643,7 @@ namespace spot if (!s.res_X->empty() && !opt_.favor_event_univ) { s.res_X->push_back(allGF); - allGF = 0; + allGF = nullptr; s.res_X->insert(s.res_X->end(), s.res_EventUniv->begin(), s.res_EventUniv->end()); @@ -3799,7 +2677,7 @@ namespace spot // or, degeneralizing the automaton amplifies // these differences) s.res_F->push_back(allGF); - allGF = 0; + allGF = nullptr; s.res_F->insert(s.res_F->end(), s.res_EventUniv->begin(), s.res_EventUniv->end()); @@ -3807,7 +2685,7 @@ namespace spot else if (opt_.favor_event_univ) { s.res_EventUniv->push_back(allGF); - allGF = 0; + allGF = nullptr; bool seen_f = false; if (s.res_EventUniv->size() > 1) { @@ -3815,27 +2693,23 @@ namespace spot // with an F, Gather them all under the // same F. Striping any leading F. for (auto& f: *s.res_EventUniv) - if (const unop* u = is_F(f)) + if (f.is(op::F)) { - f = u->child()->clone(); - u->destroy(); seen_f = true; + f = f.nth(0); } if (seen_f) { - const formula* eu = - unop_multop(unop::F, multop::Or, - s.res_EventUniv); - s.res_EventUniv = 0; + formula eu = + unop_multop(op::F, op::Or, + std::move(*s.res_EventUniv)); s.res_other->push_back(eu); } } if (!seen_f) - { - s.res_other->insert(s.res_other->end(), - s.res_EventUniv->begin(), - s.res_EventUniv->end()); - } + s.res_other->insert(s.res_other->end(), + s.res_EventUniv->begin(), + s.res_EventUniv->end()); } else { @@ -3843,7 +2717,6 @@ namespace spot s.res_EventUniv->begin(), s.res_EventUniv->end()); } - delete s.res_EventUniv; // Xa | Xb | f1...fn = X(a | b | f1...fn) // is built at the end of this multop::Or case. // F(a) | F(b) = F(a | b) @@ -3860,9 +2733,7 @@ namespace spot // G(a) | (a W b) = a W b // G(b) | (a R b) = a R b. // G(b) | (a M b) = a R b. - typedef std::unordered_map> fmap_t; + typedef std::unordered_map fmap_t; fmap_t uwmap; // associates "a" to "a U b" or "a W b" fmap_t rmmap; // associates "b" to "a R b" or "a M b" // (a U b) | (a U c) = a U (b | c) @@ -3871,9 +2742,8 @@ namespace spot for (auto i = s.res_U_or_W->begin(); i != s.res_U_or_W->end(); ++i) { - const binop* bo = static_cast(*i); - const formula* a = bo->first(); - fmap_t::iterator j = uwmap.find(a); + formula a = i->nth(0); + auto j = uwmap.find(a); if (j == uwmap.end()) { // First occurrence. @@ -3881,21 +2751,14 @@ namespace spot continue; } // We already have one occurrence. Merge them. - const binop* old = - static_cast(*j->second); - binop::type op = binop::U; - if (bo->op() == binop::W - || old->op() == binop::W) - op = binop::W; - const formula* snd_arg = - multop::instance(multop::Or, - old->second()->clone(), - bo->second()->clone()); - *j->second = binop::instance(op, a->clone(), snd_arg); - assert((*j->second)->kind() == formula::BinOp); - *i = 0; - old->destroy(); - bo->destroy(); + formula old = *j->second; + op o = op::U; + if (i->is(op::W) || old.is(op::W)) + o = op::W; + formula snd_arg = formula::Or({old.nth(1), i->nth(1)}); + *j->second = formula::binop(o, a, snd_arg); + assert(j->second->is(o)); + *i = nullptr; } // (a R b) | (c R b) = (a | c) R b // (a R b) | (c M b) = (a | c) R b @@ -3903,9 +2766,8 @@ namespace spot for (auto i = s.res_R_or_M->begin(); i != s.res_R_or_M->end(); ++i) { - const binop* bo = static_cast(*i); - const formula* b = bo->second(); - fmap_t::iterator j = rmmap.find(b); + formula b = i->nth(1); + auto j = rmmap.find(b); if (j == rmmap.end()) { // First occurrence. @@ -3913,21 +2775,14 @@ namespace spot continue; } // We already have one occurrence. Merge them. - const binop* old = - static_cast(*j->second); - binop::type op = binop::M; - if (bo->op() == binop::R - || old->op() == binop::R) - op = binop::R; - const formula* fst_arg = - multop::instance(multop::Or, - old->first()->clone(), - bo->first()->clone()); - *j->second = binop::instance(op, fst_arg, b->clone()); - assert((*j->second)->kind() == formula::BinOp); - *i = 0; - old->destroy(); - bo->destroy(); + formula old = *j->second; + op o = op::M; + if (i->is(op::R) || old.is(op::R)) + o = op::R; + formula fst_arg = formula::Or({old.nth(0), i->nth(0)}); + *j->second = formula::binop(o, fst_arg, b); + assert(j->second->is(o)); + *i = nullptr; } // G(a) | (a U b) = a W b // G(a) | (a W b) = a W b @@ -3936,46 +2791,32 @@ namespace spot for (auto& f: *s.res_G) { bool superfluous = false; - const unop* uo = static_cast(f); - const formula* c = uo->child(); + formula c = f.nth(0); fmap_t::iterator j = uwmap.find(c); if (j != uwmap.end()) { superfluous = true; - const binop* bo = - static_cast(*j->second); - if (bo->op() == binop::U) + formula bo = *j->second; + if (bo.is(op::U)) { - *j->second = - binop::instance(binop::W, - bo->first()->clone(), - bo->second()->clone()); - assert((*j->second)->kind() == formula::BinOp); - bo->destroy(); + *j->second = formula::W(bo.nth(0), bo.nth(1)); + assert(j->second->is(op::W)); } } j = rmmap.find(c); if (j != rmmap.end()) { superfluous = true; - const binop* bo = - static_cast(*j->second); - if (bo->op() == binop::M) + formula bo = *j->second; + if (bo.is(op::M)) { - *j->second = - binop::instance(binop::R, - bo->first()->clone(), - bo->second()->clone()); - assert((*j->second)->kind() == formula::BinOp); - bo->destroy(); + *j->second = formula::R(bo.nth(0), bo.nth(1)); + assert(j->second->is(op::R)); } } if (superfluous) - { - f->destroy(); - f = 0; - } + f = nullptr; } s.res_other->reserve(s.res_other->size() @@ -3986,15 +2827,12 @@ namespace spot s.res_other->insert(s.res_other->end(), s.res_G->begin(), s.res_G->end()); - delete s.res_G; s.res_other->insert(s.res_other->end(), s.res_U_or_W->begin(), s.res_U_or_W->end()); - delete s.res_U_or_W; s.res_other->insert(s.res_other->end(), s.res_R_or_M->begin(), s.res_R_or_M->end()); - delete s.res_R_or_M; // Those "F" formulae that are universal can be // postponed inside the X term if there is one. @@ -4003,208 +2841,144 @@ namespace spot // Xa|Xb|FGc|FGd|Fe as X(a|b|F(Gc|Gd))|Fe if (!s.res_X->empty()) { - multop::vec* univ = new multop::vec; + vec univ; for (auto& f: *s.res_F) - if (f->is_universal()) + if (f.is_universal()) { - univ->push_back(f); - f = 0; // Remove it from res_F. + univ.push_back(f); + f = nullptr; // Remove it from res_F. } - s.res_X->push_back(unop_multop(unop::F, - multop::Or, univ)); + s.res_X->push_back(unop_multop(op::F, op::Or, + std::move(univ))); } // F(a) | F(b) | ... = F(a | b | ...) - const formula* allF = - unop_multop(unop::F, multop::Or, s.res_F); + formula allF = unop_multop(op::F, op::Or, + std::move(*s.res_F)); // Xa | Xb | ... = X(a | b | ...) - const formula* allX = - unop_multop(unop::X, multop::Or, s.res_X); - + formula allX = unop_multop(op::X, op::Or, + std::move(*s.res_X)); s.res_other->push_back(allX); s.res_other->push_back(allF); s.res_other->push_back(allGF); - result_ = multop::instance(multop::Or, s.res_other); + formula r = formula::Or(std::move(*s.res_other)); // If we altered the formula in some way, process // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; + if (r != mo) + return recurse(r); + return r; } - case multop::OrRat: - // FIXME: No simplifications yet. - break; - case multop::AndNLM: + case op::AndNLM: { mospliter s(mospliter::Split_Bool, res, c_); if (!s.res_Bool->empty()) { // b1 & b2 & b3 = b1 ∧ b2 ∧ b3 - const formula* b = - multop::instance(multop::And, s.res_Bool); + formula b = formula::And(std::move(*s.res_Bool)); // now we just consider b & rest - const formula* rest = - multop::instance(multop::AndNLM, - s.res_other); + formula rest = formula::AndNLM(std::move(*s.res_other)); // We have b & rest = b : rest if rest does not // accept [*0]. Otherwise b & rest = b | (b : rest) // FIXME: It would be nice to remove [*0] from rest. - if (rest->accepts_eword()) + formula r = nullptr; + if (rest.accepts_eword()) { // The b & rest = b | (b : rest) rewriting // augment the size, so do that only when // explicitly requested. if (!opt_.reduce_size_strictly) - { - const formula* brest = - multop::instance(multop::Fusion, b->clone(), - rest); - result_ = - multop::instance(multop::OrRat, b, brest); - } + return recurse(formula::OrRat + ({b, formula::Fusion({b, rest})})); else - { - result_ = multop::instance(multop::AndNLM, - b, rest); - } + return mo; } else { - result_ = multop::instance(multop::Fusion, - b, rest); + return recurse(formula::Fusion({b, rest})); } - // If we altered the formula in some way, process - // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; } - else + // No Boolean as argument of &&. + + // Look for occurrences of {b;r} or {b:r}. We have + // {b1;r1}&{b2;r2} = {b1∧b2};{r1&r2} + // head1 tail1 + // {b1:r1}&{b2:r2} = {b1∧b2}:{r1&r2} + // head2 tail2 + // BEWARE: The second rule is correct only when + // both r1 and r2 do not accept [*0]. + + vec head1; + vec tail1; + vec head2; + vec tail2; + for (auto& i: *s.res_other) { - // No Boolean as argument of &&. - delete s.res_Bool; - - // Look for occurrences of {b;r} or {b:r}. We have - // {b1;r1}&{b2;r2} = {b1∧b2};{r1&r2} - // head1 tail1 - // {b1:r1}&{b2:r2} = {b1∧b2}:{r1&r2} - // head2 tail2 - // BEWARE: The second rule is correct only when - // both r1 and r2 do not accept [*0]. - - multop::vec* head1 = new multop::vec; - multop::vec* tail1 = new multop::vec; - multop::vec* head2 = new multop::vec; - multop::vec* tail2 = new multop::vec; - for (auto& i: *s.res_other) + if (!i) + continue; + if (!i.is(op::Concat, op::Fusion)) + continue; + formula h = i.nth(0); + if (!h.is_boolean()) + continue; + if (i.is(op::Concat)) { - if (!i) + head1.push_back(h); + tail1.push_back(i.all_but(0)); + } + else // op::Fusion + { + formula t = i.all_but(0); + if (t.accepts_eword()) continue; - if (i->kind() != formula::MultOp) - continue; - const multop* f = down_cast(i); - const formula* h = f->nth(0); - if (!h->is_boolean()) - continue; - multop::type op = f->op(); - if (op == multop::Concat) - { - head1->push_back(h->clone()); - tail1->push_back(f->all_but(0)); - i->destroy(); - i = 0; - } - else if (op == multop::Fusion) - { - const formula* t = f->all_but(0); - if (t->accepts_eword()) - { - t->destroy(); - continue; - } - head2->push_back(h->clone()); - tail2->push_back(t); - i->destroy(); - i = 0; - } - else - { - continue; - } + head2.push_back(h); + tail2.push_back(t); } - if (!head1->empty()) - { - const formula* h = - multop::instance(multop::And, head1); - const formula* t = - multop::instance(multop::AndNLM, tail1); - const formula* f = - multop::instance(multop::Concat, h, t); - s.res_other->push_back(f); - } - else - { - delete head1; - delete tail1; - } - if (!head2->empty()) - { - const formula* h = - multop::instance(multop::And, head2); - const formula* t = - multop::instance(multop::AndNLM, tail2); - const formula* f = - multop::instance(multop::Fusion, h, t); - s.res_other->push_back(f); - } - else - { - delete head2; - delete tail2; - } - - result_ = multop::instance(multop::AndNLM, s.res_other); - // If we altered the formula in some way, process - // it another time. - if (result_ != mo) - result_ = recurse_destroy(result_); - return; + i = nullptr; } - break; + if (!head1.empty()) + { + formula h = formula::And(std::move(head1)); + formula t = formula::AndNLM(std::move(tail1)); + s.res_other->push_back(formula::Concat({h, t})); + } + if (!head2.empty()) + { + formula h = formula::And(std::move(head2)); + formula t = formula::AndNLM(std::move(tail2)); + s.res_other->push_back(formula::Fusion({h, t})); + } + + formula r = formula::AndNLM(std::move(*s.res_other)); + // If we altered the formula in some way, process + // it another time. + if (r != mo) + return recurse(r); + return r; } - case multop::Concat: - case multop::Fusion: - break; + case op::OrRat: + case op::Concat: + case op::Fusion: + // FIXME: No simplifications yet. + return mo; + default: + SPOT_UNIMPLEMENTED(); + return nullptr; } + SPOT_UNREACHABLE(); } - result_ = multop::instance(mo->op(), res); - } - - const formula* - recurse(const formula* f) - { - return simplify_recursively(f, c_); - } - - const formula* - recurse_destroy(const formula* f) - { - const formula* tmp = recurse(f); - f->destroy(); - return tmp; + return mo; } protected: - const formula* result_; ltl_simplifier_cache* c_; const ltl_simplifier_options& opt_; }; - const formula* - simplify_recursively(const formula* f, + formula + simplify_recursively(formula f, ltl_simplifier_cache* c) { #ifdef TRACE @@ -4214,7 +2988,7 @@ namespace spot trace << "** simplify_recursively(" << str_psl(f) << ')'; #endif - const formula* result = c->lookup_simplified(f); + formula result = c->lookup_simplified(f); if (result) { trace << " cached: " << str_psl(result) << std::endl; @@ -4229,15 +3003,14 @@ namespace spot ++srec; #endif - if (f->is_boolean() && c->options.boolean_to_isop) + if (f.is_boolean() && c->options.boolean_to_isop) { result = c->boolean_to_isop(f); } else { simplify_visitor v(c); - f->accept(v); - result = v.result(); + result = v.visit(f); } #ifdef TRACE @@ -4252,7 +3025,7 @@ namespace spot return result; } - } + } // anonymous namespace ////////////////////////////////////////////////////////////////////// // ltl_simplifier_cache @@ -4263,378 +3036,269 @@ namespace spot // appendix in the documentation for temporal logic operators.) inline bool - ltl_simplifier_cache::syntactic_implication_aux(const formula* f, - const formula* g) + ltl_simplifier_cache::syntactic_implication_aux(formula f, formula g) { - formula::opkind fk = f->kind(); - formula::opkind gk = g->kind(); - // We first process all lines from the table except the // first two, and then we process the first two as a fallback. // // However for Boolean formulas we skip the bottom lines // (keeping only the first one) to prevent them from being // further split. - if (!f->is_boolean()) + if (!f.is_boolean()) // Deal with all lines of the table except the first two. - switch (fk) + switch (f.kind()) { - case formula::Constant: - case formula::AtomicProp: - case formula::BUnOp: + case op::X: + if (g.is_eventual() && syntactic_implication(f.nth(0), g)) + return true; + if (g.is(op::X) && syntactic_implication(f.nth(0), g.nth(0))) + return true; break; - case formula::UnOp: - { - const unop* f_ = down_cast(f); - unop::type fo = f_->op(); + case op::F: + if (g.is_eventual() && syntactic_implication(f.nth(0), g)) + return true; + break; - if ((fo == unop::X || fo == unop::F) && g->is_eventual() - && syntactic_implication(f_->child(), g)) + case op::G: + if (g.is(op::U, op::R) && syntactic_implication(f.nth(0), g.nth(1))) + return true; + if (g.is(op::W) && (syntactic_implication(f.nth(0), g.nth(0)) + || syntactic_implication(f.nth(0), g.nth(1)))) + return true; + if (g.is(op::M) && (syntactic_implication(f.nth(0), g.nth(0)) + && syntactic_implication(f.nth(0), g.nth(1)))) + return true; + // First column. + if (syntactic_implication(f.nth(0), g)) + return true; + break; + + case op::U: + { + formula f1 = f.nth(0); + formula f2 = f.nth(1); + if (g.is(op::U, op::W) + && syntactic_implication(f1, g.nth(0)) + && syntactic_implication(f2, g.nth(1))) + return true; + if (g.is(op::M, op::R) + && syntactic_implication(f1, g.nth(1)) + && syntactic_implication(f2, g.nth(0)) + && syntactic_implication(f2, g.nth(1))) + return true; + if (g.is(op::F) && syntactic_implication(f2, g.nth(0))) return true; - if (gk == formula::UnOp) - { - const unop* g_ = down_cast(g); - unop::type go = g_->op(); - if (fo == unop::X) - { - if (go == unop::X - && syntactic_implication(f_->child(), g_->child())) - return true; - } - } - else if (gk == formula::BinOp && fo == unop::G) - { - const binop* g_ = down_cast(g); - binop::type go = g_->op(); - const formula* g1 = g_->first(); - const formula* g2 = g_->second(); - if ((go == binop::U || go == binop::R) - && syntactic_implication(f_->child(), g2)) - return true; - else if (go == binop::W - && (syntactic_implication(f_->child(), g1) - || syntactic_implication(f_->child(), g2))) - return true; - else if (go == binop::M - && (syntactic_implication(f_->child(), g1) - && syntactic_implication(f_->child(), g2))) - return true; - } // First column. - if (fo == unop::G && syntactic_implication(f_->child(), g)) + if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) return true; break; } - - case formula::BinOp: + case op::W: { - const binop* f_ = down_cast(f); - binop::type fo = f_->op(); - const formula* f1 = f_->first(); - const formula* f2 = f_->second(); - - if (gk == formula::UnOp) - { - const unop* g_ = down_cast(g); - unop::type go = g_->op(); - if (go == unop::F) - { - if (fo == binop::U) - { - if (syntactic_implication(f2, g_->child())) - return true; - } - else if (fo == binop::W) - { - if (syntactic_implication(f1, g_->child()) - && syntactic_implication(f2, g_->child())) - return true; - } - else if (fo == binop::R) - { - if (syntactic_implication(f2, g_->child())) - return true; - } - else if (fo == binop::M) - { - if (syntactic_implication(f1, g_->child()) - || syntactic_implication(f2, g_->child())) - return true; - } - } - } - else if (gk == formula::BinOp) - { - const binop* g_ = down_cast(g); - binop::type go = g_->op(); - const formula* g1 = g_->first(); - const formula* g2 = g_->second(); - - if ((fo == binop::U && (go == binop::U || go == binop::W)) - || (fo == binop::W && go == binop::W) - || (fo == binop::R && go == binop::R) - || (fo == binop::M && (go == binop::R - || go == binop::M))) - { - if (syntactic_implication(f1, g1) - && syntactic_implication(f2, g2)) - return true; - } - else if (fo == binop::W && go == binop::U) - { - if (syntactic_implication(f1, g2) - && syntactic_implication(f2, g2)) - return true; - } - else if (fo == binop::R && go == binop::M) - { - if (syntactic_implication(f2, g1) - && syntactic_implication(f2, g2)) - return true; - } - else if ((fo == binop::U - && (go == binop::R || go == binop::M)) - || (fo == binop::W && go == binop::R)) - { - if (syntactic_implication(f1, g2) - && syntactic_implication(f2, g1) - && syntactic_implication(f2, g2)) - return true; - } - else if ((fo == binop::M - && (go == binop::U || go == binop::W)) - || (fo == binop::R && go == binop::W)) - { - if (syntactic_implication(f1, g2) - && syntactic_implication(f2, g1)) - return true; - } - } - + formula f1 = f.nth(0); + formula f2 = f.nth(1); + if (g.is(op::U) && (syntactic_implication(f1, g.nth(1)) + && syntactic_implication(f2, g.nth(1)))) + return true; + if (g.is(op::W) && (syntactic_implication(f1, g.nth(0)) + && syntactic_implication(f2, g.nth(1)))) + return true; + if (g.is(op::R) && (syntactic_implication(f1, g.nth(1)) + && syntactic_implication(f2, g.nth(0)) + && syntactic_implication(f2, g.nth(1)))) + return true; + if (g.is(op::F) && (syntactic_implication(f1, g.nth(0)) + && syntactic_implication(f2, g.nth(0)))) + return true; // First column. - if (fo == binop::U || fo == binop::W) - { - if (syntactic_implication(f1, g) - && syntactic_implication(f2, g)) - return true; - } - else if (fo == binop::R || fo == binop::M) - { - if (syntactic_implication(f2, g)) - return true; - } + if (syntactic_implication(f1, g) && syntactic_implication(f2, g)) + return true; break; } - case formula::MultOp: + case op::R: { - const multop* f_ = down_cast(f); - multop::type fo = f_->op(); - unsigned fs = f_->size(); - + formula f1 = f.nth(0); + formula f2 = f.nth(1); + if (g.is(op::W) && (syntactic_implication(f1, g.nth(1)) + && syntactic_implication(f2, g.nth(0)))) + return true; + if (g.is(op::R) && (syntactic_implication(f1, g.nth(0)) + && syntactic_implication(f2, g.nth(1)))) + return true; + if (g.is(op::M) && (syntactic_implication(f2, g.nth(0)) + && syntactic_implication(f2, g.nth(1)))) + return true; + if (g.is(op::F) && syntactic_implication(f2, g.nth(0))) + return true; // First column. - switch (fo) - { - case multop::Or: - { - if (f->is_boolean()) - break; - unsigned i = 0; - // If we are checking something like - // (a | b | Xc) => g, - // split it into - // (a | b) => g - // Xc => g - if (const formula* bops = f_->boolean_operands(&i)) - { - bool r = syntactic_implication(bops, g); - bops->destroy(); - if (!r) - break; - } - bool b = true; - for (; i < fs; ++i) - if (!syntactic_implication(f_->nth(i), g)) - { - b = false; - break; - } - if (b) - return true; - break; - } - case multop::And: - { - if (f->is_boolean()) - break; - unsigned i = 0; - // If we are checking something like - // (a & b & Xc) => g, - // split it into - // (a & b) => g - // Xc => g - if (const formula* bops = f_->boolean_operands(&i)) - { - bool r = syntactic_implication(bops, g); - bops->destroy(); - if (r) - return true; - } - for (; i < fs; ++i) - if (syntactic_implication(f_->nth(i), g)) - return true; - break; - } - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: + if (syntactic_implication(f2, g)) + return true; + break; + } + case op::M: + { + formula f1 = f.nth(0); + formula f2 = f.nth(1); + if (g.is(op::U, op::W) && (syntactic_implication(f1, g.nth(1)) + && syntactic_implication(f2, + g.nth(0)))) + return true; + if (g.is(op::R, op::M) && (syntactic_implication(f1, g.nth(0)) + && syntactic_implication(f2, + g.nth(1)))) + return true; + if (g.is(op::F) && (syntactic_implication(f1, g.nth(0)) + || syntactic_implication(f2, g.nth(0)))) + return true; + // First column. + if (syntactic_implication(f2, g)) + return true; + break; + } + case op::Or: + { + // If we are checking something like + // (a | b | Xc) => g, + // split it into + // (a | b) => g + // Xc => g + unsigned i = 0; + if (formula bops = f.boolean_operands(&i)) + if (!syntactic_implication(bops, g)) break; - } + bool b = true; + unsigned fs = f.size(); + for (; i < fs; ++i) + if (!syntactic_implication(f.nth(i), g)) + { + b = false; + break; + } + if (b) + return true; break; } + case op::And: + { + // If we are checking something like + // (a & b & Xc) => g, + // split it into + // (a & b) => g + // Xc => g + unsigned i = 0; + if (formula bops = f.boolean_operands(&i)) + if (syntactic_implication(bops, g)) + return true; + unsigned fs = f.size(); + for (; i < fs; ++i) + if (syntactic_implication(f.nth(i), g)) + return true; + break; + } + default: + break; } // First two lines of the table. // (Don't check equality, it has already be done.) - if (!g->is_boolean()) - switch (gk) + if (!g.is_boolean()) + switch (g.kind()) { - case formula::Constant: - case formula::AtomicProp: - case formula::BUnOp: + case op::F: + if (syntactic_implication(f, g.nth(0))) + return true; break; - case formula::UnOp: - { - const unop* g_ = down_cast(g); - unop::type go = g_->op(); - if (go == unop::F) - { - if (syntactic_implication(f, g_->child())) - return true; - } - else if (go == unop::G || go == unop::X) - { - if (f->is_universal() - && syntactic_implication(f, g_->child())) - return true; - } - break; - } - case formula::BinOp: - { - const binop* g_ = down_cast(g); - binop::type go = g_->op(); - const formula* g1 = g_->first(); - const formula* g2 = g_->second(); + case op::G: + case op::X: + if (f.is_universal() && syntactic_implication(f, g.nth(0))) + return true; + break; - if (go == binop::U || go == binop::W) - { - if (syntactic_implication(f, g2)) - return true; - } - else if (go == binop::M || go == binop::R) - { - if (syntactic_implication(f, g1) - && syntactic_implication(f, g2)) - return true; - } - break; - } - case formula::MultOp: - { - const multop* g_ = down_cast(g); - multop::type go = g_->op(); - unsigned gs = g_->size(); + case op::U: + case op::W: + if (syntactic_implication(f, g.nth(1))) + return true; + break; - switch (go) - { - case multop::And: - { - unsigned i = 0; - // If we are checking something like - // f => (a & b & Xc), - // split it into - // f => (a & b) - // f => Xc - if (const formula* bops = g_->boolean_operands(&i)) - { - bool r = syntactic_implication(f, bops); - bops->destroy(); - if (!r) - break; - } - bool b = true; - for (; i < gs; ++i) - if (!syntactic_implication(f, g_->nth(i))) - { - b = false; - break; - } - if (b) - return true; - break; - } - case multop::Or: - { - unsigned i = 0; - // If we are checking something like - // f => (a | b | Xc), - // split it into - // f => (a | b) - // f => Xc - if (const formula* bops = g_->boolean_operands(&i)) - { - bool r = syntactic_implication(f, bops); - bops->destroy(); - if (r) - return true; - } - for (; i < gs; ++i) - if (syntactic_implication(f, g_->nth(i))) - return true; - break; - } - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: + case op::M: + case op::R: + if (syntactic_implication(f, g.nth(0)) + && syntactic_implication(f, g.nth(1))) + return true; + break; + + case op::And: + { + // If we are checking something like + // f => (a & b & Xc), + // split it into + // f => (a & b) + // f => Xc + unsigned i = 0; + if (formula bops = g.boolean_operands(&i)) + if (!syntactic_implication(f, bops)) break; - } + bool b = true; + unsigned gs = g.size(); + for (; i < gs; ++i) + if (!syntactic_implication(f, g.nth(i))) + { + b = false; + break; + } + if (b) + return true; break; } + + case op::Or: + { + // If we are checking something like + // f => (a | b | Xc), + // split it into + // f => (a | b) + // f => Xc + unsigned i = 0; + if (formula bops = g.boolean_operands(&i)) + if (syntactic_implication(f, bops)) + return true; + unsigned gs = g.size(); + for (; i < gs; ++i) + if (syntactic_implication(f, g.nth(i))) + return true; + break; + } + default: + break; } return false; } // Return true if f => g syntactically bool - ltl_simplifier_cache::syntactic_implication(const formula* f, - const formula* g) + ltl_simplifier_cache::syntactic_implication(formula f, + formula g) { // We cannot run syntactic_implication on SERE formulae, // except on Boolean formulae. - if (f->is_sere_formula() && !f->is_boolean()) + if (f.is_sere_formula() && !f.is_boolean()) return false; - if (g->is_sere_formula() && !g->is_boolean()) + if (g.is_sere_formula() && !g.is_boolean()) return false; if (f == g) return true; - if (g == constant::true_instance() - || f == constant::false_instance()) + if (g.is(op::True) || f.is(op::False)) return true; - if (g == constant::false_instance() - || f == constant::true_instance()) + if (g.is(op::False) || f.is(op::True)) return false; // Often we compare a literal (an atomic_prop or its negation) // to another literal. The result is necessarily false. To be // true, the two literals would have to be equal, but we have // already checked that. - if (is_literal(f) && is_literal(g)) + if (f.is_literal() && g.is_literal()) return false; // Cache lookup @@ -4647,14 +3311,14 @@ namespace spot bool result; - if (f->is_boolean() && g->is_boolean()) + if (f.is_boolean() && g.is_boolean()) result = bdd_implies(as_bdd(f), as_bdd(g)); else result = syntactic_implication_aux(f, g); // Cache result { - pairf p(f->clone(), g->clone()); + pairf p(f, g); syntimpl_[p] = result; // std::cerr << str_psl(f) << (result ? " ==> " : " =/=> ") // << str_psl(g) << std::endl; @@ -4666,27 +3330,21 @@ namespace spot // If right==false, true if !f1 => f2, false otherwise. // If right==true, true if f1 => !f2, false otherwise. bool - ltl_simplifier_cache::syntactic_implication_neg(const formula* f1, - const formula* f2, + ltl_simplifier_cache::syntactic_implication_neg(formula f1, + formula f2, bool right) { // We cannot run syntactic_implication_neg on SERE formulae, // except on Boolean formulae. - if (f1->is_sere_formula() && !f1->is_boolean()) + if (f1.is_sere_formula() && !f1.is_boolean()) return false; - if (f2->is_sere_formula() && !f2->is_boolean()) + if (f2.is_sere_formula() && !f2.is_boolean()) return false; - if (right) - f2 = nenoform_recursively(f2, true, this); + f2 = nenoform_rec(f2, true, this); else - f1 = nenoform_recursively(f1, true, this); - - bool result = syntactic_implication(f1, f2); - - (right ? f2 : f1)->destroy(); - - return result; + f1 = nenoform_rec(f1, true, this); + return syntactic_implication(f1, f2); } @@ -4709,63 +3367,59 @@ namespace spot delete cache_; } - const formula* - ltl_simplifier::simplify(const formula* f) + formula + ltl_simplifier::simplify(formula f) { - const formula* neno = 0; - if (!f->is_in_nenoform()) - f = neno = negative_normal_form(f, false); - const formula* res = simplify_recursively(f, cache_); - if (neno) - neno->destroy(); - return res; + if (!f.is_in_nenoform()) + f = negative_normal_form(f, false); + return simplify_recursively(f, cache_); } - const formula* - ltl_simplifier::negative_normal_form(const formula* f, bool negated) + formula + ltl_simplifier::negative_normal_form(formula f, bool negated) { - return nenoform_recursively(f, negated, cache_); + return nenoform_rec(f, negated, cache_); } bool - ltl_simplifier::syntactic_implication(const formula* f1, const formula* f2) + ltl_simplifier::syntactic_implication(formula f1, formula f2) { return cache_->syntactic_implication(f1, f2); } bool - ltl_simplifier::syntactic_implication_neg(const formula* f1, - const formula* f2, bool right) + ltl_simplifier::syntactic_implication_neg(formula f1, + formula f2, bool right) { return cache_->syntactic_implication_neg(f1, f2, right); } bool - ltl_simplifier::are_equivalent(const formula* f, const formula* g) + ltl_simplifier::are_equivalent(formula f, formula g) { return cache_->lcc.equal(f, g); } bool - ltl_simplifier::implication(const formula* f, const formula* g) + ltl_simplifier::implication(formula f, formula g) { return cache_->lcc.contained(f, g); } bdd - ltl_simplifier::as_bdd(const formula* f) + ltl_simplifier::as_bdd(formula f) { return cache_->as_bdd(f); } - const formula* - ltl_simplifier::star_normal_form(const formula* f) + formula + ltl_simplifier::star_normal_form(formula f) { return cache_->star_normal_form(f); } - const formula* - ltl_simplifier::boolean_to_isop(const formula* f) + formula + ltl_simplifier::boolean_to_isop(formula f) { return cache_->boolean_to_isop(f); } diff --git a/src/ltlvisit/simplify.hh b/src/ltlvisit/simplify.hh index a8a7a38aa..7623fdb7a 100644 --- a/src/ltlvisit/simplify.hh +++ b/src/ltlvisit/simplify.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et -// Developpement de l'Epita (LRDE). +// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche +// et Developpement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -106,7 +106,7 @@ namespace spot /// Simplify the formula \a f (using options supplied to the /// constructor). - const formula* simplify(const formula* f); + formula simplify(formula f); /// Build the negative normal form of formula \a f. /// All negations of the formula are pushed in front of the @@ -116,8 +116,8 @@ namespace spot /// \param f The formula to normalize. /// \param negated If \c true, return the negative normal form of /// \c !f - const formula* - negative_normal_form(const formula* f, bool negated = false); + formula + negative_normal_form(formula f, bool negated = false); /// \brief Syntactic implication. /// @@ -138,20 +138,20 @@ namespace spot } \endverbatim */ /// - bool syntactic_implication(const formula* f, const formula* g); + bool syntactic_implication(formula f, formula g); /// \brief Syntactic implication with one negated argument. /// /// If \a right is true, this method returns whether /// \a f implies !\a g. If \a right is false, this returns /// whether !\a f implies \a g. - bool syntactic_implication_neg(const formula* f, const formula* g, + bool syntactic_implication_neg(formula f, formula g, bool right); /// \brief check whether two formulae are equivalent. /// /// This costly check performs up to four translations, /// two products, and two emptiness checks. - bool are_equivalent(const formula* f, const formula* g); + bool are_equivalent(formula f, formula g); /// \brief Check whether \a f implies \a g. @@ -159,13 +159,13 @@ namespace spot /// This operation is costlier than syntactic_implication() /// because it requires two translation, one product and one /// emptiness check. - bool implication(const formula* f, const formula* g); + bool implication(formula f, formula g); /// \brief Convert a Boolean formula as a BDD. /// /// If you plan to use this method, be sure to pass a bdd_dict /// to the constructor. - bdd as_bdd(const formula* f); + bdd as_bdd(formula f); /// \brief Clear the as_bdd() cache. /// @@ -182,14 +182,14 @@ namespace spot bdd_dict_ptr get_dict() const; /// Cached version of spot::ltl::star_normal_form(). - const formula* star_normal_form(const formula* f); + formula star_normal_form(formula f); /// \brief Rewrite a Boolean formula \a f into as an irredundant /// sum of product. /// /// This uses a cache, so it is OK to call this with identical /// arguments. - const formula* boolean_to_isop(const formula* f); + formula boolean_to_isop(formula f); /// Dump statistics about the caches. void print_stats(std::ostream& os) const; diff --git a/src/ltlvisit/snf.cc b/src/ltlvisit/snf.cc index 4cb3ac6a6..b4fe0539a 100644 --- a/src/ltlvisit/snf.cc +++ b/src/ltlvisit/snf.cc @@ -18,8 +18,6 @@ // along with this program. If not, see . #include "snf.hh" -#include "ltlast/allnodes.hh" -#include "ltlast/visitor.hh" namespace spot { @@ -27,82 +25,51 @@ namespace spot { namespace { - // E° - class snf_visitor: public visitor + // E° if bounded=false + // E^□ if nounded=true + template + class snf_visitor { protected: - const formula* result_; + formula result_; snf_cache* cache_; public: - - snf_visitor(snf_cache* c): cache_(c) + snf_visitor(snf_cache* c) + : cache_(c) { } - const formula* - result() const + formula visit(formula f) { - return result_; - } + if (!f.accepts_eword()) + return f; - void - visit(const atomic_prop*) - { - SPOT_UNIMPLEMENTED(); - } + snf_cache::const_iterator i = cache_->find(f); + if (i != cache_->end()) + return i->second; - void - visit(const constant* c) - { - assert(c == constant::empty_word_instance()); - (void)c; - result_ = constant::false_instance(); - } - - void - visit(const bunop* bo) - { - bunop::type op = bo->op(); - switch (op) + formula out; + switch (f.kind()) { - case bunop::Star: - assert(bo->accepts_eword()); - // Strip the star. - result_ = recurse(bo->child()); + case op::EmptyWord: + out = formula::ff(); break; - case bunop::FStar: - // FIXME: Can we deal with FStar in a better way? - result_ = bo->clone(); + case op::Star: + if (!bounded) + out = visit(f.nth(0)); // Strip the star. + else + out = formula::Star(visit(f.nth(0)), + std::max(unsigned(f.min()), 1U), f.max()); break; - - } - } - - void - visit(const unop*) - { - SPOT_UNIMPLEMENTED(); - } - - void - visit(const binop*) - { - SPOT_UNIMPLEMENTED(); - } - - void - visit(const multop* mo) - { - multop::type op = mo->op(); - switch (op) - { - case multop::And: - case multop::Or: - case multop::Fusion: - SPOT_UNIMPLEMENTED(); - break; - case multop::Concat: - case multop::AndNLM: + case op::Concat: + if (bounded) + { + out = f; + break; + } + // Fall through + case op::OrRat: + case op::AndNLM: // Let F designate expressions that accept [*0], // and G designate expressions that do not. @@ -112,100 +79,70 @@ namespace spot // // AndNLM can be dealt with similarly. // - // This case is already handled in recurse(). - // if we reach this switch, we only have to - // deal with... + // The above cases are already handled by the + // accepts_eword() tests at the top of this method. So + // we reach this switch, we only have to deal with... // // (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°) // (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°) - // so we fall through to the OrRat case... - case multop::OrRat: - assert(mo->accepts_eword()); + // (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°) { - unsigned s = mo->size(); - multop::vec* v = new multop::vec; - v->reserve(s); + unsigned s = f.size(); + std::vector v; + v.reserve(s); for (unsigned pos = 0; pos < s; ++pos) - v->push_back(recurse(mo->nth(pos))); - result_ = multop::instance(multop::OrRat, v); + v.emplace_back(visit(f.nth(pos))); + out = formula::OrRat(v); + break; } - break; - case multop::AndRat: - // FIXME: Can we deal with AndRat in a better way - // when it accepts [*0]? - result_ = mo->clone(); + case op::False: + case op::True: + case op::AP: + case op::Not: + case op::X: + case op::F: + case op::G: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::Xor: + case op::Implies: + case op::Equiv: + case op::U: + case op::R: + case op::W: + case op::M: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::Fusion: + case op::Or: + case op::And: + SPOT_UNREACHABLE(); + case op::AndRat: // Can AndRat be handled better? + case op::FStar: // Can FStar be handled better? + out = f; break; } - } - const formula* - recurse(const formula* f) - { - if (!f->accepts_eword()) - return f->clone(); - - snf_cache::const_iterator i = cache_->find(f); - if (i != cache_->end()) - return i->second->clone(); - - f->accept(*this); - - (*cache_)[f->clone()] = result_->clone(); - return result_; - } - }; - - // E^□ - class snf_visitor_bounded: public snf_visitor - { - public: - snf_visitor_bounded(snf_cache* c): snf_visitor(c) - { - } - - void - visit(const bunop* bo) - { - bunop::type op = bo->op(); - switch (op) - { - case bunop::Star: - assert(bo->accepts_eword()); - result_ = bunop::instance(bunop::Star, - recurse(bo->child()), - std::max(bo->min(), 1U), - bo->max()); - break; - case bunop::FStar: - result_ = bo->clone(); - break; - } - } - - void - visit(const multop* mo) - { - if (mo->op() == multop::Concat) - result_ = mo->clone(); - else - this->snf_visitor::visit(mo); + return (*cache_)[f] = out; } }; } - const formula* - star_normal_form(const formula* sere, snf_cache* cache) + formula + star_normal_form(formula sere, snf_cache* cache) { - snf_visitor v(cache); - return v.recurse(sere); + snf_visitor v(cache); + return v.visit(sere); } - const formula* - star_normal_form_bounded(const formula* sere, snf_cache* cache) + formula + star_normal_form_bounded(formula sere, snf_cache* cache) { - snf_visitor_bounded v(cache); - return v.recurse(sere); + snf_visitor v(cache); + return v.visit(sere); } } diff --git a/src/ltlvisit/snf.hh b/src/ltlvisit/snf.hh index acf714f7b..340934d1c 100644 --- a/src/ltlvisit/snf.hh +++ b/src/ltlvisit/snf.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et +// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et // Developpement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -20,15 +20,14 @@ #pragma once #include "ltlast/formula.hh" -#include "misc/hash.hh" +#include namespace spot { namespace ltl { - typedef std::unordered_map> snf_cache; + typedef std::unordered_map snf_cache; /// Helper to rewrite a sere in Star Normal Form. /// @@ -49,11 +48,11 @@ namespace spot /// /// \param sere the SERE to rewrite /// \param cache an optional cache - SPOT_API const formula* - star_normal_form(const formula* sere, snf_cache* cache = 0); + SPOT_API formula + star_normal_form(formula sere, snf_cache* cache = 0); /// A variant of star_normal_form() for r[*0..j] where j < ω. - SPOT_API const formula* - star_normal_form_bounded(const formula* sere, snf_cache* cache = 0); + SPOT_API formula + star_normal_form_bounded(formula sere, snf_cache* cache = 0); } } diff --git a/src/ltlvisit/unabbrev.cc b/src/ltlvisit/unabbrev.cc index e4f97c9e6..0b1ec74d1 100644 --- a/src/ltlvisit/unabbrev.cc +++ b/src/ltlvisit/unabbrev.cc @@ -19,8 +19,6 @@ #include "unabbrev.hh" -#include "ltlast/allnodes.hh" -#include namespace spot { @@ -70,242 +68,183 @@ namespace spot } } - unabbreviator::~unabbreviator() - { - auto i = cache_.begin(); - auto end = cache_.end(); - while (i != end) - { - auto old = i++; - old->second->destroy(); - old->first->destroy(); - } - } - - - const formula* unabbreviator::run(const formula* in) + formula unabbreviator::run(formula in) { auto entry = cache_.emplace(in, nullptr); if (!entry.second) - return entry.first->second->clone(); - in->clone(); + return entry.first->second; // Skip recursion whenever possible - bool no_boolean_rewrite = !re_some_bool_ || in->is_sugar_free_boolean(); - bool no_f_g_rewrite = !re_some_f_g_ || in->is_sugar_free_ltl(); + bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean(); + bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl(); if (no_boolean_rewrite - && (in->is_boolean() || (no_f_g_rewrite && !re_some_other_))) + && (in.is_boolean() || (no_f_g_rewrite && !re_some_other_))) + return entry.first->second = in; + + auto rec = [this](formula f) { - entry.first->second = in->clone(); - return in->clone(); + return this->run(f); + }; + + formula out = in; + if (in.size() > 0) + out = in.map(rec); + + switch (out.kind()) + { + case op::False: + case op::True: + case op::EmptyWord: + case op::AP: + case op::Not: + case op::X: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::U: + case op::Or: + case op::OrRat: + case op::And: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + break; + case op::F: + // F f = true U f + if (!re_f_) + break; + out = formula::U(formula::tt(), out.nth(0)); + break; + case op::G: + // G f = false R f + // G f = f W false + // G f = !F!f + // G f = !(true U !f) + if (!re_g_) + break; + if (!re_r_) + { + out = formula::R(formula::ff(), out.nth(0)); + break; + } + if (!re_w_) + { + out = formula::W(out.nth(0), formula::ff()); + break; + } + { + auto nc = formula::Not(out.nth(0)); + if (!re_f_) + { + out = formula::Not(formula::F(nc)); + break; + } + out = formula::Not(formula::U(formula::tt(), nc)); + break; + } + case op::Xor: + // f1 ^ f2 == !(f1 <-> f2) + // f1 ^ f2 == (f1 & !f2) | (f2 & !f1) + if (!re_xor_) + break; + { + auto f1 = out.nth(0); + auto f2 = out.nth(1); + if (!re_e_) + { + out = formula::Not(formula::Equiv(f1, f2)); + } + else + { + auto a = formula::And({f1, formula::Not(f2)}); + auto b = formula::And({f2, formula::Not(f1)}); + out = formula::Or({a, b}); + } + } + break; + case op::Implies: + // f1 => f2 == !f1 | f2 + if (!re_i_) + break; + out = formula::Or({formula::Not(out.nth(0)), out.nth(1)}); + break; + case op::Equiv: + // f1 <=> f2 == (f1 & f2) | (!f1 & !f2) + if (!re_e_) + break; + { + auto f1 = out.nth(0); + auto f2 = out.nth(1); + auto nf1 = formula::Not(f1); + auto nf2 = formula::Not(f2); + auto term1 = formula::And({f1, f2}); + auto term2 = formula::And({nf1, nf2}); + out = formula::Or({term1, term2}); + break; + } + case op::R: + // f1 R f2 = f2 W (f1 & f2) + // f1 R f2 = f2 U ((f1 & f2) | Gf2) + // f1 R f2 = f2 U ((f1 & f2) | !F!f2) + // f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2)) + if (!re_r_) + break; + { + auto f1 = out.nth(0); + auto f2 = out.nth(1); + auto f12 = formula::And({f1, f2}); + if (!re_w_) + { + out = formula::W(f2, f12); + break; + } + auto gf2 = formula::G(f2); + if (re_g_) + gf2 = run(gf2); + out = formula::U(f2, formula::Or({f12, out})); + break; + } + case op::W: + // f1 W f2 = f2 R (f2 | f1) + // f1 W f2 = f1 U (f2 | G f1) + // f1 W f2 = f1 U (f2 | !F !f1) + // f1 W f2 = f1 U (f2 | !(1 U !f1)) + if (!re_w_) + break; + { + auto f1 = out.nth(0); + auto f2 = out.nth(1); + if (!re_r_) + { + out = formula::R(f2, formula::Or({f2, f1})); + break; + } + auto gf1 = formula::G(f1); + if (re_g_) + gf1 = rec(gf1); + out = formula::U(f1, formula::Or({f2, out})); + break; + } + case op::M: + // f1 M f2 = f2 U (g2 & f1) + if (!re_m_) + break; + { + auto f2 = out.nth(1); + out = formula::U(f2, formula::And({f2, out.nth(0)})); + break; + } } - - const formula* out = nullptr; - switch (in->kind()) - { - case formula::AtomicProp: - case formula::Constant: - out = in->clone(); - break; - case formula::UnOp: - { - const unop* uo = static_cast(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(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(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(in); - out = bunop::instance(bo->op(), run(bo->child()), - bo->min(), bo->max()); - break; - } - } - - assert(out != nullptr); - - entry.first->second = out; - return out->clone(); + return entry.first->second = out; } - const formula* unabbreviate(const formula* in, const char* opt) + formula unabbreviate(formula in, const char* opt) { unabbreviator un(opt); return un.run(in); diff --git a/src/ltlvisit/unabbrev.hh b/src/ltlvisit/unabbrev.hh index 0cb30c123..d0d5a35a7 100644 --- a/src/ltlvisit/unabbrev.hh +++ b/src/ltlvisit/unabbrev.hh @@ -47,7 +47,7 @@ namespace spot bool re_some_f_g_ = false; // rewrite F or G bool re_some_other_ = false; // rewrite W, M, or R // Cache of rewritten subformulas - std::unordered_map cache_; + std::unordered_map cache_; public: /// \brief Constructor /// @@ -55,8 +55,7 @@ namespace spot /// which in which each letter denote an operator (using LBT's /// convention). unabbreviator(const char* opt = default_unabbrev_string); - const formula* run(const formula* in); - ~unabbreviator(); + formula run(formula in); }; /// \ingroup ltl_rewriting @@ -66,8 +65,8 @@ namespace spot /// The set of operators to remove should be passed as a string /// which in which each letter denote an operator (using LBT's /// convention). - SPOT_API const formula* - unabbreviate(const formula* in, const char* opt= default_unabbrev_string); + SPOT_API formula + unabbreviate(formula in, const char* opt= default_unabbrev_string); } } diff --git a/src/parseaut/parseaut.yy b/src/parseaut/parseaut.yy index 0539188c9..57d139b97 100644 --- a/src/parseaut/parseaut.yy +++ b/src/parseaut/parseaut.yy @@ -34,7 +34,6 @@ #include #include #include -#include "ltlast/constant.hh" #include "twa/formula2bdd.hh" #include "public.hh" #include "priv/accmap.hh" @@ -45,9 +44,10 @@ typedef std::map map_t; /* Cache parsed formulae. Labels on arcs are frequently identical - and it would be a waste of time to parse them to formula* over and - over, and to register all their atomic_propositions in the - bdd_dict. Keep the bdd result around so we can reuse it. */ + and it would be a waste of time to parse them to ltl::formula + over and over, and to register all their atomic_propositions in + the bdd_dict. Keep the bdd result around so we can reuse + it. */ typedef std::map formula_cache; typedef std::pair pair; @@ -152,7 +152,6 @@ %code { #include -#include "ltlast/constant.hh" #include "ltlparse/public.hh" /* parseaut.hh and parsedecl.hh include each other recursively. @@ -561,9 +560,7 @@ ap-name: STRING std::ostringstream out; out << "unknown atomic proposition \"" << *$1 << "\""; error(@1, out.str()); - f = spot::ltl::default_environment::instance() - .require("$unknown$"); - b = res.h->aut->register_ap(f); + b = res.h->aut->register_ap("$unknown$"); } else { @@ -575,7 +572,6 @@ ap-name: STRING error(@1, out.str()); } } - f->destroy(); res.ap.push_back(b); } delete $1; @@ -1422,11 +1418,7 @@ nc-formula: nc-formula-or-ident } bdd cond = bddfalse; if (f) - { - cond = spot::formula_to_bdd(f, res.h->aut->get_dict(), - res.h->aut); - f->destroy(); - } + cond = spot::formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut); $$ = (res.fcache[*$1] = cond).id(); } else @@ -1587,7 +1579,7 @@ lbtt-acc: { $$ = 0U; } lbtt-guard: STRING { spot::ltl::parse_error_list pel; - auto* f = spot::ltl::parse_prefix_ltl(*$1, pel, *res.env); + auto f = spot::ltl::parse_prefix_ltl(*$1, pel, *res.env); if (!f || !pel.empty()) { std::string s = "failed to parse guard: "; @@ -1611,7 +1603,7 @@ lbtt-guard: STRING } else { - if (!f->is_boolean()) + if (!f.is_boolean()) { error(@$, "non-Boolean transition label (replaced by true)"); @@ -1622,7 +1614,6 @@ lbtt-guard: STRING res.cur_label = formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut); } - f->destroy(); } delete $1; } diff --git a/src/sanity/style.test b/src/sanity/style.test index 4add43e81..b24e405df 100755 --- a/src/sanity/style.test +++ b/src/sanity/style.test @@ -130,7 +130,7 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do perl -pe 'sub f {my $a = shift; $a =~ s:[^\n]*://:g; return "$a"} s,/\*(.*?)\*/,f($1),sge; s,//.*?\n,//\n,g; - s,"[^"\n]*","",g; + s,"(\\.|[^"\\\n])*","",g; s,SPOT_API ,,g' -0777 <$file >$tmp $GREP '[ ]$' $tmp && @@ -231,10 +231,14 @@ for dir in "${INCDIR-..}" "${INCDIR-..}"/../iface; do $GREP -v 'for (.*;;)' $tmp | $GREP ';[^ ")'"']" && diag 'Must have space or newline after semicolon.' - $GREP '}.*}' $tmp && + # Allow several { or } on the same line only if they are mixed + # with parentheses, as this often occur with lambdas or + # initializer lists. What we want to forbid is cases where + # multiple scopes are opened/closed on the same line. + $GREP '^[^()]*}[^()]*}[^()]*$' $tmp && diag 'No two } on the same line.' - $GREP '{.*{' $tmp && + $GREP '^[^()]{[^()]*{[^()]$' $tmp && diag 'No two { on the same line.' $GREP 'delete[ ]*[(][^(]*[)];' $tmp && diff --git a/src/ta/taexplicit.cc b/src/ta/taexplicit.cc index a7bde3c08..c15a7aae4 100644 --- a/src/ta/taexplicit.cc +++ b/src/ta/taexplicit.cc @@ -26,8 +26,6 @@ #define trace while (0) std::clog #endif -#include "ltlast/atomic_prop.hh" -#include "ltlast/constant.hh" #include "taexplicit.hh" #include "twa/formula2bdd.hh" #include diff --git a/src/ta/tgtaexplicit.cc b/src/ta/tgtaexplicit.cc index 678512f79..f2c2d8548 100644 --- a/src/ta/tgtaexplicit.cc +++ b/src/ta/tgtaexplicit.cc @@ -17,8 +17,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "ltlast/atomic_prop.hh" -#include "ltlast/constant.hh" #include "tgtaexplicit.hh" #include "twa/formula2bdd.hh" #include "twa/bddprint.hh" diff --git a/src/taalgos/minimize.cc b/src/taalgos/minimize.cc index 7b42a7d73..db9a4a660 100644 --- a/src/taalgos/minimize.cc +++ b/src/taalgos/minimize.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010, 2011, 2012, 2013, 2014 Laboratoire de Recherche -// et Développement de l'Epita (LRDE). +// 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. // @@ -30,7 +30,6 @@ #include #include #include "minimize.hh" -#include "ltlast/allnodes.hh" #include "misc/hash.hh" #include "misc/bddlt.hh" #include "ta/tgtaexplicit.hh" diff --git a/src/taalgos/tgba2ta.cc b/src/taalgos/tgba2ta.cc index 4d5eeff80..5707da956 100644 --- a/src/taalgos/tgba2ta.cc +++ b/src/taalgos/tgba2ta.cc @@ -26,8 +26,6 @@ #define trace while (0) std::clog #endif -#include "ltlast/atomic_prop.hh" -#include "ltlast/constant.hh" #include "twa/formula2bdd.hh" #include #include diff --git a/src/tests/bare.test b/src/tests/bare.test index 7395a067c..2cc955f3f 100755 --- a/src/tests/bare.test +++ b/src/tests/bare.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2012 Laboratoire de Recherche et Développement +# Copyright (C) 2012, 2015 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -21,10 +21,10 @@ . ./defs set -e -test "`../../bin/ltlfilt -p -f 'GFP_0.b_c'`" = "(G(F(P_0.b_c)))" +test "`../../bin/ltlfilt -p -f 'GFP_0.b_c'`" = "G(F(P_0.b_c))" test "`../../bin/ltlfilt -f 'GFP_0.b_c'`" = "GFP_0.b_c" foo=`../../bin/ltlfilt -p -f 'GF"P_0.b_c"'` -test "$foo" = "(G(F(P_0.b_c)))" +test "$foo" = "G(F(P_0.b_c))" foo=`../../bin/ltlfilt -p -f '"a.b" U c.d.e'` test "$foo" = "(a.b) U (c.d.e)" diff --git a/src/tests/checkpsl.cc b/src/tests/checkpsl.cc index fecda1924..a6b407704 100644 --- a/src/tests/checkpsl.cc +++ b/src/tests/checkpsl.cc @@ -23,7 +23,6 @@ #include #include #include "ltlparse/public.hh" -#include "ltlast/allnodes.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/ltl2taa.hh" #include "twaalgos/sccfilter.hh" @@ -67,8 +66,7 @@ main(int argc, char** argv) if (spot::ltl::format_parse_errors(std::cerr, s, pe)) return 2; - auto fneg = - spot::ltl::unop::instance(spot::ltl::unop::Not, fpos->clone()); + auto fneg = spot::ltl::formula::Not(fpos); { auto apos = scc_filter(ltl_to_tgba_fm(fpos, d)); @@ -90,7 +88,7 @@ main(int argc, char** argv) } } - if (fpos->is_ltl_formula()) + if (fpos.is_ltl_formula()) { auto apos = scc_filter(make_twa_graph(ltl_to_taa(fpos, d), @@ -104,17 +102,8 @@ main(int argc, char** argv) exit(2); } } - fpos->destroy(); - fneg->destroy(); } - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/checkta.cc b/src/tests/checkta.cc index 5b13ad4e8..3155cc077 100644 --- a/src/tests/checkta.cc +++ b/src/tests/checkta.cc @@ -24,7 +24,6 @@ #include #include #include "ltlparse/public.hh" -#include "ltlast/allnodes.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/sccfilter.hh" #include "twaalgos/degen.hh" @@ -219,17 +218,8 @@ main(int argc, char** argv) } } - - f->destroy(); } - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/complementation.cc b/src/tests/complementation.cc index 78a94aa49..e5dd5ad88 100644 --- a/src/tests/complementation.cc +++ b/src/tests/complementation.cc @@ -28,7 +28,6 @@ #include "ltlparse/public.hh" #include "twaalgos/stats.hh" #include "twaalgos/emptiness.hh" -#include "ltlast/unop.hh" #include "twaalgos/stats.hh" #include "twaalgos/emptiness_stats.hh" #include "twaalgos/degen.hh" @@ -149,7 +148,7 @@ int main(int argc, char* argv[]) else if (print_formula) { spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(file, p1); + auto f1 = spot::ltl::parse_infix_psl(file, p1); if (spot::ltl::format_parse_errors(std::cerr, file, p1)) return 2; @@ -159,12 +158,11 @@ int main(int argc, char* argv[]) complement = spot::make_safra_complement(a); spot::print_dot(std::cout, complement); - f1->destroy(); } else if (stats) { spot::twa_graph_ptr a; - const spot::ltl::formula* f1 = 0; + spot::ltl::formula f1 = nullptr; if (formula) { @@ -211,29 +209,25 @@ int main(int argc, char* argv[]) if (formula) { - auto nf1 = spot::ltl::unop::instance(spot::ltl::unop::Not, f1->clone()); - auto a2 = spot::ltl_to_tgba_fm(nf1, dict); - spot::tgba_statistics a_size = spot::stats_reachable(a2); + auto a2 = spot::ltl_to_tgba_fm(spot::ltl::formula::Not(f1), dict); + spot::tgba_statistics a_size = spot::stats_reachable(a2); std::cout << "Not Formula: " << a_size.states << ", " << a_size.transitions << ", " << a2->acc().num_sets() << std::endl; - - f1->destroy(); - nf1->destroy(); } } else { spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(file, p1); + auto f1 = spot::ltl::parse_infix_psl(file, p1); if (spot::ltl::format_parse_errors(std::cerr, file, p1)) return 2; auto Af = spot::ltl_to_tgba_fm(f1, dict); - auto nf1 = spot::ltl::unop::instance(spot::ltl::unop::Not, f1->clone()); + auto nf1 = spot::ltl::formula::Not(f1); auto Anf = spot::ltl_to_tgba_fm(nf1, dict); auto nAf = spot::make_safra_complement(Af); auto nAnf = spot::make_safra_complement(Anf); @@ -261,8 +255,6 @@ int main(int argc, char* argv[]) { std::cout << "OK\n"; } - nf1->destroy(); - f1->destroy(); } return return_value; diff --git a/src/tests/consterm.cc b/src/tests/consterm.cc index a9ff2516a..5b566b0f1 100644 --- a/src/tests/consterm.cc +++ b/src/tests/consterm.cc @@ -23,7 +23,6 @@ #include #include #include "ltlparse/public.hh" -#include "ltlast/allnodes.hh" void syntax(char *prog) @@ -60,11 +59,11 @@ main(int argc, char **argv) ss >> expected; spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_sere(form, p1); + auto f1 = spot::ltl::parse_infix_sere(form, p1); if (spot::ltl::format_parse_errors(std::cerr, form, p1)) return 2; - bool b = f1->accepts_eword(); + bool b = f1.accepts_eword(); std::cout << form << ',' << b << '\n'; if (b != expected) { @@ -72,13 +71,8 @@ main(int argc, char **argv) << "' but expected '" << expected << "'\n"; return 2; } - f1->destroy(); } - 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::bunop::instance_count() == 0); - assert(spot::ltl::multop::instance_count() == 0); + assert(spot::ltl::fnode::instances_check()); return 0; } diff --git a/src/tests/emptchk.cc b/src/tests/emptchk.cc index 6a27a06f1..c8490a3cc 100644 --- a/src/tests/emptchk.cc +++ b/src/tests/emptchk.cc @@ -23,7 +23,6 @@ #include #include #include "ltlparse/public.hh" -#include "ltlast/allnodes.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/ltl2taa.hh" #include "twaalgos/sccfilter.hh" @@ -198,16 +197,8 @@ main(int argc, char** argv) } } } - f->destroy(); } - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/equalsf.cc b/src/tests/equalsf.cc index 6ddcd93a6..1b8d665d8 100644 --- a/src/tests/equalsf.cc +++ b/src/tests/equalsf.cc @@ -28,9 +28,7 @@ #include #include "ltlparse/public.hh" #include "ltlvisit/unabbrev.hh" -#include "ltlvisit/dump.hh" #include "ltlvisit/nenoform.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/simplify.hh" #include "ltlvisit/print.hh" @@ -98,7 +96,7 @@ main(int argc, char** argv) } spot::ltl::parse_error_list p2; - auto* f2 = spot::ltl::parse_infix_psl(formulas[size - 1], p2); + auto f2 = spot::ltl::parse_infix_psl(formulas[size - 1], p2); if (spot::ltl::format_parse_errors(std::cerr, formulas[size - 1], p2)) return 2; @@ -107,7 +105,7 @@ main(int argc, char** argv) { spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(formulas[n], p1); + auto f1 = spot::ltl::parse_infix_psl(formulas[n], p1); if (check_first && spot::ltl::format_parse_errors(std::cerr, formulas[n], p1)) @@ -117,21 +115,17 @@ main(int argc, char** argv) { #if defined UNABBREV || defined NENOFORM - const spot::ltl::formula* tmp; + spot::ltl::formula tmp; #endif #ifdef UNABBREV tmp = f1; f1 = spot::ltl::unabbreviate(f1, UNABBREV); - tmp->destroy(); - spot::ltl::dump(std::cout, f1); - std::cout << std::endl; + f1.dump(std::cout) << std::endl; #endif #ifdef NENOFORM tmp = f1; f1 = spot::ltl::negative_normal_form(f1); - tmp->destroy(); - spot::ltl::dump(std::cout, f1); - std::cout << std::endl; + f1.dump(std::cout) << std::endl; #endif #ifdef REDUC spot::ltl::ltl_simplifier_options opt(true, true, true, @@ -141,7 +135,7 @@ main(int argc, char** argv) # endif spot::ltl::ltl_simplifier simp(opt); { - const spot::ltl::formula* tmp; + spot::ltl::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -152,18 +146,15 @@ main(int argc, char** argv) spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } - - tmp->destroy(); } - spot::ltl::dump(std::cout, f1); - std::cout << std::endl; + f1.dump(std::cout) << std::endl; #endif #ifdef REDUC_TAU spot::ltl::ltl_simplifier_options opt(false, false, false, true, false); spot::ltl::ltl_simplifier simp(opt); { - const spot::ltl::formula* tmp; + spot::ltl::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -174,18 +165,15 @@ main(int argc, char** argv) spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } - - tmp->destroy(); } - spot::ltl::dump(std::cout, f1); - std::cout << std::endl; + f1.dump(std::cout) << std::endl; #endif #ifdef REDUC_TAUSTR spot::ltl::ltl_simplifier_options opt(false, false, false, true, true); spot::ltl::ltl_simplifier simp(opt); { - const spot::ltl::formula* tmp; + spot::ltl::formula tmp; tmp = f1; f1 = simp.simplify(f1); @@ -196,11 +184,8 @@ main(int argc, char** argv) spot::ltl::print_psl(std::cerr << "Simplified: ", f1) << '\n'; exit_code = 1; } - - tmp->destroy(); } - spot::ltl::dump(std::cout, f1); - std::cout << std::endl; + f1.dump(std::cout) << std::endl; #endif exit_code |= f1 != f2; @@ -225,25 +210,11 @@ main(int argc, char** argv) exit_code ^= 1; #endif if (exit_code) - { - spot::ltl::dump(std::cerr, f1) << std::endl; - spot::ltl::dump(std::cerr, f2) << std::endl; - return exit_code; - } - + return exit_code; } - f1->destroy(); } - f2->destroy(); } - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/ikwiad.cc b/src/tests/ikwiad.cc index 6b504be67..fa5d2e8fb 100644 --- a/src/tests/ikwiad.cc +++ b/src/tests/ikwiad.cc @@ -28,7 +28,7 @@ #include #include "ltlvisit/print.hh" #include "ltlvisit/apcollect.hh" -#include "ltlast/allnodes.hh" +#include "ltlast/formula.hh" #include "ltlparse/public.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/ltl2taa.hh" @@ -923,7 +923,7 @@ checked_main(int argc, char** argv) input = argv[formula_index]; } - const spot::ltl::formula* f = 0; + spot::ltl::formula f = nullptr; if (!from_file) // Reading a formula, not reading an automaton from a file. { switch (translation) @@ -971,8 +971,7 @@ checked_main(int argc, char** argv) if (simp) { tm.start("reducing formula"); - const spot::ltl::formula* t = simp->simplify(f); - f->destroy(); + spot::ltl::formula t = simp->simplify(f); tm.stop("reducing formula"); f = t; if (display_reduced_form) @@ -987,8 +986,8 @@ checked_main(int argc, char** argv) simp->clear_as_bdd_cache(); } - if (f->is_psl_formula() - && !f->is_ltl_formula() + if (f.is_psl_formula() + && !f.is_ltl_formula() && (translation != TransFM && translation != TransCompo)) { std::cerr << "Only the FM algorithm can translate PSL formulae;" @@ -1152,7 +1151,7 @@ checked_main(int argc, char** argv) } if (opt_determinize && a->acc().num_sets() <= 1 - && (!f || f->is_syntactic_recurrence())) + && (!f || f.is_syntactic_recurrence())) { tm.start("determinization 2"); auto determinized = tba_determinize(ensure_digraph(a), 0, @@ -1642,8 +1641,6 @@ checked_main(int argc, char** argv) } while (search_many); } - if (f) - f->destroy(); } else { @@ -1653,14 +1650,7 @@ checked_main(int argc, char** argv) if (use_timer) tm.print(std::cout); - if (unobservables) - { - for (spot::ltl::atomic_prop_set::iterator i = - unobservables->begin(); i != unobservables->end(); ++i) - (*i)->destroy(); - delete unobservables; - } - + delete unobservables; return exit_code; } @@ -1669,13 +1659,6 @@ int main(int argc, char** argv) { int exit_code = checked_main(argc, argv); - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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::fnode::instances_check()); return exit_code; } diff --git a/src/tests/isop.test b/src/tests/isop.test index 2a5a1f553..ef1e72b7c 100755 --- a/src/tests/isop.test +++ b/src/tests/isop.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2013 Laboratoire de Recherche et Développement +# Copyright (C) 2013, 2015 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -35,7 +35,7 @@ cat> expected<-> ((!a & b) | (a & !b)) +{{{!a && !b} | {b && d}}[*];a[*]}<>-> ((!a & b) | (a & !b)) EOF cat output @@ -48,7 +48,7 @@ cat> expected< b) & (b -> d) (a -> b) & (b -> d) & Xc GF((a | b) & (b | d)) -{{{{a -> b} && {b -> d}}}[*];a[*]}<>-> ((a | b) & (!a | !b)) +{{{a -> b} && {b -> d}}[*];a[*]}<>-> ((a | b) & (!a | !b)) EOF cat output diff --git a/src/tests/kind.cc b/src/tests/kind.cc index 906d98c23..10e6e18f5 100644 --- a/src/tests/kind.cc +++ b/src/tests/kind.cc @@ -23,7 +23,6 @@ #include #include #include "ltlparse/public.hh" -#include "ltlast/allnodes.hh" void syntax(char *prog) @@ -60,7 +59,7 @@ main(int argc, char **argv) std::getline(ss, expected); spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(form, p1); + auto f1 = spot::ltl::parse_infix_psl(form, p1); if (spot::ltl::format_parse_errors(std::cerr, form, p1)) return 2; @@ -74,11 +73,7 @@ main(int argc, char **argv) << "' but expected '" << expected << "'\n"; return 2; } - f1->destroy(); } - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/length.cc b/src/tests/length.cc index 8542608b9..d58356acf 100644 --- a/src/tests/length.cc +++ b/src/tests/length.cc @@ -23,7 +23,6 @@ #include #include "ltlparse/public.hh" #include "ltlvisit/length.hh" -#include "ltlast/allnodes.hh" void syntax(char *prog) @@ -45,21 +44,19 @@ main(int argc, char **argv) ++argv; } - spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(argv[1], p1); + { + spot::ltl::parse_error_list p1; + auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) - return 2; + if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + return 2; - if (boolone) - std::cout << spot::ltl::length_boolone(f1) << std::endl; - else - std::cout << spot::ltl::length(f1) << std::endl; + if (boolone) + std::cout << spot::ltl::length_boolone(f1) << std::endl; + else + std::cout << spot::ltl::length(f1) << std::endl; + } - f1->destroy(); - 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::fnode::instances_check()); return 0; } diff --git a/src/tests/ltldo.test b/src/tests/ltldo.test index fb050c08a..e261dbaaf 100755 --- a/src/tests/ltldo.test +++ b/src/tests/ltldo.test @@ -27,7 +27,7 @@ genltl=../../bin/genltl run 0 $ltldo -f a -f 'a&b' -t 'echo %f,%s' >output cat >expected <output cat >expected <exp <out diff --git a/src/tests/ltlgrind.test b/src/tests/ltlgrind.test index 3584250d7..25890a903 100755 --- a/src/tests/ltlgrind.test +++ b/src/tests/ltlgrind.test @@ -25,7 +25,7 @@ set -e checkopt() { cat >exp - run 0 ../../bin/ltlgrind "$@" > out + run 0 ../../bin/ltlgrind --sort "$@" > out # The result must be parsable ../../bin/ltlfilt out diff exp out @@ -34,44 +34,44 @@ checkopt() checkopt -f 'Xp1 U (p4 | (p3 xor (p4 W p0)))' <-> G(d <-> e) xor f' --split-ops \ <-> (f & !G(d <-> e)) -{{{a | b}}[*4] & {{{a | b}}[*];c}}<>-> (!f & G(d <-> e)) -{{{a | b}}[*4] & {{{a | b}}[*];c}}<>-> (f xor G(d -> e)) -{{{a | b}}[*4] & {{{a | b}}[*];c}}<>-> (f xor G(e -> d)) -{{{a | b}}[*4] & {{{a | b}}[*];c}}<>-> (f xor G(d & e)) -{{{a | b}}[*4] & {{{a | b}}[*];c}}<>-> (f xor G(!d & !e)) -{{{{a | b}}[*];c} && {{{a | b}}[*4];[*]}}<>-> (f xor G(d <-> e)) -{{{a | b}}[*4] && {{{a | b}}[*];c;[*]}}<>-> (f xor G(d <-> e)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (f xor G(d -> e)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (f xor G(e -> d)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (f xor G(d & e)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (f & !G(d <-> e)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (!f & G(d <-> e)) +{{a | b}[*4] & {{a | b}[*];c}}<>-> (f xor G(!d & !e)) +{{{a | b}[*];c} && {{a | b}[*4];[*]}}<>-> (f xor G(d <-> e)) +{{a | b}[*4] && {{a | b}[*];c;[*]}}<>-> (f xor G(d <-> e)) EOF @@ -108,9 +108,9 @@ checkopt -f '!(!XXp1 M X(p4 U p2))' --rewrite-ops < 0' --simplify-bounds < p4)' -m 2 < p4 p4 -> p3 p3 & p4 -!p4 -!p3 -!p3 & !p4 1 U p3 1 U p4 1 U !p3 1 U !p4 +!p3 & !p4 1 U (p3 & !p4) 1 U (!p3 & p4) EOF checkopt -f 'F({{p2;p0}[:*]}[]-> Xp0)' < Xp0 -F!{{p2;p0}[:*]} FXp0 +F!{{p2;p0}[:*]} +{{p2;p0}[:*]}[]-> Xp0 +F({p2;p0}[]-> Xp0) F({{p2;p0}[:*]}[]-> p0) F({{p2;p0}[:*]}[]-> 0) -F({p2;p0}[]-> Xp0) -F({{1;p0}[:*]}[]-> Xp0) -F({{p2;1}[:*]}[]-> Xp0) F({p0[*2][:*]}[]-> Xp0) F({p2[*2][:*]}[]-> Xp2) +F({{1;p0}[:*]}[]-> Xp0) +F({{p2;1}[:*]}[]-> Xp0) EOF diff --git a/src/tests/ltlprod.cc b/src/tests/ltlprod.cc index ede56f587..0831523e9 100644 --- a/src/tests/ltlprod.cc +++ b/src/tests/ltlprod.cc @@ -23,7 +23,6 @@ #include #include #include -#include "ltlast/allnodes.hh" #include "ltlparse/public.hh" #include "twaalgos/product.hh" #include "twaalgos/ltl2tgba_fm.hh" @@ -48,13 +47,13 @@ main(int argc, char** argv) spot::ltl::environment& env(spot::ltl::default_environment::instance()); spot::ltl::parse_error_list pel1; - auto* f1 = spot::ltl::parse_infix_psl(argv[1], pel1, env); + auto f1 = spot::ltl::parse_infix_psl(argv[1], pel1, env); if (spot::ltl::format_parse_errors(std::cerr, argv[1], pel1)) return 2; spot::ltl::parse_error_list pel2; - auto* f2 = spot::ltl::parse_infix_psl(argv[2], pel2, env); + auto f2 = spot::ltl::parse_infix_psl(argv[2], pel2, env); if (spot::ltl::format_parse_errors(std::cerr, argv[2], pel2)) return 2; @@ -63,14 +62,9 @@ main(int argc, char** argv) { auto a1 = spot::ltl_to_tgba_fm(f1, dict); auto a2 = spot::ltl_to_tgba_fm(f2, dict); - f1->destroy(); - f2->destroy(); spot::print_dot(std::cout, product(a1, a2)); } } - 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::fnode::instances_check()); return exit_code; } diff --git a/src/tests/ltlrel.cc b/src/tests/ltlrel.cc index 723e4d35a..73f039e88 100644 --- a/src/tests/ltlrel.cc +++ b/src/tests/ltlrel.cc @@ -22,7 +22,6 @@ #include #include "ltlparse/public.hh" #include "ltlvisit/relabel.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/print.hh" void @@ -38,38 +37,30 @@ main(int argc, char **argv) if (argc != 2) syntax(argv[0]); - spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(argv[1], p1); + { + spot::ltl::parse_error_list p1; + auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) - return 2; + if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + return 2; - spot::ltl::relabeling_map* m = new spot::ltl::relabeling_map; - const spot::ltl::formula* f2 = spot::ltl::relabel_bse(f1, spot::ltl::Pnn, m); - f1->destroy(); - spot::ltl::print_psl(std::cout, f2) << '\n'; + spot::ltl::relabeling_map* m = new spot::ltl::relabeling_map; + auto f2 = spot::ltl::relabel_bse(f1, spot::ltl::Pnn, m); + spot::ltl::print_psl(std::cout, f2) << '\n'; - typedef std::map map_t; - map_t sorted_map; - for (spot::ltl::relabeling_map::const_iterator i = m->begin(); - i != m->end(); ++i) - sorted_map[spot::ltl::str_psl(i->first)] = - spot::ltl::str_psl(i->second); - for (map_t::const_iterator i = sorted_map.begin(); - i != sorted_map.end(); ++i) - std::cout << " " << i->first << " -> " - << i->second << '\n'; - f2->destroy(); - delete m; - - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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); + typedef std::map map_t; + map_t sorted_map; + for (spot::ltl::relabeling_map::const_iterator i = m->begin(); + i != m->end(); ++i) + sorted_map[spot::ltl::str_psl(i->first)] = + spot::ltl::str_psl(i->second); + for (map_t::const_iterator i = sorted_map.begin(); + i != sorted_map.end(); ++i) + std::cout << " " << i->first << " -> " + << i->second << '\n'; + delete m; + } + assert(spot::ltl::fnode::instances_check()); return 0; } diff --git a/src/tests/parse_print_test.cc b/src/tests/parse_print_test.cc index 7452c259b..9e2a6f564 100644 --- a/src/tests/parse_print_test.cc +++ b/src/tests/parse_print_test.cc @@ -20,8 +20,6 @@ #include "kripkeparse/public.hh" #include "kripke/kripkeprint.hh" -#include "ltlast/allnodes.hh" - using namespace spot; @@ -44,9 +42,6 @@ int main(int argc, char** argv) kripke_save_reachable(std::cout, k); } - assert(ltl::atomic_prop::instance_count() == 0); - assert(ltl::unop::instance_count() == 0); - assert(ltl::binop::instance_count() == 0); - assert(ltl::multop::instance_count() == 0); + assert(spot::ltl::fnode::instances_check()); return return_value; } diff --git a/src/tests/parseaut.test b/src/tests/parseaut.test index 914fb8d97..09bfad85e 100755 --- a/src/tests/parseaut.test +++ b/src/tests/parseaut.test @@ -690,8 +690,8 @@ State: 1 never { /* a U b */ T0_init: if - :: ((b)) -> goto accept_all - :: ((a)) -> goto T0_init + :: (b) -> goto accept_all + :: (a) -> goto T0_init fi; accept_all: skip @@ -1452,7 +1452,7 @@ expectok input --spin=c < goto accept_all + :: (b) -> goto accept_all :: ((a) && (!(b))) -> goto T0_init fi; accept_all: /* s0 */ diff --git a/src/tests/parseerr.test b/src/tests/parseerr.test index 141029e13..46e132494 100755 --- a/src/tests/parseerr.test +++ b/src/tests/parseerr.test @@ -1,7 +1,7 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2011, 2013, 2014 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2009, 2010, 2011, 2013, 2014, 2015 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre # et Marie Curie. @@ -96,7 +96,7 @@ a & (, a & 0 EOF run 0 ../equals -E recover.txt -check 'a - b' 'AP(a)' '>>> a - b +check 'a - b' 'AP(@3 #0 "a")' '>>> a - b ^ syntax error, unexpected $undefined @@ -105,7 +105,7 @@ syntax error, unexpected $undefined ignoring trailing garbage ' -check '{a[*9999999999]}' 'unop(Closure, bunop(Star, AP(a), 0, unbounded))' \ +check '{a[*9999999999]}' 'Closure(@5 #0 [Star(@4 #0 0.. [AP(@3 #0 "a")])])' \ '>>> {a[*9999999999]} ^^^^^^^^^^ value too large ignored diff --git a/src/tests/randtgba.cc b/src/tests/randtgba.cc index bf5844285..e783ec604 100644 --- a/src/tests/randtgba.cc +++ b/src/tests/randtgba.cc @@ -486,7 +486,7 @@ print_ar_stats(ar_stats_type& ar_stats, const std::string& s) std::cout << std::setiosflags(old); } -const spot::ltl::formula* +spot::ltl::formula generate_formula(const spot::ltl::random_ltl& rl, spot::ltl::ltl_simplifier& simp, int opt_f, int opt_s, @@ -498,21 +498,16 @@ generate_formula(const spot::ltl::random_ltl& rl, while (max_tries_u--) { spot::srand(opt_s++); - const spot::ltl::formula* f; + spot::ltl::formula f; int max_tries_l = 1000; while (max_tries_l--) { f = rl.generate(opt_f); if (opt_l) { - const spot::ltl::formula* g = simp.simplify(f); - f->destroy(); - if (spot::ltl::length(g) < opt_l) - { - g->destroy(); - continue; - } - f = g; + f = simp.simplify(f); + if (spot::ltl::length(f) < opt_l) + continue; } else { @@ -529,10 +524,7 @@ generate_formula(const spot::ltl::random_ltl& rl, } std::string txt = spot::ltl::str_psl(f); if (!opt_u || unique.insert(txt).second) - { - return f; - } - f->destroy(); + return f; } assert(opt_u); std::cerr << "Failed to generate another unique formula." @@ -855,12 +847,11 @@ main(int argc, char** argv) { if (opt_F) { - const spot::ltl::formula* f = + spot::ltl::formula f = generate_formula(rl, simp, opt_f, opt_ec_seed, opt_l, opt_u); if (!f) exit(1); formula = spot::ltl_to_tgba_fm(f, dict, true); - f->destroy(); } else if (opt_i) { @@ -872,20 +863,16 @@ main(int argc, char** argv) else if (input == "") break; spot::ltl::parse_error_list pel; - auto* f = spot::ltl::parse_infix_psl(input, pel, env); + auto f = spot::ltl::parse_infix_psl(input, pel, env); if (spot::ltl::format_parse_errors(std::cerr, input, pel)) { exit_code = 1; break; } formula = spot::ltl_to_tgba_fm(f, dict, true); - spot::ltl::atomic_prop_set* tmp = - spot::ltl::atomic_prop_collect(f); - for (spot::ltl::atomic_prop_set::iterator i = tmp->begin(); - i != tmp->end(); ++i) - apf->insert(down_cast - ((*i)->clone())); - f->destroy(); + auto* tmp = spot::ltl::atomic_prop_collect(f); + for (auto i: *tmp) + apf->insert(i); delete tmp; } else @@ -896,9 +883,8 @@ main(int argc, char** argv) } } - for (spot::ltl::atomic_prop_set::iterator i = ap->begin(); - i != ap->end(); ++i) - apf->insert(static_cast((*i)->clone())); + for (auto i: *ap) + apf->insert(i); if (!opt_S) { @@ -1147,9 +1133,6 @@ main(int argc, char** argv) if (opt_F) --opt_F; opt_ec = init_opt_ec; - for (spot::ltl::atomic_prop_set::iterator i = apf->begin(); - i != apf->end(); ++i) - (*i)->destroy(); apf->clear(); } while (opt_F || opt_i); @@ -1294,10 +1277,6 @@ main(int argc, char** argv) std::cout << std::endl; } - for (spot::ltl::atomic_prop_set::iterator i = ap->begin(); - i != ap->end(); ++i) - (*i)->destroy(); - if (opt_i && strcmp(opt_i, "-")) { dynamic_cast(formula_file)->close(); diff --git a/src/tests/readltl.cc b/src/tests/readltl.cc index ddc4ec585..3a5621472 100644 --- a/src/tests/readltl.cc +++ b/src/tests/readltl.cc @@ -25,9 +25,7 @@ #include #include #include "ltlparse/public.hh" -#include "ltlvisit/dump.hh" #include "ltlvisit/dot.hh" -#include "ltlast/allnodes.hh" void syntax(char* prog) @@ -36,16 +34,6 @@ syntax(char* prog) exit(2); } -void -dump_instances(const std::string& label) -{ - std::cerr << "=== " << label << " ===" << std::endl; - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); -} - int main(int argc, char** argv) { @@ -55,7 +43,6 @@ main(int argc, char** argv) syntax(argv[0]); bool debug = false; - bool debug_ref = false; int formula_index = 1; if (!strcmp(argv[1], "-d")) @@ -65,47 +52,30 @@ main(int argc, char** argv) syntax(argv[0]); formula_index = 2; } - else if (!strcmp(argv[1], "-r")) - { - debug_ref = true; - if (argc < 3) - syntax(argv[0]); - formula_index = 2; - } - spot::ltl::environment& env(spot::ltl::default_environment::instance()); - spot::ltl::parse_error_list pel; - auto* f = spot::ltl::parse_infix_psl(argv[formula_index], pel, env, debug); + { + spot::ltl::environment& env(spot::ltl::default_environment::instance()); + spot::ltl::parse_error_list pel; + auto f = spot::ltl::parse_infix_psl(argv[formula_index], pel, env, debug); - exit_code = - spot::ltl::format_parse_errors(std::cerr, argv[formula_index], pel); + exit_code = + spot::ltl::format_parse_errors(std::cerr, argv[formula_index], pel); - if (f) - { - if (debug_ref) - dump_instances("before"); - + if (f) + { #ifdef DOTTY - spot::ltl::print_dot_psl(std::cout, f); + spot::ltl::print_dot_psl(std::cout, f); #else - spot::ltl::dump(std::cout, f); - std::cout << std::endl; + f.dump(std::cout) << std::endl; #endif - f->destroy(); + } + else + { + exit_code = 1; + } - if (debug_ref) - dump_instances("after"); - } - else - { - exit_code = 1; - } - - 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::bunop::instance_count() == 0); - assert(spot::ltl::multop::instance_count() == 0); + } + assert(spot::ltl::fnode::instances_check()); return exit_code; } diff --git a/src/tests/reduc.cc b/src/tests/reduc.cc index 38187fe93..56a16214a 100644 --- a/src/tests/reduc.cc +++ b/src/tests/reduc.cc @@ -27,11 +27,9 @@ #include #include #include "ltlparse/public.hh" -#include "ltlvisit/dump.hh" #include "ltlvisit/print.hh" #include "ltlvisit/simplify.hh" #include "ltlvisit/length.hh" -#include "ltlast/allnodes.hh" void syntax(char* prog) @@ -146,183 +144,170 @@ main(int argc, char** argv) return 2; } - spot::ltl::ltl_simplifier* simp = new spot::ltl::ltl_simplifier(o); - o.reduce_size_strictly = true; - spot::ltl::ltl_simplifier* simp_size = new spot::ltl::ltl_simplifier(o); - - const spot::ltl::formula* f1 = 0; - const spot::ltl::formula* f2 = 0; - - std::ifstream* fin = 0; - - if (readfile) - { - fin = new std::ifstream(argv[2]); - if (!*fin) - { - std::cerr << "Cannot open " << argv[2] << std::endl; - exit(2); - } - } - int exit_code = 0; - next_line: - - if (fin) - { - std::string input; - do - { - if (!std::getline(*fin, input)) - goto end; - } - while (input == ""); - - spot::ltl::parse_error_list p1; - f1 = spot::ltl::parse_infix_psl(input, p1); - if (spot::ltl::format_parse_errors(std::cerr, input, p1)) - return 2; - } - else - { - spot::ltl::parse_error_list p1; - f1 = spot::ltl::parse_infix_psl(argv[2], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) - return 2; - } - - if (argc == 4) - { - if (readfile) - { - std::cerr << "Cannot read from file and check result." << std::endl; - exit(2); - } - - spot::ltl::parse_error_list p2; - f2 = spot::ltl::parse_infix_psl(argv[3], p2); - if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) - return 2; - } - { - const spot::ltl::formula* ftmp1; + spot::ltl::ltl_simplifier* simp = new spot::ltl::ltl_simplifier(o); + o.reduce_size_strictly = true; + spot::ltl::ltl_simplifier* simp_size = new spot::ltl::ltl_simplifier(o); - ftmp1 = f1; - f1 = simp_size->negative_normal_form(f1, false); - ftmp1->destroy(); + spot::ltl::formula f1 = nullptr; + spot::ltl::formula f2 = nullptr; - int length_f1_before = spot::ltl::length(f1); - std::string f1s_before = spot::ltl::str_psl(f1); - std::string f1l; + std::ifstream* fin = 0; - const spot::ltl::formula* input_f = f1; - f1 = simp_size->simplify(input_f); - if (!simp_size->are_equivalent(input_f, f1)) + if (readfile) { - std::cerr << "Incorrect reduction from `" << f1s_before - << "' to `"; - print_psl(std::cerr, f1) << "'.\n"; - exit_code = 3; - } - else - { - const spot::ltl::formula* maybe_larger = simp->simplify(input_f); - f1l = spot::ltl::str_psl(maybe_larger); - if (!simp->are_equivalent(input_f, maybe_larger)) + fin = new std::ifstream(argv[2]); + if (!*fin) { - std::cerr << "Incorrect reduction (reduce_size_strictly=0) from `" - << f1s_before << "' to `" << f1l << "'." << std::endl; - exit_code = 3; - } - maybe_larger->destroy(); - } - - input_f->destroy(); - - int length_f1_after = spot::ltl::length(f1); - std::string f1s_after = spot::ltl::str_psl(f1); - - std::string f2s = ""; - if (f2) - { - ftmp1 = f2; - f2 = simp_size->negative_normal_form(f2, false); - ftmp1->destroy(); - f2s = spot::ltl::str_psl(f2); - } - - sum_before += length_f1_before; - sum_after += length_f1_after; - - // If -h is set, we want to print only formulae that have become larger. - if (!f2 && (!hidereduc || (length_f1_after > length_f1_before))) - { - std::cout << length_f1_before << ' ' << length_f1_after - << " '" << f1s_before << "' reduce to '" << f1s_after << '\''; - if (f1l != "" && f1l != f1s_after) - std::cout << " or (w/o rss) to '" << f1l << '\''; - std::cout << '\n'; - } - - if (f2) - { - if (f1 != f2) - { - if (length_f1_after < length_f1_before) - std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after - << " KOREDUC " << std::endl; - else - std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after - << " KOIDEM " << std::endl; - exit_code = 1; - } - else - { - if (f1s_before != f1s_after) - std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after - << " OKREDUC " << std::endl; - else - std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after - << " OKIDEM" << std::endl; - exit_code = 0; + std::cerr << "Cannot open " << argv[2] << std::endl; + exit(2); } } - else - { - if (length_f1_after > length_f1_before) - exit_code = 1; - } - f1->destroy(); - if (f2) - f2->destroy(); + next_line: if (fin) - goto next_line; - } - end: + { + std::string input; + do + { + if (!std::getline(*fin, input)) + goto end; + } + while (input == ""); - delete simp_size; - delete simp; + spot::ltl::parse_error_list p1; + f1 = spot::ltl::parse_infix_psl(input, p1); + if (spot::ltl::format_parse_errors(std::cerr, input, p1)) + return 2; + } + else + { + spot::ltl::parse_error_list p1; + f1 = spot::ltl::parse_infix_psl(argv[2], p1); + if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) + return 2; + } + + if (argc == 4) + { + if (readfile) + { + std::cerr << "Cannot read from file and check result." << std::endl; + exit(2); + } + + spot::ltl::parse_error_list p2; + f2 = spot::ltl::parse_infix_psl(argv[3], p2); + if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) + return 2; + } - if (fin) { - float before = sum_before; - float after = sum_after; - std::cout << "gain: " - << (1 - (after / before)) * 100 << '%' << std::endl; - delete fin; - } + spot::ltl::formula ftmp1; - spot::ltl::atomic_prop::dump_instances(std::cerr); - spot::ltl::unop::dump_instances(std::cerr); - spot::ltl::binop::dump_instances(std::cerr); - spot::ltl::multop::dump_instances(std::cerr); - 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); + ftmp1 = f1; + f1 = simp_size->negative_normal_form(f1, false); + + int length_f1_before = spot::ltl::length(f1); + std::string f1s_before = spot::ltl::str_psl(f1); + std::string f1l; + + spot::ltl::formula input_f = f1; + f1 = simp_size->simplify(input_f); + if (!simp_size->are_equivalent(input_f, f1)) + { + std::cerr << "Incorrect reduction from `" << f1s_before + << "' to `"; + print_psl(std::cerr, f1) << "'.\n"; + exit_code = 3; + } + else + { + spot::ltl::formula maybe_larger = simp->simplify(input_f); + f1l = spot::ltl::str_psl(maybe_larger); + if (!simp->are_equivalent(input_f, maybe_larger)) + { + std::cerr << "Incorrect reduction (reduce_size_strictly=0) from `" + << f1s_before << "' to `" << f1l << "'." << std::endl; + exit_code = 3; + } + } + + int length_f1_after = spot::ltl::length(f1); + std::string f1s_after = spot::ltl::str_psl(f1); + + std::string f2s = ""; + if (f2) + { + ftmp1 = f2; + f2 = simp_size->negative_normal_form(f2, false); + f2s = spot::ltl::str_psl(f2); + } + + sum_before += length_f1_before; + sum_after += length_f1_after; + + // If -h is set, we want to print only formulae that have become larger. + if (!f2 && (!hidereduc || (length_f1_after > length_f1_before))) + { + std::cout << length_f1_before << ' ' << length_f1_after + << " '" << f1s_before << "' reduce to '" + << f1s_after << '\''; + if (f1l != "" && f1l != f1s_after) + std::cout << " or (w/o rss) to '" << f1l << '\''; + std::cout << '\n'; + } + + if (f2) + { + if (f1 != f2) + { + if (length_f1_after < length_f1_before) + std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after + << " KOREDUC " << std::endl; + else + std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after + << " KOIDEM " << std::endl; + exit_code = 1; + } + else + { + if (f1s_before != f1s_after) + std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after + << " OKREDUC " << std::endl; + else + std::cout << f1s_before << " ** " << f2s << " ** " << f1s_after + << " OKIDEM" << std::endl; + exit_code = 0; + } + } + else + { + if (length_f1_after > length_f1_before) + exit_code = 1; + } + + if (fin) + goto next_line; + } + end: + + delete simp_size; + delete simp; + + if (fin) + { + float before = sum_before; + float after = sum_after; + std::cout << "gain: " + << (1 - (after / before)) * 100 << '%' << std::endl; + delete fin; + } + } + + assert(spot::ltl::fnode::instances_check()); return exit_code; } diff --git a/src/tests/syntimpl.cc b/src/tests/syntimpl.cc index 7c9252112..1f98fa9cb 100644 --- a/src/tests/syntimpl.cc +++ b/src/tests/syntimpl.cc @@ -24,10 +24,8 @@ #include #include #include "ltlparse/public.hh" -#include "ltlvisit/dump.hh" #include "ltlvisit/print.hh" #include "ltlvisit/simplify.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/nenoform.hh" void @@ -44,74 +42,66 @@ main(int argc, char** argv) syntax(argv[0]); int opt = atoi(argv[1]); - - spot::ltl::parse_error_list p1; - auto* ftmp1 = spot::ltl::parse_infix_psl(argv[2], p1); - - if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) - return 2; - - spot::ltl::parse_error_list p2; - auto* ftmp2 = spot::ltl::parse_infix_psl(argv[3], p2); - - if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) - return 2; - - const spot::ltl::formula* f1 = spot::ltl::negative_normal_form(ftmp1); - const spot::ltl::formula* f2 = spot::ltl::negative_normal_form(ftmp2); - - std::string f1s = spot::ltl::str_psl(f1); - std::string f2s = spot::ltl::str_psl(f2); - int exit_return = 0; - spot::ltl::ltl_simplifier* c = new spot::ltl::ltl_simplifier; - switch (opt) - { - case 0: - std::cout << "Test f1 < f2" << std::endl; - if (c->syntactic_implication(f1, f2)) - { - std::cout << f1s << " < " << f2s << '\n'; - exit_return = 1; - } - break; + { + spot::ltl::parse_error_list p1; + auto ftmp1 = spot::ltl::parse_infix_psl(argv[2], p1); - case 1: - std::cout << "Test !f1 < f2" << std::endl; - if (c->syntactic_implication_neg(f1, f2, false)) - { - std::cout << "!(" << f1s << ") < " << f2s << '\n'; - exit_return = 1; - } - break; + if (spot::ltl::format_parse_errors(std::cerr, argv[2], p1)) + return 2; - case 2: - std::cout << "Test f1 < !f2" << std::endl; - if (c->syntactic_implication_neg(f1, f2, true)) - { - std::cout << f1s << " < !(" << f2s << ")\n"; - exit_return = 1; - } - break; - default: - break; - } + spot::ltl::parse_error_list p2; + auto ftmp2 = spot::ltl::parse_infix_psl(argv[3], p2); - spot::ltl::dump(std::cout, f1) << '\n'; - spot::ltl::dump(std::cout, f2) << '\n'; + if (spot::ltl::format_parse_errors(std::cerr, argv[3], p2)) + return 2; - f1->destroy(); - f2->destroy(); - ftmp1->destroy(); - ftmp2->destroy(); + spot::ltl::formula f1 = spot::ltl::negative_normal_form(ftmp1); + spot::ltl::formula f2 = spot::ltl::negative_normal_form(ftmp2); - delete c; + std::string f1s = spot::ltl::str_psl(f1); + std::string f2s = spot::ltl::str_psl(f2); - 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); + spot::ltl::ltl_simplifier* c = new spot::ltl::ltl_simplifier; + switch (opt) + { + case 0: + std::cout << "Test f1 < f2" << std::endl; + if (c->syntactic_implication(f1, f2)) + { + std::cout << f1s << " < " << f2s << '\n'; + exit_return = 1; + } + break; + + case 1: + std::cout << "Test !f1 < f2" << std::endl; + if (c->syntactic_implication_neg(f1, f2, false)) + { + std::cout << "!(" << f1s << ") < " << f2s << '\n'; + exit_return = 1; + } + break; + + case 2: + std::cout << "Test f1 < !f2" << std::endl; + if (c->syntactic_implication_neg(f1, f2, true)) + { + std::cout << f1s << " < !(" << f2s << ")\n"; + exit_return = 1; + } + break; + default: + break; + } + + f1.dump(std::cout) << '\n'; + f2.dump(std::cout) << '\n'; + + delete c; + } + assert(spot::ltl::fnode::instances_check()); return exit_return; } diff --git a/src/tests/taatgba.cc b/src/tests/taatgba.cc index 88fb8d06f..f4d80d9d0 100644 --- a/src/tests/taatgba.cc +++ b/src/tests/taatgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2014 Laboratoire de Recherche et Développement +// Copyright (C) 2009, 2014, 2015 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -21,7 +21,6 @@ #include #include "misc/hash.hh" #include "ltlenv/defaultenv.hh" -#include "ltlast/allnodes.hh" #include "twaalgos/dot.hh" #include "twa/taatgba.hh" @@ -49,8 +48,6 @@ main() spot::print_dot(std::cout, a); } - 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::fnode::instances_check()); + return 0; } diff --git a/src/tests/tostring.cc b/src/tests/tostring.cc index 720f8001a..6f57e620b 100644 --- a/src/tests/tostring.cc +++ b/src/tests/tostring.cc @@ -25,7 +25,6 @@ #include #include "ltlparse/public.hh" #include "ltlvisit/print.hh" -#include "ltlast/allnodes.hh" void syntax(char *prog) @@ -40,42 +39,38 @@ main(int argc, char **argv) if (argc != 2) syntax(argv[0]); - spot::ltl::parse_error_list p1; - auto* f1 = spot::ltl::parse_infix_psl(argv[1], p1); + { + spot::ltl::parse_error_list p1; + auto f1 = spot::ltl::parse_infix_psl(argv[1], p1); - if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) - return 2; + if (spot::ltl::format_parse_errors(std::cerr, argv[1], p1)) + return 2; - // The string generated from an abstract tree should be parsable - // again. + // The string generated from an abstract tree should be parsable + // again. - std::string f1s = spot::ltl::str_psl(f1); - std::cout << f1s << '\n'; + std::string f1s = spot::ltl::str_psl(f1); + std::cout << f1s << '\n'; - auto* f2 = spot::ltl::parse_infix_psl(f1s, p1); + auto f2 = spot::ltl::parse_infix_psl(f1s, p1); - if (spot::ltl::format_parse_errors(std::cerr, f1s, p1)) - return 2; + if (spot::ltl::format_parse_errors(std::cerr, f1s, p1)) + return 2; - // This second abstract tree should be equal to the first. + // This second abstract tree should be equal to the first. - if (f1 != f2) - return 1; + if (f1 != f2) + return 1; - // It should also map to the same string. + // It should also map to the same string. - std::string f2s = spot::ltl::str_psl(f2); - std::cout << f2s << '\n'; + std::string f2s = spot::ltl::str_psl(f2); + std::cout << f2s << '\n'; - if (f2s != f1s) - return 1; + if (f2s != f1s) + return 1; + } - f1->destroy(); - f2->destroy(); - assert(spot::ltl::atomic_prop::instance_count() == 0); - assert(spot::ltl::unop::instance_count() == 0); - assert(spot::ltl::binop::instance_count() == 0); - assert(spot::ltl::multop::instance_count() == 0); - assert(spot::ltl::bunop::instance_count() == 0); + assert(spot::ltl::fnode::instances_check()); return 0; } diff --git a/src/tests/twagraph.cc b/src/tests/twagraph.cc index 1f448600f..4a1af2312 100644 --- a/src/tests/twagraph.cc +++ b/src/tests/twagraph.cc @@ -31,8 +31,8 @@ void f1() bdd p2 = bdd_ithvar(tg->register_ap("p2")); tg->acc().add_sets(2); - for (auto *f: tg->ap()) - std::cout << f->name() << '\n'; + for (auto f: tg->ap()) + std::cout << f.ap_name() << '\n'; auto s1 = tg->new_state(); auto s2 = tg->new_state(); diff --git a/src/tests/utf8.test b/src/tests/utf8.test index 5cc440e5b..6ccca4991 100755 --- a/src/tests/utf8.test +++ b/src/tests/utf8.test @@ -26,12 +26,12 @@ set -e # ---- run 0 ../ltl2text '□◯a' >out -echo 'unop(G, unop(X, AP(a)))' > exp +echo 'G(@5 #0 [X(@4 #0 [AP(@3 #0 "a")])])' > exp cmp out exp # ---- run 0 ../ltl2text '□◯"αβγ"' >out -echo 'unop(G, unop(X, AP(αβγ)))' > exp +echo 'G(@5 #0 [X(@4 #0 [AP(@3 #0 "αβγ")])])' > exp cmp out exp diff --git a/src/twa/acc.cc b/src/twa/acc.cc index 211b2dad4..d3364e7e2 100644 --- a/src/twa/acc.cc +++ b/src/twa/acc.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "acc.hh" #include "priv/bddalloc.hh" #include "misc/minato.hh" diff --git a/src/twa/bdddict.cc b/src/twa/bdddict.cc index b1c01ce90..0a2a494df 100644 --- a/src/twa/bdddict.cc +++ b/src/twa/bdddict.cc @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include "priv/bddalloc.hh" #include "bdddict.hh" @@ -95,7 +95,7 @@ namespace spot } int - bdd_dict::register_proposition(const ltl::formula* f, const void* for_me) + bdd_dict::register_proposition(ltl::formula f, const void* for_me) { int num; // Do not build a variable that already exists. @@ -106,7 +106,6 @@ namespace spot } else { - f = f->clone(); num = priv_->allocate_variables(1); var_map[f] = num; bdd_map.resize(bdd_varnum()); @@ -118,7 +117,7 @@ namespace spot } int - bdd_dict::has_registered_proposition(const ltl::formula* f, + bdd_dict::has_registered_proposition(ltl::formula f, const void* me) { auto ssi = var_map.find(f); @@ -132,8 +131,8 @@ namespace spot } int - bdd_dict::register_acceptance_variable(const ltl::formula* f, - const void* for_me) + bdd_dict::register_acceptance_variable(ltl::formula f, + const void* for_me) { int num; // Do not build an acceptance variable that already exists. @@ -144,7 +143,6 @@ namespace spot } else { - f = f->clone(); num = priv_->allocate_variables(1); acc_map[f] = num; bdd_map.resize(bdd_varnum()); @@ -173,7 +171,7 @@ namespace spot register_acceptance_variables(bdd_low(f), for_me); } - const ltl::formula* + ltl::formula bdd_dict::oneacc_to_formula(int var) const { assert(unsigned(var) < bdd_map.size()); @@ -182,7 +180,7 @@ namespace spot return i.f; } - const ltl::formula* + ltl::formula bdd_dict::oneacc_to_formula(bdd oneacc) const { assert(oneacc != bddfalse); @@ -276,7 +274,7 @@ namespace spot // Let's free it. First, we need to find // if this is a Var or an Acc variable. int n = 1; - const ltl::formula* f = 0; + ltl::formula f = nullptr; switch (bdd_map[v].type) { case var: @@ -302,8 +300,6 @@ namespace spot // Actually release the associated BDD variables, and the // formula itself. priv_->release_variables(v, n); - if (f) - f->destroy(); while (n--) bdd_map[v + n].type = anon; } diff --git a/src/twa/bdddict.hh b/src/twa/bdddict.hh index 9c15bacc1..a56ed0950 100644 --- a/src/twa/bdddict.hh +++ b/src/twa/bdddict.hh @@ -66,9 +66,9 @@ namespace spot ~bdd_dict(); /// Formula-to-BDD-variable maps. - typedef std::map fv_map; + typedef std::map fv_map; /// BDD-variable-to-formula maps. - typedef std::map vf_map; + typedef std::map vf_map; fv_map var_map; ///< Maps atomic propositions to BDD variables fv_map acc_map; ///< Maps acceptance conditions to BDD variables @@ -80,7 +80,7 @@ namespace spot struct bdd_info { bdd_info() : type(anon) {} var_type type; - const ltl::formula* f; // Used unless t==anon. + ltl::formula f; // Used unless t==anon. ref_set refs; int clone_counts; }; @@ -99,11 +99,10 @@ namespace spot /// \return The variable number. Use bdd_ithvar() or bdd_nithvar() /// to convert this to a BDD. /// @{ - int register_proposition(const ltl::formula* f, const void* for_me); + int register_proposition(ltl::formula f, const void* for_me); template - int register_proposition(const ltl::formula* f, - std::shared_ptr for_me) + int register_proposition(ltl::formula f, std::shared_ptr for_me) { return register_proposition(f, for_me.get()); } @@ -115,11 +114,10 @@ namespace spot /// a non-negative value that is the BDD variable number. /// Otherwise this returns -1. /// @{ - int has_registered_proposition(const ltl::formula* f, - const void* me); + int has_registered_proposition(ltl::formula f, const void* me); + template - int has_registered_proposition(const ltl::formula* f, - std::shared_ptr for_me) + int has_registered_proposition(ltl::formula f, std::shared_ptr for_me) { return has_registered_proposition(f, for_me.get()); } @@ -136,11 +134,10 @@ namespace spot /// \return The variable number. Use bdd_ithvar() or bdd_nithvar() /// to convert this to a BDD. /// @{ - int register_acceptance_variable(const ltl::formula* f, const void* for_me); + int register_acceptance_variable(ltl::formula f, const void* for_me); template - int register_acceptance_variable(const ltl::formula* f, - std::shared_ptr for_me) + int register_acceptance_variable(ltl::formula f, std::shared_ptr for_me) { return register_acceptance_variable(f, for_me.get()); } @@ -172,7 +169,7 @@ namespace spot /// /// The returned formula is not cloned, and is valid until the BDD /// variable used in \a oneacc are unregistered. - const ltl::formula* oneacc_to_formula(bdd oneacc) const; + ltl::formula oneacc_to_formula(bdd oneacc) const; /// \brief Convert one acceptance condition into the associated /// formula. @@ -182,7 +179,7 @@ namespace spot /// /// The returned formula is not cloned, and is valid until the BDD /// variable \a var is unregistered. - const ltl::formula* oneacc_to_formula(int var) const; + ltl::formula oneacc_to_formula(int var) const; /// \brief Register anonymous BDD variables. /// diff --git a/src/twa/bddprint.cc b/src/twa/bddprint.cc index ea849d9c4..e51a1769a 100644 --- a/src/twa/bddprint.cc +++ b/src/twa/bddprint.cc @@ -40,7 +40,7 @@ namespace spot static bool utf8; static - std::ostream& print_(std::ostream& o, const ltl::formula* f) + std::ostream& print_(std::ostream& o, ltl::formula f) { if (utf8) ltl::print_utf8_psl(o, f); @@ -172,10 +172,7 @@ namespace spot std::ostream& bdd_print_formula(std::ostream& os, const bdd_dict_ptr& d, bdd b) { - const ltl::formula* f = bdd_to_formula(b, d); - print_(os, f); - f->destroy(); - return os; + return print_(os, bdd_to_formula(b, d)); } std::string diff --git a/src/twa/formula2bdd.cc b/src/twa/formula2bdd.cc index 87eaffa9e..414f708ad 100644 --- a/src/twa/formula2bdd.cc +++ b/src/twa/formula2bdd.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2010, 2012, 2014 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2009, 2010, 2012, 2014, 2015 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // Université Pierre et Marie Curie. @@ -22,8 +22,6 @@ #include #include "formula2bdd.hh" -#include "ltlast/allnodes.hh" -#include "ltlast/visitor.hh" #include "misc/minato.hh" namespace spot @@ -32,163 +30,24 @@ namespace spot namespace { - class formula_to_bdd_visitor: public ltl::visitor - { - public: - formula_to_bdd_visitor(const bdd_dict_ptr& d, void* owner) - : d_(d), owner_(owner) - { - } - - virtual - ~formula_to_bdd_visitor() - { - } - - virtual void - visit(const atomic_prop* node) - { - res_ = bdd_ithvar(d_->register_proposition(node, owner_)); - } - - virtual void - visit(const constant* node) - { - switch (node->val()) - { - case constant::True: - res_ = bddtrue; - return; - case constant::False: - res_ = bddfalse; - return; - case constant::EmptyWord: - SPOT_UNIMPLEMENTED(); - } - SPOT_UNREACHABLE(); - } - - virtual void - visit(const bunop*) - { - SPOT_UNIMPLEMENTED(); - } - - virtual void - visit(const unop* node) - { - switch (node->op()) - { - case unop::F: - case unop::G: - case unop::X: - case unop::Closure: - case unop::NegClosure: - case unop::NegClosureMarked: - SPOT_UNIMPLEMENTED(); - case unop::Not: - { - res_ = bdd_not(recurse(node->child())); - return; - } - } - SPOT_UNREACHABLE(); - } - - virtual void - visit(const binop* node) - { - bdd f1 = recurse(node->first()); - bdd f2 = recurse(node->second()); - - switch (node->op()) - { - case binop::Xor: - res_ = bdd_apply(f1, f2, bddop_xor); - return; - case binop::Implies: - res_ = bdd_apply(f1, f2, bddop_imp); - return; - case binop::Equiv: - res_ = bdd_apply(f1, f2, bddop_biimp); - return; - case binop::U: - case binop::R: - case binop::W: - case binop::M: - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - SPOT_UNIMPLEMENTED(); - } - SPOT_UNREACHABLE(); - } - - virtual void - visit(const multop* node) - { - int op = -1; - switch (node->op()) - { - case multop::And: - op = bddop_and; - res_ = bddtrue; - break; - case multop::Or: - op = bddop_or; - res_ = bddfalse; - break; - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::OrRat: - case multop::AndRat: - SPOT_UNIMPLEMENTED(); - } - assert(op != -1); - unsigned s = node->size(); - for (unsigned n = 0; n < s; ++n) - { - res_ = bdd_apply(res_, recurse(node->nth(n)), op); - } - } - - bdd - result() const - { - return res_; - } - - bdd - recurse(const formula* f) const - { - return formula_to_bdd(f, d_, owner_); - } - - private: - bdd_dict_ptr d_; - void* owner_; - bdd res_; - }; - // Convert a BDD which is known to be a conjonction into a formula. - static const ltl::formula* + static formula conj_to_formula(bdd b, const bdd_dict_ptr d) { if (b == bddfalse) - return constant::false_instance(); - multop::vec* v = new multop::vec; + return formula::ff(); + std::vector v; while (b != bddtrue) { int var = bdd_var(b); const bdd_dict::bdd_info& i = d->bdd_map[var]; assert(i.type == bdd_dict::var); - const formula* res = i.f->clone(); + formula res = i.f; bdd high = bdd_high(b); if (high == bddfalse) { - res = unop::instance(unop::Not, res); + res = formula::Not(res); b = bdd_low(b); } else @@ -198,34 +57,89 @@ namespace spot b = high; } assert(b != bddfalse); - v->push_back(res); + v.push_back(res); } - return multop::instance(multop::And, v); + return formula::And(v); } - } // anonymous bdd - formula_to_bdd(const formula* f, const bdd_dict_ptr& d, void* for_me) + formula_to_bdd(formula f, const bdd_dict_ptr& d, void* owner) { - formula_to_bdd_visitor v(d, for_me); - f->accept(v); - return v.result(); + auto recurse = [&d, owner](formula f) + { + return formula_to_bdd(f, d, owner); + }; + switch (f.kind()) + { + case op::False: + return bddfalse; + case op::True: + return bddtrue; + case op::EmptyWord: + case op::Star: + case op::FStar: + case op::F: + case op::G: + case op::X: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::U: + case op::R: + case op::W: + case op::M: + case op::UConcat: + case op::EConcat: + case op::EConcatMarked: + case op::Concat: + case op::Fusion: + case op::AndNLM: + case op::OrRat: + case op::AndRat: + SPOT_UNIMPLEMENTED(); + case op::AP: + return bdd_ithvar(d->register_proposition(f, owner)); + case op::Not: + return bdd_not(recurse(f.nth(0))); + case op::Xor: + return bdd_apply(recurse(f.nth(0)), recurse(f.nth(1)), bddop_xor); + case op::Implies: + return bdd_apply(recurse(f.nth(0)), recurse(f.nth(1)), bddop_imp); + case op::Equiv: + return bdd_apply(recurse(f.nth(0)), recurse(f.nth(1)), bddop_biimp); + case op::And: + case op::Or: + { + int op = bddop_and; + bdd res = bddtrue; + if (f.is(ltl::op::Or)) + { + op = bddop_or; + res = bddfalse; + } + unsigned s = f.size(); + for (unsigned n = 0; n < s; ++n) + res = bdd_apply(res, recurse(f.nth(n)), op); + return res; + } + } + SPOT_UNREACHABLE(); + return bddfalse; } - const formula* + formula bdd_to_formula(bdd f, const bdd_dict_ptr d) { if (f == bddfalse) - return constant::false_instance(); + return formula::ff(); - multop::vec* v = new multop::vec; + std::vector v; minato_isop isop(f); bdd cube; while ((cube = isop.next()) != bddfalse) - v->push_back(conj_to_formula(cube, d)); - - return multop::instance(multop::Or, v); + v.emplace_back(conj_to_formula(cube, d)); + return formula::Or(std::move(v)); } } diff --git a/src/twa/formula2bdd.hh b/src/twa/formula2bdd.hh index 6a51b43d0..67eab157f 100644 --- a/src/twa/formula2bdd.hh +++ b/src/twa/formula2bdd.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -39,11 +39,11 @@ namespace spot /// for_me. See bdd_dict::unregister_all_my_variables(). /// @{ SPOT_API bdd - formula_to_bdd(const ltl::formula* f, const bdd_dict_ptr& d, void* for_me); + formula_to_bdd(ltl::formula f, const bdd_dict_ptr& d, void* for_me); template SPOT_API bdd - formula_to_bdd(const ltl::formula* f, const bdd_dict_ptr& d, + formula_to_bdd(ltl::formula f, const bdd_dict_ptr& d, const std::shared_ptr& for_me) { return formula_to_bdd(f, d, for_me.get()); @@ -57,7 +57,7 @@ namespace spot /// into their atomic propositions. This works only for Boolean /// formulas, and all the BDD variables used in \a f should have /// been registered in \a d. Although the result has type - /// ltl::formula*, it obviously does not use any temporal operator. - SPOT_API const - ltl::formula* bdd_to_formula(bdd f, const bdd_dict_ptr d); + /// ltl::formula, it obviously does not use any temporal operator. + SPOT_API + ltl::formula bdd_to_formula(bdd f, const bdd_dict_ptr d); } diff --git a/src/twa/taatgba.cc b/src/twa/taatgba.cc index 1f37ceeef..942e83f69 100644 --- a/src/twa/taatgba.cc +++ b/src/twa/taatgba.cc @@ -23,7 +23,6 @@ #include #include "twa/formula2bdd.hh" #include "ltlvisit/print.hh" -#include "ltlvisit/clone.hh" #include "taatgba.hh" namespace spot @@ -47,10 +46,9 @@ namespace spot } void - taa_tgba::add_condition(transition* t, const ltl::formula* f) + taa_tgba::add_condition(transition* t, ltl::formula f) { t->condition &= formula_to_bdd(f, get_dict(), this); - f->destroy(); } state* @@ -313,59 +311,19 @@ namespace spot | taa_tgba_string | `----------------*/ - taa_tgba_string::~taa_tgba_string() - { - ns_map::iterator i; - for (i = name_state_map_.begin(); i != name_state_map_.end(); ++i) - { - taa_tgba::state::iterator i2; - for (i2 = i->second->begin(); i2 != i->second->end(); ++i2) - delete *i2; - delete i->second; - } - } - std::string taa_tgba_string::label_to_string(const label_t& label) const { return label; } - std::string - taa_tgba_string::clone_if(const label_t& label) const - { - return label; - } - /*-----------------. | taa_tgba_formula | `-----------------*/ - taa_tgba_formula::~taa_tgba_formula() - { - ns_map::iterator i; - for (i = name_state_map_.begin(); i != name_state_map_.end();) - { - taa_tgba::state::iterator i2; - for (i2 = i->second->begin(); i2 != i->second->end(); ++i2) - delete *i2; - // Advance the iterator before destroying the formula. - const ltl::formula* s = i->first; - delete i->second; - ++i; - s->destroy(); - } - } - std::string taa_tgba_formula::label_to_string(const label_t& label) const { return ltl::str_psl(label); } - - const ltl::formula* - taa_tgba_formula::clone_if(const label_t& label) const - { - return label->clone(); - } } diff --git a/src/twa/taatgba.hh b/src/twa/taatgba.hh index 7394bfea8..4345fdb8c 100644 --- a/src/twa/taatgba.hh +++ b/src/twa/taatgba.hh @@ -49,7 +49,7 @@ namespace spot const state_set* dst; }; - void add_condition(transition* t, const ltl::formula* f); + void add_condition(transition* t, ltl::formula f); /// TGBA interface. virtual ~taa_tgba(); @@ -66,8 +66,7 @@ namespace spot taa_tgba::state_set* init_; ss_vec state_set_vec_; - std::map acc_map_; + std::map acc_map_; private: // Disallow copy. @@ -151,11 +150,14 @@ namespace spot taa_tgba_labelled(const bdd_dict_ptr& dict) : taa_tgba(dict) {}; ~taa_tgba_labelled() - { - auto i = acc_map_.begin(); - while (i != acc_map_.end()) - (i++)->first->destroy(); - } + { + for (auto i: name_state_map_) + { + for (auto i2: *i.second) + delete i2; + delete i.second; + } + } void set_init_state(const label& s) { @@ -190,13 +192,11 @@ namespace spot return create_transition(s, vec); } - void add_acceptance_condition(transition* t, const ltl::formula* f) + void add_acceptance_condition(transition* t, ltl::formula f) { auto p = acc_map_.emplace(f, 0); if (p.second) p.first->second = acc_.marks({acc_.add_set()}); - else - f->destroy(); t->acceptance_conditions |= p.first->second; } @@ -246,10 +246,6 @@ namespace spot /// \brief Return a label as a string. virtual std::string label_to_string(const label_t& lbl) const = 0; - /// \brief Clone the label if necessary to assure it is owned by - /// this, avoiding memory issues when label is a pointer. - virtual label_t clone_if(const label_t& lbl) const = 0; - private: /// \brief Return the taa_tgba::state for \a name, creating it /// when it does not exist already. @@ -258,10 +254,9 @@ namespace spot typename ns_map::iterator i = name_state_map_.find(name); if (i == name_state_map_.end()) { - const label& name_ = clone_if(name); taa_tgba::state* s = new taa_tgba::state; - name_state_map_[name_] = s; - state_name_map_[s] = name_; + name_state_map_[name] = s; + state_name_map_[s] = name; return s; } return i->second; @@ -314,11 +309,11 @@ namespace spot { public: taa_tgba_string(const bdd_dict_ptr& dict) : - taa_tgba_labelled(dict) {}; - ~taa_tgba_string(); + taa_tgba_labelled(dict) {} + ~taa_tgba_string() + {} protected: virtual std::string label_to_string(const std::string& label) const; - virtual std::string clone_if(const std::string& label) const; }; typedef std::shared_ptr taa_tgba_string_ptr; @@ -331,18 +326,18 @@ namespace spot class SPOT_API taa_tgba_formula final: #ifndef SWIG - public taa_tgba_labelled + public taa_tgba_labelled #else public taa_tgba #endif { public: taa_tgba_formula(const bdd_dict_ptr& dict) : - taa_tgba_labelled(dict) {}; - ~taa_tgba_formula(); + taa_tgba_labelled(dict) {} + ~taa_tgba_formula() + {} protected: virtual std::string label_to_string(const label_t& label) const; - virtual const ltl::formula* clone_if(const label_t& label) const; }; typedef std::shared_ptr taa_tgba_formula_ptr; diff --git a/src/twa/twa.cc b/src/twa/twa.cc index 6627b6c3f..23cdf63c4 100644 --- a/src/twa/twa.cc +++ b/src/twa/twa.cc @@ -39,8 +39,6 @@ namespace spot twa::~twa() { - for (auto* ap: aps_) - ap->destroy(); if (last_support_conditions_input_) last_support_conditions_input_->destroy(); delete iter_cache_; diff --git a/src/twa/twa.hh b/src/twa/twa.hh index a82161fa8..fc3b7af71 100644 --- a/src/twa/twa.hh +++ b/src/twa/twa.hh @@ -33,7 +33,7 @@ #include #include "misc/casts.hh" #include "misc/hash.hh" -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" namespace spot { @@ -594,11 +594,10 @@ namespace spot /// \brief Register an atomic proposition designated by formula \a ap. /// - /// \return The variable number inside of the BDD. - int register_ap(const ltl::formula* ap) + /// \return The BDD variable number. + int register_ap(ltl::formula ap) { - aps_.push_back(dynamic_cast(ap)); - ap->clone(); + aps_.push_back(ap); int res = dict_->register_proposition(ap, this); bddaps_ &= bdd_ithvar(res); return res; @@ -606,23 +605,15 @@ namespace spot /// \brief Register an atomic proposition designated by string \a ap. /// - /// This string is converted into a formula and registered - /// inside of the BDD manager. - /// - /// \return The variable number inside of the BDD. - int register_ap(std::string name, - ltl::environment& e = ltl::default_environment::instance()) + /// \return The BDD variable number. + int register_ap(std::string name) { - auto* ap = e.require(name); - aps_.push_back(dynamic_cast(ap)); - int res = dict_->register_proposition(ap, this); - bddaps_ &= bdd_ithvar(res); - return res; + return register_ap(ltl::formula::ap(name)); } /// \brief Get the vector of atomic propositions used by this /// automaton. - const std::vector& ap() const + const std::vector& ap() const { return aps_; } @@ -730,7 +721,7 @@ namespace spot void copy_ap_of(const const_twa_ptr& a) { get_dict()->register_all_propositions_of(a, this); - for (auto *f: a->ap()) + for (auto f: a->ap()) this->register_ap(f); } @@ -754,7 +745,7 @@ namespace spot mutable const state* last_support_conditions_input_; private: mutable bdd last_support_conditions_output_; - std::vector aps_; + std::vector aps_; bdd bddaps_; protected: diff --git a/src/twa/twagraph.cc b/src/twa/twagraph.cc index 52ddbe9d3..7acb18fa0 100644 --- a/src/twa/twagraph.cc +++ b/src/twa/twagraph.cc @@ -18,13 +18,12 @@ // along with this program. If not, see . #include "twagraph.hh" -#include "ltlast/constant.hh" #include "ltlvisit/print.hh" namespace spot { void - twa_graph::release_formula_namer(namer* namer, + twa_graph::release_formula_namer(namer* namer, bool keep_names) { if (keep_names) @@ -37,19 +36,10 @@ namespace spot { auto f = n[i]; if (f) - { - (*v)[i] = str_psl(f); - f->destroy(); - } + (*v)[i] = str_psl(f); } set_named_prop("state-names", v); } - else - { - for (auto n: namer->names()) - if (n) - n->destroy(); - } delete namer; } diff --git a/src/twa/twagraph.hh b/src/twa/twagraph.hh index 51d401104..824da7f6b 100644 --- a/src/twa/twagraph.hh +++ b/src/twa/twagraph.hh @@ -219,14 +219,14 @@ namespace spot return new named_graph(g_); } - namer* + namer* create_formula_namer() { - return create_namer(); + return create_namer(); } void - release_formula_namer(namer* namer, bool keep_names); + release_formula_namer(namer* namer, bool keep_names); #endif graph_t& get_graph() diff --git a/src/twa/twasafracomplement.cc b/src/twa/twasafracomplement.cc index afecae999..453fa8572 100644 --- a/src/twa/twasafracomplement.cc +++ b/src/twa/twasafracomplement.cc @@ -31,7 +31,6 @@ #include "twa/twa.hh" #include "misc/hashfunc.hh" #include "ltlast/formula.hh" -#include "ltlast/constant.hh" #include "twaalgos/dot.hh" #include "twa/twasafracomplement.hh" #include "twaalgos/degen.hh" diff --git a/src/twaalgos/compsusp.cc b/src/twaalgos/compsusp.cc index 2d4821e52..024f71ec1 100644 --- a/src/twaalgos/compsusp.cc +++ b/src/twaalgos/compsusp.cc @@ -25,9 +25,7 @@ #include "minimize.hh" #include "simulation.hh" #include "safety.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/print.hh" -#include "ltlvisit/clone.hh" #include #include #include "ltlenv/environment.hh" @@ -36,163 +34,111 @@ namespace spot { namespace { - typedef std::map formula_bdd_map; - - // An environment to store atomic proposition associated to - // suspended variable. (We don't use the default environment to - // avoid conflicts with user-defined atomic propositions that - // would share the same name.) - class suspended_environment: public ltl::environment - { - public: - const ltl::formula* - require(const std::string& s) - { - return ltl::atomic_prop::instance(s, *this); - } - - const std::string& - name() const - { - static std::string name("suspended environment"); - return name; - } - }; - static suspended_environment suspenv; + typedef std::map formula_bdd_map; + typedef std::vector vec; // Rewrite the suspendable subformulae "s" of an LTL formula in // the form Gg where "g" is an atomic proposition representing // "s". At the same time, populate maps that associate "s" to "g" // and vice-versa. - class ltl_suspender_visitor: public ltl::clone_visitor + class ltl_suspender_visitor final { public: - typedef std::map fmap_t; + typedef std::map fmap_t; ltl_suspender_visitor(fmap_t& g2s, fmap_t& a2o, bool oblig) : g2s_(g2s), a2o_(a2o), oblig_(oblig) { } - void - visit(const ltl::multop* mo) + ltl::formula + visit(ltl::formula f) { - ltl::multop::type op = mo->op(); - switch (op) + switch (ltl::op op = f.kind()) { - case ltl::multop::Or: - case ltl::multop::And: + case ltl::op::Or: + case ltl::op::And: { - ltl::multop::vec* res = new ltl::multop::vec; - ltl::multop::vec* oblig = oblig_ ? new ltl::multop::vec : 0; - ltl::multop::vec* susp = new ltl::multop::vec; - unsigned mos = mo->size(); + vec res; + vec oblig; + vec susp; + unsigned mos = f.size(); for (unsigned i = 0; i < mos; ++i) { - const ltl::formula* c = mo->nth(i); - if (c->is_boolean()) - res->push_back(c->clone()); - else if (oblig_ && c->is_syntactic_obligation()) - oblig->push_back(c->clone()); - else if (c->is_eventual() && c->is_universal()) - susp->push_back(c->clone()); + ltl::formula c = f.nth(i); + if (c.is_boolean()) + res.push_back(c); + else if (oblig_ && c.is_syntactic_obligation()) + oblig.push_back(c); + else if (c.is_eventual() && c.is_universal()) + susp.push_back(c); else - res->push_back(recurse(c)); + res.push_back(recurse(c)); } - if (!oblig_ || oblig->empty()) + if (!oblig.empty()) { - delete oblig; + res.push_back(recurse(ltl::formula::multop(op, oblig))); } - else + if (!susp.empty()) { - const ltl::formula* o = ltl::multop::instance(op, oblig); - res->push_back(recurse(o)); - o->destroy(); - } - if (susp->empty()) - { - delete susp; - } - else - { - const ltl::formula* o = ltl::multop::instance(op, susp); + ltl::formula o = ltl::formula::multop(op, susp); // Rewrite 'o' as 'G"o"' - const ltl::formula* g = recurse(o); - o->destroy(); - if (op == ltl::multop::And) + ltl::formula g = recurse(o); + if (op == ltl::op::And) { - res->push_back(g); + res.push_back(g); } else { // res || susp -> (res && G![susp]) || G[susp]) - const ltl::formula* r = ltl::multop::instance(op, res); - const ltl::unop* u = - down_cast(g); - const ltl::formula* gn = - ltl::unop::instance - (ltl::unop::G, ltl::unop::instance - (ltl::unop::Not, u->child()->clone())); - result_ = ltl::multop::instance - (ltl::multop::Or, ltl::multop::instance - (ltl::multop::And, r, gn), - g); - return; + auto r = ltl::formula::multop(op, res); + auto gn = ltl::formula::G(ltl::formula::Not(g.nth(0))); + return ltl::formula::Or({ltl::formula::And({r, gn}), g}); } } - result_ = ltl::multop::instance(op, res); + return ltl::formula::multop(op, res); } break; - case ltl::multop::OrRat: - case ltl::multop::AndRat: - case ltl::multop::AndNLM: - case ltl::multop::Concat: - case ltl::multop::Fusion: - this->ltl::clone_visitor::visit(mo); - break; + default: + return f.map([this](ltl::formula f) + { + return this->recurse(f); + }); } } - - const ltl::formula* - recurse(const ltl::formula* f) + ltl::formula + recurse(ltl::formula f) { - const ltl::formula* res; - if (f->is_boolean()) - return f->clone(); - if (oblig_ && f->is_syntactic_obligation()) + ltl::formula res; + if (f.is_boolean()) + return f; + if (oblig_ && f.is_syntactic_obligation()) { fmap_t::const_iterator i = assoc_.find(f); if (i != assoc_.end()) - return i->second->clone(); + return i->second; std::ostringstream s; - s << "〈"; - print_psl(s, f) << "〉"; - res = suspenv.require(s.str()); - // We have to clone f, because it is not always a sub-tree - // of the original formula. (Think n-ary operators.) - a2o_[res] = f->clone(); + print_psl(s << "〈", f) << "〉"; + res = ltl::formula::ap(s.str()); + a2o_[res] = f; assoc_[f] = res; return res; } - if (f->is_eventual() && f->is_universal()) + if (f.is_eventual() && f.is_universal()) { fmap_t::const_iterator i = assoc_.find(f); if (i != assoc_.end()) - return ltl::unop::instance(ltl::unop::G, i->second->clone()); + return ltl::formula::G(i->second); std::ostringstream s; - s << '['; - print_psl(s, f) << ']'; - res = suspenv.require(s.str()); - // We have to clone f, because it is not always a sub-tree - // of the original formula. (Think n-ary operators.) - g2s_[res] = f->clone(); + print_psl(s << '[', f) << "]$"; + res = ltl::formula::ap(s.str()); + g2s_[res] = f; assoc_[f] = res; - return ltl::unop::instance(ltl::unop::G, res); + return ltl::formula::G(res); } - f->accept(*this); - return result_; + return visit(f); } private: @@ -210,7 +156,7 @@ namespace spot static twa_graph_ptr - susp_prod(const const_twa_ptr& left, const ltl::formula* f, bdd v) + susp_prod(const const_twa_ptr& left, ltl::formula f, bdd v) { bdd_dict_ptr dict = left->get_dict(); auto right = @@ -320,7 +266,7 @@ namespace spot twa_graph_ptr - compsusp(const ltl::formula* f, const bdd_dict_ptr& dict, + compsusp(ltl::formula f, const bdd_dict_ptr& dict, bool no_wdba, bool no_simulation, bool early_susp, bool no_susp_product, bool wdba_smaller, bool oblig) @@ -328,7 +274,7 @@ namespace spot ltl_suspender_visitor::fmap_t g2s; ltl_suspender_visitor::fmap_t a2o; ltl_suspender_visitor v(g2s, a2o, oblig); - const ltl::formula* g = v.recurse(f); + ltl::formula g = v.recurse(f); // Translate the patched formula, and remove useless SCCs. twa_graph_ptr res = @@ -392,14 +338,6 @@ namespace spot if ((allaccap & i->second) == allaccap) res = susp_prod(res, i->first, i->second); - g->destroy(); - - for (ltl_suspender_visitor::fmap_t::iterator i = g2s.begin(); - i != g2s.end(); ++i) - i->second->destroy(); - for (ltl_suspender_visitor::fmap_t::iterator i = a2o.begin(); - i != a2o.end(); ++i) - i->second->destroy(); return res; } } diff --git a/src/twaalgos/compsusp.hh b/src/twaalgos/compsusp.hh index c6d21329b..f50f649ef 100644 --- a/src/twaalgos/compsusp.hh +++ b/src/twaalgos/compsusp.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et +// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -49,7 +49,7 @@ namespace spot /// long-term stability should better use the services of the /// spot::translator class instead. SPOT_API twa_graph_ptr - compsusp(const ltl::formula* f, const bdd_dict_ptr& dict, + compsusp(ltl::formula f, const bdd_dict_ptr& dict, bool no_wdba = false, bool no_simulation = false, bool early_susp = false, bool no_susp_product = false, bool wdba_smaller = false, bool oblig = false); diff --git a/src/twaalgos/dtgbasat.cc b/src/twaalgos/dtgbasat.cc index ee6e774c2..bd2dafa09 100644 --- a/src/twaalgos/dtgbasat.cc +++ b/src/twaalgos/dtgbasat.cc @@ -26,7 +26,6 @@ #include #include "sccinfo.hh" #include "twa/bddprint.hh" -#include "ltlast/constant.hh" #include "stats.hh" #include "ltlenv/defaultenv.hh" #include "misc/satsolver.hh" diff --git a/src/twaalgos/hoa.cc b/src/twaalgos/hoa.cc index 208340232..011c05424 100644 --- a/src/twaalgos/hoa.cc +++ b/src/twaalgos/hoa.cc @@ -32,7 +32,7 @@ #include "misc/bddlt.hh" #include "misc/minato.hh" #include "twa/formula2bdd.hh" -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" namespace spot { @@ -292,11 +292,7 @@ namespace spot << "AP: " << nap; auto d = aut->get_dict(); for (auto& i: md.vap) - { - auto f = ltl::is_atomic_prop(d->bdd_map[i].f); - assert(f); - escape_str(os << " \"", f->name()) << '"'; - } + escape_str(os << " \"", d->bdd_map[i].f.ap_name()) << '"'; os << nl; unsigned num_acc = aut->num_sets(); diff --git a/src/twaalgos/lbtt.cc b/src/twaalgos/lbtt.cc index 9eb069c2b..e494c45b3 100644 --- a/src/twaalgos/lbtt.cc +++ b/src/twaalgos/lbtt.cc @@ -103,11 +103,8 @@ namespace spot body_ << s << ' '; body_ << "-1 "; } - const ltl::formula* f = bdd_to_formula(si->current_condition(), - aut_->get_dict()); - print_lbt_ltl(body_, f); - f->destroy(); - body_ << '\n'; + print_lbt_ltl(body_, bdd_to_formula(si->current_condition(), + aut_->get_dict())) << '\n'; } void diff --git a/src/twaalgos/ltl2taa.cc b/src/twaalgos/ltl2taa.cc index 79c8fba8f..ffddc5694 100644 --- a/src/twaalgos/ltl2taa.cc +++ b/src/twaalgos/ltl2taa.cc @@ -19,8 +19,6 @@ #include #include -#include "ltlast/visitor.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/unabbrev.hh" #include "ltlvisit/nenoform.hh" #include "ltlvisit/contain.hh" @@ -33,14 +31,14 @@ namespace spot using namespace ltl; /// \brief Recursively translate a formula into a TAA. - class ltl2taa_visitor : public visitor + class ltl2taa_visitor { public: ltl2taa_visitor(const taa_tgba_formula_ptr& res, language_containment_checker* lcc, bool refined = false, bool negated = false) : res_(res), refined_(refined), negated_(negated), - lcc_(lcc), init_(), succ_(), to_free_() + lcc_(lcc), init_(), succ_() { } @@ -52,116 +50,116 @@ namespace spot taa_tgba_formula_ptr& result() { - for (unsigned i = 0; i < to_free_.size(); ++i) - to_free_[i]->destroy(); res_->set_init_state(init_); return res_; } void - visit(const atomic_prop* node) + visit(formula f) { - const formula* f = node; // Handle negation - if (negated_) - { - f = unop::instance(unop::Not, node->clone()); - to_free_.push_back(f); - } init_ = f; - std::vector empty; - taa_tgba::transition* t = res_->create_transition(init_, empty); - res_->add_condition(t, f->clone()); - succ_state ss = { empty, f, empty }; - succ_.push_back(ss); - } - - void - visit(const constant* node) - { - init_ = node; - switch (node->val()) - { - case constant::True: + switch (f.kind()) { - std::vector empty; - res_->create_transition(init_, empty); - succ_state ss = { empty, node, empty }; - succ_.push_back(ss); + case op::False: return; - } - case constant::False: - return; - case constant::EmptyWord: - SPOT_UNIMPLEMENTED(); - } - SPOT_UNREACHABLE(); - } - - void - visit(const unop* node) - { - negated_ = node->op() == unop::Not; - ltl2taa_visitor v = recurse(node->child()); - - init_ = node; - switch (node->op()) - { - case unop::X: - { - std::vector dst; - std::vector a; - if (v.succ_.empty()) // Handle X(0) + case op::True: + { + std::vector empty; + res_->create_transition(init_, empty); + succ_state ss = { empty, f, empty }; + succ_.push_back(ss); return; - dst.push_back(v.init_); - res_->create_transition(init_, dst); - succ_state ss = - { dst, constant::true_instance(), a }; - succ_.push_back(ss); - return; - } - case unop::F: - case unop::G: + } + case op::EmptyWord: + SPOT_UNIMPLEMENTED(); + case op::AP: + { + if (negated_) + f = formula::Not(f); + init_ = f; + std::vector empty; + taa_tgba::transition* t = res_->create_transition(init_, empty); + res_->add_condition(t, f); + succ_state ss = { empty, f, empty }; + succ_.push_back(ss); + return; + } + case op::X: + { + ltl2taa_visitor v = recurse(f.nth(0)); + std::vector dst; + std::vector a; + if (v.succ_.empty()) // Handle X(0) + return; + dst.push_back(v.init_); + res_->create_transition(init_, dst); + succ_state ss = { dst, formula::tt(), a }; + succ_.push_back(ss); + return; + } + case op::F: + case op::G: SPOT_UNIMPLEMENTED(); // TBD return; - case unop::Not: - // Done in recurse - succ_ = v.succ_; - return; - case unop::Closure: - case unop::NegClosure: - case unop::NegClosureMarked: + case op::Not: + { + negated_ = true; + ltl2taa_visitor v = recurse(f.nth(0)); + // Done in recurse + succ_ = v.succ_; + return; + } + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::Star: + case op::FStar: + case op::Xor: + case op::Implies: + case op::Equiv: + case op::UConcat: + case op::EConcat: + case op::EConcatMarked: + case op::Concat: + case op::Fusion: + case op::AndNLM: + case op::AndRat: + case op::OrRat: SPOT_UNIMPLEMENTED(); - } - SPOT_UNREACHABLE(); + + case op::U: + case op::W: + case op::R: + case op::M: + visit_binop(f); + return; + case op::And: + case op::Or: + visit_multop(f); + return; + } } void - visit(const bunop*) + visit_binop(formula f) { - SPOT_UNIMPLEMENTED(); - } + ltl2taa_visitor v1 = recurse(f.nth(0)); + ltl2taa_visitor v2 = recurse(f.nth(1)); - void - visit(const binop* node) - { - ltl2taa_visitor v1 = recurse(node->first()); - ltl2taa_visitor v2 = recurse(node->second()); - - init_ = node; std::vector::iterator i1; std::vector::iterator i2; taa_tgba::transition* t = 0; bool contained = false; bool strong = false; - switch (node->op()) + switch (f.kind()) { - case binop::U: + case op::U: strong = true; // fall thru - case binop::W: + case op::W: if (refined_) - contained = lcc_->contained(node->second(), node->first()); + contained = lcc_->contained(f.nth(0), f.nth(1)); for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1) { // Refined rule @@ -171,46 +169,44 @@ namespace spot i1->Q.push_back(init_); // Add the initial state if (strong) - i1->acc.push_back(node->second()); + i1->acc.push_back(f.nth(1)); t = res_->create_transition(init_, i1->Q); - res_->add_condition(t, i1->condition->clone()); + res_->add_condition(t, i1->condition); if (strong) - res_->add_acceptance_condition(t, node->second()->clone()); + res_->add_acceptance_condition(t, f.nth(1)); else for (unsigned i = 0; i < i1->acc.size(); ++i) - res_->add_acceptance_condition(t, i1->acc[i]->clone()); + res_->add_acceptance_condition(t, i1->acc[i]); succ_.push_back(*i1); } for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2) { t = res_->create_transition(init_, i2->Q); - res_->add_condition(t, i2->condition->clone()); + res_->add_condition(t, i2->condition); succ_.push_back(*i2); } return; - case binop::M: // Strong Release + case op::M: // Strong Release strong = true; - case binop::R: // Weak Release + case op::R: // Weak Release if (refined_) - contained = lcc_->contained(node->first(), node->second()); + contained = lcc_->contained(f.nth(0), f.nth(1)); for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2) { for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1) { - std::vector u; // Union - std::vector a; // Acceptance conditions + std::vector u; // Union + std::vector a; // Acceptance conditions std::copy(i1->Q.begin(), i1->Q.end(), ii(u, u.end())); - const formula* f = i1->condition->clone(); // Refined rule + formula f = i1->condition; // Refined rule if (!refined_ || !contained) { std::copy(i2->Q.begin(), i2->Q.end(), ii(u, u.end())); - f = multop::instance(multop::And, f, - i2->condition->clone()); + f = formula::And({f, i2->condition}); } - to_free_.push_back(f); t = res_->create_transition(init_, u); - res_->add_condition(t, f->clone()); + res_->add_condition(t, f); succ_state ss = { u, f, a }; succ_.push_back(ss); } @@ -222,113 +218,102 @@ namespace spot i2->Q.push_back(init_); // Add the initial state t = res_->create_transition(init_, i2->Q); - res_->add_condition(t, i2->condition->clone()); + res_->add_condition(t, i2->condition); if (strong) { - i2->acc.push_back(node->first()); - res_->add_acceptance_condition(t, node->first()->clone()); + i2->acc.push_back(f.nth(0)); + res_->add_acceptance_condition(t, f.nth(0)); } else if (refined_) for (unsigned i = 0; i < i2->acc.size(); ++i) - res_->add_acceptance_condition(t, i2->acc[i]->clone()); + res_->add_acceptance_condition(t, i2->acc[i]); succ_.push_back(*i2); } return; - case binop::Xor: - case binop::Implies: - case binop::Equiv: - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: + default: SPOT_UNIMPLEMENTED(); } SPOT_UNREACHABLE(); } void - visit(const multop* node) + visit_multop(formula f) { bool ok = true; std::vector vs; - for (unsigned n = 0; n < node->size(); ++n) + for (unsigned n = 0, s = f.size(); n < s; ++n) { - vs.push_back(recurse(node->nth(n))); + vs.push_back(recurse(f.nth(n))); if (vs[n].succ_.empty()) // Handle 0 ok = false; } - init_ = node; std::vector::iterator i; taa_tgba::transition* t = 0; - switch (node->op()) - { - case multop::And: + switch (f.kind()) { - if (!ok) - return; - std::vector p = all_n_tuples(vs); - for (unsigned n = 0; n < p.size(); ++n) + case op::And: { - if (refined_) - { - std::vector v; // All sub initial states. - sort(p[n].Q.begin(), p[n].Q.end()); - for (unsigned m = 0; m < node->size(); ++m) + if (!ok) + return; + std::vector p = all_n_tuples(vs); + for (unsigned n = 0; n < p.size(); ++n) { - if (!binary_search(p[n].Q.begin(), p[n].Q.end(), vs[m].init_)) - break; - v.push_back(vs[m].init_); - } + if (refined_) + { + std::vector v; // All sub initial states. + sort(p[n].Q.begin(), p[n].Q.end()); + for (unsigned m = 0; m < f.size(); ++m) + { + if (!binary_search(p[n].Q.begin(), p[n].Q.end(), + vs[m].init_)) + break; + v.push_back(vs[m].init_); + } - if (v.size() == node->size()) - { - std::vector Q; - sort(v.begin(), v.end()); - for (unsigned m = 0; m < p[n].Q.size(); ++m) - if (!binary_search(v.begin(), v.end(), p[n].Q[m])) - Q.push_back(p[n].Q[m]); - Q.push_back(init_); - t = res_->create_transition(init_, Q); - res_->add_condition(t, p[n].condition->clone()); - for (unsigned i = 0; i < p[n].acc.size(); ++i) - res_->add_acceptance_condition(t, p[n].acc[i]->clone()); + if (v.size() == f.size()) + { + std::vector Q; + sort(v.begin(), v.end()); + for (unsigned m = 0; m < p[n].Q.size(); ++m) + if (!binary_search(v.begin(), v.end(), p[n].Q[m])) + Q.push_back(p[n].Q[m]); + Q.push_back(init_); + t = res_->create_transition(init_, Q); + res_->add_condition(t, p[n].condition); + for (unsigned i = 0; i < p[n].acc.size(); ++i) + res_->add_acceptance_condition(t, p[n].acc[i]); + succ_.push_back(p[n]); + continue; + } + } + t = res_->create_transition(init_, p[n].Q); + res_->add_condition(t, p[n].condition); succ_.push_back(p[n]); - continue; } - } - t = res_->create_transition(init_, p[n].Q); - res_->add_condition(t, p[n].condition->clone()); - succ_.push_back(p[n]); + return; } + case op::Or: + for (unsigned n = 0, s = f.size(); n < s; ++n) + for (auto i: vs[n].succ_) + { + t = res_->create_transition(init_, i.Q); + res_->add_condition(t, i.condition); + succ_.push_back(i); + } return; - } - case multop::Or: - for (unsigned n = 0; n < node->size(); ++n) - for (i = vs[n].succ_.begin(); i != vs[n].succ_.end(); ++i) - { - t = res_->create_transition(init_, i->Q); - res_->add_condition(t, i->condition->clone()); - succ_.push_back(*i); - } - return; - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: + default: SPOT_UNIMPLEMENTED(); - } + } SPOT_UNREACHABLE(); } ltl2taa_visitor - recurse(const formula* f) + recurse(formula f) { ltl2taa_visitor v(res_, lcc_, refined_, negated_); - f->accept(v); - for (unsigned i = 0; i < v.to_free_.size(); ++i) - to_free_.push_back(v.to_free_[i]); + v.visit(f); return v; } @@ -338,20 +323,18 @@ namespace spot bool negated_; language_containment_checker* lcc_; - typedef std::insert_iterator> ii; + typedef std::insert_iterator> ii; struct succ_state { - std::vector Q; // States - const formula* condition; - std::vector acc; + std::vector Q; // States + formula condition; + std::vector acc; }; - const formula* init_; + formula init_; std::vector succ_; - std::vector to_free_; - public: std::vector all_n_tuples(const std::vector& vs) @@ -364,24 +347,22 @@ namespace spot while (pos[0] != 0) { - std::vector u; // Union - std::vector a; // Acceptance conditions - const formula* f = constant::true_instance(); + std::vector u; // Union + std::vector a; // Acceptance conditions + formula f = formula::tt(); for (unsigned i = 0; i < vs.size(); ++i) { if (vs[i].succ_.empty()) continue; const succ_state& ss(vs[i].succ_[pos[i] - 1]); std::copy(ss.Q.begin(), ss.Q.end(), ii(u, u.end())); - f = multop::instance(multop::And, ss.condition->clone(), f); + f = formula::And({ss.condition, f}); for (unsigned i = 0; i < ss.acc.size(); ++i) { - const formula* g = ss.acc[i]->clone(); + formula g = ss.acc[i]; a.push_back(g); - to_free_.push_back(g); } } - to_free_.push_back(f); succ_state ss = { u, f, a }; product.push_back(ss); @@ -404,22 +385,18 @@ namespace spot } // anonymous taa_tgba_formula_ptr - ltl_to_taa(const ltl::formula* f, + ltl_to_taa(ltl::formula f, const bdd_dict_ptr& dict, bool refined_rules) { // TODO: implement translation of F and G - auto f1 = ltl::unabbreviate(f, "^ieFG"); - auto f2 = ltl::negative_normal_form(f1); - f1->destroy(); - + auto f2 = ltl::negative_normal_form(ltl::unabbreviate(f, "^ieFG")); auto res = make_taa_tgba_formula(dict); language_containment_checker* lcc = new language_containment_checker(make_bdd_dict(), false, false, false, false); ltl2taa_visitor v(res, lcc, refined_rules); - f2->accept(v); - auto taa = v.result(); // Careful: before the destroy! - f2->destroy(); + v.visit(f2); + auto taa = v.result(); delete lcc; taa->acc().set_generalized_buchi(); return taa; diff --git a/src/twaalgos/ltl2taa.hh b/src/twaalgos/ltl2taa.hh index b501bf440..90dd8067e 100644 --- a/src/twaalgos/ltl2taa.hh +++ b/src/twaalgos/ltl2taa.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2010, 2013, 2014 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2009, 2010, 2013, 2014, 2015 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -48,6 +48,6 @@ namespace spot /// \param refined_rules If this parameter is set, refined rules are used. /// \return A spot::taa that recognizes the language of \a f. SPOT_API taa_tgba_formula_ptr - ltl_to_taa(const ltl::formula* f, const bdd_dict_ptr& dict, + ltl_to_taa(ltl::formula f, const bdd_dict_ptr& dict, bool refined_rules = false); } diff --git a/src/twaalgos/ltl2tgba_fm.cc b/src/twaalgos/ltl2tgba_fm.cc index 912f93010..7ba1402d5 100644 --- a/src/twaalgos/ltl2tgba_fm.cc +++ b/src/twaalgos/ltl2tgba_fm.cc @@ -23,11 +23,8 @@ #include "misc/hash.hh" #include "misc/bddlt.hh" #include "misc/minato.hh" -#include "ltlast/visitor.hh" -#include "ltlast/allnodes.hh" #include "ltlvisit/nenoform.hh" #include "ltlvisit/print.hh" -#include "ltlvisit/postfix.hh" #include "ltlvisit/apcollect.hh" #include "ltlvisit/mark.hh" #include "ltlvisit/print.hh" @@ -46,7 +43,7 @@ namespace spot namespace { - + typedef std::vector vec; // This should only be called on And formulae and return // the set of subformula that are implied by the formulas @@ -57,46 +54,45 @@ namespace spot // d, e R (g R h), g R h, h, Xj # implied by G(d & ...) // } // Leave recurring to false on first call. - typedef std::set formula_set; + typedef std::set formula_set; void - implied_subformulae(const formula* in, formula_set& rec, - bool recurring = false) + implied_subformulae(formula f, formula_set& rec, bool recurring = false) { - const multop* f = is_And(in); - if (!f) + if (!f.is(op::And)) { // Only recursive calls should be made with an operator that // is not And. assert(recurring); - rec.insert(in); + rec.insert(f); return; } - unsigned s = f->size(); + unsigned s = f.size(); for (unsigned n = 0; n < s; ++n) { - const formula* sub = f->nth(n); + formula sub = f.nth(n); // Recurring is set if we are under "G(...)" or "0 R (...)" // or (...) W 0". if (recurring) rec.insert(sub); - if (const unop* g = is_G(sub)) + if (sub.is(op::G)) { - implied_subformulae(g->child(), rec, true); + implied_subformulae(sub.nth(0), rec, true); } - else if (const binop* w = is_W(sub)) + else if (sub.is(op::W)) { // f W 0 = Gf - if (w->second() == constant::false_instance()) - implied_subformulae(w->first(), rec, true); + if (sub.nth(1).is(op::False)) + implied_subformulae(sub.nth(0), rec, true); } else - while (const binop* b = is_binop(sub, binop::R, binop::M)) + while (sub.is(op::R, op::M)) { // in 'f R g' and 'f M g' always evaluate 'g'. - sub = b->second(); - if (b->first() == constant::false_instance()) + formula b = sub; + sub = b.nth(1); + if (b.nth(0) == formula::ff()) { - assert(b->op() == binop::R); // because 0 M g = 0 + assert(b.is(op::R)); // because 0 M g = 0 // 0 R f = Gf implied_subformulae(sub, rec, true); break; @@ -110,20 +106,20 @@ namespace spot class ratexp_to_dfa { - typedef twa_graph::namer namer; + typedef twa_graph::namer namer; public: ratexp_to_dfa(translate_dict& dict); std::tuple - succ(const formula* f); + succ(formula f); ~ratexp_to_dfa(); protected: typedef std::pair labelled_aut; - labelled_aut translate(const formula* f); + labelled_aut translate(formula f); private: translate_dict& dict_; - typedef std::unordered_map f2a_t; + typedef std::unordered_map f2a_t; std::vector automata_; f2a_t f2a_; }; @@ -139,13 +135,10 @@ namespace spot { public: - translate_dict(twa_graph_ptr& a, - const bdd_dict_ptr& dict, - acc_cond& acc, - ltl_simplifier* ls, bool exprop, + translate_dict(twa_graph_ptr& a, ltl_simplifier* ls, bool exprop, bool single_acc, bool unambiguous) : a_(a), - dict(dict), + dict(a->get_dict()), ls(ls), a_set(bddtrue), var_set(bddtrue), @@ -153,21 +146,14 @@ namespace spot transdfa(*this), exprop(exprop), single_acc(single_acc), - acc(acc), + acc(a->acc()), unambiguous(unambiguous) { } ~translate_dict() { - for (auto& i: next_map) - i.first->destroy(); dict->unregister_all_my_variables(this); - - flagged_formula_to_bdd_map::iterator j = ltl_bdd_.begin(); - // Advance the iterator before destroying previous value. - while (j != ltl_bdd_.end()) - j++->first.f->destroy(); } twa_graph_ptr& a_; @@ -176,7 +162,7 @@ namespace spot mark_tools mt; typedef bdd_dict::fv_map fv_map; - typedef std::vector vf_map; + typedef std::vector vf_map; fv_map next_map; ///< Maps "Next" variables to BDD variables vf_map next_formula_map; ///< Maps BDD variables to "Next" variables @@ -204,7 +190,7 @@ namespace spot struct flagged_formula { - const formula* f; + formula f; unsigned flags; // a combination of translate_flags bool @@ -220,7 +206,7 @@ namespace spot size_t operator()(const flagged_formula& that) const { - return that.f->hash() ^ size_t(that.flags); + return that.f.id() ^ size_t(that.flags); } }; @@ -241,7 +227,7 @@ namespace spot int - register_proposition(const formula* f) + register_proposition(formula f) { int num = dict->register_proposition(f, this); var_set &= bdd_ithvar(num); @@ -273,12 +259,12 @@ namespace spot } int - register_a_variable(const formula* f) + register_a_variable(formula f) { if (single_acc) { int num = dict->register_acceptance_variable - (ltl::constant::true_instance(), this); + (ltl::formula::tt(), this); a_set &= bdd_ithvar(num); auto p = bm.emplace(num, 0U); @@ -316,40 +302,27 @@ namespace spot // and P(c U !d) should not both be simplified to P(!d). for (;;) { - if (const binop* b = is_binop(f)) + if (f.is(op::U)) { - binop::type op = b->op(); - if (op == binop::U) - { - // P(a U b) = P(b) - f = b->second(); - } - else if (op == binop::M) - { - // P(a M b) = P(a & b) - const formula* g = - multop::instance(multop::And, - b->first()->clone(), - b->second()->clone()); - int num = dict->register_acceptance_variable(g, this); - a_set &= bdd_ithvar(num); - g->destroy(); - - auto p = bm.emplace(num, 0U); - if (p.second) - p.first->second = acc.add_set(); - - return num; - } - else - { - break; - } + // P(a U b) = P(b) + f = f.nth(1); } - else if (const unop* u = is_unop(f, unop::F)) + else if (f.is(op::M)) + { + // P(a M b) = P(a & b) + formula g = formula::And({f.nth(0), f.nth(1)}); + int num = dict->register_acceptance_variable(g, this); + a_set &= bdd_ithvar(num); + + auto p = bm.emplace(num, 0U); + if (p.second) + p.first->second = acc.add_set(); + return num; + } + else if (f.is(op::F)) { // P(F(a)) = P(a) - f = u->child(); + f = f.nth(0); } else { @@ -367,7 +340,7 @@ namespace spot } int - register_next_variable(const formula* f) + register_next_variable(formula f) { int num; // Do not build a Next variable that already exists. @@ -378,7 +351,6 @@ namespace spot } else { - f = f->clone(); num = dict->register_anonymous_variables(1, this); next_map[f] = num; next_formula_map.resize(bdd_varnum()); @@ -402,22 +374,22 @@ namespace spot return os; } - const formula* + formula var_to_formula(int var) const { const bdd_dict::bdd_info& i = dict->bdd_map[var]; if (i.type != bdd_dict::anon) { assert(i.type == bdd_dict::acc || i.type == bdd_dict::var); - return i.f->clone(); + return i.f; } - const formula* f = next_formula_map[var]; + formula f = next_formula_map[var]; assert(f); - return f->clone(); + return f; } bdd - boolean_to_bdd(const formula* f) + boolean_to_bdd(formula f) { bdd res = ls->as_bdd(f); var_set &= bdd_support(res); @@ -430,9 +402,7 @@ namespace spot while (one != bddtrue) { int v = bdd_var(one); - auto *f = var_to_formula(v); - a_->register_ap(f); - f->destroy(); + a_->register_ap(var_to_formula(v)); if (bdd_high(one) == bddfalse) one = bdd_low(one); else @@ -444,20 +414,20 @@ namespace spot return res; } - const formula* - conj_bdd_to_formula(bdd b, multop::type op = multop::And) const + formula + conj_bdd_to_formula(bdd b, op o = op::And) const { if (b == bddfalse) - return constant::false_instance(); - multop::vec* v = new multop::vec; + return formula::ff(); + vec v; while (b != bddtrue) { int var = bdd_var(b); - const formula* res = var_to_formula(var); + formula res = var_to_formula(var); bdd high = bdd_high(b); if (high == bddfalse) { - res = unop::instance(unop::Not, res); + res = formula::Not(res); b = bdd_low(b); } else @@ -466,51 +436,47 @@ namespace spot b = high; } assert(b != bddfalse); - v->push_back(res); + v.push_back(res); } - return multop::instance(op, v); + return formula::multop(o, std::move(v)); } - const formula* + formula conj_bdd_to_sere(bdd b) const { - return conj_bdd_to_formula(b, multop::AndRat); + return conj_bdd_to_formula(b, op::AndRat); } - const formula* + formula bdd_to_formula(bdd f) { if (f == bddfalse) - return constant::false_instance(); - - multop::vec* v = new multop::vec; + return formula::ff(); + vec v; minato_isop isop(f); bdd cube; while ((cube = isop.next()) != bddfalse) - v->push_back(conj_bdd_to_formula(cube)); - - return multop::instance(multop::Or, v); + v.push_back(conj_bdd_to_formula(cube)); + return formula::Or(std::move(v)); } - const formula* + formula bdd_to_sere(bdd f) { if (f == bddfalse) - return constant::false_instance(); - - multop::vec* v = new multop::vec; + return formula::ff(); + vec v; minato_isop isop(f); bdd cube; while ((cube = isop.next()) != bddfalse) - v->push_back(conj_bdd_to_sere(cube)); - - return multop::instance(multop::OrRat, v); + v.push_back(conj_bdd_to_sere(cube)); + return formula::OrRat(std::move(v)); } const translated& - ltl_to_bdd(const formula* f, bool mark_all, bool recurring = false); + ltl_to_bdd(formula f, bool mark_all, bool recurring = false); }; @@ -534,86 +500,27 @@ namespace spot { bdd label = bdd_exist(cube, d.next_set); bdd dest_bdd = bdd_existcomp(cube, d.next_set); - const formula* dest = d.conj_bdd_to_formula(dest_bdd); + formula dest = d.conj_bdd_to_formula(dest_bdd); bdd_print_set(std::cerr, d.dict, label) << " => "; bdd_print_set(std::cerr, d.dict, dest_bdd) << " = "; print_psl(std::cerr, dest) << '\n'; - dest->destroy(); } return std::cerr; } - - - // Gather all promises of a formula. These are the - // right-hand sides of U or F operators. - class ltl_promise_visitor: public postfix_visitor - { - public: - ltl_promise_visitor(translate_dict& dict) - : dict_(dict), res_(bddtrue) - { - } - - virtual - ~ltl_promise_visitor() - { - } - - bdd - result() const - { - return res_; - } - - using postfix_visitor::doit; - - virtual void - doit(const unop* node) - { - if (node->op() == unop::F) - res_ &= bdd_ithvar(dict_.register_a_variable(node->child())); - } - - virtual void - doit(const binop* node) - { - if (node->op() == binop::U) - res_ &= bdd_ithvar(dict_.register_a_variable(node->second())); - } - - private: - translate_dict& dict_; - bdd res_; - }; - - bdd translate_ratexp(const formula* f, translate_dict& dict, - const formula* to_concat = 0); + bdd translate_ratexp(formula f, translate_dict& dict, + formula to_concat = nullptr); // Rewrite rule for rational operators. - class ratexp_trad_visitor: public visitor + class ratexp_trad_visitor final { public: // negated should only be set for constants or atomic properties - ratexp_trad_visitor(translate_dict& dict, - const formula* to_concat = 0) + ratexp_trad_visitor(translate_dict& dict, formula to_concat = nullptr) : dict_(dict), to_concat_(to_concat) { } - virtual - ~ratexp_trad_visitor() - { - if (to_concat_) - to_concat_->destroy(); - } - - bdd - result() const - { - return res_; - } - bdd next_to_concat() { // Encoding X[*0] when there is nothing to concatenate is a @@ -625,22 +532,20 @@ namespace spot // "a;[*]" as it would occur if we had simply encoded "a" as // "a". if (!to_concat_) - to_concat_ = constant::empty_word_instance(); + to_concat_ = formula::eword(); int x = dict_.register_next_variable(to_concat_); return bdd_ithvar(x); } bdd now_to_concat() { - if (to_concat_ && to_concat_ != constant::empty_word_instance()) + if (to_concat_ && !to_concat_.is(op::EmptyWord)) return recurse(to_concat_); - return bddfalse; } // Append to_concat_ to all Next variables in IN. - bdd - concat_dests(bdd in) + bdd concat_dests(bdd in) { if (!to_concat_) return in; @@ -651,369 +556,279 @@ namespace spot { bdd label = bdd_exist(cube, dict_.next_set); bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); - if (dest == constant::empty_word_instance()) + formula dest = dict_.conj_bdd_to_sere(dest_bdd); + if (dest.is(op::EmptyWord)) { out |= label & next_to_concat(); } else { - const formula* dest2 = multop::instance(multop::Concat, dest, - to_concat_->clone()); - if (dest2 != constant::false_instance()) - { - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - out |= label & bdd_ithvar(x); - } + formula dest2 = formula::Concat({dest, to_concat_}); + if (!dest2.is(op::False)) + out |= + label & bdd_ithvar(dict_.register_next_variable(dest2)); } } return out; } - void - visit(const atomic_prop* node) + bdd visit(formula f) { - res_ = bdd_ithvar(dict_.register_proposition(node)); - res_ &= next_to_concat(); - } - - void - visit(const constant* node) - { - switch (node->val()) + switch (op o = f.kind()) { - case constant::True: - res_ = next_to_concat(); - return; - case constant::False: - res_ = bddfalse; - return; - case constant::EmptyWord: - res_ = now_to_concat(); - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const unop* node) - { - switch (node->op()) - { - case unop::F: - case unop::G: - case unop::X: - case unop::Closure: - case unop::NegClosure: - case unop::NegClosureMarked: + case op::False: + return bddfalse; + case op::True: + return next_to_concat(); + case op::EmptyWord: + return now_to_concat(); + case op::AP: + return (bdd_ithvar(dict_.register_proposition(f)) + & next_to_concat()); + case op::F: + case op::G: + case op::X: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: SPOT_UNREACHABLE(); // Because not rational operator - case unop::Not: + case op::Not: { // Not can only appear in front of Boolean // expressions. - const formula* f = node->child(); - assert(f->is_boolean()); - res_ = !recurse(f); - res_ &= next_to_concat(); - return; + formula g = f.nth(0); + assert(g.is_boolean()); + return (!recurse(g)) & next_to_concat(); } - } - SPOT_UNREACHABLE(); - } - - void - visit(const bunop* bo) - { - const formula* f; - unsigned min = bo->min(); - unsigned max = bo->max(); - - assert(max > 0); - bunop::type op = bo->op(); - - // we will interpret - // c[*i..j] - // or c[:*i..j] - // as - // c;c[*i-1..j-1] - // or c:c[*i-1..j-1] - // \........../ - // this is f - unsigned min2 = (min == 0) ? 0 : (min - 1); - unsigned max2 = - (max == bunop::unbounded) ? bunop::unbounded : (max - 1); - f = bunop::instance(op, bo->child()->clone(), min2, max2); - - // If we have something to append, we can actually append it - // to f. This is correct even in the case of FStar, as f - // cannot accept [*0]. - if (to_concat_) - f = multop::instance(multop::Concat, f, to_concat_->clone()); - - switch (op) - { - case bunop::Star: - if (!bo->child()->accepts_eword()) - { - // f*;g -> f;f*;g | g - // - // If f does not accept the empty word, we can easily - // add "f*;g" as to_concat_ when translating f. - res_ = recurse(bo->child(), f); - if (min == 0) - res_ |= now_to_concat(); - } - else - { - // if "f" accepts the empty word, doing the above would - // lead to an infinite loop: - // f*;g -> f;f*;g | g - // f;f*;g -> f*;g | ... - // - // So we do it in three steps: - // 1. translate f, - // 2. append f*;g to all destinations - // 3. add |g - res_ = recurse(bo->child()); - - // f*;g -> f;f*;g - minato_isop isop(res_); - bdd cube; - res_ = bddfalse; - while ((cube = isop.next()) != bddfalse) - { - bdd label = bdd_exist(cube, dict_.next_set); - bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); - int x; - if (dest == constant::empty_word_instance()) - { - x = dict_.register_next_variable(f); - res_ |= label & bdd_ithvar(x); - } - else - { - const formula* - dest2 = multop::instance(multop::Concat, dest, - f->clone()); - if (dest2 != constant::false_instance()) - { - x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); - } - } - } - f->destroy(); - res_ |= now_to_concat(); - } - return; - case bunop::FStar: + case op::Star: + case op::FStar: { - res_ = recurse(bo->child()); - bdd tail_bdd; - bool tail_computed = false; + formula bo = f; + unsigned min = f.min(); + unsigned max = f.max(); + assert(max > 0); - minato_isop isop(res_); - bdd cube; - res_ = bddfalse; - if (min == 0) - { - // f[:*0..j];g can be satisfied by X(g). - res_ = next_to_concat(); - } - while ((cube = isop.next()) != bddfalse) - { - bdd label = bdd_exist(cube, dict_.next_set); - bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); + // we will interpret + // c[*i..j] + // or c[:*i..j] + // as + // c;c[*i-1..j-1] + // or c:c[*i-1..j-1] + // \........../ + // this is f + unsigned min2 = (min == 0) ? 0 : (min - 1); + unsigned max2 = + max == formula::unbounded() ? formula::unbounded() : (max - 1); + f = formula::bunop(o, f.nth(0), min2, max2); - // The destination is a final state. Make sure we - // can also exit if tail is satisfied. We do not - // even have to check the tail if min == 0. - if (dest->accepts_eword() && min != 0) + // If we have something to append, we can actually append it + // to f. This is correct even in the case of FStar, as f + // cannot accept [*0]. + if (to_concat_) + f = formula::Concat({f, to_concat_}); + + if (o == op::Star) + { + if (!bo.nth(0).accepts_eword()) { - if (!tail_computed) - { - tail_bdd = recurse(f); - tail_computed = true; - } - res_ |= label & tail_bdd; + // f*;g -> f;f*;g | g + // + // If f does not accept the empty word, we can easily + // add "f*;g" as to_concat_ when translating f. + bdd res = recurse(bo.nth(0), f); + if (min == 0) + res |= now_to_concat(); + return res; } - - // If the destination is not 0 or [*0], it means it - // can have successors. Fusion the tail. - if (dest != constant::false_instance() - && dest != constant::empty_word_instance()) - { - const formula* dest2 = - multop::instance(multop::Fusion, dest, f->clone()); - if (dest2 != constant::false_instance()) - { - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); - } - } - } - f->destroy(); - } - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const binop*) - { - SPOT_UNREACHABLE(); // Not a rational operator - } - - void - visit(const multop* node) - { - multop::type op = node->op(); - switch (op) - { - case multop::AndNLM: - { - unsigned s = node->size(); - multop::vec* final = new multop::vec; - multop::vec* non_final = new multop::vec; - - for (unsigned n = 0; n < s; ++n) - { - const formula* f = node->nth(n); - if (f->accepts_eword()) - final->push_back(f->clone()); else - non_final->push_back(f->clone()); + { + // if "f" accepts the empty word, doing the above + // would lead to an infinite loop: + // f*;g -> f;f*;g | g + // f;f*;g -> f*;g | ... + // + // So we do it in three steps: + // 1. translate f, + // 2. append f*;g to all destinations + // 3. add |g + bdd res = recurse(bo.nth(0)); + // f*;g -> f;f*;g + minato_isop isop(res); + bdd cube; + res = bddfalse; + while ((cube = isop.next()) != bddfalse) + { + bdd label = bdd_exist(cube, dict_.next_set); + bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); + formula dest = dict_.conj_bdd_to_sere(dest_bdd); + if (dest.is(op::EmptyWord)) + { + res |= label & + bdd_ithvar(dict_.register_next_variable(f)); + } + else + { + formula dest2 = formula::Concat({dest, f}); + if (!dest2.is(op::False)) + res |= label & bdd_ithvar + (dict_.register_next_variable(dest2)); + } + } + return res | now_to_concat(); + } } - - if (non_final->empty()) + else // FStar { - delete non_final; - // (a* & b*);c = (a*|b*);c - const formula* f = multop::instance(multop::OrRat, final); - res_ = recurse_and_concat(f); - f->destroy(); - break; + bdd tail_bdd; + bool tail_computed = false; + + minato_isop isop(recurse(bo.nth(0))); + bdd cube; + bdd res = bddfalse; + if (min == 0) + { + // f[:*0..j];g can be satisfied by X(g). + res = next_to_concat(); + } + while ((cube = isop.next()) != bddfalse) + { + bdd label = bdd_exist(cube, dict_.next_set); + bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); + formula dest = dict_.conj_bdd_to_sere(dest_bdd); + + // The destination is a final state. Make sure we + // can also exit if tail is satisfied. We do not + // even have to check the tail if min == 0. + if (dest.accepts_eword() && min != 0) + { + if (!tail_computed) + { + tail_bdd = recurse(f); // FIXME: inf call!!! + tail_computed = true; + } + res |= label & tail_bdd; + } + + // If the destination is not 0 or [*0], it means it + // can have successors. Fusion the tail. + if (!dest.is(op::False, op::EmptyWord)) + { + formula dest2 = formula::Fusion({dest, f}); + if (!dest2.is(op::False)) + res |= label & + bdd_ithvar(dict_.register_next_variable(dest2)); + } + } + return res; } - if (!final->empty()) + } + 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::And: + case op::Or: + // Not a rational operator + SPOT_UNREACHABLE(); + case op::AndNLM: + { + unsigned s = f.size(); + vec final; + vec non_final; + + for (auto g: f) + if (g.accepts_eword()) + final.push_back(g); + else + non_final.push_back(g); + + if (non_final.empty()) + // (a* & b*);c = (a*|b*);c + return recurse_and_concat(formula::OrRat(std::move(final))); + if (!final.empty()) { // let F_i be final formulae // N_i be non final formula // (F_1 & ... & F_n & N_1 & ... & N_m) // = (F_1 | ... | F_n);[*] && (N_1 & ... & N_m) // | (F_1 | ... | F_n) && (N_1 & ... & N_m);[*] - const formula* f = - multop::instance(multop::OrRat, final); - const formula* n = - multop::instance(multop::AndNLM, non_final); - const formula* t = - bunop::instance(bunop::Star, constant::true_instance()); - const formula* ft = - multop::instance(multop::Concat, f->clone(), t->clone()); - const formula* nt = - multop::instance(multop::Concat, n->clone(), t); - const formula* ftn = - multop::instance(multop::AndRat, ft, n); - const formula* fnt = - multop::instance(multop::AndRat, f, nt); - const formula* all = - multop::instance(multop::OrRat, ftn, fnt); - res_ = recurse_and_concat(all); - all->destroy(); - break; + formula f = formula::OrRat(std::move(final)); + formula n = formula::AndNLM(std::move(non_final)); + formula t = formula::one_star(); + formula ft = formula::Concat({f, t}); + formula nt = formula::Concat({n, t}); + formula ftn = formula::AndRat({ft, n}); + formula fnt = formula::AndRat({f, nt}); + return recurse_and_concat(formula::OrRat({ftn, fnt})); } // No final formula. - delete final; - for (unsigned n = 0; n < s; ++n) - (*non_final)[n]->destroy(); - delete non_final; // Translate N_1 & N_2 & ... & N_n into // N_1 && (N_2;[*]) && ... && (N_n;[*]) // | (N_1;[*]) && N_2 && ... && (N_n;[*]) // | (N_1;[*]) && (N_2;[*]) && ... && N_n - const formula* star = - bunop::instance(bunop::Star, constant::true_instance()); - multop::vec* disj = new multop::vec; + formula star = formula::one_star(); + vec disj; for (unsigned n = 0; n < s; ++n) { - multop::vec* conj = new multop::vec; + vec conj; for (unsigned m = 0; m < s; ++m) { - const formula* f = node->nth(m)->clone(); + formula g = f.nth(m); if (n != m) - f = multop::instance(multop::Concat, - f, star->clone()); - conj->push_back(f); + g = formula::Concat({g, star}); + conj.push_back(g); } - disj->push_back(multop::instance(multop::AndRat, conj)); + disj.push_back(formula::AndRat(std::move(conj))); } - star->destroy(); - const formula* all = multop::instance(multop::OrRat, disj); - res_ = recurse_and_concat(all); - all->destroy(); - break; + return recurse_and_concat(formula::OrRat(std::move(disj))); } - case multop::AndRat: + case op::AndRat: { - unsigned s = node->size(); - - res_ = bddtrue; - for (unsigned n = 0; n < s; ++n) - { - bdd res = recurse(node->nth(n)); - // trace_ltl_bdd(dict_, res); - res_ &= res; - } - - //std::cerr << "Pre-Concat:" << std::endl; - //trace_ltl_bdd(dict_, res_); + bdd res = bddtrue; + for (auto g: f) + res &= recurse(g); // If we have translated (a* && b*) in (a* && b*);c, we // have to append ";c" to all destinations. - res_ = concat_dests(res_); + res = concat_dests(res); - if (node->accepts_eword()) - res_ |= now_to_concat(); - - if (op == multop::AndNLM) - node->destroy(); - break; + if (f.accepts_eword()) + res |= now_to_concat(); + return res; } - case multop::OrRat: + case op::OrRat: { - res_ = bddfalse; - unsigned s = node->size(); - for (unsigned n = 0; n < s; ++n) - res_ |= recurse_and_concat(node->nth(n)); - break; + bdd res = bddfalse; + for (auto g: f) + res |= recurse_and_concat(g); + return res; } - case multop::Concat: + case op::Concat: { - multop::vec* v = new multop::vec; - unsigned s = node->size(); - v->reserve(s); + vec v; + unsigned s = f.size(); + v.reserve(s); for (unsigned n = 1; n < s; ++n) - v->push_back(node->nth(n)->clone()); + v.push_back(f.nth(n)); if (to_concat_) - v->push_back(to_concat_->clone()); - res_ = recurse(node->nth(0), - multop::instance(multop::Concat, v)); - break; + v.push_back(to_concat_); + return recurse(f.nth(0), formula::Concat(std::move(v))); } - case multop::Fusion: + case op::Fusion: { - assert(node->size() >= 2); + assert(f.size() >= 2); // the head - bdd res = recurse(node->nth(0)); + bdd res = recurse(f.nth(0)); // the tail - const formula* tail = node->all_but(0); + formula tail = f.all_but(0); bdd tail_bdd; bool tail_computed = false; @@ -1021,14 +836,14 @@ namespace spot minato_isop isop(res); bdd cube; - res_ = bddfalse; + res = bddfalse; while ((cube = isop.next()) != bddfalse) { bdd label = bdd_exist(cube, dict_.next_set); bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); + formula dest = dict_.conj_bdd_to_sere(dest_bdd); - if (dest->accepts_eword()) + if (dest.accepts_eword()) { // The destination is a final state. Make sure we // can also exit if tail is satisfied. @@ -1037,78 +852,63 @@ namespace spot tail_bdd = recurse(tail); tail_computed = true; } - res_ |= concat_dests(label & tail_bdd); - + res |= concat_dests(label & tail_bdd); } // If the destination is not 0 or [*0], it means it // can have successors. Fusion the tail and append // anything to concatenate. - if (dest != constant::false_instance() - && dest != constant::empty_word_instance()) + if (!dest.is(op::False, op::EmptyWord)) { - const formula* dest2 = - multop::instance(multop::Fusion, dest, tail->clone()); + formula dest2 = formula::Fusion({dest, tail}); if (to_concat_) - dest2 = multop::instance(multop::Concat, dest2, - to_concat_->clone()); - if (dest2 != constant::false_instance()) - { - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); - } + dest2 = formula::Concat({dest2, to_concat_}); + if (!dest2.is(op::False)) + res |= label + & bdd_ithvar(dict_.register_next_variable(dest2)); } } - - tail->destroy(); - break; + return res; } - case multop::And: - case multop::Or: - SPOT_UNREACHABLE(); // Not a rational operator } + SPOT_UNREACHABLE(); } bdd - recurse(const formula* f, const formula* to_concat = 0) + recurse(formula f, formula to_concat = nullptr) { return translate_ratexp(f, dict_, to_concat); } bdd - recurse_and_concat(const formula* f) + recurse_and_concat(formula f) { - return translate_ratexp(f, dict_, - to_concat_ ? to_concat_->clone() : 0); + return translate_ratexp(f, dict_, to_concat_); } private: translate_dict& dict_; - bdd res_; - const formula* to_concat_; + formula to_concat_; }; bdd - translate_ratexp(const formula* f, translate_dict& dict, - const formula* to_concat) + translate_ratexp(formula f, translate_dict& dict, + formula to_concat) { bdd res; - if (!f->is_boolean()) + if (!f.is_boolean()) { ratexp_trad_visitor v(dict, to_concat); - f->accept(v); - res = v.result(); + res = v.visit(f); } else { res = dict.boolean_to_bdd(f); // See comment for similar code in next_to_concat. if (!to_concat) - to_concat = constant::empty_word_instance(); + to_concat = formula::eword(); int x = dict.register_next_variable(to_concat); res &= bdd_ithvar(x); - to_concat->destroy(); } return res; } @@ -1122,25 +922,20 @@ namespace spot ratexp_to_dfa::~ratexp_to_dfa() { for (auto i: automata_) - { - for (auto n: i.second->names()) - n->destroy(); - delete i.second; - } + delete i.second; } ratexp_to_dfa::labelled_aut - ratexp_to_dfa::translate(const formula* f) + ratexp_to_dfa::translate(formula f) { - assert(f->is_in_nenoform()); + assert(f.is_in_nenoform()); auto a = make_twa_graph(dict_.dict); - auto namer = a->create_namer(); + auto namer = a->create_namer(); - typedef std::set set_type; + typedef std::set set_type; set_type formulae_to_translate; - f->clone(); formulae_to_translate.insert(f); namer->new_state(f); //a->set_init_state(f); @@ -1148,7 +943,7 @@ namespace spot while (!formulae_to_translate.empty()) { // Pick one formula. - const formula* now = *formulae_to_translate.begin(); + formula now = *formulae_to_translate.begin(); formulae_to_translate.erase(formulae_to_translate.begin()); // Translate it @@ -1162,27 +957,18 @@ namespace spot bdd label = bdd_satoneset(all_props, var_set, bddtrue); all_props -= label; - const formula* dest = + formula dest = dict_.bdd_to_sere(bdd_exist(res & label, dict_.var_set)); f2a_t::const_iterator i = f2a_.find(dest); if (i != f2a_.end() && i->second.first == nullptr) - { - // This state is useless. Ignore it. - dest->destroy(); - continue; - } + continue; if (!namer->has_state(dest)) { formulae_to_translate.insert(dest); namer->new_state(dest); } - else - { - dest->destroy(); - } - namer->new_edge(now, dest, label); } } @@ -1205,7 +991,7 @@ namespace spot bool coacc = false; auto& st = sm->states_of(n); for (auto l: st) - if (namer->get_name(l)->accepts_eword()) + if (namer->get_name(l).accepts_eword()) { coacc = true; break; @@ -1245,8 +1031,6 @@ namespace spot } else { - for (auto n: namer->names()) - n->destroy(); delete namer; return labelled_aut(nullptr, nullptr); } @@ -1256,7 +1040,7 @@ namespace spot std::tuple - ratexp_to_dfa::succ(const formula* f) + ratexp_to_dfa::succ(formula f) { f2a_t::const_iterator it = f2a_.find(f); labelled_aut a; @@ -1276,8 +1060,12 @@ namespace spot } // The rewrite rules used here are adapted from Jean-Michel - // Couvreur's FM paper, augmented to support rational operators. - class ltl_trad_visitor: public visitor + // Couvreur's FM'99 paper, augmented to support rational operators + // (from PSL), and a view other optimization. See the + // Duret-Lutz's paper "LTL Translation Improvements in Spot 1.0" + // (IJCCBS 2014), for the optimization. The PSL stuff is + // unpublished yet. + class ltl_trad_visitor { public: ltl_trad_visitor(translate_dict& dict, bool mark_all = false, @@ -1293,12 +1081,9 @@ namespace spot } bdd - neg_of(const formula* node) + neg_of(formula node) { - const formula* n = dict_.ls->negative_normal_form(node, true); - bdd r = recurse(n); - n->destroy(); - return r; + return recurse(dict_.ls->negative_normal_form(node, true)); } void @@ -1309,12 +1094,6 @@ namespace spot mark_all_ = mark_all; } - bdd - result() const - { - return res_; - } - const translate_dict& get_dict() const { @@ -1333,51 +1112,33 @@ namespace spot return has_marked_; } - void - visit(const atomic_prop* node) + bdd + visit(formula node) { - res_ = bdd_ithvar(dict_.register_proposition(node)); - } - - void - visit(const constant* node) - { - switch (node->val()) + switch (op o = node.kind()) { - case constant::True: - res_ = bddtrue; - return; - case constant::False: - res_ = bddfalse; - return; - case constant::EmptyWord: + case op::False: + return bddfalse; + case op::True: + return bddtrue; + case op::EmptyWord: SPOT_UNIMPLEMENTED(); - } - SPOT_UNREACHABLE(); - } - - void - visit(const unop* node) - { - unop::type op = node->op(); - - switch (op) - { - case unop::F: + case op::AP: + return bdd_ithvar(dict_.register_proposition(node)); + case op::F: { // r(Fy) = r(y) + a(y)X(Fy) if not recurring // r(Fy) = r(y) + a(y) if recurring (see comment in G) - const formula* child = node->child(); + formula child = node.nth(0); bdd y = recurse(child); bdd a = bdd_ithvar(dict_.register_a_variable(child)); if (!recurring_) a &= bdd_ithvar(dict_.register_next_variable(node)); if (dict_.unambiguous) a &= neg_of(child); - res_ = y | a; - break; + return y | a; } - case unop::G: + case op::G: { // Couvreur's paper suggests that we optimize GFy // as @@ -1411,17 +1172,15 @@ namespace spot // r(Gy) = r(y)X(Gy) int x = dict_.register_next_variable(node); - bdd y = recurse(node->child(), /* recurring = */ true); - res_ = y & bdd_ithvar(x); - break; + bdd y = recurse(node.nth(0), /* recurring = */ true); + return y & bdd_ithvar(x); } - case unop::Not: + case op::Not: { // r(!y) = !r(y) - res_ = bdd_not(recurse(node->child())); - break; + return bdd_not(recurse(node.nth(0))); } - case unop::X: + case op::X: { // r(Xy) = Next[y] // r(X(a&b&c)) = Next[a]&Next[b]&Next[c] @@ -1442,198 +1201,168 @@ namespace spot // effect is less clear. Benchmarks show that it // reduces the number of states and transitions, but it // increases the number of non-deterministic states... - const formula* y = node->child(); - if (const multop* m = is_And(y)) + formula y = node.nth(0); + bdd res; + if (y.is(op::And)) { - res_ = bddtrue; - unsigned s = m->size(); - for (unsigned n = 0; n < s; ++n) - { - int x = dict_.register_next_variable(m->nth(n)); - res_ &= bdd_ithvar(x); - } + res = bddtrue; + for (auto f: y) + res &= bdd_ithvar(dict_.register_next_variable(f)); } #if 0 - else if (const multop* m = is_Or(y)) + else if (y.is(op::Or)) { - res_ = bddfalse; - unsigned s = m->size(); - for (unsigned n = 0; n < s; ++n) - { - int x = dict_.register_next_variable(m->nth(n)); - res_ |= bdd_ithvar(x); - } + res = bddfalse; + for (auto f: y) + res |= bdd_ithvar(dict_.register_next_variable(f)); } #endif else { - int x = dict_.register_next_variable(y); - res_ = bdd_ithvar(x); + res = bdd_ithvar(dict_.register_next_variable(y)); } - break; + return res; } - case unop::Closure: + case op::Closure: { // rat_seen_ = true; - const formula* f = node->child(); + formula f = node.nth(0); auto p = dict_.transdfa.succ(f); - res_ = bddfalse; + bdd res = bddfalse; auto aut = std::get<0>(p); auto namer = std::get<1>(p); auto st = std::get<2>(p); if (!aut) - break; + return res; for (auto i: aut->succ(st)) { bdd label = i->current_condition(); state* s = i->current_state(); - const formula* dest = + formula dest = namer->get_name(aut->state_number(s)); - if (dest->accepts_eword()) + if (dest.accepts_eword()) { - res_ |= label; + res |= label; } else { - const formula* dest2 = unop::instance(op, dest->clone()); - if (dest2 == constant::false_instance()) + formula dest2 = formula::unop(o, dest); + if (dest2 == formula::ff()) continue; - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); + res |= + label & bdd_ithvar(dict_.register_next_variable(dest2)); } } + return res; } - break; - - case unop::NegClosureMarked: + case op::NegClosureMarked: has_marked_ = true; - case unop::NegClosure: + case op::NegClosure: rat_seen_ = true; { if (mark_all_) { - op = unop::NegClosureMarked; + o = op::NegClosureMarked; has_marked_ = true; } - const formula* f = node->child(); + formula f = node.nth(0); auto p = dict_.transdfa.succ(f); - res_ = bddtrue; auto aut = std::get<0>(p); - auto namer = std::get<1>(p); - auto st = std::get<2>(p); if (!aut) - break; + return bddtrue; - res_ = bddfalse; + auto namer = std::get<1>(p); + auto st = std::get<2>(p); + bdd res = bddfalse; bdd missing = bddtrue; + for (auto i: aut->succ(st)) { bdd label = i->current_condition(); state* s = i->current_state(); - const formula* dest = namer->get_name(aut->state_number(s)); + formula dest = namer->get_name(aut->state_number(s)); missing -= label; - if (!dest->accepts_eword()) + if (!dest.accepts_eword()) { - const formula* dest2 = unop::instance(op, dest->clone()); - if (dest2 == constant::false_instance()) + formula dest2 = formula::unop(o, dest); + if (dest2 == formula::ff()) continue; - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); + res |= label + & bdd_ithvar(dict_.register_next_variable(dest2)); } } - res_ |= missing & + res |= missing & // stick X(1) to preserve determinism. - bdd_ithvar(dict_.register_next_variable - (constant::true_instance())); + bdd_ithvar(dict_.register_next_variable(formula::tt())); //trace_ltl_bdd(dict_, res_); + return res; } - break; - } - } - - void - visit(const bunop*) - { - SPOT_UNREACHABLE(); // Not an LTL operator - } - - void - visit(const binop* node) - { - binop::type op = node->op(); - - switch (op) - { + case op::Star: + case op::FStar: + SPOT_UNREACHABLE(); // Not an LTL operator // r(f1 logical-op f2) = r(f1) logical-op r(f2) - case binop::Xor: - case binop::Implies: - case binop::Equiv: + case op::Xor: + case op::Implies: + case op::Equiv: // These operators should only appear in Boolean formulas, // which must have been dealt with earlier (in // translate_dict::ltl_to_bdd()). SPOT_UNREACHABLE(); - case binop::U: + case op::U: { - bdd f1 = recurse(node->first()); - bdd f2 = recurse(node->second()); + bdd f1 = recurse(node.nth(0)); + bdd f2 = recurse(node.nth(1)); // r(f1 U f2) = r(f2) + a(f2)r(f1)X(f1 U f2) if not recurring // r(f1 U f2) = r(f2) + a(f2)r(f1) if recurring - f1 &= bdd_ithvar(dict_.register_a_variable(node->second())); + f1 &= bdd_ithvar(dict_.register_a_variable(node.nth(1))); if (!recurring_) f1 &= bdd_ithvar(dict_.register_next_variable(node)); if (dict_.unambiguous) - f1 &= neg_of(node->second()); - res_ = f2 | f1; - break; + f1 &= neg_of(node.nth(1)); + return f2 | f1; } - case binop::W: + case op::W: { // r(f1 W f2) = r(f2) + r(f1)X(f1 W f2) if not recurring // r(f1 W f2) = r(f2) + r(f1) if recurring // // also f1 W 0 = G(f1), so we can enable recurring on f1 - bdd f1 = recurse(node->first(), - node->second() == constant::false_instance()); - bdd f2 = recurse(node->second()); + bdd f1 = recurse(node.nth(0), node.nth(1) == formula::ff()); + bdd f2 = recurse(node.nth(1)); if (!recurring_) f1 &= bdd_ithvar(dict_.register_next_variable(node)); if (dict_.unambiguous) - f1 &= neg_of(node->second()); - res_ = f2 | f1; - break; + f1 &= neg_of(node.nth(1)); + return f2 | f1; } - case binop::R: + case op::R: { // r(f2) is in factor, so we can propagate the recurring_ flag. // if f1=false, we can also turn it on (0 R f = Gf). - res_ = recurse(node->second(), - recurring_ - || node->first() == constant::false_instance()); + bdd res = recurse(node.nth(1), + recurring_ || node.nth(0) == formula::ff()); // r(f1 R f2) = r(f2)(r(f1) + X(f1 R f2)) if not recurring // r(f1 R f2) = r(f2) if recurring if (recurring_ && !dict_.unambiguous) - break; - bdd f1 = recurse(node->first()); + return res; + bdd f1 = recurse(node.nth(0)); bdd f2 = bddtrue; if (!recurring_) f2 = bdd_ithvar(dict_.register_next_variable(node)); if (dict_.unambiguous) - f2 &= neg_of(node->first()); - res_ &= f1 | f2; - break; + f2 &= neg_of(node.nth(0)); + return res & (f1 | f2); } - case binop::M: + case op::M: { - res_ = recurse(node->second(), recurring_); - bdd f1 = recurse(node->first()); + bdd res = recurse(node.nth(1), recurring_); + bdd f1 = recurse(node.nth(0)); // r(f1 M f2) = r(f2)(r(f1) + a(f1&f2)X(f1 M f2)) if not recurring // r(f1 M f2) = r(f2)(r(f1) + a(f1&f2)) if recurring // @@ -1654,25 +1383,24 @@ namespace spot if (!recurring_) a &= bdd_ithvar(dict_.register_next_variable(node)); if (dict_.unambiguous) - a &= neg_of(node->first()); - res_ &= f1 | a; - break; + a &= neg_of(node.nth(0)); + return res & (f1 | a); } - case binop::EConcatMarked: + case op::EConcatMarked: has_marked_ = true; /* fall through */ - case binop::EConcat: + case op::EConcat: rat_seen_ = true; { // Recognize f2 on transitions going to destinations // that accept the empty word. - bdd f2 = recurse(node->second()); - bdd f1 = translate_ratexp(node->first(), dict_); - res_ = bddfalse; + bdd f2 = recurse(node.nth(1)); + bdd f1 = translate_ratexp(node.nth(0), dict_); + bdd res = bddfalse; if (mark_all_) { - op = binop::EConcatMarked; + o = op::EConcatMarked; has_marked_ = true; } @@ -1685,38 +1413,36 @@ namespace spot bdd label = bdd_satoneset(all_props, var_set, bddtrue); all_props -= label; - const formula* dest = + formula dest = dict_.bdd_to_sere(bdd_exist(f1 & label, dict_.var_set)); - const formula* dest2 = - binop::instance(op, dest, node->second()->clone()); + formula dest2 = formula::binop(o, dest, node.nth(1)); bool unamb = dict_.unambiguous; - if (dest2 != constant::false_instance()) + if (!dest2.is(op::False)) { // If the rhs is Boolean, the // unambiguous code will produce a more // deterministic automaton at no additional // cost. You can test this on // G({{1;1}*}<>->a) - if (node->second()->is_boolean()) + if (node.nth(1).is_boolean()) unamb = true; - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - bdd toadd = label & bdd_ithvar(x); - if (dest->accepts_eword() && unamb) - toadd &= neg_of(node->second()); - res_ |= toadd; + bdd toadd = label & + bdd_ithvar(dict_.register_next_variable(dest2)); + if (dest.accepts_eword() && unamb) + toadd &= neg_of(node.nth(1)); + res |= toadd; } - if (dest->accepts_eword()) + if (dest.accepts_eword()) { bdd toadd = label & f2; if (unamb) // Preserve determinism toadd &= bdd_ithvar(dict_.register_next_variable - (constant::true_instance())); - res_ |= toadd; + (formula::tt())); + res |= toadd; } } } @@ -1728,31 +1454,26 @@ namespace spot { bdd label = bdd_exist(cube, dict_.next_set); bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); + formula dest = dict_.conj_bdd_to_sere(dest_bdd); - if (dest == constant::empty_word_instance()) + if (dest.is(op::EmptyWord)) { - res_ |= label & f2; + res |= label & f2; } else { - const formula* dest2 = - binop::instance(op, dest, node->second()->clone()); - if (dest2 != constant::false_instance()) - { - int x = dict_.register_next_variable(dest2); - dest2->destroy(); - res_ |= label & bdd_ithvar(x); - } - if (dest->accepts_eword()) - res_ |= label & f2; + formula dest2 = formula::binop(o, dest, node.nth(1)); + if (!dest2.is(op::False)) + res |= label & + bdd_ithvar(dict_.register_next_variable(dest2)); + if (dest.accepts_eword()) + res |= label & f2; } } } + return res; } - break; - - case binop::UConcat: + case op::UConcat: { // Transitions going to destinations accepting the empty // word should recognize f2, and the automaton for f1 @@ -1762,12 +1483,12 @@ namespace spot // interpretation of first() as a universal automaton, // and using implication to encode it) was explained // to me (adl) by Felix Klaedtke. - bdd f2 = recurse(node->second()); - bdd f1 = translate_ratexp(node->first(), dict_); + bdd f2 = recurse(node.nth(1)); + bdd f1 = translate_ratexp(node.nth(0), dict_); if (exprop_) { - res_ = bddfalse; + bdd res = bddfalse; bdd var_set = bdd_existcomp(bdd_support(f1), dict_.var_set); bdd all_props = bdd_existcomp(f1, dict_.var_set); bdd missing = !all_props; @@ -1776,70 +1497,56 @@ namespace spot bdd label = bdd_satoneset(all_props, var_set, bddtrue); all_props -= label; - const formula* dest = + formula dest = dict_.bdd_to_sere(bdd_exist(f1 & label, dict_.var_set)); - const formula* dest2 = - binop::instance(op, dest, node->second()->clone()); + formula dest2 = formula::binop(o, dest, node.nth(1)); bdd udest = bdd_ithvar(dict_.register_next_variable(dest2)); - if (dest->accepts_eword()) + if (dest.accepts_eword()) udest &= f2; - dest2->destroy(); - res_ |= label & udest; + res |= label & udest; } // Make the automaton complete. - res_ |= missing & + res |= missing & // stick X(1) to preserve determinism. - bdd_ithvar(dict_.register_next_variable - (constant::true_instance())); + bdd_ithvar(dict_.register_next_variable(formula::tt())); + return res; } else { - res_ = bddtrue; + bdd res = bddtrue; minato_isop isop(f1); bdd cube; while ((cube = isop.next()) != bddfalse) { bdd label = bdd_exist(cube, dict_.next_set); bdd dest_bdd = bdd_existcomp(cube, dict_.next_set); - const formula* dest = dict_.conj_bdd_to_sere(dest_bdd); - const formula* dest2 = - binop::instance(op, dest, node->second()->clone()); + formula dest = dict_.conj_bdd_to_sere(dest_bdd); + formula dest2 = formula::binop(o, dest, node.nth(1)); bdd udest = bdd_ithvar(dict_.register_next_variable(dest2)); - if (dest->accepts_eword()) + if (dest.accepts_eword()) udest &= f2; - dest2->destroy(); - res_ &= bdd_apply(label, udest, bddop_imp); + res &= bdd_apply(label, udest, bddop_imp); } + return res; } } - break; - } - } - - void - visit(const multop* node) - { - switch (node->op()) - { - case multop::And: + case op::And: { formula_set implied; implied_subformulae(node, implied); - res_ = bddtrue; - unsigned s = node->size(); - for (unsigned n = 0; n < s; ++n) + bdd res = bddtrue; + for (auto sub: node) { - const formula* sub = node->nth(n); // Skip implied subformula. For instance // when translating Fa & GFa, we should not // attempt to translate Fa. @@ -1855,48 +1562,42 @@ namespace spot // Propagate the recurring_ flag so that // G(Fa & Fb) get optimized. See the comment in // the case handling G. - bdd res = recurse(sub, recurring_); - res_ &= res; + res &= recurse(sub, recurring_); } - break; + return res; } - case multop::Or: + case op::Or: { + bdd res = bddfalse; if (!dict_.unambiguous) { - res_ = bddfalse; - unsigned s = node->size(); - for (unsigned n = 0; n < s; ++n) - res_ |= recurse(node->nth(n)); - break; + for (auto sub: node) + res |= recurse(sub); } else { bdd prev = bddtrue; - res_ = bddfalse; - unsigned s = node->size(); - for (unsigned n = 0; n < s; ++n) + for (auto sub: node) { - const formula* sub = node->nth(n); - res_ |= prev & recurse(sub); + res |= prev & recurse(sub); prev &= neg_of(sub); } - break; - } + return res; } - case multop::Concat: - case multop::Fusion: - case multop::AndNLM: - case multop::AndRat: - case multop::OrRat: + case op::Concat: + case op::Fusion: + case op::AndNLM: + case op::AndRat: + case op::OrRat: SPOT_UNREACHABLE(); // Not an LTL operator } - + SPOT_UNREACHABLE(); + return bddfalse; } bdd - recurse(const formula* f, bool recurring = false) + recurse(formula f, bool recurring = false) { const translate_dict::translated& t = dict_.ltl_to_bdd(f, mark_all_, recurring); @@ -1908,7 +1609,6 @@ namespace spot private: translate_dict& dict_; - bdd res_; bool rat_seen_; bool has_marked_; bool mark_all_; @@ -1917,12 +1617,12 @@ namespace spot }; const translate_dict::translated& - translate_dict::ltl_to_bdd(const formula* f, bool mark_all, bool recurring) + translate_dict::ltl_to_bdd(formula f, bool mark_all, bool recurring) { flagged_formula ff; ff.f = f; ff.flags = - ((mark_all || f->is_ltl_formula()) ? flags_mark_all : flags_none) + ((mark_all || f.is_ltl_formula()) ? flags_mark_all : flags_none) | (recurring ? flags_recurring : flags_none); flagged_formula_to_bdd_map::const_iterator i = ltl_bdd_.find(ff); @@ -1931,7 +1631,7 @@ namespace spot return i->second; translated t; - if (f->is_boolean()) + if (f.is_boolean()) { t.symbolic = boolean_to_bdd(f); t.has_rational = false; @@ -1940,104 +1640,28 @@ namespace spot else { ltl_trad_visitor v(*this, mark_all, exprop, recurring); - f->accept(v); - t.symbolic = v.result(); + t.symbolic = v.visit(f); t.has_rational = v.has_rational(); t.has_marked = v.has_marked(); } - f->clone(); return ltl_bdd_.emplace(ff, t).first->second; } // Check whether a formula has a R, W, or G operator at its // top-level (preceding logical operators do not count). - class ltl_possible_fair_loop_visitor: public visitor + bool ltl_possible_fair_loop_check(formula f) { - public: - ltl_possible_fair_loop_visitor() - : res_(false) - { - } - - virtual - ~ltl_possible_fair_loop_visitor() - { - } - - bool - result() const - { - return res_; - } - - void - visit(const atomic_prop*) - { - } - - void - visit(const constant*) - { - } - - void - visit(const unop* node) - { - if (node->op() == unop::G) - res_ = true; - } - - void - visit(const binop* node) - { - switch (node->op()) - { - // r(f1 logical-op f2) = r(f1) logical-op r(f2) - case binop::Xor: - case binop::Implies: - case binop::Equiv: - node->first()->accept(*this); - if (!res_) - node->second()->accept(*this); - return; - case binop::U: - case binop::M: - return; - case binop::R: - case binop::W: - res_ = true; - return; - case binop::UConcat: - case binop::EConcat: - case binop::EConcatMarked: - node->second()->accept(*this); - // FIXME: we might need to add Acc[1] - return; - } - SPOT_UNREACHABLE(); - } - - void - visit(const bunop*) - { - SPOT_UNIMPLEMENTED(); - } - - void - visit(const multop* node) - { - unsigned s = node->size(); - for (unsigned n = 0; n < s && !res_; ++n) - { - node->nth(n)->accept(*this); - } - } - - private: - bool res_; - }; + if (f.is(op::G) || f.is(op::R, op::W)) + return true; + if (f.is(op::Xor, op::Equiv) || f.is(op::Implies) + || f.is(op::And, op::Or)) + for (auto g: f) + if (ltl_possible_fair_loop_check(g)) + return true; + return false; + } // Check whether a formula can be part of a fair loop. // Cache the result for efficiency. @@ -2045,20 +1669,16 @@ namespace spot { public: bool - check(const formula* f) + check(formula f) { pfl_map::const_iterator i = pfl_.find(f); if (i != pfl_.end()) return i->second; - ltl_possible_fair_loop_visitor v; - f->accept(v); - bool rel = v.result(); - pfl_[f] = rel; - return rel; + return pfl_[f] = ltl_possible_fair_loop_check(f); } private: - typedef std::unordered_map pfl_map; + typedef std::unordered_map pfl_map; pfl_map pfl_; }; @@ -2073,22 +1693,14 @@ namespace spot { // For cosmetics, register 1 initially, so the algorithm will // not register an equivalent formula first. - b2f_[bddtrue] = constant::true_instance(); - } - - ~formula_canonizer() - { - formula_to_bdd_map::iterator i = f2b_.begin(); - while (i != f2b_.end()) - // Advance the iterator before destroying previous value. - i++->first->destroy(); + b2f_[bddtrue] = formula::tt(); } // This wrap translate_dict::ltl_to_bdd() for top-level formulas. // In case the formula contains SERE operators, we need to decide // if we have to mark unmarked operators, and more const translate_dict::translated& - translate(const formula* f, bool* new_flag = 0) + translate(formula f, bool* new_flag = 0) { // Use the cached result if available. formula_to_bdd_map::const_iterator i = f2b_.find(f); @@ -2099,13 +1711,13 @@ namespace spot *new_flag = true; // Perform the actual translation. - translate_dict::translated t = d_.ltl_to_bdd(f, !f->is_marked()); + translate_dict::translated t = d_.ltl_to_bdd(f, !f.is_marked()); // std::cerr << "-----" << std::endl; // std::cerr << "Formula: " << str_psl(f) << std::endl; // std::cerr << "Rational: " << t.has_rational << std::endl; // std::cerr << "Marked: " << t.has_marked << std::endl; - // std::cerr << "Mark all: " << !f->is_marked() << std::endl; + // std::cerr << "Mark all: " << !f.is_marked() << std::endl; // std::cerr << "Transitions:" << std::endl; // trace_ltl_bdd(d_, t.symbolic); // std::cerr << "-----" << std::endl; @@ -2129,21 +1741,19 @@ namespace spot { bdd label = bdd_exist(cube, d_.next_set); bdd dest_bdd = bdd_existcomp(cube, d_.next_set); - const formula* dest = + formula dest = d_.conj_bdd_to_formula(dest_bdd); // Handle a Miyano-Hayashi style unrolling for // rational operators. Marked nodes correspond to // subformulae in the Miyano-Hayashi set. - const formula* tmp = d_.mt.simplify_mark(dest); - dest->destroy(); - dest = tmp; + dest = d_.mt.simplify_mark(dest); - if (dest->is_marked()) + if (dest.is_marked()) { // Make the promise that we will exit marked sets. int a = - d_.register_a_variable(constant::true_instance()); + d_.register_a_variable(formula::tt()); label &= bdd_ithvar(a); } else @@ -2151,13 +1761,10 @@ namespace spot // We have no marked operators, but still // have other rational operator to check. // Start a new marked cycle. - const formula* dest2 = d_.mt.mark_concat_ops(dest); - dest->destroy(); - dest = dest2; + dest = d_.mt.mark_concat_ops(dest); } // Note that simplify_mark may have changed dest. dest_bdd = bdd_ithvar(d_.register_next_variable(dest)); - dest->destroy(); res |= label & dest_bdd; } } @@ -2172,7 +1779,7 @@ namespace spot // If the source cannot possibly be part of a fair // loop, make all possible promises. if (fair_loop_approx_ - && f != constant::true_instance() + && f != formula::tt() && !pflc_.check(f)) t.symbolic &= all_promises_; } @@ -2181,11 +1788,11 @@ namespace spot if (b2f_.find(t.symbolic) == b2f_.end()) b2f_[t.symbolic] = f; - return f2b_.emplace(f->clone(), t).first->second; + return f2b_.emplace(f, t).first->second; } - const formula* - canonize(const formula* f) + formula + canonize(formula f) { bool new_variable = false; bdd b = translate(f, &new_variable).symbolic; @@ -2196,11 +1803,8 @@ namespace spot assert(i != b2f_.end()); if (i->second != f) - { - // The translated bdd maps to an already seen formula. - f->destroy(); - f = i->second->clone(); - } + // The translated bdd maps to an already seen formula. + f = i->second; return f; } @@ -2214,13 +1818,13 @@ namespace spot // We do this because many formulae (such as `aR(bRc)' and // `aR(bRc).(bRc)') are equivalent, and are trivially identified // by looking at the set of successors. - typedef std::unordered_map bdd_to_formula_map; bdd_to_formula_map b2f_; // Map each formula to its associated bdd. This speed things up when // the same formula is translated several times, which especially // occurs when canonize() is called repeatedly inside exprop. - typedef std::unordered_map formula_to_bdd_map; formula_to_bdd_map f2b_; @@ -2236,11 +1840,11 @@ namespace spot { struct transition { - const formula* dest; + formula dest; bdd prom; bdd cond; - transition(const formula* dest, bdd cond, bdd prom) + transition(formula dest, bdd cond, bdd prom) : dest(dest), prom(prom), cond(cond) { } @@ -2252,10 +1856,9 @@ namespace spot bool operator<(const transition& other) const { - ltl::formula_ptr_less_than lt; - if (lt(dest, other.dest)) + if (dest < other.dest) return true; - if (lt(other.dest, dest)) + if (other.dest < dest) return false; if (prom.id() < other.prom.id()) return true; @@ -2275,20 +1878,18 @@ namespace spot return true; if (lhs.cond.id() > rhs.cond.id()) return false; - ltl::formula_ptr_less_than lt; - return lt(lhs.dest, rhs.dest); + return lhs.dest < rhs.dest; } typedef std::vector dest_map; } twa_graph_ptr - ltl_to_tgba_fm(const formula* f, const bdd_dict_ptr& dict, + ltl_to_tgba_fm(formula f2, const bdd_dict_ptr& dict, bool exprop, bool symb_merge, bool branching_postponement, bool fair_loop_approx, const atomic_prop_set* unobs, ltl_simplifier* simplifier, bool unambiguous) { - const formula* f2; ltl_simplifier* s = simplifier; // Simplify the formula, if requested. @@ -2296,7 +1897,7 @@ namespace spot { // This will normalize the formula regardless of the // configuration of the simplifier. - f2 = s->simplify(f); + f2 = s->simplify(f2); } else { @@ -2305,30 +1906,39 @@ namespace spot // logic abbreviations such as <=>, =>, or XOR, since they // would involve negations at the BDD level. s = new ltl_simplifier(dict); - f2 = s->negative_normal_form(f, false); + f2 = s->negative_normal_form(f2, false); } + assert(f2.is_in_nenoform()); - typedef std::set set_type; + typedef std::set set_type; set_type formulae_to_translate; assert(dict == s->get_dict()); twa_graph_ptr a = make_twa_graph(dict); - auto namer = a->create_namer(); + auto namer = a->create_namer(); - translate_dict d(a, dict, a->acc(), s, exprop, - f->is_syntactic_persistence(), + translate_dict d(a, s, exprop, f2.is_syntactic_persistence(), unambiguous); - // Compute the set of all promises that can possibly occur - // inside the formula. + // Compute the set of all promises that can possibly occur inside + // the formula. These are the right-hand sides of U or F + // operators. bdd all_promises = bddtrue; if (fair_loop_approx || unobs) - { - ltl_promise_visitor pv(d); - f2->accept(pv); - all_promises = pv.result(); - } + f2.traverse([&all_promises, &d](formula f) + { + if (f.is(op::F)) + all_promises &= + bdd_ithvar(d.register_a_variable(f.nth(0))); + else if (f.is(op::U)) + all_promises &= + bdd_ithvar(d.register_a_variable(f.nth(1))); + else if (f.is(op::M)) + all_promises &= + bdd_ithvar(d.register_a_variable(f)); + return f.is_boolean(); + }); formula_canonizer fc(d, fair_loop_approx, all_promises); @@ -2342,7 +1952,7 @@ namespace spot if (unobs) { bdd neg_events = bddtrue; - auto aps = std::unique_ptr(atomic_prop_collect(f)); + auto aps = std::unique_ptr(atomic_prop_collect(f2)); for (auto pi: *aps) { int p = d.register_proposition(pi); @@ -2364,8 +1974,6 @@ namespace spot } bdd all_events = observable_events | unobservable_events; - - // This is in case the initial state is equivalent to true... if (symb_merge) f2 = fc.canonize(f2); @@ -2377,7 +1985,7 @@ namespace spot while (!formulae_to_translate.empty()) { // Pick one formula. - const formula* now = *formulae_to_translate.begin(); + formula now = *formulae_to_translate.begin(); formulae_to_translate.erase(formulae_to_translate.begin()); // Translate it into a BDD to simplify it. @@ -2465,16 +2073,14 @@ namespace spot { bdd label = bdd_exist(cube, d.next_set); bdd dest_bdd = bdd_existcomp(cube, d.next_set); - const formula* dest = d.conj_bdd_to_formula(dest_bdd); + formula dest = d.conj_bdd_to_formula(dest_bdd); // Simplify the formula, if requested. if (simplifier) { - const formula* tmp = simplifier->simplify(dest); - dest->destroy(); - dest = tmp; + dest = simplifier->simplify(dest); // Ignore the arc if the destination reduces to false. - if (dest == constant::false_instance()) + if (dest == formula::ff()) continue; } @@ -2502,7 +2108,7 @@ namespace spot transition t = *in; while (++in != dests.end() && t.cond == in->cond && t.prom == in->prom) - t.dest = multop::instance(multop::Or, t.dest, in->dest); + t.dest = formula::Or({t.dest, in->dest}); *out++ = t; } while (in != dests.end()); @@ -2525,11 +2131,11 @@ namespace spot // f ----> 1 // // because there is no point in looping on f if we can go to 1. - if (dests.front().dest == constant::true_instance()) + if (dests.front().dest == formula::tt()) { dest_map::iterator i = dests.begin(); bdd c = bddfalse; - while (i != dests.end() && i->dest == constant::true_instance()) + while (i != dests.end() && i->dest == formula::tt()) c |= i++->cond; for (; i != dests.end(); ++i) i->cond -= c; @@ -2545,10 +2151,7 @@ namespace spot transition t = *in; while (++in != dests.end() && t.prom == in->prom && t.dest == in->dest) - { - t.cond |= in->cond; - in->dest->destroy(); - } + t.cond |= in->cond; // Actually create the transition if (t.cond != bddfalse) { @@ -2556,26 +2159,18 @@ namespace spot // with unobservable events, the 1 state should // accept all events, even unobservable events. if (unobs - && t.dest == constant::true_instance() - && now == constant::true_instance()) + && t.dest == formula::tt() + && now == formula::tt()) t.cond = all_events; // Will this be a new state? - bool seen = namer->has_state(t.dest); - - if (!seen) + if (!namer->has_state(t.dest)) { formulae_to_translate.insert(t.dest); namer->new_state(t.dest); } - - namer->new_edge(now, t.dest, t.cond, - d.bdd_to_mark(t.prom)); - if (seen) - t.dest->destroy(); + namer->new_edge(now, t.dest, t.cond, d.bdd_to_mark(t.prom)); } - else - t.dest->destroy(); } while (in != dests.end()); } @@ -2592,11 +2187,11 @@ namespace spot acc.set_generalized_buchi(); - a->prop_inherently_weak(f->is_syntactic_persistence()); - a->prop_stutter_invariant(f->is_syntactic_stutter_invariant()); + a->prop_inherently_weak(f2.is_syntactic_persistence()); + a->prop_stutter_invariant(f2.is_syntactic_stutter_invariant()); // Currently the unambiguous option work only with LTL. - a->prop_unambiguous(f->is_ltl_formula() && unambiguous); + a->prop_unambiguous(f2.is_ltl_formula() && unambiguous); if (!simplifier) // This should not be deleted before we have registered all propositions. diff --git a/src/twaalgos/ltl2tgba_fm.hh b/src/twaalgos/ltl2tgba_fm.hh index a4f4cbabe..7e8db5787 100644 --- a/src/twaalgos/ltl2tgba_fm.hh +++ b/src/twaalgos/ltl2tgba_fm.hh @@ -140,7 +140,7 @@ namespace spot /// /// \return A spot::twa_graph that recognizes the language of \a f. SPOT_API twa_graph_ptr - ltl_to_tgba_fm(const ltl::formula* f, const bdd_dict_ptr& dict, + ltl_to_tgba_fm(ltl::formula f, const bdd_dict_ptr& dict, bool exprop = false, bool symb_merge = true, bool branching_postponement = false, bool fair_loop_approx = false, diff --git a/src/twaalgos/minimize.cc b/src/twaalgos/minimize.cc index 9eeea160b..7f04a4a29 100644 --- a/src/twaalgos/minimize.cc +++ b/src/twaalgos/minimize.cc @@ -33,7 +33,6 @@ #include #include #include "minimize.hh" -#include "ltlast/allnodes.hh" #include "misc/hash.hh" #include "misc/bddlt.hh" #include "twaalgos/product.hh" @@ -593,7 +592,7 @@ namespace spot twa_graph_ptr minimize_obligation(const const_twa_graph_ptr& aut_f, - const ltl::formula* f, + ltl::formula f, const_twa_graph_ptr aut_neg_f, bool reject_bigger) { @@ -614,7 +613,7 @@ namespace spot // if f is a syntactic obligation formula, the WDBA minimization // must be correct. - if (f && f->is_syntactic_obligation()) + if (f && f.is_syntactic_obligation()) return min_aut_f; // If aut_f is a guarantee automaton, the WDBA minimization must be @@ -629,10 +628,7 @@ namespace spot { // If we know the formula, simply build the automaton for // its negation. - const ltl::formula* neg_f = - ltl::unop::instance(ltl::unop::Not, f->clone()); - aut_neg_f = ltl_to_tgba_fm(neg_f, aut_f->get_dict()); - neg_f->destroy(); + aut_neg_f = ltl_to_tgba_fm(ltl::formula::Not(f), aut_f->get_dict()); // Remove useless SCCs. aut_neg_f = scc_filter(aut_neg_f, true); } diff --git a/src/twaalgos/minimize.hh b/src/twaalgos/minimize.hh index 8eaf6ee86..b25d6d791 100644 --- a/src/twaalgos/minimize.hh +++ b/src/twaalgos/minimize.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de +// 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. @@ -151,7 +151,7 @@ namespace spot /// that the minimized WDBA is correct. SPOT_API twa_graph_ptr minimize_obligation(const const_twa_graph_ptr& aut_f, - const ltl::formula* f = 0, + ltl::formula f = nullptr, const_twa_graph_ptr aut_neg_f = nullptr, bool reject_bigger = false); /// @} diff --git a/src/twaalgos/neverclaim.cc b/src/twaalgos/neverclaim.cc index 9580ebb0d..a38d360f8 100644 --- a/src/twaalgos/neverclaim.cc +++ b/src/twaalgos/neverclaim.cc @@ -151,7 +151,7 @@ namespace spot os_ << " :: atomic { ("; else os_ << " :: ("; - const ltl::formula* f = bdd_to_formula(t.cond, aut_->get_dict()); + ltl::formula f = bdd_to_formula(t.cond, aut_->get_dict()); // This is actually a Boolean formula, but the LTL printer // is all we have. print_spin_ltl(os_, f, true); @@ -166,7 +166,6 @@ namespace spot os_ << ") -> goto "; print_state(t.dst); } - f->destroy(); os_ << '\n'; } if (!did_output) diff --git a/src/twaalgos/postproc.cc b/src/twaalgos/postproc.cc index 6d173496a..7e20e3939 100644 --- a/src/twaalgos/postproc.cc +++ b/src/twaalgos/postproc.cc @@ -144,7 +144,7 @@ namespace spot #define SBACC_ (pref_ & SBAcc) twa_graph_ptr - postprocessor::run(twa_graph_ptr a, const ltl::formula* f) + postprocessor::run(twa_graph_ptr a, ltl::formula f) { if (type_ != Generic && !a->acc().is_generalized_buchi()) a = to_generalized_buchi(a); @@ -300,7 +300,7 @@ namespace spot twa_graph_ptr tmpd = nullptr; if (PREF_ == Deterministic && f - && f->is_syntactic_recurrence() + && f.is_syntactic_recurrence() && sim->num_sets() > 1) tmpd = degeneralize_tba(sim); diff --git a/src/twaalgos/postproc.hh b/src/twaalgos/postproc.hh index 8c4d3bfc7..6b41464a0 100644 --- a/src/twaalgos/postproc.hh +++ b/src/twaalgos/postproc.hh @@ -99,8 +99,7 @@ namespace spot /// /// The returned automaton might be a new automaton, /// or an in-place modification of the \a input automaton. - twa_graph_ptr run(twa_graph_ptr input, - const ltl::formula* f); + twa_graph_ptr run(twa_graph_ptr input, ltl::formula f); protected: twa_graph_ptr do_simul(const twa_graph_ptr& input, int opt); diff --git a/src/twaalgos/powerset.cc b/src/twaalgos/powerset.cc index 6a5a4ecb6..bbd1a691a 100644 --- a/src/twaalgos/powerset.cc +++ b/src/twaalgos/powerset.cc @@ -35,7 +35,6 @@ #include "twaalgos/sccfilter.hh" #include "twaalgos/ltl2tgba_fm.hh" #include "twaalgos/dtgbacomp.hh" -#include "ltlast/unop.hh" #include "misc/bitvect.hh" #include "misc/bddlt.hh" @@ -405,13 +404,13 @@ namespace spot tba_determinize_check(const twa_graph_ptr& aut, unsigned threshold_states, unsigned threshold_cycles, - const ltl::formula* f, + ltl::formula f, const_twa_graph_ptr neg_aut) { - if (f == 0 && neg_aut == 0) - return 0; + if (f == nullptr && neg_aut == nullptr) + return nullptr; if (aut->num_sets() > 1) - return 0; + return nullptr; auto det = tba_determinize(aut, threshold_states, threshold_cycles); @@ -420,11 +419,7 @@ namespace spot if (neg_aut == nullptr) { - const ltl::formula* neg_f = - ltl::unop::instance(ltl::unop::Not, f->clone()); - neg_aut = ltl_to_tgba_fm(neg_f, aut->get_dict()); - neg_f->destroy(); - + neg_aut = ltl_to_tgba_fm(ltl::formula::Not(f), aut->get_dict()); // Remove useless SCCs. neg_aut = scc_filter(neg_aut, true); } diff --git a/src/twaalgos/powerset.hh b/src/twaalgos/powerset.hh index e00fccf16..30b063c1a 100644 --- a/src/twaalgos/powerset.hh +++ b/src/twaalgos/powerset.hh @@ -133,7 +133,7 @@ namespace spot tba_determinize_check(const twa_graph_ptr& aut, unsigned threshold_states = 0, unsigned threshold_cycles = 0, - const ltl::formula* f = 0, - const_twa_graph_ptr neg_aut = 0); + ltl::formula f = nullptr, + const_twa_graph_ptr neg_aut = nullptr); } diff --git a/src/twaalgos/randomgraph.cc b/src/twaalgos/randomgraph.cc index 47eb2e801..cd8f2e4b8 100644 --- a/src/twaalgos/randomgraph.cc +++ b/src/twaalgos/randomgraph.cc @@ -24,7 +24,6 @@ #include "twa/twagraph.hh" #include "misc/random.hh" #include "misc/bddlt.hh" -#include "ltlast/atomic_prop.hh" #include #include #include diff --git a/src/twaalgos/remprop.cc b/src/twaalgos/remprop.cc index 59d5160dc..779af1eb5 100644 --- a/src/twaalgos/remprop.cc +++ b/src/twaalgos/remprop.cc @@ -18,7 +18,6 @@ // along with this program. If not, see . #include "remprop.hh" -#include "ltlenv/defaultenv.hh" #include "twaalgos/mask.hh" #include "misc/casts.hh" #include @@ -26,16 +25,6 @@ namespace spot { - remove_ap::~remove_ap() - { - for (auto& ap: props_exist) - ap->destroy(); - for (auto& ap: props_pos) - ap->destroy(); - for (auto& ap: props_neg) - ap->destroy(); - } - namespace { static @@ -56,7 +45,6 @@ namespace spot void remove_ap::add_ap(const char* arg) { - auto& env = spot::ltl::default_environment::instance(); auto start = arg; while (*start) { @@ -66,7 +54,7 @@ namespace spot break; if (*start == ',' || *start == '=') unexpected_char(arg, start); - const spot::ltl::atomic_prop* the_ap = nullptr; + ltl::formula the_ap = nullptr; if (*start == '"') { @@ -84,8 +72,7 @@ namespace spot throw std::invalid_argument(s); } std::string ap(start, end - start); - auto* t = env.require(ap); - the_ap = down_cast(t); + the_ap = ltl::formula::ap(ap); do ++end; while (*end == ' ' || *end == '\t'); @@ -100,8 +87,7 @@ namespace spot while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t')) --rend; std::string ap(start, rend - start); - auto* t = env.require(ap); - the_ap = down_cast(t); + the_ap = ltl::formula::ap(ap); start = end; } if (*start) diff --git a/src/twaalgos/remprop.hh b/src/twaalgos/remprop.hh index d450801bf..85d187bbb 100644 --- a/src/twaalgos/remprop.hh +++ b/src/twaalgos/remprop.hh @@ -20,18 +20,17 @@ #pragma once #include -#include "ltlast/atomic_prop.hh" +#include "ltlast/formula.hh" #include "twa/twagraph.hh" namespace spot { class SPOT_API remove_ap { - std::set props_exist; - std::set props_pos; - std::set props_neg; + std::set props_exist; + std::set props_pos; + std::set props_neg; public: - ~remove_ap(); void add_ap(const char* ap_csv); bool empty() const diff --git a/src/twaalgos/stats.cc b/src/twaalgos/stats.cc index f516f3937..a74d2e360 100644 --- a/src/twaalgos/stats.cc +++ b/src/twaalgos/stats.cc @@ -162,8 +162,7 @@ namespace spot std::ostream& stat_printer::print(const const_twa_graph_ptr& aut, - const ltl::formula* f, - double run_time) + ltl::formula f, double run_time) { form_ = f; run_time_ = run_time; diff --git a/src/twaalgos/stats.hh b/src/twaalgos/stats.hh index ac842e0d7..e2a24df87 100644 --- a/src/twaalgos/stats.hh +++ b/src/twaalgos/stats.hh @@ -55,11 +55,11 @@ namespace spot SPOT_API tgba_sub_statistics sub_stats_reachable(const const_twa_ptr& g); - class SPOT_API printable_formula: public printable_value + class SPOT_API printable_formula: public printable_value { public: printable_formula& - operator=(const ltl::formula* new_val) + operator=(ltl::formula new_val) { val_ = new_val; return *this; @@ -84,7 +84,7 @@ namespace spot /// The \a f argument is not needed if the Formula does not need /// to be output, and so is \a run_time). std::ostream& - print(const const_twa_graph_ptr& aut, const ltl::formula* f = 0, + print(const const_twa_graph_ptr& aut, ltl::formula f = nullptr, double run_time = -1.); private: diff --git a/src/twaalgos/stutter.cc b/src/twaalgos/stutter.cc index 10a855543..7bc01b600 100644 --- a/src/twaalgos/stutter.cc +++ b/src/twaalgos/stutter.cc @@ -24,8 +24,6 @@ #include "misc/hashfunc.hh" #include "ltlvisit/apcollect.hh" #include "translate.hh" -#include "ltlast/unop.hh" -#include "ltlast/binop.hh" #include "ltlvisit/remove_x.hh" #include "twaalgos/product.hh" #include "twaalgos/ltl2tgba_fm.hh" @@ -542,9 +540,9 @@ namespace spot } bool - is_stutter_invariant(const ltl::formula* f) + is_stutter_invariant(ltl::formula f) { - if (f->is_ltl_formula() && f->is_syntactic_stutter_invariant()) + if (f.is_ltl_formula() && f.is_syntactic_stutter_invariant()) return true; int algo = default_stutter_check_algorithm(); @@ -552,35 +550,30 @@ namespace spot if (algo == 0 || algo == 9) // Etessami's check via syntactic transformation. { - if (!f->is_ltl_formula()) + if (!f.is_ltl_formula()) throw std::runtime_error("Cannot use the syntactic " "stutter-invariance check " "for non-LTL formulas"); - const ltl::formula* g = remove_x(f); + ltl::formula g = remove_x(f); bool res; if (algo == 0) // Equivalence check { ltl::ltl_simplifier ls; res = ls.are_equivalent(f, g); - g->destroy(); } else { - const ltl::formula* h = ltl::binop::instance(ltl::binop::Xor, - f->clone(), g); + ltl::formula h = ltl::formula::Xor(f, g); res = ltl_to_tgba_fm(h, make_bdd_dict())->is_empty(); - h->destroy(); } return res; } // Prepare for an automata-based check. - const ltl::formula* nf = ltl::unop::instance(ltl::unop::Not, f->clone()); translator trans; auto aut_f = trans.run(f); - auto aut_nf = trans.run(nf); + auto aut_nf = trans.run(ltl::formula::Not(f)); bdd aps = atomic_prop_collect_as_bdd(f, aut_f); - nf->destroy(); return is_stutter_invariant(std::move(aut_f), std::move(aut_nf), aps, algo); } @@ -625,7 +618,7 @@ namespace spot } bool - check_stutter_invariance(const twa_graph_ptr& aut, const ltl::formula* f) + check_stutter_invariance(const twa_graph_ptr& aut, ltl::formula f) { bool is_stut = aut->is_stutter_invariant(); if (is_stut) @@ -634,9 +627,7 @@ namespace spot twa_graph_ptr neg = nullptr; if (f) { - auto* nf = ltl::unop::instance(ltl::unop::Not, f->clone()); - neg = translator(aut->get_dict()).run(nf); - nf->destroy(); + neg = translator(aut->get_dict()).run(ltl::formula::Not(f)); } else { diff --git a/src/twaalgos/stutter.hh b/src/twaalgos/stutter.hh index 5be5d0164..b096e75eb 100644 --- a/src/twaalgos/stutter.hh +++ b/src/twaalgos/stutter.hh @@ -51,7 +51,7 @@ namespace spot /// \ingroup ltl_misc /// \brief Check if a formula has the stutter invariance property. SPOT_API bool - is_stutter_invariant(const ltl::formula* f); + is_stutter_invariant(ltl::formula f); SPOT_API bool is_stutter_invariant(twa_graph_ptr&& aut_f, @@ -65,5 +65,5 @@ namespace spot /// of the automaton is updated and also returned. SPOT_API bool check_stutter_invariance(const twa_graph_ptr& aut, - const ltl::formula* f = nullptr); + ltl::formula f = nullptr); } diff --git a/src/twaalgos/translate.cc b/src/twaalgos/translate.cc index db915f9bf..ca98db778 100644 --- a/src/twaalgos/translate.cc +++ b/src/twaalgos/translate.cc @@ -61,7 +61,7 @@ namespace spot simpl_owned_ = simpl_ = new ltl::ltl_simplifier(options, dict); } - twa_graph_ptr translator::run(const ltl::formula** f) + twa_graph_ptr translator::run(ltl::formula* f) { bool unambiguous = (pref_ & postprocessor::Unambiguous); if (unambiguous && type_ == postprocessor::Monitor) @@ -72,8 +72,7 @@ namespace spot set_pref(pref_ | postprocessor::Deterministic); } - const ltl::formula* r = simpl_->simplify(*f); - (*f)->destroy(); + ltl::formula r = simpl_->simplify(*f); *f = r; // This helps ltl_to_tgba_fm() to order BDD variables in a more @@ -102,12 +101,9 @@ namespace spot return aut; } - twa_graph_ptr translator::run(const ltl::formula* f) + twa_graph_ptr translator::run(ltl::formula f) { - f->clone(); - auto aut = run(&f); - f->destroy(); - return aut; + return run(&f); } } diff --git a/src/twaalgos/translate.hh b/src/twaalgos/translate.hh index e7673728d..7bf93961e 100644 --- a/src/twaalgos/translate.hh +++ b/src/twaalgos/translate.hh @@ -100,16 +100,14 @@ namespace spot /// \brief Convert \a f into an automaton. /// - /// The formula \a f is simplified internally, but it is not - /// not destroyed (this is the responsibility of the caller). - twa_graph_ptr run(const ltl::formula* f); + /// The formula \a f is simplified internally. + twa_graph_ptr run(ltl::formula f); /// \brief Convert \a f into an automaton, and update f. /// - /// The formula *f is destroyed, and replaced - /// by the simplified version, which should be destroyed by - /// the caller. - twa_graph_ptr run(const ltl::formula** f); + /// The formula *f is replaced + /// by the simplified version. + twa_graph_ptr run(ltl::formula* f); protected: void setup_opt(const option_map* opt); diff --git a/wrap/python/Makefile.am b/wrap/python/Makefile.am index 68a84a48a..1869561b4 100644 --- a/wrap/python/Makefile.am +++ b/wrap/python/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2010, 2011, 2013, 2014 Laboratoire de Recherche et Development de +## Copyright (C) 2010, 2011, 2013, 2014, 2015 Laboratoire de Recherche et Development 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 @@ -35,7 +35,7 @@ MAINTAINERCLEANFILES = \ ## spot -_spot_impl_la_SOURCES = $(srcdir)/spot_impl_wrap.cxx $(srcdir)/spot_impl_wrap.h +_spot_impl_la_SOURCES = $(srcdir)/spot_impl_wrap.cxx _spot_impl_la_LDFLAGS = -avoid-version -module _spot_impl_la_LIBADD = $(top_builddir)/src/libspot.la diff --git a/wrap/python/ajax/spotcgi.in b/wrap/python/ajax/spotcgi.in index 8c6c564c7..3db1e98ef 100755 --- a/wrap/python/ajax/spotcgi.in +++ b/wrap/python/ajax/spotcgi.in @@ -478,12 +478,9 @@ for r in form.getlist('r'): simpopt.containment_checks = True simpopt.containment_checks_stronger = True if dored: - simp = spot.ltl_simplifier(simpopt) - f2 = simp.simplify(f) - f.destroy() - f = f2 - # This also clears the as_bdd() cache. - simp = None + # Not keeping the ltl simplifier aive will also clear the as_bdd() + # cache. + f = spot.ltl_simplifier(simpopt).simplify(f) # Formula manipulation only. if output_type == 'f': diff --git a/wrap/python/spot.py b/wrap/python/spot.py index f28ca45ab..c3b8498c7 100644 --- a/wrap/python/spot.py +++ b/wrap/python/spot.py @@ -433,9 +433,8 @@ def randltl(ap, n = -1, **kwargs): if isinstance(ap, list): aprops = atomic_prop_set() - e = default_environment.instance() for elt in ap: - aprops.insert(is_atomic_prop(e.require(elt))) + aprops.insert(formula.ap(elt)) ap = aprops ltl_priorities = kwargs.get("ltl_priorities", None) sere_priorities = kwargs.get("sere_priorities", None) diff --git a/wrap/python/spot_impl.i b/wrap/python/spot_impl.i index d6dcfb93f..1ef2cd651 100644 --- a/wrap/python/spot_impl.i +++ b/wrap/python/spot_impl.i @@ -26,13 +26,15 @@ #include %} -%module(directors="1") spot_impl +%module(director="1") spot_impl %include "std_shared_ptr.i" +%include "std_vector.i" %include "std_string.i" %include "std_list.i" %include "std_set.i" %include "std_map.i" +%include "stdint.i" %include "exception.i" %include "typemaps.i" @@ -64,10 +66,6 @@ %shared_ptr(spot::emptiness_check_instantiator) %shared_ptr(spot::tgbasl) -namespace std { - %template(liststr) list; -}; - %import "buddy.i" %{ @@ -83,12 +81,6 @@ namespace std { #include "misc/random.hh" #include "ltlast/formula.hh" -#include "ltlast/atomic_prop.hh" -#include "ltlast/binop.hh" -#include "ltlast/constant.hh" -#include "ltlast/multop.hh" -#include "ltlast/unop.hh" -#include "ltlast/visitor.hh" #include "ltlenv/environment.hh" #include "ltlenv/defaultenv.hh" @@ -99,7 +91,6 @@ namespace std { #include "ltlvisit/apcollect.hh" #include "ltlvisit/dot.hh" -#include "ltlvisit/dump.hh" #include "ltlvisit/nenoform.hh" #include "ltlvisit/print.hh" #include "ltlvisit/simplify.hh" @@ -158,6 +149,8 @@ using namespace spot::ltl; using namespace spot; %} + + // For spot::emptiness_check_instantiator::construct and any other // function that return errors via a "char **err" argument. %typemap(in, numinputs=0) char** OUTPUT (char* temp) @@ -216,36 +209,17 @@ using namespace spot; %include "misc/optionmap.hh" %include "misc/random.hh" +%implicitconv std::vector; + %include "ltlast/formula.hh" -%include "ltlast/atomic_prop.hh" -%include "ltlast/binop.hh" -%include "ltlast/constant.hh" -%include "ltlast/multop.hh" -%include "ltlast/unop.hh" -%include "ltlast/visitor.hh" namespace std { - %template(atomic_prop_set) set; - - %template(mapff) map; + %template(liststr) list; + %template(vectorformula) vector; + %template(atomic_prop_set) set; + %template(relabeling_map) map; } -%{ - namespace swig { - template <> struct traits { - typedef pointer_category category; - static const char* type_name() { return "spot::ltl::atomic_prop"; } - }; - template <> struct traits { - typedef pointer_category category; - static const char* type_name() { return "spot::ltl::formula"; } - }; - } -%} - - %include "ltlenv/environment.hh" %include "ltlenv/defaultenv.hh" @@ -263,7 +237,6 @@ namespace std { %include "ltlvisit/apcollect.hh" %include "ltlvisit/dot.hh" -%include "ltlvisit/dump.hh" %include "ltlvisit/nenoform.hh" %include "ltlvisit/print.hh" %include "ltlvisit/simplify.hh" @@ -323,33 +296,16 @@ namespace std { #undef ltl %extend spot::ltl::formula { - - // When comparing formula, make sure Python compare our - // pointers, not the pointers to its wrappers. - // __cmp__ is for Python 2.0 - int __cmp__(const spot::ltl::formula* b) { return self - b; } - // These are for Python 2.1+ or 3.x. They more closely match - // the logic in Spot. - bool __lt__(const spot::ltl::formula* b) - { spot::ltl::formula_ptr_less_than lt; return lt(self, b); } - bool __le__(const spot::ltl::formula* b) - { spot::ltl::formula_ptr_less_than lt; return !lt(b, self); } - bool __eq__(const spot::ltl::formula* b) { return self == b; } - bool __ne__(const spot::ltl::formula* b) { return self != b; } - bool __gt__(const spot::ltl::formula* b) - { spot::ltl::formula_ptr_less_than lt; return lt(b, self); } - bool __ge__(const spot::ltl::formula* b) - { spot::ltl::formula_ptr_less_than lt; return !lt(self, b); } + int __cmp__(spot::ltl::formula b) { return self->id() - b.id(); } + size_t __hash__() { return self->id(); } - size_t __hash__() { return self->hash(); } - - std::string __repr__() { return spot::ltl::str_psl(self); } + std::string __repr__() { return spot::ltl::str_psl(*self); } std::string _repr_latex_() { - return std::string("$") + spot::ltl::str_sclatex_psl(self) + '$'; + return std::string("$") + spot::ltl::str_sclatex_psl(*self) + '$'; } - std::string __str__() { return spot::ltl::str_psl(self); } + std::string __str__() { return spot::ltl::str_psl(*self); } } %extend spot::acc_cond::acc_code { @@ -387,6 +343,11 @@ namespace std { } %inline %{ +bool fnode_instances_check() +{ + return spot::ltl::fnode::instances_check(); +} + spot::ltl::parse_error_list empty_parse_error_list() { diff --git a/wrap/python/tests/automata-io.ipynb b/wrap/python/tests/automata-io.ipynb index 2bbdaf162..99ede1e25 100644 --- a/wrap/python/tests/automata-io.ipynb +++ b/wrap/python/tests/automata-io.ipynb @@ -70,7 +70,7 @@ "never {\n", "T0_init:\n", " if\n", - " :: ((b)) -> goto accept_all\n", + " :: (b) -> goto accept_all\n", " :: ((a) && (!(b))) -> goto T0_init\n", " fi;\n", "accept_all:\n", @@ -805,4 +805,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/wrap/python/tests/formulas.ipynb b/wrap/python/tests/formulas.ipynb index df761ef22..52882384d 100644 --- a/wrap/python/tests/formulas.ipynb +++ b/wrap/python/tests/formulas.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:08d5f61ba93d2879f4f77ca0c4434c1a60ffcb195505e6065dd5a1c79e07c26a" + "signature": "sha256:049261ec6b13505007796a9e9d1d8279783233b9b1eb8f9b8e5727070a38db7a" }, "nbformat": 3, "nbformat_minor": 0, @@ -405,7 +405,7 @@ "Concat\n", "\n", "\n", - "0->1\n", + "0->1\n", "\n", "\n", "L\n", @@ -416,7 +416,7 @@ "G\n", "\n", "\n", - "0->7\n", + "0->7\n", "\n", "\n", "R\n", @@ -427,7 +427,7 @@ "a\n", "\n", "\n", - "1->2\n", + "1->2\n", "\n", "\n", "1\n", @@ -435,7 +435,7 @@ "\n", "3\n", "\n", - "[*]\n", + "Star\n", "\n", "\n", "1->3\n", @@ -446,7 +446,7 @@ "\n", "5\n", "\n", - "[+]\n", + "Star\n", "\n", "\n", "1->5\n", @@ -460,7 +460,7 @@ "b\n", "\n", "\n", - "3->4\n", + "3->4\n", "\n", "\n", "\n", @@ -470,7 +470,7 @@ "c\n", "\n", "\n", - "5->6\n", + "5->6\n", "\n", "\n", "\n", @@ -485,7 +485,7 @@ "\n", "\n", "\n", - "8->4\n", + "8->4\n", "\n", "\n", "\n", @@ -493,7 +493,7 @@ "" ], "text": [ - "" + "" ] } ], diff --git a/wrap/python/tests/ltl2tgba.py b/wrap/python/tests/ltl2tgba.py index 0c4318020..2661667d2 100755 --- a/wrap/python/tests/ltl2tgba.py +++ b/wrap/python/tests/ltl2tgba.py @@ -103,7 +103,6 @@ if f: a = spot.ensure_digraph(a) a = spot.minimize_obligation(a, f) - f.destroy() del f degeneralized = None @@ -128,7 +127,5 @@ if f: else: exit_code = 1 -assert spot.atomic_prop.instance_count() == 0 -assert spot.unop.instance_count() == 0 -assert spot.binop.instance_count() == 0 -assert spot.multop.instance_count() == 0 +del dict +assert spot.fnode_instances_check() diff --git a/wrap/python/tests/ltlparse.py b/wrap/python/tests/ltlparse.py index 31ce218a1..f30f7ea27 100755 --- a/wrap/python/tests/ltlparse.py +++ b/wrap/python/tests/ltlparse.py @@ -33,16 +33,13 @@ for str1 in l: if spot.format_parse_errors(spot.get_cout(), str1, p): sys.exit(1) str2 = str(f) - f.destroy() + del f sys.stdout.write(str2 + "\n") # Try to reparse the stringified formula f = spot.parse_infix_psl(str2, p, e) if spot.format_parse_errors(spot.get_cout(), str2, p): sys.exit(1) sys.stdout.write(str(f) + "\n") - f.destroy() + del f -assert spot.atomic_prop.instance_count() == 0 -assert spot.binop.instance_count() == 0 -assert spot.unop.instance_count() == 0 -assert spot.multop.instance_count() == 0 +assert spot.fnode_instances_check() diff --git a/wrap/python/tests/ltlsimple.py b/wrap/python/tests/ltlsimple.py index 1b4f63761..7a5c6c38d 100755 --- a/wrap/python/tests/ltlsimple.py +++ b/wrap/python/tests/ltlsimple.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012 Laboratoire de Recherche et Développement +# Copyright (C) 2009, 2010, 2012, 2015 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systemes Répartis Coopératifs (SRC), Université Pierre @@ -23,19 +23,19 @@ import spot import sys -e = spot.default_environment.instance() - #---------------------------------------------------------------------- -a = e.require('a') -b = e.require('b') -c = e.require('c') -c2 = e.require('c') +a = spot.formula.ap('a') +b = spot.formula.ap('b') +c = spot.formula.ap('c') +c2 = spot.formula.ap('c') assert c == c2 -assert spot.atomic_prop.instance_count() == 3 -op = spot.multop.instance(spot.multop.And, a.clone(), b.clone()) -op2 = spot.multop.instance(spot.multop.And, op, c) +op = spot.formula.And([a, b]) +op2 = spot.formula.And([op, c]) +op3 = spot.formula.And([a, c, b]) + +assert op2 == op3 # The symbol for a subformula which hasn't been cloned is better # suppressed, so we don't attempt to reuse it elsewhere. @@ -43,87 +43,31 @@ del op, c sys.stdout.write('op2 = %s\n' % str(op2)) -op3 = spot.multop.instance(spot.multop.And, b, - spot.multop.instance(spot.multop.And, c2, a)) del a, b, c2 sys.stdout.write('op3 = %s\n' % str(op3)) assert op2 == op3 -op4 = spot.multop.instance(spot.multop.Or, op2, op3) +op4 = spot.formula.Or([op2, op3]) sys.stdout.write('op4 = %s\n' % str(op4)) assert op4 == op2 -del op2, op3 - -assert spot.atomic_prop.instance_count() == 3 -assert spot.multop.instance_count() == 1 - -op4.destroy() -del op4 - -assert spot.atomic_prop.instance_count() == 0 -assert spot.multop.instance_count() == 0 +del op2, op3, op4 #---------------------------------------------------------------------- -a = e.require('a') -b = e.require('b') -c = e.require('c') -T = spot.constant.true_instance() -F = spot.constant.false_instance() +a = spot.formula.ap('a') +b = spot.formula.ap('b') +c = spot.formula.ap('c') +T = spot.formula.tt() +F = spot.formula.ff() -f1 = spot.binop.instance(spot.binop.Equiv, c.clone(), a.clone()) -f2 = spot.binop.instance(spot.binop.Implies, a.clone(), b.clone()) -f3 = spot.binop.instance(spot.binop.Xor, b.clone(), c.clone()) -f4 = spot.unop.instance(spot.unop.Not, f3); del f3 -f5 = spot.binop.instance(spot.binop.Xor, F, c.clone()) +f1 = spot.formula.Equiv(c, a) +f2 = spot.formula.Implies(a, b) +f3 = spot.formula.Xor(b, c) +f4 = spot.formula.Not(f3); del f3 +f5 = spot.formula.Xor(F, c) -assert spot.atomic_prop.instance_count() == 3 -assert spot.binop.instance_count() == 3 -assert spot.unop.instance_count() == 1 -assert spot.multop.instance_count() == 0 +del a, b, c, T, F, f1, f2, f4, f5 -a.destroy() -del a -b.destroy() -del b -c.destroy() -del c - -assert spot.atomic_prop.instance_count() == 3 -assert spot.binop.instance_count() == 3 -assert spot.unop.instance_count() == 1 -assert spot.multop.instance_count() == 0 - -f1.destroy() -del f1 - -assert spot.atomic_prop.instance_count() == 3 -assert spot.binop.instance_count() == 2 -assert spot.unop.instance_count() == 1 -assert spot.multop.instance_count() == 0 - -f5.destroy() -del f5 - -assert spot.atomic_prop.instance_count() == 3 -assert spot.binop.instance_count() == 2 -assert spot.unop.instance_count() == 1 -assert spot.multop.instance_count() == 0 - -f4.destroy() -del f4 - -assert spot.atomic_prop.instance_count() == 2 -assert spot.binop.instance_count() == 1 -assert spot.unop.instance_count() == 0 -assert spot.multop.instance_count() == 0 - -f2.destroy() -del f2 - -assert spot.atomic_prop.instance_count() == 0 -assert spot.binop.instance_count() == 0 -assert spot.unop.instance_count() == 0 -assert spot.multop.instance_count() == 0 +assert spot.fnode_instances_check() diff --git a/wrap/python/tests/randltl.ipynb b/wrap/python/tests/randltl.ipynb index 4c36a574b..cdd2a3acd 100644 --- a/wrap/python/tests/randltl.ipynb +++ b/wrap/python/tests/randltl.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:b16de2c9883c0fecb450f9d7f4885937abcfc1d1fef584d47109fcee13a68c9a" + "signature": "sha256:54b0c9570ca91322c466f914d622a3723cd3e450612a055a188927bf50dfdaf8" }, "nbformat": 3, "nbformat_minor": 0, @@ -164,11 +164,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "G(p1 U Gp0)\n" + "G(p2 U Gp0)\n" ] } ], - "prompt_number": 12 + "prompt_number": 5 }, { "cell_type": "heading", @@ -199,11 +199,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "{{p0 && p2}[*]}<>-> (Fp2 & Fp0)\n" + "{{p0 && p1}[*]}<>-> (Fp1 & Fp0)\n" ] } ], - "prompt_number": 26 + "prompt_number": 6 }, { "cell_type": "heading", @@ -242,7 +242,7 @@ ] } ], - "prompt_number": 27 + "prompt_number": 7 }, { "cell_type": "heading", @@ -273,11 +273,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "G(((p0 U !Xp2) M Gp2) U Gp0)\n" + "G(((p0 U !Xp1) M Gp1) U Gp0)\n" ] } ], - "prompt_number": 29 + "prompt_number": 8 }, { "cell_type": "markdown", @@ -301,12 +301,12 @@ "output_type": "stream", "stream": "stdout", "text": [ - "X!(Gp2 M p1) R (!p1 M Xp2)\n", - "F(G(F(Gp0 R (1 U Fp1)) M (p1 -> Gp0)) M F((p0 | Fp0) W Gp1))\n" + "X!(Gp1 M p2) R (!p2 M Xp1)\n", + "F(G(F(Gp0 R (1 U Fp2)) M (p2 -> Gp0)) M F((p0 | Fp0) W Gp2))\n" ] } ], - "prompt_number": 30 + "prompt_number": 9 }, { "cell_type": "heading", @@ -332,14 +332,14 @@ "stream": "stdout", "text": [ "0\n", - "!p1 & (p1 <-> p2)\n", - "p1\n", + "!p2 & (p1 <-> p2)\n", + "p2\n", "p0 & ((p1 & p2) <-> !(!p0 & p1 & p2))\n", "1\n" ] } ], - "prompt_number": 31 + "prompt_number": 10 }, { "cell_type": "markdown", @@ -461,7 +461,7 @@ ] } ], - "prompt_number": 32 + "prompt_number": 11 }, { "cell_type": "heading", @@ -498,12 +498,12 @@ "output_type": "stream", "stream": "stdout", "text": [ - "G!(!p2 & (Xp1 | F(p0 R Xp1)))\n", - "G(p2 | (X!p1 & G(!p0 U X!p1)))\n" + "G!(!p1 & (Xp2 | F(p0 R Xp2)))\n", + "G(p1 | (X!p2 & G(!p0 U X!p2)))\n" ] } ], - "prompt_number": 36 + "prompt_number": 12 }, { "cell_type": "heading", @@ -536,17 +536,17 @@ "stream": "stdout", "text": [ "0\n", - "0 R p1\n", - "F(p0 R !p2)\n", - "G(p0 | Fp1) W (FGp1 R !p1)\n", - "(p1 R G!p2) | G(p1 U !p0)\n", - "(p1 W p0) U p1\n", - "F!G(!Gp2 W p2)\n", - "G!p2 & (!((p1 & Fp2) M p2) U p2)\n" + "0 R p2\n", + "F(p0 R !p1)\n", + "G(p0 | Fp2) W (FGp2 R !p2)\n", + "(p2 R G!p1) | G(p2 U !p0)\n", + "(p2 W p0) U p2\n", + "F!G(!Gp1 W p1)\n", + "G!p1 & (!((p2 & Fp1) M p1) U p1)\n" ] } ], - "prompt_number": 40 + "prompt_number": 13 }, { "cell_type": "markdown", @@ -571,15 +571,15 @@ "stream": "stdout", "text": [ "0\n", - "!(F!p1 M 1)\n", - "(Fp1 | Gp0) M 1\n", - "F!(!p1 <-> FGp1)\n", - "Gp1 U (p1 U GFp1)\n", - "(!p1 U p1) U ((p0 & (p0 U (!p0 & (!p0 -> Fp1))) & ((p1 U !p0) | (!p1 U !p0))) | (!p0 & (!p0 U (p0 & (!p0 -> Fp1))) & ((!p1 U p0) | (p1 U p0))) | (p1 & (p1 U (!p1 & (!p0 -> Fp1))) & ((!p0 U !p1) | (p0 U !p1))) | (!p1 & (!p1 U (p1 & (!p0 -> Fp1))) & ((!p0 U p1) | (p0 U p1))) | ((!p0 -> Fp1) & (Gp0 | G!p0) & (Gp1 | G!p1)))\n" + "!(F!p0 M 1)\n", + "(Gp1 | Fp0) M 1\n", + "F!(!p0 <-> FGp0)\n", + "Gp0 U (p0 U GFp0)\n", + "(!p0 U p0) U ((p1 & (p1 U (!p1 & (!p1 -> Fp0))) & ((!p0 U !p1) | (p0 U !p1))) | (!p1 & (!p1 U (p1 & (!p1 -> Fp0))) & ((!p0 U p1) | (p0 U p1))) | (p0 & (p0 U (!p0 & (!p1 -> Fp0))) & ((!p1 U !p0) | (p1 U !p0))) | (!p0 & (!p0 U (p0 & (!p1 -> Fp0))) & ((!p1 U p0) | (p1 U p0))) | ((!p1 -> Fp0) & (Gp1 | G!p1) & (Gp0 | G!p0)))\n" ] } ], - "prompt_number": 42 + "prompt_number": 14 }, { "cell_type": "markdown", @@ -615,15 +615,7 @@ ] } ], - "prompt_number": 44 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "prompt_number": 15 } ], "metadata": {}