parseaut: add support for negated properties

* spot/parseaut/parseaut.yy: Here.
* tests/core/parseaut.test: Test it.
* NEWS: Mention it.
This commit is contained in:
Alexandre Duret-Lutz 2016-01-14 11:12:30 +01:00
parent da391492f3
commit 6c62362fe9
3 changed files with 207 additions and 90 deletions

12
NEWS
View file

@ -74,14 +74,18 @@ New in spot 1.99.6a (not yet released)
acc_cond::is_ff() -> acc_cond::is_f() acc_cond::is_ff() -> acc_cond::is_f()
parse_acc_code() -> acc_cond::acc_code(...) parse_acc_code() -> acc_cond::acc_code(...)
* Automata property bits (those that tell whether the automaton is * Automata property bits (those that tell whether the automaton is
deterministic, weak, stutter-invariant, etc.) are now stored using deterministic, weak, stutter-invariant, etc.) are now stored using
three-valued logic: in addition to "maybe"/"yes" they can now also three-valued logic: in addition to "maybe"/"yes" they can now also
represent "no". This is some preparation for the upcomming represent "no". This is some preparation for the upcomming
support of the HOA v1.1 format, but it also saves some time in support of the HOA v1.1 format, but it also saves time in some
some algorithms (e.g, is_deterministic() can now return algorithms (e.g, is_deterministic() can now return immediately on
immediately on automata marked as not deterministic). automata marked as not deterministic).
* The automaton parser now accept negated properties as they will be
introduced in HOA v1.1, and will check for some inconsistencies.
These properties are stored and used when appropriate, but they
are not yet output by the HOA printer.
Python: Python:

View file

@ -97,7 +97,16 @@ extern "C" int strverscmp(const char *s1, const char *s2);
std::vector<state_info> info_states; // States declared and used. std::vector<state_info> info_states; // States declared and used.
std::vector<std::pair<spot::location, unsigned>> start; // Initial states; std::vector<std::pair<spot::location, unsigned>> start; // Initial states;
std::unordered_map<std::string, bdd> alias; std::unordered_map<std::string, bdd> alias;
std::unordered_map<std::string, spot::location> props; struct prop_info
{
spot::location loc;
bool val;
operator bool() const
{
return val;
};
};
std::unordered_map<std::string, prop_info> props;
spot::location states_loc; spot::location states_loc;
spot::location ap_loc; spot::location ap_loc;
spot::location state_label_loc; spot::location state_label_loc;
@ -138,6 +147,14 @@ extern "C" int strverscmp(const char *s1, const char *s2);
std::map<std::string, spot::location> labels; std::map<std::string, spot::location> labels;
prop_info prop_is_true(const std::string& p)
{
auto i = props.find(p);
if (i == props.end())
return prop_info{spot::location(), false};
return i->second;
}
~result_() ~result_()
{ {
delete namer; delete namer;
@ -323,52 +340,52 @@ header: format-version header-items
} }
// Process properties. // Process properties.
{ {
auto explicit_labels = res.props.find("explicit-labels"); auto explicit_labels = res.prop_is_true("explicit-labels");
auto implicit_labels = res.props.find("implicit-labels"); auto implicit_labels = res.prop_is_true("implicit-labels");
if (implicit_labels != res.props.end() && res.opts.want_kripke) if (implicit_labels)
error(implicit_labels->second,
"Kripke structure may not use implicit labels");
if (implicit_labels != res.props.end())
{ {
if (explicit_labels == res.props.end()) if (res.opts.want_kripke)
error(implicit_labels.loc,
"Kripke structure may not use implicit labels");
if (explicit_labels)
{ {
res.label_style = Implicit_Labels; error(implicit_labels.loc,
"'properties: implicit-labels' is incompatible "
"with...");
error(explicit_labels.loc,
"... 'properties: explicit-labels'.");
} }
else else
{ {
error(implicit_labels->second, res.label_style = Implicit_Labels;
"'properties: implicit-labels' is incompatible "
"with...");
error(explicit_labels->second,
"... 'properties: explicit-labels'.");
} }
} }
auto trans_labels = res.props.find("trans-labels"); auto trans_labels = res.prop_is_true("trans-labels");
auto state_labels = res.props.find("state-labels"); auto state_labels = res.prop_is_true("state-labels");
if (trans_labels != res.props.end() && res.opts.want_kripke) if (trans_labels)
error(trans_labels->second,
"Kripke structure may not use transition labels");
if (trans_labels != res.props.end())
{ {
if (state_labels == res.props.end()) if (res.opts.want_kripke)
error(trans_labels.loc,
"Kripke structure may not use transition labels");
if (state_labels)
{
error(trans_labels.loc,
"'properties: trans-labels' is incompatible with...");
error(state_labels.loc,
"... 'properties: state-labels'.");
}
else
{ {
if (res.label_style != Implicit_Labels) if (res.label_style != Implicit_Labels)
res.label_style = Trans_Labels; res.label_style = Trans_Labels;
} }
else
{
error(trans_labels->second,
"'properties: trans-labels' is incompatible with...");
error(state_labels->second,
"... 'properties: state-labels'.");
}
} }
else if (state_labels != res.props.end()) else if (state_labels)
{ {
if (res.label_style != Implicit_Labels) if (res.label_style != Implicit_Labels)
{ {
@ -376,9 +393,9 @@ header: format-version header-items
} }
else else
{ {
error(state_labels->second, error(state_labels.loc,
"'properties: state-labels' is incompatible with..."); "'properties: state-labels' is incompatible with...");
error(implicit_labels->second, error(implicit_labels.loc,
"... 'properties: implicit-labels'."); "... 'properties: implicit-labels'.");
} }
} }
@ -387,35 +404,35 @@ header: format-version header-items
error(@$, error(@$,
"Kripke structure should use 'properties: state-labels'"); "Kripke structure should use 'properties: state-labels'");
auto state_acc = res.props.find("state-acc"); auto state_acc = res.prop_is_true("state-acc");
auto trans_acc = res.props.find("trans-acc"); auto trans_acc = res.prop_is_true("trans-acc");
if (trans_acc != res.props.end()) if (trans_acc)
{ {
if (state_acc == res.props.end()) if (state_acc)
{ {
res.acc_style = Trans_Acc; error(trans_acc.loc,
"'properties: trans-acc' is incompatible with...");
error(state_acc.loc,
"... 'properties: state-acc'.");
} }
else else
{ {
error(trans_acc->second, res.acc_style = Trans_Acc;
"'properties: trans-acc' is incompatible with...");
error(state_acc->second,
"... 'properties: state-acc'.");
} }
} }
else if (state_acc != res.props.end()) else if (state_acc)
{ {
res.acc_style = State_Acc; res.acc_style = State_Acc;
} }
} }
{ {
unsigned ss = res.start.size(); unsigned ss = res.start.size();
auto det = res.props.find("deterministic"); auto det = res.prop_is_true("deterministic");
if (ss > 1) if (ss > 1)
{ {
if (det != res.props.end()) if (det)
{ {
error(det->second, error(det.loc,
"deterministic automata should have at most " "deterministic automata should have at most "
"one initial state"); "one initial state");
} }
@ -427,12 +444,12 @@ header: format-version header-items
// wrong, or unless we are building a Kripke structure. // wrong, or unless we are building a Kripke structure.
res.deterministic = !res.opts.want_kripke; res.deterministic = !res.opts.want_kripke;
} }
auto complete = res.props.find("complete"); auto complete = res.prop_is_true("complete");
if (ss < 1) if (ss < 1)
{ {
if (complete != res.props.end()) if (complete)
{ {
error(complete->second, error(complete.loc,
"complete automata should have at least " "complete automata should have at least "
"one initial state"); "one initial state");
} }
@ -447,9 +464,8 @@ header: format-version header-items
// if ap_count == 0, then a Kripke structure could be // if ap_count == 0, then a Kripke structure could be
// declared complete, although that probably doesn't // declared complete, although that probably doesn't
// matter. // matter.
if (res.opts.want_kripke && complete != res.props.end() if (res.opts.want_kripke && complete && res.ap_count > 0)
&& res.ap_count > 0) error(complete.loc,
error(complete->second,
"Kripke structure may not be complete"); "Kripke structure may not be complete");
} }
if (res.opts.trust_hoa) if (res.opts.trust_hoa)
@ -457,25 +473,68 @@ header: format-version header-items
auto& a = res.aut_or_ks; auto& a = res.aut_or_ks;
auto& p = res.props; auto& p = res.props;
auto e = p.end(); auto e = p.end();
if (p.find("stutter-invariant") != e) auto si = p.find("stutter-invariant");
if (si != e)
{ {
a->prop_stutter_invariant(true); a->prop_stutter_invariant(si->second.val);
auto i = p.find("stutter-sensitive"); auto i = p.find("stutter-sensitive");
if (i != e) if (i != e && si->second.val == i->second.val)
error(i->second, error(i->second.loc,
"automaton cannot be both stutter-invariant" "automaton cannot be both stutter-invariant"
"and stutter-sensitive"); "and stutter-sensitive");
} }
if (p.find("stutter-sensitive") != e) else
a->prop_stutter_invariant(false); {
if (p.find("inherently-weak") != e) auto ss = p.find("stutter-sensitive");
a->prop_inherently_weak(true); if (ss != e)
if (p.find("weak") != e) a->prop_stutter_invariant(!ss->second.val);
a->prop_weak(true); }
if (p.find("terminal") != e) auto iw = p.find("inherently-weak");
a->prop_terminal(true); auto w = p.find("weak");
if (p.find("unambiguous") != e) auto t = p.find("terminal");
a->prop_unambiguous(true); if (iw != e)
{
a->prop_inherently_weak(iw->second.val);
if (w != e && !iw->second.val && w->second.val)
{
error(w->second.loc, "'properties: weak' contradicts...");
error(iw->second.loc,
"... 'properties: !inherently-weak' given here");
}
if (t != e && !iw->second.val && t->second.val)
{
error(t->second.loc,
"'properties: terminal' contradicts...");
error(iw->second.loc,
"... 'properties: !inherently-weak' given here");
}
}
if (w != e)
{
a->prop_weak(w->second.val);
if (t != e && !w->second.val && t->second.val)
{
error(t->second.loc,
"'properties: terminal' contradicts...");
error(w->second.loc,
"... 'properties: !weak' given here");
}
}
if (t != e)
a->prop_terminal(t->second.val);
auto u = p.find("unambiguous");
if (u != e)
{
a->prop_unambiguous(u->second.val);
auto d = p.find("deterministic");
if (d != e && !u->second.val && d->second.val)
{
error(d->second.loc,
"'properties: deterministic' contradicts...");
error(u->second.loc,
"... 'properties: !unambiguous' given here");
}
}
} }
} }
@ -654,9 +713,34 @@ acc-spec: | acc-spec BOOLEAN
} }
properties: | properties IDENTIFIER properties: | properties IDENTIFIER
{ {
res.props.emplace(*$2, @2); auto pos = res.props.emplace(*$2, result_::prop_info{@2, true});
if (!pos.first->second.val)
{
std::ostringstream out(std::ios_base::ate);
error(@2, std::string("'properties: ")
+ *$2 + "' contradicts...");
error(pos.first->second.loc,
std::string("... 'properties: !") + *$2
+ "' previously given here.");
}
delete $2; delete $2;
} }
| properties '!' IDENTIFIER
{
auto loc = @2 + @3;
auto pos =
res.props.emplace(*$3, result_::prop_info{loc, false});
if (pos.first->second.val)
{
std::ostringstream out(std::ios_base::ate);
error(loc, std::string("'properties: !")
+ *$3 + "' contradicts...");
error(pos.first->second.loc,
std::string("... 'properties: ") + *$3
+ "' previously given here.");
}
delete $3;
}
header-spec: | header-spec BOOLEAN header-spec: | header-spec BOOLEAN
| header-spec INT | header-spec INT
| header-spec STRING | header-spec STRING
@ -918,22 +1002,20 @@ states: | states state
if (res.deterministic && !det) if (res.deterministic && !det)
{ {
res.deterministic = false; res.deterministic = false;
auto p = res.props.find("deterministic"); if (auto p = res.prop_is_true("deterministic"))
if (p != res.props.end())
{ {
error(@2, "automaton is not deterministic..."); error(@2, "automaton is not deterministic...");
error(p->second, error(p.loc,
"... despite 'properties: deterministic'"); "... despite 'properties: deterministic'");
} }
} }
if (res.complete && available != bddfalse) if (res.complete && available != bddfalse)
{ {
res.complete = false; res.complete = false;
auto p = res.props.find("complete"); if (auto p = res.prop_is_true("complete"))
if (p != res.props.end())
{ {
error(@2, "automaton is not complete..."); error(@2, "automaton is not complete...");
error(p->second, "... despite 'properties: complete'"); error(p.loc, "... despite 'properties: complete'");
} }
} }
} }
@ -950,7 +1032,7 @@ state: state-name labeled-edges
{ {
error(@2, "these transitions have implicit labels but the" error(@2, "these transitions have implicit labels but the"
" automaton is..."); " automaton is...");
error(res.props["state-labels"], "... declared with " error(res.props["state-labels"].loc, "... declared with "
"'properties: state-labels'"); "'properties: state-labels'");
// Do not repeat this message. // Do not repeat this message.
res.label_style = Mixed_Labels; res.label_style = Mixed_Labels;
@ -1019,11 +1101,11 @@ state-label_opt: { res.has_state_label = false; }
error(@$, error(@$,
"state label used although the automaton was..."); "state label used although the automaton was...");
if (res.label_style == Trans_Labels) if (res.label_style == Trans_Labels)
error(res.props["trans-labels"], error(res.props["trans-labels"].loc,
"... declared with 'properties: trans-labels'" "... declared with 'properties: trans-labels'"
" here"); " here");
else else
error(res.props["implicit-labels"], error(res.props["implicit-labels"].loc,
"... declared with 'properties: implicit-labels'" "... declared with 'properties: implicit-labels'"
" here"); " here");
// Do not show this error anymore. // Do not show this error anymore.
@ -1045,11 +1127,11 @@ trans-label: label
error(@$, "transition label used although the " error(@$, "transition label used although the "
"automaton was..."); "automaton was...");
if (res.label_style == State_Labels) if (res.label_style == State_Labels)
error(res.props["state-labels"], error(res.props["state-labels"].loc,
"... declared with 'properties: state-labels' " "... declared with 'properties: state-labels' "
"here"); "here");
else else
error(res.props["implicit-labels"], error(res.props["implicit-labels"].loc,
"... declared with 'properties: implicit-labels'" "... declared with 'properties: implicit-labels'"
" here"); " here");
// Do not show this error anymore. // Do not show this error anymore.
@ -1094,7 +1176,7 @@ state-acc_opt:
if (res.acc_style == Trans_Acc) if (res.acc_style == Trans_Acc)
{ {
error(@$, "state-based acceptance used despite..."); error(@$, "state-based acceptance used despite...");
error(res.props["trans-acc"], error(res.props["trans-acc"].loc,
"... declaration of transition-based acceptance."); "... declaration of transition-based acceptance.");
res.acc_style = Mixed_Acc; res.acc_style = Mixed_Acc;
} }
@ -1110,7 +1192,7 @@ trans-acc_opt:
if (res.acc_style == State_Acc) if (res.acc_style == State_Acc)
{ {
error(@$, "trans-based acceptance used despite..."); error(@$, "trans-based acceptance used despite...");
error(res.props["state-acc"], error(res.props["state-acc"].loc,
"... declaration of state-based acceptance."); "... declaration of state-based acceptance.");
res.acc_style = Mixed_Acc; res.acc_style = Mixed_Acc;
} }

View file

@ -374,8 +374,8 @@ cat >input<<EOF
Start: 0 Start: 0
acc-name: generalized-Buchi 2 acc-name: generalized-Buchi 2
Acceptance: 2 (Inf(0) & Inf(1)) Acceptance: 2 (Inf(0) & Inf(1))
properties: implicit-labels explicit-labels /* ? */ complete properties: !foo implicit-labels explicit-labels /* ? */ complete
properties: trans-acc state-acc /* ? */ properties: trans-acc state-acc /* ? */ !complete foo
AP: 2 "a" "b" AP: 2 "a" "b"
--BODY-- --BODY--
State: 0 "foo" { 0 } State: 0 "foo" { 0 }
@ -415,6 +415,7 @@ cat >input<<EOF
acc-name: generalized-Buchi 2 acc-name: generalized-Buchi 2
Acceptance: 2 (Inf(0) & Inf(1)) Acceptance: 2 (Inf(0) & Inf(1))
properties: implicit-labels trans-acc properties: implicit-labels trans-acc
properties: deterministic !unambiguous
AP: 2 "a" "b" AP: 2 "a" "b"
--BODY-- --BODY--
State: 0 "foo" { 0 } State: 0 "foo" { 0 }
@ -430,16 +431,22 @@ cat >input<<EOF
EOF EOF
expecterr input <<EOF expecterr input <<EOF
input:7.17-31: 'properties: implicit-labels' is incompatible with... input:8.45-53: 'properties: !complete' contradicts...
input:7.33-47: ... 'properties: explicit-labels'. input:7.62-69: ... 'properties: complete' previously given here.
input:8.55-57: 'properties: foo' contradicts...
input:7.17-20: ... 'properties: !foo' previously given here.
input:7.22-36: 'properties: implicit-labels' is incompatible with...
input:7.38-52: ... 'properties: explicit-labels'.
input:8.17-25: 'properties: trans-acc' is incompatible with... input:8.17-25: 'properties: trans-acc' is incompatible with...
input:8.27-35: ... 'properties: state-acc'. input:8.27-35: ... 'properties: state-acc'.
input:16.7: too many transitions for this state, ignoring this one input:16.7: too many transitions for this state, ignoring this one
input:28.33-44: 'properties: state-labels' is incompatible with... input:28.33-44: 'properties: state-labels' is incompatible with...
input:28.17-31: ... 'properties: implicit-labels'. input:28.17-31: ... 'properties: implicit-labels'.
input:50.20-24: state-based acceptance used despite... input:48.17-29: 'properties: deterministic' contradicts...
input:48.31-42: ... 'properties: !unambiguous' given here
input:51.20-24: state-based acceptance used despite...
input:47.33-41: ... declaration of transition-based acceptance. input:47.33-41: ... declaration of transition-based acceptance.
input:58.7-9: transition label used although the automaton was... input:59.7-9: transition label used although the automaton was...
input:47.17-31: ... declared with 'properties: implicit-labels' here input:47.17-31: ... declared with 'properties: implicit-labels' here
EOF EOF
@ -1814,6 +1821,24 @@ State: 9
[0&!1] 9 [0&!1] 9
[0&!1] 5 [0&!1] 5
--END-- --END--
HOA: v1
States: 1
Start: 0
AP: 0
Acceptance: 0 t
properties: !inherently-weak !weak terminal
--BODY--
State: 0 0
--END--
HOA: v1
States: 1
Start: 0
AP: 0
Acceptance: 0 t
properties: !inherently-weak weak
--BODY--
State: 0 0
--END--
EOF EOF
expecterr input <<EOF expecterr input <<EOF
@ -1821,6 +1846,12 @@ input:3.1-8: initial state 0 has no definition
input:13.9: state 2 has no definition input:13.9: state 2 has no definition
input:17.7: state 5 has no definition input:17.7: state 5 has no definition
input:14.9: state 8 has no definition input:14.9: state 8 has no definition
input:36.36-43: 'properties: terminal' contradicts...
input:36.13-28: ... 'properties: !inherently-weak' given here
input:36.36-43: 'properties: terminal' contradicts...
input:36.30-34: ... 'properties: !weak' given here
input:45.30-33: 'properties: weak' contradicts...
input:45.13-28: ... 'properties: !inherently-weak' given here
EOF EOF