From 2da0053c53f6d552eb73031d64e483bf1d9fd35e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 24 Jul 2013 19:16:18 +0200 Subject: [PATCH] dstarparse: Preliminary work on a parser for ltl2dstar. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports reading Rabin and Streett automata, and converting them to nondeterministic Büchi automata (for Rabin) or TGBA (for Streett). * src/dstarparse/Makefile.am, src/dstarparse/dstarparse.yy, src/dstarparse/dstarscan.ll, src/dstarparse/fmterror.cc, src/dstarparse/parsedecl.hh, src/dstarparse/public.hh, src/dstarparse/nra2nba.cc, src/dstarparse/nsa2tgba.cc: New files. * configure.ac, src/Makefile.am, README: Adjust. * src/tgbatest/ltl2tgba.cc: Add options -XD, -XDB. * src/tgbatest/dstar.test: New file. * src/tgbatest/Makefile.am (TESTS): Add it. --- README | 1 + configure.ac | 1 + src/Makefile.am | 7 +- src/dstarparse/.gitignore | 7 + src/dstarparse/Makefile.am | 59 ++++++ src/dstarparse/dstarparse.yy | 352 +++++++++++++++++++++++++++++++++++ src/dstarparse/dstarscan.ll | 149 +++++++++++++++ src/dstarparse/fmterror.cc | 41 ++++ src/dstarparse/nra2nba.cc | 128 +++++++++++++ src/dstarparse/nsa2tgba.cc | 245 ++++++++++++++++++++++++ src/dstarparse/parsedecl.hh | 39 ++++ src/dstarparse/public.hh | 120 ++++++++++++ src/tgbatest/Makefile.am | 1 + src/tgbatest/dstar.test | 131 +++++++++++++ src/tgbatest/ltl2tgba.cc | 164 ++++++++++------ 15 files changed, 1390 insertions(+), 55 deletions(-) create mode 100644 src/dstarparse/.gitignore create mode 100644 src/dstarparse/Makefile.am create mode 100644 src/dstarparse/dstarparse.yy create mode 100644 src/dstarparse/dstarscan.ll create mode 100644 src/dstarparse/fmterror.cc create mode 100644 src/dstarparse/nra2nba.cc create mode 100644 src/dstarparse/nsa2tgba.cc create mode 100644 src/dstarparse/parsedecl.hh create mode 100644 src/dstarparse/public.hh create mode 100755 src/tgbatest/dstar.test diff --git a/README b/README index 0f9f965b4..671c2df9f 100644 --- a/README +++ b/README @@ -157,6 +157,7 @@ Core directories src/ Sources for libspot. bin/ User tools built using the Spot library. man/ Man pages for the above tools. + dstarparse/ Parser for the output of ltl2dstar. eltlparse/ Parser for ELTL formulae. eltltest/ Tests for ELTL nodes in ltlast/ and eltlparse/. kripke/ Kripke Structure interface. diff --git a/configure.ac b/configure.ac index d054f0fa8..59227c9e6 100644 --- a/configure.ac +++ b/configure.ac @@ -147,6 +147,7 @@ AC_CONFIG_FILES([ lib/Makefile src/bin/Makefile src/bin/man/Makefile + src/dstarparse/Makefile src/eltlparse/Makefile src/eltltest/defs src/eltltest/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 4ba2cf2b8..a7aba7799 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,14 +26,15 @@ AUTOMAKE_OPTIONS = subdir-objects # end, after building '.' (since the current directory contains # libspot.la needed by the tests) SUBDIRS = misc priv ltlenv ltlast ltlvisit ltlparse eltlparse tgba \ - tgbaalgos tgbaparse ta taalgos kripke saba sabaalgos \ - neverparse kripkeparse . bin ltltest eltltest tgbatest \ - sabatest sanity kripketest + tgbaalgos tgbaparse ta taalgos kripke saba sabaalgos \ + neverparse kripkeparse dstarparse . bin ltltest eltltest \ + tgbatest sabatest kripketest sanity lib_LTLIBRARIES = libspot.la libspot_la_SOURCES = libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined libspot_la_LIBADD = \ + dstarparse/libdstarparse.la \ eltlparse/libeltlparse.la \ kripke/libkripke.la \ kripkeparse/libkripkeparse.la \ diff --git a/src/dstarparse/.gitignore b/src/dstarparse/.gitignore new file mode 100644 index 000000000..39850e7cd --- /dev/null +++ b/src/dstarparse/.gitignore @@ -0,0 +1,7 @@ +position.hh +dstarparse.cc +dstarparse.output +dstarparse.hh +dstarscan.cc +stack.hh +location.hh diff --git a/src/dstarparse/Makefile.am b/src/dstarparse/Makefile.am new file mode 100644 index 000000000..4c8c18cf0 --- /dev/null +++ b/src/dstarparse/Makefile.am @@ -0,0 +1,59 @@ +## -*- coding: utf-8 -*- +## Copyright (C) 2013 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 . + +AM_CPPFLAGS = -I$(srcdir)/.. -I.. $(BUDDY_CPPFLAGS) -DYY_NO_INPUT +# Disable -Werror because too many versions of flex yield warnings. +AM_CXXFLAGS = $(WARNING_CXXFLAGS:-Werror=) + +dstarparsedir = $(pkgincludedir)/dstarparse + +dstarparse_HEADERS = public.hh + +noinst_LTLIBRARIES = libdstarparse.la + +DSTARPARSE_YY = dstarparse.yy +FROM_DSTARPARSE_YY_MAIN = dstarparse.cc +FROM_DSTARPARSE_YY_OTHERS = \ + stack.hh \ + dstarparse.hh + +FROM_DSTARPARSE_YY = $(FROM_DSTARPARSE_YY_MAIN) $(FROM_DSTARPARSE_YY_OTHERS) + +BUILT_SOURCES = $(FROM_DSTARPARSE_YY) +MAINTAINERCLEANFILES = $(FROM_DSTARPARSE_YY) + +$(FROM_DSTARPARSE_YY_MAIN): $(srcdir)/$(DSTARPARSE_YY) +## We must cd into $(srcdir) first because if we tell bison to read +## $(srcdir)/$(DSTARPARSE_YY), it will also use the value of $(srcdir)/ +## in the generated include statements. + cd $(srcdir) && \ + bison -Wall -Werror --report=all \ + $(DSTARPARSE_YY) -o $(FROM_DSTARPARSE_YY_MAIN) +$(FROM_DSTARPARSE_YY_OTHERS): $(DSTARPARSE_YY) + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $(FROM_DSTARPARSE_YY_MAIN) + +EXTRA_DIST = $(DSTARPARSE_YY) + +libdstarparse_la_SOURCES = \ + fmterror.cc \ + nra2nba.cc \ + nsa2tgba.cc \ + $(FROM_DSTARPARSE_YY) \ + dstarscan.ll \ + parsedecl.hh diff --git a/src/dstarparse/dstarparse.yy b/src/dstarparse/dstarparse.yy new file mode 100644 index 000000000..640344399 --- /dev/null +++ b/src/dstarparse/dstarparse.yy @@ -0,0 +1,352 @@ +/* -*- coding: utf-8 -*- +** Copyright (C) 2013 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 . +*/ +%language "C++" +%locations +%defines +%expect 0 // No shift/reduce +%name-prefix "dstaryy" +%debug +%error-verbose +%lex-param { spot::dstar_parse_error_list& error_list } +%define api.location.type "spot::location" + +%code requires +{ +#include +#include +#include "ltlast/constant.hh" +#include "public.hh" + + typedef std::map map_t; + + struct result_ + { + spot::dstar_aut* d; + spot::ltl::environment* env; + std::vector guards; + std::vector::const_iterator cur_guard; + map_t dest_map; + int cur_state; + + unsigned state_count; + std::vector aps; + + bool state_count_seen:1; + bool accpair_count_seen:1; + bool start_state_seen:1; + bool aps_seen:1; + + result_() : + state_count_seen(false), + accpair_count_seen(false), + start_state_seen(false), + aps_seen(false) + { + } + }; +} + +%parse-param {spot::dstar_parse_error_list& error_list} +%parse-param {result_& result} +%union +{ + std::string* str; + unsigned int num; +} + +%code +{ +#include +/* dstarparse.hh and parsedecl.hh include each other recursively. + We must ensure that YYSTYPE is declared (by the above %union) + before parsedecl.hh uses it. */ +#include "parsedecl.hh" + + static void fill_guards(result_& res); +} + +%token DRA "DRA" +%token DSA "DSA" +%token V2 "v2" +%token EXPLICIT "explicit" +%token ACCPAIRS "Acceptance-Pairs:" +%token AP "AP:"; +%token START "Start:"; +%token STATES "States:"; +%token STATE "State:"; +%token ACCSIG "Acc-Sig:"; +%token ENDOFHEADER "---"; +%token EOL "new line"; +%token STRING "string"; +%token NUMBER "number"; + +%type sign + +%destructor { delete $$; } +%printer { + if ($$) + debug_stream() << *$$; + else + debug_stream() << "\"\""; } +%printer { debug_stream() << $$; } + +%% +dstar: header ENDOFHEADER eols states + +eols : EOL | eols EOL +opt_eols: | opt_eols EOL + +auttype: DRA { result.d->type = spot::Rabin; } + | DSA { result.d->type = spot::Streett; } + + +header: auttype opt_eols V2 opt_eols EXPLICIT opt_eols sizes + { + bool err = false; + if (!result.state_count_seen) + { + error(@5, "missing state count"); + err = true; + } + if (!result.accpair_count_seen) + { + error(@5, "missing acceptance-pair count"); + err = true; + } + if (!result.start_state_seen) + { + error(@5, "missing start-state number"); + err = true; + } + if (!result.aps_seen) + { + error(@5, "missing atomic proposition definition"); + err = true; + } + if (err) + { + delete result.d->aut; + result.d->aut = 0; + YYABORT; + } + fill_guards(result); + } + +aps: + | aps STRING opt_eols + { + result.aps.push_back(*$2); + delete $2; + } + +sizes: + | sizes error eols + { + error(@2, "unknown header ignored"); + } + | sizes ACCPAIRS opt_eols NUMBER opt_eols + { + result.d->accpair_count = $4; + result.accpair_count_seen = true; + } + | sizes STATES opt_eols NUMBER opt_eols + { + result.state_count = $4; + result.state_count_seen = true; + } + | sizes START opt_eols NUMBER opt_eols + { + result.d->aut->set_init_state($4); + result.start_state_seen = true; + } + | sizes AP opt_eols NUMBER opt_eols aps + { + int announced = $4; + int given = result.aps.size(); + if (given != announced) + { + std::ostringstream str; + str << announced << " atomic propositions announced but " + << given << " given"; + error(@4 + @6, str.str()); + } + if (given > 31) + { + error(@4 + @6, + "ltl2star does not support more than 31 atomic propositions"); + } + result.aps_seen = true; + } + +opt_name: | STRING opt_eols + { + delete $1; + } + +state_id: STATE opt_eols NUMBER opt_eols opt_name + { + if (result.cur_guard != result.guards.end()) + error(@1, "not enough transitions for previous state"); + if ($3 >= result.state_count) + { + std::ostringstream o; + if (result.state_count > 0) + { + o << "state numbers should be in the range [0.." + << result.state_count - 1<< "]"; + } + else + { + o << "no states have been declared"; + } + error(@3, o.str()); + } + result.cur_guard = result.guards.begin(); + result.dest_map.clear(); + result.cur_state = $3; + } + +sign: '+' { $$ = 0; } + | '-' { $$ = 1; } + +// Membership to a pair is represented as (+NUM,-NUM) +accsigs: opt_eols + | accsigs sign NUMBER opt_eols + { + if ((unsigned) result.cur_state >= result.state_count) + break; + assert(result.d->accsets); + if ($3 < result.d->accpair_count) + { + result.d->accsets->at(result.cur_state * 2 + $2).set($3); + } + else + { + std::ostringstream o; + if (result.d->accpair_count > 0) + { + o << "acceptance pairs should be in the range [0.." + << result.d->accpair_count - 1<< "]"; + } + else + { + o << "no acceptance pairs have been declared"; + } + error(@3, o.str()); + } + } + +state_accsig: ACCSIG accsigs + +transitions: + | transitions NUMBER opt_eols + { + std::pair i = + result.dest_map.insert(std::make_pair($2, *result.cur_guard)); + if (!i.second) + i.first->second |= *result.cur_guard; + ++result.cur_guard; + } + +states: + | states state_id state_accsig transitions + { + for (map_t::const_iterator i = result.dest_map.begin(); + i != result.dest_map.end(); ++i) + { + spot::tgba_explicit_number::transition* t = + result.d->aut->create_transition(result.cur_state, i->first); + t->condition = i->second; + } + } +%% + +static void fill_guards(result_& r) +{ + spot::bdd_dict* d = r.d->aut->get_dict(); + + size_t nap = r.aps.size(); + int* vars = new int[nap]; + + // Get a BDD variable for each atomic proposition + for (size_t i = 0; i < nap; ++i) + { + const spot::ltl::formula* f = r.env->require(r.aps[i]); + vars[nap - 1 - i] = d->register_proposition(f, r.d->aut); + f->destroy(); + } + + // build the 2^nap possible guards + r.guards.reserve(1U << nap); + for (size_t i = 0; i < (1U << nap); ++i) + r.guards.push_back(bdd_ibuildcube(i, nap, vars)); + + delete[] vars; + r.cur_guard = r.guards.end(); + + r.d->accsets = spot::make_bitvect_array(r.d->accpair_count, + 2 * r.state_count); +} + +void +dstaryy::parser::error(const location_type& location, + const std::string& message) +{ + error_list.push_back(spot::dstar_parse_error(location, message)); +} + +namespace spot +{ + dstar_aut* + dstar_parse(const std::string& name, + dstar_parse_error_list& error_list, + bdd_dict* dict, + ltl::environment& env, + bool debug) + { + if (dstaryyopen(name)) + { + error_list.push_back + (dstar_parse_error(spot::location(), + std::string("Cannot open file ") + name)); + return 0; + } + result_ r; + r.d = new dstar_aut; + r.d->aut = new tgba_explicit_number(dict); + r.d->accsets = 0; + r.env = &env; + dstaryy::parser parser(error_list, r); + parser.set_debug_level(debug); + parser.parse(); + dstaryyclose(); + + if (!r.d->aut || !r.d->accsets) + { + delete r.d; + return 0; + } + return r.d; + } +} + +// Local Variables: +// mode: c++ +// End: diff --git a/src/dstarparse/dstarscan.ll b/src/dstarparse/dstarscan.ll new file mode 100644 index 000000000..431da852e --- /dev/null +++ b/src/dstarparse/dstarscan.ll @@ -0,0 +1,149 @@ +/* Copyright (C) 2013 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 . +*/ +%option noyywrap +%option prefix="dstaryy" +%option outfile="lex.yy.c" +/* %option debug */ + +%{ +#include +#include "dstarparse/parsedecl.hh" + +#define YY_USER_ACTION yylloc->columns(yyleng); +#define YY_NEVER_INTERACTIVE 1 + +typedef dstaryy::parser::token token; +%} + +eol \n|\r|\n\r|\r\n +%x in_COMMENT in_STRING + +%% + +%{ + std::string s; + yylloc->step(); +%} + +{eol} yylloc->lines(yyleng); return token::EOL; + + /* skip blanks and comments */ +[ \t]+ yylloc->step(); +"/*" BEGIN(in_COMMENT); +"//".* continue; +"Comment:".* yylloc->step(); + +"DRA" return token::DRA; +"DSA" return token::DSA; +"v2" return token::V2; +"explicit" return token::EXPLICIT; + +"Acceptance-Pairs:" return token::ACCPAIRS; +"AP:" return token::AP; +"Start:" return token::START; +"States:" return token::STATES; +"State:" return token::STATE; +"Acc-Sig:" return token::ACCSIG; +"---" return token::ENDOFHEADER; + +[0-9]+ { + errno = 0; + unsigned long n = strtoul(yytext, 0, 10); + yylval->num = n; + if (errno || yylval->num != n) + { + error_list.push_back( + spot::dstar_parse_error(*yylloc, + "value too large")); + yylval->num = 0; + } + return token::NUMBER; + } + +"\"" BEGIN(in_STRING); + +{ + [^*\n]* continue; + "*"+[^*/\n]* continue; + "\n"+ yylloc->end.column = 1; yylloc->lines(yyleng); + "*"+"/" BEGIN(INITIAL); + <> { + error_list.push_back( + spot::dstar_parse_error(*yylloc, + "unclosed comment")); + return 0; + } +} + +{ + \" { + BEGIN(INITIAL); + yylval->str = new std::string(s); + return token::STRING; + } + \\\" s += '"'; + \\. s += yytext[1]; + [^\\\"]+ s.append(yytext, yyleng); + <> { + error_list.push_back( + spot::dstar_parse_error(*yylloc, + "unclosed string")); + return 0; + } +} + +. return *yytext; + + +%{ + /* Dummy use of yyunput to shut up a gcc warning. */ + (void) &yyunput; +%} + +%% + +namespace spot +{ + int + dstaryyopen(const std::string &name) + { + // yy_flex_debug = 1; + if (name == "-") + { + yyin = stdin; + } + else + { + yyin = fopen(name.c_str(), "r"); + if (!yyin) + return 1; + } + // Reset the lexer in case a previous parse + // ended badly. + YY_NEW_FILE; + BEGIN(INITIAL); + return 0; + } + + void + dstaryyclose() + { + fclose(yyin); + } +} diff --git a/src/dstarparse/fmterror.cc b/src/dstarparse/fmterror.cc new file mode 100644 index 000000000..67c468061 --- /dev/null +++ b/src/dstarparse/fmterror.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2013 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 +#include "public.hh" + +namespace spot +{ + bool + format_dstar_parse_errors(std::ostream& os, + const std::string& filename, + dstar_parse_error_list& error_list) + { + bool printed = false; + spot::dstar_parse_error_list::iterator it; + for (it = error_list.begin(); it != error_list.end(); ++it) + { + if (filename != "-") + os << filename << ":"; + os << it->first << ": "; + os << it->second << std::endl; + printed = true; + } + return printed; + } +} diff --git a/src/dstarparse/nra2nba.cc b/src/dstarparse/nra2nba.cc new file mode 100644 index 000000000..cfcec40a7 --- /dev/null +++ b/src/dstarparse/nra2nba.cc @@ -0,0 +1,128 @@ +// Copyright (C) 2013 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 "public.hh" +#include "tgbaalgos/reachiter.hh" +#include "tgbaalgos/sccfilter.hh" +#include "ltlast/constant.hh" + +namespace spot +{ + namespace + { + // Christof Löding's Diploma Thesis: Methods for the + // Transformation of ω-Automata: Complexity and Connection to + // Second Order Logic. Section 3.4.3: Rabin to Büchi. + // + // However beware that the {...,(Ei,Fi),...} pairs used by + // are the reversed compared to the {...,(Li,Ui),...} pairs + // used by several other people. We have Ei=Ui and Fi=Li. + class nra_to_nba_worker: public tgba_reachable_iterator_depth_first + { + public: + nra_to_nba_worker(const dstar_aut* a): + tgba_reachable_iterator_depth_first(a->aut), + out_(new tgba_explicit_number(a->aut->get_dict())), + d_(a), + num_states_(a->aut->num_states()) + { + bdd_dict* bd = out_->get_dict(); + bd->register_all_variables_of(a->aut, out_); + + // Invent a new acceptance set for the degeneralized automaton. + int accvar = + bd->register_acceptance_variable(ltl::constant::true_instance(), + out_); + acc_ = bdd_ithvar(accvar); + out_->set_acceptance_conditions(acc_); + } + + tgba_explicit_number* + result() + { + return out_; + } + + void + process_link(const state* sin, int, + const state* sout, int, + const tgba_succ_iterator* si) + { + int in = d_->aut->get_label(sin); + int out = d_->aut->get_label(sout); + + typedef state_explicit_number::transition trans; + trans* t = out_->create_transition(in, out); + bdd cond = t->condition = si->current_condition(); + + // Create one clone of the automaton per accepting pair, + // removing states from the Ui part of the (Li, Ui) pairs. + // (Or the Ei part of Löding's (Ei, Fi) pairs.) + bitvect& l = d_->accsets->at(2 * in); + bitvect& u = d_->accsets->at(2 * in + 1); + for (size_t i = 0; i < d_->accpair_count; ++i) + { + int shift = num_states_ * (i + 1); + // In the Ui set. (Löding's Ei set.) + if (!u.get(i)) + { + // Transition t1 is a non-deterministic jump + // from the original automaton to the i-th clone. + // + // Transition t2 constructs the clone. + // + // Löding creates transition t1 regardless of the + // acceptance set. We restrict it to the non-Li + // states. Both his definition and this + // implementation create more transitions than needed: + // we do not need more than one transition per + // accepting cycle. + trans* t1 = out_->create_transition(in, out + shift); + t1->condition = cond; + + trans* t2 = out_->create_transition(in + shift, out + shift); + t2->condition = cond; + // In the Li set. (Löding's Fi set.) + if (l.get(i)) + t2->acceptance_conditions = acc_; + } + } + } + + protected: + tgba_explicit_number* out_; + const dstar_aut* d_; + size_t num_states_; + bdd acc_; + }; + + } + + SPOT_API + tgba* nra_to_nba(const dstar_aut* nra) + { + assert(nra->type == Rabin); + nra_to_nba_worker w(nra); + w.run(); + tgba_explicit_number* aut = w.result(); + tgba* res = scc_filter_states(aut); + delete aut; + return res; + } + +} diff --git a/src/dstarparse/nsa2tgba.cc b/src/dstarparse/nsa2tgba.cc new file mode 100644 index 000000000..39a53efde --- /dev/null +++ b/src/dstarparse/nsa2tgba.cc @@ -0,0 +1,245 @@ +// Copyright (C) 2013 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 +#include +#include "public.hh" +#include "tgbaalgos/sccfilter.hh" +#include "ltlenv/defaultenv.hh" + +namespace spot +{ + // Christof Löding's Diploma Thesis: Methods for the + // Transformation of ω-Automata: Complexity and Connection to + // Second Order Logic. Section 3.4.3, gives a transition + // from Streett with |Q| states to BA with |Q|*(4^n-3^n+2) + // states, if n is the number of acceptance pairs. + // + // Duret-Lutz et al. (ATVA'2009): On-the-fly Emptiness Check of + // Transition-based Streett Automata. Section 3.3 contains a + // conversion from transition-based Streett Automata to TGBA using + // the generalized Büchi acceptance to limit the explosion. It goes + // from Streett with |Q| states to (T)GBA with |Q|*(2^n+1) states. + // However the definition of the number of acceptance sets in that + // paper is suboptimal: only n are needed, not 2^n. + // + // This implements this second version. + + namespace + { + // A state in the resulting automaton corresponds is either a + // state of the original automaton (in which case bv == 0) or a + // state of the original automaton associated to a set of pending + // acceptance represented by a bitvect. + + struct build_state + { + int s; + const bitvect* pend; + + build_state(int st, const bitvect* bv = 0): + s(st), + pend(bv) + { + } + }; + + typedef std::pair degen_state; + + struct build_state_hash + { + size_t + operator()(const build_state& s) const + { + if (!s.pend) + return s.s; + else + return s.s ^ s.pend->hash(); + } + }; + + struct build_state_equal + { + bool + operator()(const build_state& left, + const build_state& right) const + { + if (left.s != right.s) + return false; + if (left.pend == right.pend) + return true; + if (!right.pend || !left.pend) + return false; + return *left.pend == *right.pend; + } + }; + + // Associate the build state to its number. + typedef Sgi::hash_map bs2num_map; + + // Queue of state to be processed. + typedef std::deque queue_t; + + } + + int label(const tgba_explicit_number* aut, state* s) + { + int label = aut->get_label(s); + s->destroy(); + return label; + } + + SPOT_API + tgba* nsa_to_tgba(const dstar_aut* nsa) + { + assert(nsa->type == Streett); + tgba_explicit_number* a = nsa->aut; + bdd_dict* dict = a->get_dict(); + + tgba_explicit_number* res = new tgba_explicit_number(dict); + dict->register_all_variables_of(a, res); + + // Create accpair_count acceptance sets for tge TGBA. + size_t npairs = nsa->accpair_count; + std::vector acc_b(npairs); + { + ltl::environment& envacc = ltl::default_environment::instance(); + std::vector acc_f(npairs); + for (unsigned n = 0; n < npairs; ++n) + { + std::ostringstream s; + s << n; + const ltl::formula* af = acc_f[n] = envacc.require(s.str()); + res->declare_acceptance_condition(af->clone()); + } + bdd allacc = bddfalse; + for (unsigned n = 0; n < npairs; ++n) + { + const ltl::formula* af = acc_f[n]; + allacc |= acc_b[n] = res->get_acceptance_condition(af); + } + } + + // These maps make it possible to convert build_state to number + // and vice-versa. + bs2num_map bs2num; + + queue_t todo; + + build_state s(label(a, a->get_init_state())); + + bs2num[s] = 0; + todo.push_back(s); + + while (!todo.empty()) + { + s = todo.front(); + todo.pop_front(); + int src = bs2num[s]; + + tgba_succ_iterator* i = a->succ_iter(a->get_state(s.s)); + for (i->first(); !i->done(); i->next()) + { + int dlabel = label(a, i->current_state()); + + bitvect* pend = 0; + bdd acc = bddfalse; + if (s.pend) + { + pend = s.pend->clone(); + *pend |= nsa->accsets->at(2 * dlabel); // L + *pend -= nsa->accsets->at(2 * dlabel + 1); // U + + for (size_t i = 0; i < npairs; ++i) + if (!pend->get(i)) + acc |= acc_b[i]; + } + + + build_state d(dlabel, pend); + // Have we already seen this destination? + int dest; + std::pair dres = + bs2num.insert(bs2num_map::value_type(d, 0)); + if (!dres.second) + { + dest = dres.first->second; + delete d.pend; + } + else + { + dest = dres.first->second = bs2num.size() - 1; + todo.push_back(d); + } + state_explicit_number::transition* t = + res->create_transition(src, dest); + t->condition = i->current_condition(); + t->acceptance_conditions = acc; + + // Jump to level ∅ + if (s.pend == 0) + { + bitvect* pend = make_bitvect(npairs); + build_state d(label(a, i->current_state()), pend); + // Have we already seen this destination? + int dest; + std::pair dres = + bs2num.insert(bs2num_map::value_type(d, 0)); + if (!dres.second) + { + dest = dres.first->second; + delete d.pend; + } + else + { + dest = dres.first->second = bs2num.size() - 1; + todo.push_back(d); + } + state_explicit_number::transition* t = + res->create_transition(src, dest); + t->condition = i->current_condition(); + } + } + delete i; + } + + + // { + // bs2num_map::iterator i = bs2num.begin(); + // while (i != bs2num.end()) + // { + // std::cerr << i->second << ": (" << i->first.s << ","; + // if (i->first.pend) + // std::cerr << *i->first.pend << ")\n"; + // else + // std::cerr << "-)\n"; + // ++i; + // } + // } + + // Cleanup the bs2num map. + bs2num_map::iterator i = bs2num.begin(); + while (i != bs2num.end()) + delete (i++)->first.pend; + + return res; + } + +} + diff --git a/src/dstarparse/parsedecl.hh b/src/dstarparse/parsedecl.hh new file mode 100644 index 000000000..d69170eb8 --- /dev/null +++ b/src/dstarparse/parsedecl.hh @@ -0,0 +1,39 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2013 Laboratoire de Recherche et Développement +// de l'EPITA. +// +// 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 . + +#ifndef SPOT_DSTARPARSE_PARSEDECL_HH +# define SPOT_DSTARPARSE_PARSEDECL_HH + +#include +#include "dstarparse.hh" +#include "misc/location.hh" + +# define YY_DECL \ + int dstaryylex(dstaryy::parser::semantic_type *yylval, \ + spot::location *yylloc, \ + spot::dstar_parse_error_list& error_list) +YY_DECL; + +namespace spot +{ + int dstaryyopen(const std::string& name); + void dstaryyclose(); +} + +#endif // SPOT_DSTARPARSE_PARSEDECL_HH diff --git a/src/dstarparse/public.hh b/src/dstarparse/public.hh new file mode 100644 index 000000000..634d15e65 --- /dev/null +++ b/src/dstarparse/public.hh @@ -0,0 +1,120 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2013 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 . + +#ifndef SPOT_DSTARPARSE_PUBLIC_HH +# define SPOT_DSTARPARSE_PUBLIC_HH + +# include "tgba/tgbaexplicit.hh" +# include "misc/location.hh" +# include "ltlenv/defaultenv.hh" +# include +# include +# include +# include +# include + +namespace spot +{ + /// \addtogroup tgba_io + /// @{ + + /// \brief A parse diagnostic with its location. + typedef std::pair dstar_parse_error; + /// \brief A list of parser diagnostics, as filled by parse. + typedef std::list dstar_parse_error_list; + + enum dstar_type { Rabin, Streett }; + + /// \brief Temporary encoding of an omega automaton produced by + /// ltl2dstar. + struct SPOT_API dstar_aut + { + // Transition structure of the automaton. + // This is encoded as a TGBA without acceptance condition. + tgba_explicit_number* aut; + /// Type of the acceptance. + dstar_type type; + /// Number of acceptance pairs. + size_t accpair_count; + /// \brief acceptance sets encoded as 2*num_state bit-vectors of + /// num_pairs bits + /// + /// Assuming $F=\{(L_0,U_0),\ldots,(L_i,U_i),\ldots\}$, + /// $s\in L_i$ iff accsets->at(s * 2).get(i)$, + /// an $s\in $U_i$ iff accsets->at(s * 2 + 1).get(i). + bitvect_array* accsets; + + ~dstar_aut() + { + delete aut; + delete accsets; + } + }; + + + /// \brief Build a spot::tgba_explicit from ltl2dstar's output. + /// \param filename The name of the file to parse. + /// \param error_list A list that will be filled with + /// parse errors that occured during parsing. + /// \param dict The BDD dictionary where to use. + /// \param env The environment of atomic proposition into which parsing + /// should take place. + /// \param debug When true, causes the parser to trace its execution. + /// \return A pointer to the tgba built from \a filename, or + /// 0 if the file could not be opened. + /// + /// Note that the parser usually tries to recover from errors. It can + /// return an non zero value even if it encountered error during the + /// parsing of \a filename. If you want to make sure \a filename + /// was parsed succesfully, check \a error_list for emptiness. + /// + /// \warning This function is not reentrant. + SPOT_API dstar_aut* + dstar_parse(const std::string& filename, + dstar_parse_error_list& error_list, + bdd_dict* dict, + ltl::environment& env = ltl::default_environment::instance(), + bool debug = false); + + /// \brief Format diagnostics produced by spot::dstar_parse. + /// \param os Where diagnostics should be output. + /// \param filename The filename that should appear in the diagnostics. + /// \param error_list The error list filled by spot::ltl::parse while + /// parsing \a ltl_string. + /// \return \c true iff any diagnostic was output. + SPOT_API bool + format_dstar_parse_errors(std::ostream& os, + const std::string& filename, + dstar_parse_error_list& error_list); + + + /// \brief Convert a non-deterministic Rabin automaton into a + /// non-deterministic Büchi automaton. + SPOT_API + tgba* nra_to_nba(const dstar_aut* nra); + + /// \brief Convert a non-deterministic Streett automaton into a + /// non-deterministic tgba. + SPOT_API + tgba* nsa_to_tgba(const dstar_aut* nra); + + /// @} +} + +#endif // SPOT_DSTARPARSE_PUBLIC_HH diff --git a/src/tgbatest/Makefile.am b/src/tgbatest/Makefile.am index b6ab29824..1f87f14e1 100644 --- a/src/tgbatest/Makefile.am +++ b/src/tgbatest/Makefile.am @@ -80,6 +80,7 @@ TESTS = \ renault.test \ nondet.test \ neverclaimread.test \ + dstar.test \ readsave.test \ simdet.test \ sim.test \ diff --git a/src/tgbatest/dstar.test b/src/tgbatest/dstar.test new file mode 100755 index 000000000..0d6c1863b --- /dev/null +++ b/src/tgbatest/dstar.test @@ -0,0 +1,131 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2013 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 . + +# Do some quick translations to make sure the neverclaims produced by +# spot actually look correct! We do that by parsing them via ltlcross. +# ltl2neverclaim-lbtt.test does the same with LBTT if it is installed. + +. ./defs +set -e + +cat >dra.dstar <expected < 1 + 1 [label="0"] + 1 -> 1 [label="a & !b\n"] + 1 -> 2 [label="!a & !b\n"] + 1 -> 3 [label="b\n"] + 2 [label="1"] + 2 -> 2 [label="1\n"] + 3 [label="2"] + 3 -> 3 [label="1\n"] +} +EOF + +diff expected stdout + +cat >dsa.dstar <expected < 1 + 1 [label="0"] + 1 -> 2 [label="1\n"] + 1 -> 3 [label="1\n"] + 2 [label="1"] + 2 -> 2 [label="!a\n"] + 2 -> 3 [label="!a\n"] + 2 -> 4 [label="a\n"] + 2 -> 5 [label="a\n"] + 3 [label="2"] + 3 -> 6 [label="!a\n"] + 3 -> 5 [label="a\n{Acc[\"0\"]}"] + 4 [label="3"] + 4 -> 2 [label="!a\n"] + 4 -> 3 [label="!a\n"] + 4 -> 4 [label="a\n"] + 4 -> 5 [label="a\n"] + 5 [label="4"] + 5 -> 6 [label="!a\n"] + 5 -> 5 [label="a\n{Acc[\"0\"]}"] + 6 [label="5"] + 6 -> 6 [label="!a\n"] + 6 -> 7 [label="a\n"] + 7 [label="6"] + 7 -> 6 [label="!a\n"] + 7 -> 7 [label="a\n"] +} +EOF + +diff expected stdout diff --git a/src/tgbatest/ltl2tgba.cc b/src/tgbatest/ltl2tgba.cc index 9e8ba524e..e450f5b6b 100644 --- a/src/tgbatest/ltl2tgba.cc +++ b/src/tgbatest/ltl2tgba.cc @@ -45,6 +45,7 @@ #include "tgbaalgos/reducerun.hh" #include "tgbaparse/public.hh" #include "neverparse/public.hh" +#include "dstarparse/public.hh" #include "tgbaalgos/dupexp.hh" #include "tgbaalgos/minimize.hh" #include "taalgos/minimize.hh" @@ -119,6 +120,10 @@ syntax(char* prog) << std::endl << " -X do not compute an automaton, read it from a file" << std::endl + << " -XD do not compute an automaton, read it from an" + << " ltl2dstar file" << std::endl + << " -XDB read the from an ltl2dstar file and convert it to " + << "TGBA" << std::endl << " -XL do not compute an automaton, read it from an" << " LBTT file" << std::endl << " -XN do not compute an automaton, read it from a" @@ -361,8 +366,8 @@ main(int argc, char** argv) bool accepting_run = false; bool accepting_run_replay = false; bool from_file = false; - bool read_neverclaim = false; - bool read_lbtt = false; + enum { ReadSpot, ReadLbtt, ReadNeverclaim, ReadDstar } readformat = ReadSpot; + bool nra2nba = false; bool scc_filter = false; bool simpltl = false; spot::ltl::ltl_simplifier_options redopt(false, false, false, false, @@ -899,16 +904,28 @@ main(int argc, char** argv) else if (!strcmp(argv[formula_index], "-X")) { from_file = true; + readformat = ReadSpot; } - else if (!strcmp(argv[formula_index], "-XN")) + else if (!strcmp(argv[formula_index], "-XD")) { from_file = true; - read_neverclaim = true; + readformat = ReadDstar; + } + else if (!strcmp(argv[formula_index], "-XDB")) + { + from_file = true; + readformat = ReadDstar; + nra2nba = true; } else if (!strcmp(argv[formula_index], "-XL")) { from_file = true; - read_lbtt = true; + readformat = ReadLbtt; + } + else if (!strcmp(argv[formula_index], "-XN")) + { + from_file = true; + readformat = ReadNeverclaim; } else if (!strcmp(argv[formula_index], "-y")) { @@ -1018,59 +1035,102 @@ main(int argc, char** argv) if (from_file) { spot::tgba_explicit_string* e = 0; - if (!read_neverclaim && !read_lbtt) + switch (readformat) { - spot::tgba_parse_error_list pel; - tm.start("parsing automaton"); - to_free = a = e = spot::tgba_parse(input, pel, dict, - env, env, debug_opt); - tm.stop("parsing automaton"); - if (spot::format_tgba_parse_errors(std::cerr, input, pel)) + case ReadSpot: { - delete to_free; - delete dict; - return 2; - } - } - else if (read_neverclaim) - { - spot::neverclaim_parse_error_list pel; - tm.start("parsing neverclaim"); - to_free = a = e = spot::neverclaim_parse(input, pel, dict, - env, debug_opt); - tm.stop("parsing neverclaim"); - if (spot::format_neverclaim_parse_errors(std::cerr, input, pel)) - { - delete to_free; - delete dict; - return 2; - } - assume_sba = true; - } - else - { - std::string error; - std::fstream f(input.c_str()); - if (!f) - { - std::cerr << "cannot open " << input << std::endl; - delete dict; - return 2; - } - tm.start("parsing lbtt"); - to_free = a = - const_cast(spot::lbtt_parse(f, error, dict, - env, env)); - tm.stop("parsing lbtt"); - if (!to_free) - { - std::cerr << error << std::endl; - delete dict; - return 2; + spot::tgba_parse_error_list pel; + tm.start("parsing automaton"); + to_free = a = e = spot::tgba_parse(input, pel, dict, + env, env, debug_opt); + tm.stop("parsing automaton"); + if (spot::format_tgba_parse_errors(std::cerr, input, pel)) + { + delete to_free; + delete dict; + return 2; + } } + break; + case ReadNeverclaim: + { + spot::neverclaim_parse_error_list pel; + tm.start("parsing neverclaim"); + to_free = a = e = spot::neverclaim_parse(input, pel, dict, + env, debug_opt); + tm.stop("parsing neverclaim"); + if (spot::format_neverclaim_parse_errors(std::cerr, input, pel)) + { + delete to_free; + delete dict; + return 2; + } + assume_sba = true; + } + break; + case ReadLbtt: + { + std::string error; + std::fstream f(input.c_str()); + if (!f) + { + std::cerr << "cannot open " << input << std::endl; + delete dict; + return 2; + } + tm.start("parsing lbtt"); + to_free = a = + const_cast(spot::lbtt_parse(f, error, dict, + env, env)); + tm.stop("parsing lbtt"); + if (!to_free) + { + std::cerr << error << std::endl; + delete dict; + return 2; + } + } + break; + case ReadDstar: + { + spot::dstar_parse_error_list pel; + tm.start("parsing dstar"); + spot::dstar_aut* daut; + daut = spot::dstar_parse(input, pel, dict, + env, debug_opt); + if (nra2nba) + { + if (daut->type == spot::Rabin) + { + to_free = a = spot::nra_to_nba(daut); + assume_sba = true; + } + else + { + to_free = a = spot::nsa_to_tgba(daut); + assume_sba = false; + } + } + else + { + to_free = a = daut->aut; + daut->aut = 0; + assume_sba = false; + } + delete daut; + tm.stop("parsing dstar"); + if (spot::format_dstar_parse_errors(std::cerr, input, pel)) + { + delete to_free; + delete dict; + return 2; + } + } + break; } if (e) e->merge_transitions(); + } else {