diff --git a/ChangeLog b/ChangeLog index a5e6015c5..121503ce4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2009-04-08 Damien Lefortier + + Correct LaCIM for ELTL and make it work with LBTT. + + * src/eltlparse/eltlparse.yy: Adjust. + * src/ltlast/automatop.cc, src/ltlast/automatop.hh, + src/ltlvisit/clone.cc, src/ltlvisit/nenoform.cc: Clean the way we + handle the negation of automaton operators. + * src/ltlvisit/tostring.cc, src/ltlvisit/tostring.hh: Add an + optional argument to output a fully parenthesized string. + * src/tgbaalgos/eltl2tgba_lacim.cc: Fix it. + * src/tgbatest/eltl2tgba.cc: Add a new option (-L) to read formulae + from an LBTT-compatible file. + * src/tgbatest/eltl2tgba.test: A new tests. + * src/tgbatest/spotlbtt.test: Add LaCIM for ELTL. + 2009-04-04 Damien Lefortier Extend the ELTL parser to support basic aliases of automaton diff --git a/src/eltlparse/eltlparse.yy b/src/eltlparse/eltlparse.yy index 6b9e524a4..e875e45c7 100644 --- a/src/eltlparse/eltlparse.yy +++ b/src/eltlparse/eltlparse.yy @@ -59,13 +59,20 @@ namespace spot { alias_ptr s; }; - enum type { Xor, Implies, Equiv, Or, And }; struct alias_binary : alias { - type ty; + virtual ~alias_binary() = 0; // Should not be instanciate alias_ptr lhs; alias_ptr rhs; }; + struct alias_binop : alias_binary + { + binop::type ty; + }; + struct alias_multop : alias_binary + { + multop::type ty; + }; struct alias_nfa : alias { nfa::ptr nfa; @@ -100,9 +107,18 @@ namespace spot std::list::const_iterator i = a->s.begin(); while (i != a->s.end()) va->push_back(alias2formula(*i++, v)); - return automatop::instance(a->nfa, va); + return automatop::instance(a->nfa, va, false); } - /// TODO. + if (alias_binop* a = dynamic_cast(ap.get())) + return binop::instance(a->ty, + alias2formula(a->lhs, v), + alias2formula(a->rhs, v)); + if (alias_multop* a = dynamic_cast(ap.get())) + return multop::instance(a->ty, + alias2formula(a->lhs, v), + alias2formula(a->rhs, v)); + + /* Unreachable code. */ assert(0); } @@ -122,9 +138,15 @@ namespace spot res = std::max(arity(*i++), res); return res; } - /// TODO. + if (alias_binary* a = dynamic_cast(ap.get())) + return std::max(arity(a->lhs), arity(a->rhs)); + + /* Unreachable code. */ assert(0); } + + /// Create a new alias from an existing one according to \a v. + /// TODO. } } @@ -268,6 +290,17 @@ nfa: IDENT "=" "(" nfa_def ")" } | IDENT "=" nfa_alias { + /// Recursivity issues of aliases are handled by a parse error. + aliasmap::iterator i = amap.find(*$1); + if (i != amap.end()) + { + std::string s = "`"; + s += *$1; + s += "' is already aliased"; + PARSE_ERROR(@1, s); + delete $1; + YYERROR; + } amap[*$1] = alias_ptr($3); delete $1; } @@ -305,13 +338,17 @@ nfa_alias: IDENT "(" nfa_alias_arg_list ")" } delete $1; } + /// TODO + // | IDENT "(" nfa_alias ")" // Should be a list + // { + // assert(0); + // } | OP_NOT nfa_alias { alias_not* a = new alias_not; a->s = alias_ptr($2); $$ = a; } - // TODO: more complicated aliases like | IDENT "(" nfa_alias ")" nfa_alias_arg_list: nfa_alias_arg { @@ -337,7 +374,7 @@ nfa_alias_arg: nfa_arg a->s = alias_ptr($2); $$ = a; } - // TODO + // TODO: factoring with nfa_alias nfa_arg: ARG { @@ -394,7 +431,7 @@ subformula: ATOMIC_PROP automatop::vec* v = new automatop::vec; v->push_back($1); v->push_back($3); - $$ = automatop::instance(np, v); + $$ = automatop::instance(np, v, false); } delete $2; } @@ -413,7 +450,7 @@ subformula: ATOMIC_PROP nfa::ptr np = nmap[*$1]; CHECK_ARITY(@1, $1, $3->size(), np->arity()); - $$ = automatop::instance(np, $3); + $$ = automatop::instance(np, $3, false); } delete $1; } diff --git a/src/ltlast/automatop.cc b/src/ltlast/automatop.cc index 60193146c..e4d10db44 100644 --- a/src/ltlast/automatop.cc +++ b/src/ltlast/automatop.cc @@ -27,10 +27,11 @@ namespace spot { namespace ltl { - automatop::automatop(const nfa::ptr nfa, vec* v) - : negated_(false), nfa_(nfa), children_(v) + automatop::automatop(const nfa::ptr nfa, vec* v, bool negated) + : nfa_(nfa), children_(v), negated_(negated) { - dump_= nfa->get_name(); + dump_ = negated ? "!" : ""; + dump_ += nfa->get_name(); dump_ += "("; dump_ += (*v)[0]->dump(); for (unsigned n = 1; n < v->size(); ++n) @@ -42,7 +43,7 @@ namespace spot automatop::~automatop() { // Get this instance out of the instance map. - pair p(nfa(), children_); + triplet p(std::make_pair(nfa(), negated_), children_); map::iterator i = instances.find(p); assert (i != instances.end()); instances.erase(i); @@ -65,17 +66,17 @@ namespace spot automatop::map automatop::instances; automatop* - automatop::instance(const nfa::ptr nfa, vec* v) + automatop::instance(const nfa::ptr nfa, vec* v, bool negated) { assert(nfa != 0); - pair p(nfa, v); + triplet p(std::make_pair(nfa, negated), v); map::iterator i = instances.find(p); if (i != instances.end()) { delete v; return static_cast(i->second->ref()); } - automatop* res = new automatop(nfa, v); + automatop* res = new automatop(nfa, v, negated); instances[p] = res; return static_cast(res->ref()); } @@ -104,5 +105,11 @@ namespace spot assert(nfa_ != 0); return nfa_; } + + bool + automatop::is_negated() const + { + return negated_; + } } } diff --git a/src/ltlast/automatop.hh b/src/ltlast/automatop.hh index aea84bbeb..4ba2177a9 100644 --- a/src/ltlast/automatop.hh +++ b/src/ltlast/automatop.hh @@ -49,7 +49,7 @@ namespace spot /// (especially not destroy it) after it has been passed to /// spot::ltl::automatop. static automatop* - instance(const nfa::ptr nfa, vec* v); + instance(const nfa::ptr nfa, vec* v, bool negated); virtual void accept(visitor& v); virtual void accept(const_visitor& v) const; @@ -68,29 +68,33 @@ namespace spot /// Get the NFA of this operator. const nfa::ptr nfa() const; - bool negated_; + bool is_negated() const; + protected: - typedef std::pair pair; + typedef std::pair, vec*> triplet; /// Comparison functor used internally by ltl::automatop. struct paircmp { bool - operator () (const pair& p1, const pair& p2) const + operator () (const triplet& p1, const triplet& p2) const { - if (p1.first != p2.first) - return p1.first < p2.first; + if (p1.first.first != p2.first.first) + return p1.first.first < p2.first.first; + if (p1.first.second != p2.first.second) + return p1.first.second < p2.first.second; return *p1.second < *p2.second; } }; - typedef std::map map; + typedef std::map map; static map instances; - automatop(const nfa::ptr nfa, vec* v); + automatop(const nfa::ptr nfa, vec* v, bool negated); virtual ~automatop(); private: nfa::ptr nfa_; vec* children_; + bool negated_; }; } } diff --git a/src/ltlvisit/clone.cc b/src/ltlvisit/clone.cc index b7cad0514..14415ed4d 100644 --- a/src/ltlvisit/clone.cc +++ b/src/ltlvisit/clone.cc @@ -71,7 +71,7 @@ namespace spot automatop::vec* res = new automatop::vec; for (unsigned i = 0; i < ao->size(); ++i) res->push_back(recurse(ao->nth(i))); - result_ = automatop::instance(ao->nfa(), res); + result_ = automatop::instance(ao->nfa(), res, ao->is_negated()); } void diff --git a/src/ltlvisit/nenoform.cc b/src/ltlvisit/nenoform.cc index 1e4414c16..054ae2863 100644 --- a/src/ltlvisit/nenoform.cc +++ b/src/ltlvisit/nenoform.cc @@ -154,16 +154,13 @@ namespace spot void visit(automatop* ao) { - if (negated_) - { - negated_ = false; - ao->negated_ = true; - } + bool negated = negated_; + negated_ = false; automatop::vec* res = new automatop::vec; unsigned aos = ao->size(); for (unsigned i = 0; i < aos; ++i) res->push_back(recurse(ao->nth(i))); - result_ = automatop::instance(ao->nfa(), res); + result_ = automatop::instance(ao->nfa(), res, negated); } void diff --git a/src/ltlvisit/tostring.cc b/src/ltlvisit/tostring.cc index 4c27a153c..1987ee57b 100644 --- a/src/ltlvisit/tostring.cc +++ b/src/ltlvisit/tostring.cc @@ -59,8 +59,8 @@ namespace spot class to_string_visitor: public const_visitor { public: - to_string_visitor(std::ostream& os) - : os_(os), top_level_(true) + to_string_visitor(std::ostream& os, bool full_parent = false) + : os_(os), top_level_(true), full_parent_(full_parent) { } @@ -147,17 +147,18 @@ namespace spot } top_level_ = false; - if (need_parent) + if (need_parent || full_parent_) os_ << "("; uo->child()->accept(*this); - if (need_parent) + if (need_parent || full_parent_) os_ << ")"; } void visit(const automatop* ao) { - // Warning: this string isn't parsable. + // Warning: this string isn't parsable because the automaton + // operators used may not be defined. bool top_level = top_level_; top_level_ = false; if (!top_level) @@ -206,6 +207,7 @@ namespace spot protected: std::ostream& os_; bool top_level_; + bool full_parent_; }; class to_spin_string_visitor : public to_string_visitor @@ -308,7 +310,8 @@ namespace spot void visit(const automatop* ao) { - // Warning: this string isn't parsable. + // Warning: this string isn't parsable because the automaton + // operators used may not be defined. bool top_level = top_level_; top_level_ = false; if (!top_level) @@ -359,18 +362,18 @@ namespace spot } // anonymous std::ostream& - to_string(const formula* f, std::ostream& os) + to_string(const formula* f, std::ostream& os, bool full_parent) { - to_string_visitor v(os); + to_string_visitor v(os, full_parent); f->accept(v); return os; } std::string - to_string(const formula* f) + to_string(const formula* f, bool full_parent) { std::ostringstream os; - to_string(f, os); + to_string(f, os, full_parent); return os.str(); } diff --git a/src/ltlvisit/tostring.hh b/src/ltlvisit/tostring.hh index 8b03e05c7..857a67489 100644 --- a/src/ltlvisit/tostring.hh +++ b/src/ltlvisit/tostring.hh @@ -32,14 +32,22 @@ namespace spot /// \addtogroup ltl_io /// @{ - /// \brief Output a formula as a (parsable) string. + /// \brief Output a formula as a string which is parsable unless the formula + /// contains automaton operators (used in ELTL formulae). /// \param f The formula to translate. /// \param os The stream where it should be output. - std::ostream& to_string(const formula* f, std::ostream& os); + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + std::ostream& + to_string(const formula* f, std::ostream& os, bool full_parent = false); - /// \brief Convert a formula into a (parsable) string. + /// \brief Output a formula as a string which is parsable unless the formula + /// contains automaton operators (used in ELTL formulae). /// \param f The formula to translate. - std::string to_string(const formula* f); + /// \param full_parent Whether or not the string should by fully + /// parenthesized. + std::string + to_string(const formula* f, bool full_parent = false); /// \brief Output a formula as a (parsable by Spin) string. /// \param f The formula to translate. diff --git a/src/tgbaalgos/eltl2tgba_lacim.cc b/src/tgbaalgos/eltl2tgba_lacim.cc index 910e3c06f..c1b897034 100644 --- a/src/tgbaalgos/eltl2tgba_lacim.cc +++ b/src/tgbaalgos/eltl2tgba_lacim.cc @@ -145,9 +145,7 @@ namespace spot assert(op != -1); unsigned s = node->size(); for (unsigned n = 0; n < s; ++n) - { - res_ = bdd_apply(res_, recurse(node->nth(n), root), op); - } + res_ = bdd_apply(res_, recurse(node->nth(n), root), op); } void @@ -164,11 +162,9 @@ namespace spot bdd_ithvar(it->second.second + 1), bddop_biimp); fact_.constrain_relation(bdd_apply(acc, tmp, bddop_imp)); - if (!node->nfa()->is_loop()) - fact_.declare_acceptance_condition(acc, node); - - bdd init = bdd_ithvar(vp.first); - res_ = node->negated_ ? bdd_not(init) : init; + fact_.declare_acceptance_condition(acc, node); + res_ = node->is_negated() ? + bdd_nithvar(vp.first) : bdd_ithvar(vp.first); } bdd @@ -193,6 +189,7 @@ namespace spot recurse_state(const automatop* n, const nfa::state* s, nmap& m, bdd& acc) { const nfa::ptr nfa = n->nfa(); + bool is_loop = nfa->is_loop(); nmap::iterator it; it = m.find(s); @@ -209,8 +206,7 @@ namespace spot bdd tmp1 = bddfalse; bdd tmp2 = bddfalse; - bdd tmpacc = !bdd_ithvar(v2); - + bdd tmpacc = bddfalse; for (nfa::iterator i = nfa->begin(s); i != nfa->end(s); ++i) { bdd f = (*i)->label == -1 ? bddtrue : recurse(n->nth((*i)->label)); @@ -225,13 +221,22 @@ namespace spot std::pair vp = recurse_state(n, (*i)->dst, m, acc); tmp1 |= (f & bdd_ithvar(vp.first + 1)); tmp2 |= (f & bdd_ithvar(vp.second + 1)); + if (is_loop) + tmpacc |= f; } } - acc &= tmpacc; fact_.constrain_relation(bdd_apply(bdd_ithvar(v1), tmp1, bddop_biimp)); - fact_.constrain_relation(bdd_apply(bdd_ithvar(v2), tmp2, bddop_imp)); - fact_.constrain_relation( - bdd_apply(bdd_ithvar(v2), bdd_ithvar(v1), bddop_imp)); + if (is_loop) + { + fact_.constrain_relation(bdd_apply(bdd_ithvar(v2), tmp2, bddop_invimp)); + acc &= bdd_ithvar(v2) | !tmpacc; + } + else + { + fact_.constrain_relation(bdd_apply(bdd_ithvar(v2), tmp2, bddop_imp)); + acc &= bdd_nithvar(v2) | tmpacc; + } + return m[s]; } }; diff --git a/src/tgbatest/eltl2tgba.cc b/src/tgbatest/eltl2tgba.cc index 17482c133..07e1ebaaf 100644 --- a/src/tgbatest/eltl2tgba.cc +++ b/src/tgbatest/eltl2tgba.cc @@ -24,20 +24,26 @@ #include #include #include +#include "ltlparse/public.hh" #include "eltlparse/public.hh" #include "tgbaalgos/eltl2tgba_lacim.hh" #include "tgbaalgos/dotty.hh" +#include "tgbaalgos/lbtt.hh" #include "tgbaalgos/save.hh" #include "ltlvisit/destroy.hh" +#include "ltlvisit/tostring.hh" void syntax(char* prog) { - std::cerr << "Usage: "<< prog << " [OPTIONS...] formula [file]" << std::endl - << " "<< prog << " -F [OPTIONS...] file [file]" << std::endl + std::cerr << "Usage: " << prog << " [OPTIONS...] formula [file]" << std::endl + << " " << prog << " -F [OPTIONS...] file [file]" << std::endl + << " " << prog << " -L [OPTIONS...] file [file]" << std::endl << std::endl << "Options:" << std::endl << " -F read the formula from the file (extended input format)" + << std::endl + << " -L read the formula from an LBTT-compatible file" << std::endl; } @@ -76,11 +82,28 @@ main(int argc, char** argv) f = spot::eltl::parse_file(argv[2], p, env, false); formula_index = 2; } - else + if (strcmp(argv[1], "-L") == 0) + { + std::string input; + std::ifstream ifs(argv[2]); + std::getline(ifs, input, '\0'); + + spot::ltl::parse_error_list p_; + f = spot::ltl::parse(input, p_, env, false); + input = ltl_defs(); + input += "%"; + input += spot::ltl::to_string(f, true); + spot::ltl::destroy(f); + + // std::cerr << input << std::endl; + f = spot::eltl::parse_string(input, p, env, false); + formula_index = 2; + } + if (formula_index == 0) { std::stringstream oss; oss << ltl_defs() << "%" << argv[1]; - f = spot::eltl::parse_string(oss.str(), p, env, false); + f = spot::eltl::parse_string(oss.str(), p, env, false); formula_index = 1; } @@ -97,12 +120,17 @@ main(int argc, char** argv) spot::bdd_dict* dict = new spot::bdd_dict(); spot::tgba_bdd_concrete* concrete = spot::eltl_to_tgba_lacim(f, dict); - spot::dotty_reachable(std::cout, concrete); - if (argc >= formula_index + 1) + if (strcmp(argv[1], "-L") == 0) + spot::lbtt_reachable(std::cout, concrete); + else { - std::ofstream ofs(argv[formula_index + 1]); - spot::tgba_save_reachable(ofs, concrete); - ofs.close(); + spot::dotty_reachable(std::cout, concrete); + if (formula_index + 1 < argc) + { + std::ofstream ofs(argv[formula_index + 1]); + spot::tgba_save_reachable(ofs, concrete); + ofs.close(); + } } spot::ltl::destroy(f); diff --git a/src/tgbatest/eltl2tgba.test b/src/tgbatest/eltl2tgba.test index 9bd3e8272..65c4e16d1 100755 --- a/src/tgbatest/eltl2tgba.test +++ b/src/tgbatest/eltl2tgba.test @@ -97,12 +97,12 @@ check_false '!XXa' cat >input <input <