parseaut: Add support for PGSolver's format
* spot/parseaut/parseaut.yy, spot/parseaut/scanaut.ll: Add rules for PGSolver's format. * spot/parseaut/public.hh: PGAME is a new type of output. * tests/core/pgsolver.test: New file. * tests/Makefile.am: Add it. * tests/python/games.ipynb: More exemples. * NEWS: Mention the new feature.
This commit is contained in:
parent
d6b3c757d0
commit
444e2b5b89
7 changed files with 1144 additions and 342 deletions
|
|
@ -44,6 +44,7 @@
|
|||
#include "spot/priv/accmap.hh"
|
||||
#include <spot/tl/parse.hh>
|
||||
#include <spot/twaalgos/alternation.hh>
|
||||
#include <spot/twaalgos/game.hh>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
|
|
@ -256,6 +257,11 @@ extern "C" int strverscmp(const char *s1, const char *s2);
|
|||
%token ENDOFFILE 0 "end of file"
|
||||
%token <str> '['
|
||||
|
||||
%token <str> LINEDIRECTIVE "#line"
|
||||
%token <b> BDD
|
||||
|
||||
/**** DSTAR tokens ****/
|
||||
%token ENDDSTAR "end of DSTAR automaton"
|
||||
%token DRA "DRA"
|
||||
%token DSA "DSA"
|
||||
%token V2 "v2"
|
||||
|
|
@ -263,14 +269,12 @@ extern "C" int strverscmp(const char *s1, const char *s2);
|
|||
%token ACCPAIRS "Acceptance-Pairs:"
|
||||
%token ACCSIG "Acc-Sig:"
|
||||
%token ENDOFHEADER "---"
|
||||
%token <str> LINEDIRECTIVE "#line"
|
||||
%token <b> BDD
|
||||
|
||||
%left '|'
|
||||
%left '&'
|
||||
%precedence '!'
|
||||
|
||||
%type <states> init-state-conj-2 state-conj-2 state-conj-checked
|
||||
%type <states> init-state-conj-2 state-conj-2 state-conj-checked pgame_succs
|
||||
%type <num> checked-state-num state-num acc-set sign
|
||||
%type <b> label-expr
|
||||
%type <mark> acc-sig acc-sets trans-acc_opt state-acc_opt
|
||||
|
|
@ -299,10 +303,14 @@ extern "C" int strverscmp(const char *s1, const char *s2);
|
|||
%type <str> nc-one-ident nc-ident-list
|
||||
%type <code> acceptance-cond
|
||||
|
||||
/**** PGAME tokens ****/
|
||||
// Also using INT, STRING
|
||||
%token PGAME "start of PGSolver game"
|
||||
%token ENDPGAME "end of PGSolver game"
|
||||
|
||||
/**** LBTT tokens *****/
|
||||
// Also using INT, STRING
|
||||
// Also using INT, STRING
|
||||
%token ENDAUT "-1"
|
||||
%token ENDDSTAR "end of DSTAR automaton"
|
||||
%token <num> LBTT "LBTT header"
|
||||
%token <num> INT_S "state acceptance"
|
||||
%token <num> LBTT_EMPTY "acceptance sets for empty automaton"
|
||||
|
|
@ -364,6 +372,7 @@ aut-1: hoa { res.h->type = spot::parsed_aut_type::HOA; }
|
|||
| never { res.h->type = spot::parsed_aut_type::NeverClaim; }
|
||||
| lbtt { res.h->type = spot::parsed_aut_type::LBTT; }
|
||||
| dstar /* will set type as DSA or DRA while parsing first line */
|
||||
| pgame { res.h->type = spot::parsed_aut_type::PGAME; }
|
||||
|
||||
/**********************************************************************/
|
||||
/* Rules for HOA */
|
||||
|
|
@ -1765,7 +1774,7 @@ dstar_header: dstar_sizes
|
|||
|
||||
if (res.states > 0)
|
||||
{
|
||||
res.h->aut->new_states(res.states);;
|
||||
res.h->aut->new_states(res.states);
|
||||
res.info_states.resize(res.states);
|
||||
}
|
||||
res.acc_style = State_Acc;
|
||||
|
|
@ -1908,6 +1917,93 @@ dstar_states: %empty
|
|||
res.h->aut->new_edge(res.cur_state, i.first, i.second, $3);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Rules for PGSolver games */
|
||||
/**********************************************************************/
|
||||
|
||||
pgamestart: PGAME
|
||||
{
|
||||
if (res.opts.want_kripke)
|
||||
{
|
||||
error(@$,
|
||||
"cannot read a Kripke structure out of a PGSolver game.");
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
|
||||
pgame: pgamestart pgame_nodes ENDPGAME
|
||||
{
|
||||
unsigned n = res.accset;
|
||||
auto p = spot::acc_cond::acc_code::parity_max_odd(n);
|
||||
res.h->aut->set_acceptance(n, p);
|
||||
res.acc_style = State_Acc;
|
||||
// Pretend that we have declared all states.
|
||||
n = res.h->aut->num_states();
|
||||
res.info_states.resize(n);
|
||||
for (auto& p: res.info_states)
|
||||
p.declared = true;
|
||||
}
|
||||
| pgamestart error ENDPGAME
|
||||
{
|
||||
error(@$, "failed to parse this as a PGSolver game");
|
||||
}
|
||||
|
||||
pgame_nodes: pgame_node ';'
|
||||
| pgame_nodes pgame_node ';'
|
||||
|
||||
pgame_succs: INT
|
||||
{ $$ = new std::vector<unsigned>{$1}; }
|
||||
| pgame_succs ',' INT
|
||||
{
|
||||
$$ = $1;
|
||||
$$->emplace_back($3);
|
||||
}
|
||||
|
||||
pgame_node: INT INT INT pgame_succs string_opt
|
||||
{
|
||||
unsigned state = $1;
|
||||
unsigned owner = $3;
|
||||
if (owner > 1)
|
||||
{
|
||||
error(@3, "node owner should be 0 or 1");
|
||||
owner = 0;
|
||||
}
|
||||
// Create any missing state
|
||||
unsigned max_state = state;
|
||||
for (unsigned s: *$4)
|
||||
max_state = std::max(max_state, s);
|
||||
unsigned n = res.h->aut->num_states();
|
||||
if (n <= max_state)
|
||||
res.h->aut->new_states(max_state + 1 - n);
|
||||
|
||||
// assume the source of the first edge is initial
|
||||
if (res.start.empty())
|
||||
res.start.emplace_back(@$, std::vector<unsigned>{state});
|
||||
|
||||
// Create all edges with priority $2
|
||||
spot::acc_cond::mark_t m({$2});
|
||||
for (unsigned s: *$4)
|
||||
res.h->aut->new_edge(state, s, bddtrue, m);
|
||||
res.accset = std::max(res.accset, 1 + (int) $2);
|
||||
|
||||
n = res.h->aut->num_states();
|
||||
if (!res.state_player)
|
||||
res.state_player = new std::vector<bool>(n);
|
||||
else if (res.state_player->size() < n)
|
||||
res.state_player->resize(n);
|
||||
(*res.state_player)[state] = owner;
|
||||
|
||||
if (std::string* name = $5)
|
||||
{
|
||||
if (!res.state_names)
|
||||
res.state_names = new std::vector<std::string>(n);
|
||||
else if (res.state_names->size() < n)
|
||||
res.state_names->resize(n);
|
||||
(*res.state_names)[state] = std::move(*name);
|
||||
delete name;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Rules for neverclaims */
|
||||
/**********************************************************************/
|
||||
|
|
@ -2487,7 +2583,7 @@ static void fix_initial_state(result_& r)
|
|||
start.resize(std::distance(start.begin(), res));
|
||||
|
||||
assert(start.size() >= 1);
|
||||
if (start.size() == 1)
|
||||
if (start.size() == 1)
|
||||
{
|
||||
if (r.opts.want_kripke)
|
||||
r.h->ks->set_init_state(start.front().front());
|
||||
|
|
|
|||
|
|
@ -44,7 +44,14 @@ namespace spot
|
|||
struct parse_aut_error_list {};
|
||||
#endif
|
||||
|
||||
enum class parsed_aut_type { HOA, NeverClaim, LBTT, DRA, DSA, Unknown };
|
||||
enum class parsed_aut_type {
|
||||
HOA,
|
||||
NeverClaim,
|
||||
LBTT,
|
||||
DRA, /* DSTAR format for Rabin */
|
||||
DSA, /* DSTAR format for Streett */
|
||||
PGAME, /* PG Solver Game */
|
||||
Unknown };
|
||||
|
||||
/// \brief Result of the automaton parser
|
||||
struct SPOT_API parsed_aut final
|
||||
|
|
@ -91,11 +98,11 @@ namespace spot
|
|||
|
||||
struct automaton_parser_options final
|
||||
{
|
||||
bool ignore_abort = false; ///< Skip aborted automata
|
||||
bool debug = false; ///< Run the parser in debug mode?
|
||||
bool trust_hoa = true; ///< Trust properties in HOA files
|
||||
bool raise_errors = false; ///< Raise errors as exceptions.
|
||||
bool want_kripke = false; ///< Parse as a Kripke structure.
|
||||
bool ignore_abort = false; ///< Skip aborted automata
|
||||
bool debug = false; ///< Run the parser in debug mode?
|
||||
bool trust_hoa = true; ///< Trust properties in HOA files
|
||||
bool raise_errors = false; ///< Raise errors as exceptions.
|
||||
bool want_kripke = false; ///< Parse as a Kripke structure.
|
||||
};
|
||||
|
||||
/// \brief Parse a stream of automata
|
||||
|
|
|
|||
|
|
@ -65,12 +65,18 @@ eol \n+|\r+
|
|||
eol2 (\n\r)+|(\r\n)+
|
||||
eols ({eol}|{eol2})*
|
||||
identifier [[:alpha:]_][[:alnum:]_.-]*
|
||||
pgameinit "parity"[ \t]+[0-9]+[ \t]*;
|
||||
oldpgameinit [0-9]+[ \t]+[0-9]+[ \t]+[01]+[ \t]+[0-9,]+([ \t]+".*")?[ \t]*;
|
||||
/* A pattern than match the start of an automaton, in order
|
||||
to detect the end of the previous one. We do not try to match
|
||||
LBTT automata here. */
|
||||
startaut {eols}("HOA:"|"never"|"DSA"|"DRA"|{pgameinit})
|
||||
|
||||
%x in_COMMENT in_STRING in_NEVER_PAR
|
||||
%s in_HOA in_NEVER in_LBTT_HEADER
|
||||
%s in_LBTT_STATE in_LBTT_INIT in_LBTT_TRANS
|
||||
%s in_LBTT_T_ACC in_LBTT_S_ACC in_LBTT_GUARD
|
||||
%s in_DSTAR
|
||||
%s in_DSTAR in_PGAME
|
||||
%%
|
||||
|
||||
%{
|
||||
|
|
@ -127,7 +133,20 @@ identifier [[:alpha:]_][[:alnum:]_.-]*
|
|||
<INITIAL>"never" BEGIN(in_NEVER); return token::NEVER;
|
||||
<INITIAL>"DSA" BEGIN(in_DSTAR); return token::DSA;
|
||||
<INITIAL>"DRA" BEGIN(in_DSTAR); return token::DRA;
|
||||
|
||||
<INITIAL>{pgameinit} {
|
||||
BEGIN(in_PGAME);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
unsigned long n = strtoul(yytext + 7, &end, 10);
|
||||
yylval->num = n;
|
||||
return token::PGAME;
|
||||
}
|
||||
<INITIAL>{oldpgameinit} {
|
||||
BEGIN(in_PGAME);
|
||||
yylval->num = 0;
|
||||
yyless(0);
|
||||
return token::PGAME;
|
||||
}
|
||||
<INITIAL>[0-9]+[ \t][0-9]+[ts]? {
|
||||
BEGIN(in_LBTT_HEADER);
|
||||
char* end = nullptr;
|
||||
|
|
@ -229,10 +248,8 @@ identifier [[:alpha:]_][[:alnum:]_.-]*
|
|||
return token::INT;
|
||||
}
|
||||
[0-9]+ parse_int(); return token::INT;
|
||||
/* The start of any automaton is the end of this one.
|
||||
We do not try to detect LBTT automata, as that would
|
||||
be too hard to distinguish from state numbers. */
|
||||
{eols}("HOA:"|"never"|"DSA"|"DRA") {
|
||||
/* The start of any automaton is the end of this one. */
|
||||
{startaut} {
|
||||
yylloc->end = yylloc->begin;
|
||||
yyless(0);
|
||||
BEGIN(INITIAL);
|
||||
|
|
@ -270,6 +287,24 @@ identifier [[:alpha:]_][[:alnum:]_.-]*
|
|||
}
|
||||
}
|
||||
|
||||
<in_PGAME>{
|
||||
/* Handle short numbers without going through parse_int() for efficiency. */
|
||||
[0-9] yylval->num = *yytext - '0'; return token::INT;
|
||||
[0-9][0-9] {
|
||||
yylval->num = (yytext[0] * 10) + yytext[1] - '0' * 11;
|
||||
return token::INT;
|
||||
}
|
||||
[0-9]+ parse_int(); return token::INT;
|
||||
/* The start of any automaton is the end of this one. */
|
||||
{startaut} {
|
||||
yylloc->end = yylloc->begin;
|
||||
yyless(0);
|
||||
BEGIN(INITIAL);
|
||||
return token::ENDPGAME;
|
||||
}
|
||||
<<EOF>> return token::ENDPGAME;
|
||||
}
|
||||
|
||||
/* Note: the LBTT format is scanf friendly, but not Bison-friendly.
|
||||
If we only tokenize it as a stream of INTs, the parser will have
|
||||
a very hard time recognizing what is a state from what is a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue