hoa: make the parser more resilient to errors
* src/hoaparse/hoaparse.yy: Improve error recovery, and fix location tracking in streams. * src/hoaparse/public.hh: Store the last location so that the next parse start at the correct position. * src/bin/autfilt.cc: Stop parsing a stream on irrecoverable errors. * src/tgbatest/hoaparse.test: Adjust tests.
This commit is contained in:
parent
392c527d31
commit
1d962f79ac
4 changed files with 152 additions and 80 deletions
|
|
@ -343,13 +343,14 @@ namespace
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
pel.clear();
|
||||||
auto haut = hp.parse(pel, b);
|
auto haut = hp.parse(pel, b);
|
||||||
if (!haut && pel.empty())
|
if (!haut && pel.empty())
|
||||||
break;
|
break;
|
||||||
if (spot::format_hoa_parse_errors(std::cerr, filename, pel))
|
if (spot::format_hoa_parse_errors(std::cerr, filename, pel))
|
||||||
err = 2;
|
err = 2;
|
||||||
if (!haut)
|
if (!haut)
|
||||||
error(0, 0, "failed to read automaton from %s", filename);
|
error(2, 0, "failed to read automaton from %s", filename);
|
||||||
else
|
else
|
||||||
process_automaton(haut, filename);
|
process_automaton(haut, filename);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,11 @@
|
||||||
#include "ltlast/constant.hh"
|
#include "ltlast/constant.hh"
|
||||||
#include "public.hh"
|
#include "public.hh"
|
||||||
|
|
||||||
|
// Note: because this parser is meant to be used on a stream of
|
||||||
|
// automata, it tries hard to recover from errors, so that we get a
|
||||||
|
// chance to reach the end of the current automaton in order to
|
||||||
|
// process the next one. Several variables below are used to keep
|
||||||
|
// track of various error conditions.
|
||||||
struct result_
|
struct result_
|
||||||
{
|
{
|
||||||
spot::hoa_aut_ptr h;
|
spot::hoa_aut_ptr h;
|
||||||
|
|
@ -45,6 +50,7 @@
|
||||||
spot::location state_label_loc;
|
spot::location state_label_loc;
|
||||||
spot::location start_loc;
|
spot::location start_loc;
|
||||||
spot::location accset_loc;
|
spot::location accset_loc;
|
||||||
|
spot::location last_loc;
|
||||||
unsigned cur_state;
|
unsigned cur_state;
|
||||||
int start = -1;
|
int start = -1;
|
||||||
int states = -1;
|
int states = -1;
|
||||||
|
|
@ -54,12 +60,23 @@
|
||||||
bdd cur_label;
|
bdd cur_label;
|
||||||
bool has_state_label = false;
|
bool has_state_label = false;
|
||||||
bool has_trans_label = false;
|
bool has_trans_label = false;
|
||||||
|
bool ignore_more_ap = false; // Set to true after the first "AP:"
|
||||||
|
// line has been read.
|
||||||
|
bool ignore_acc = false; // Set to true in case of missing
|
||||||
|
// Acceptance: lines.
|
||||||
|
bool ignore_acc_silent = false;
|
||||||
|
bool ignore_more_acc = false; // Set to true after the first
|
||||||
|
// "Acceptance:" line has been read.
|
||||||
spot::acc_cond::mark_t acc_state;
|
spot::acc_cond::mark_t acc_state;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
%parse-param {spot::hoa_parse_error_list& error_list}
|
%parse-param {spot::hoa_parse_error_list& error_list}
|
||||||
%parse-param {result_& res}
|
%parse-param {result_& res}
|
||||||
|
%parse-param {spot::location initial_loc}
|
||||||
|
|
||||||
|
%initial-action { @$ = initial_loc; }
|
||||||
|
|
||||||
%union
|
%union
|
||||||
{
|
{
|
||||||
std::string* str;
|
std::string* str;
|
||||||
|
|
@ -115,7 +132,11 @@
|
||||||
%printer { debug_stream() << $$; } <num>
|
%printer { debug_stream() << $$; } <num>
|
||||||
|
|
||||||
%%
|
%%
|
||||||
hoa: header "--BODY--" body "--END--" { YYACCEPT; }
|
hoa: header "--BODY--" body "--END--"
|
||||||
|
{
|
||||||
|
res.last_loc = @$;
|
||||||
|
YYACCEPT;
|
||||||
|
}
|
||||||
hoa: ENDOFFILE { YYABORT; }
|
hoa: ENDOFFILE { YYABORT; }
|
||||||
|
|
||||||
string_opt: | STRING
|
string_opt: | STRING
|
||||||
|
|
@ -126,7 +147,7 @@ header: format-version header-items
|
||||||
if (res.accset < 0)
|
if (res.accset < 0)
|
||||||
{
|
{
|
||||||
error(@$, "missing 'Acceptance:' header");
|
error(@$, "missing 'Acceptance:' header");
|
||||||
YYABORT;
|
res.ignore_acc = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,9 +181,9 @@ header-item: "States:" INT
|
||||||
error(res.start_loc,
|
error(res.start_loc,
|
||||||
"initial state number is larger than state count...");
|
"initial state number is larger than state count...");
|
||||||
error(@$, "... declared here.");
|
error(@$, "... declared here.");
|
||||||
YYABORT;
|
|
||||||
}
|
}
|
||||||
int missing = res.states - res.h->aut->num_states();
|
int missing = std::max(res.states, res.start + 1)
|
||||||
|
- res.h->aut->num_states();
|
||||||
assert(missing >= 0);
|
assert(missing >= 0);
|
||||||
res.h->aut->new_states(missing);
|
res.h->aut->new_states(missing);
|
||||||
}
|
}
|
||||||
|
|
@ -183,24 +204,31 @@ header-item: "States:" INT
|
||||||
res.start_loc = @$;
|
res.start_loc = @$;
|
||||||
}
|
}
|
||||||
| "AP:" INT {
|
| "AP:" INT {
|
||||||
if (res.ap_count != -1)
|
if (res.ignore_more_ap)
|
||||||
{
|
{
|
||||||
error(@1, "redeclaration of APs...");
|
error(@1, "ignoring this redeclaration of APs...");
|
||||||
error(res.ap_loc, "... previously defined here.");
|
error(res.ap_loc, "... previously declared here.");
|
||||||
YYABORT;
|
}
|
||||||
}
|
else
|
||||||
res.ap_count = $2; res.ap.reserve($2);
|
{
|
||||||
|
res.ap_count = $2;
|
||||||
|
res.ap.reserve($2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ap-names
|
ap-names
|
||||||
{
|
{
|
||||||
res.ap_loc = @1 + @2;
|
if (!res.ignore_more_ap)
|
||||||
if ((int) res.ap.size() != res.ap_count)
|
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
res.ap_loc = @1 + @2;
|
||||||
out << "found " << res.ap.size()
|
if ((int) res.ap.size() != res.ap_count)
|
||||||
<< " atomic propositions instead of the "
|
{
|
||||||
<< res.ap_count << " announced";
|
std::ostringstream out;
|
||||||
error(@$, out.str());
|
out << "found " << res.ap.size()
|
||||||
|
<< " atomic propositions instead of the "
|
||||||
|
<< res.ap_count << " announced";
|
||||||
|
error(@$, out.str());
|
||||||
|
}
|
||||||
|
res.ignore_more_ap = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| "Alias:" ANAME label-expr
|
| "Alias:" ANAME label-expr
|
||||||
|
|
@ -210,27 +238,30 @@ header-item: "States:" INT
|
||||||
}
|
}
|
||||||
| "Acceptance:" INT
|
| "Acceptance:" INT
|
||||||
{
|
{
|
||||||
if (res.accset >= 0)
|
if (res.ignore_more_acc)
|
||||||
{
|
{
|
||||||
error(@1 + @2, "redefinition of the acceptance condition...");
|
error(@1 + @2, "ignoring this redefinition of the "
|
||||||
error(res.accset_loc, "... previously defined here.");
|
"acceptance condition...");
|
||||||
YYABORT;
|
error(res.accset_loc, "... previously defined here.");
|
||||||
}
|
}
|
||||||
else if ($2 > 8 * sizeof(spot::acc_cond::mark_t::value_t))
|
else if ($2 > 8 * sizeof(spot::acc_cond::mark_t::value_t))
|
||||||
{
|
{
|
||||||
error(@1 + @2,
|
error(@1 + @2,
|
||||||
"this implementation cannot support such a large "
|
"this implementation cannot support such a large "
|
||||||
"number of acceptance sets");
|
"number of acceptance sets");
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.h->aut->acc().add_sets($2);
|
res.h->aut->acc().add_sets($2);
|
||||||
res.accset = $2;
|
res.accset = $2;
|
||||||
res.accset_loc = @1 + @2;
|
res.accset_loc = @1 + @2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acceptance-cond
|
acceptance-cond
|
||||||
|
{
|
||||||
|
res.ignore_more_acc = true;
|
||||||
|
}
|
||||||
| "acc-name:" IDENTIFIER acc-spec
|
| "acc-name:" IDENTIFIER acc-spec
|
||||||
{
|
{
|
||||||
delete $2;
|
delete $2;
|
||||||
|
|
@ -249,20 +280,24 @@ header-item: "States:" INT
|
||||||
ap-names: | ap-names ap-name
|
ap-names: | ap-names ap-name
|
||||||
ap-name: STRING
|
ap-name: STRING
|
||||||
{
|
{
|
||||||
auto f = res.env->require(*$1);
|
if (!res.ignore_more_ap)
|
||||||
if (f == nullptr)
|
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
auto f = res.env->require(*$1);
|
||||||
out << "unknown atomic proposition \"" << *$1 << "\"";
|
if (f == nullptr)
|
||||||
delete $1;
|
{
|
||||||
error(@1, out.str());
|
std::ostringstream out;
|
||||||
YYABORT;
|
out << "unknown atomic proposition \"" << *$1 << "\"";
|
||||||
|
delete $1;
|
||||||
|
error(@1, out.str());
|
||||||
|
f = spot::ltl::default_environment::instance()
|
||||||
|
.require("$unknown$");
|
||||||
|
}
|
||||||
|
auto b =
|
||||||
|
res.h->aut->get_dict()->register_proposition(f, res.h->aut);
|
||||||
|
f->destroy();
|
||||||
|
res.ap.push_back(b);
|
||||||
}
|
}
|
||||||
delete $1;
|
delete $1;
|
||||||
auto b =
|
|
||||||
res.h->aut->get_dict()->register_proposition(f, res.h->aut);
|
|
||||||
f->destroy();
|
|
||||||
res.ap.push_back(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acc-spec: | acc-spec BOOLEAN
|
acc-spec: | acc-spec BOOLEAN
|
||||||
|
|
@ -343,24 +378,30 @@ acc-set: INT
|
||||||
{
|
{
|
||||||
if ((int) $1 >= res.accset)
|
if ((int) $1 >= res.accset)
|
||||||
{
|
{
|
||||||
error(@1, "number is larger than the count "
|
if (!res.ignore_acc)
|
||||||
"of acceptance sets...");
|
{
|
||||||
error(res.accset_loc, "... declared here.");
|
error(@1, "number is larger than the count "
|
||||||
YYABORT;
|
"of acceptance sets...");
|
||||||
|
error(res.accset_loc, "... declared here.");
|
||||||
|
}
|
||||||
|
$$ = -1U;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
}
|
}
|
||||||
$$ = $1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptance-cond: IDENTIFIER '(' acc-set ')'
|
acceptance-cond: IDENTIFIER '(' acc-set ')'
|
||||||
{
|
{
|
||||||
if (*$1 != "Inf")
|
if (!res.ignore_more_acc && *$1 != "Inf")
|
||||||
error(@1, "this implementation only supports "
|
error(@1, "this implementation only supports "
|
||||||
"'Inf' acceptance");
|
"'Inf' acceptance");
|
||||||
delete $1;
|
delete $1;
|
||||||
}
|
}
|
||||||
| IDENTIFIER '(' '!' acc-set ')'
|
| IDENTIFIER '(' '!' acc-set ')'
|
||||||
{
|
{
|
||||||
error(@1, "this implementation does not support "
|
error(@3 + @4, "this implementation does not support "
|
||||||
"negated sets");
|
"negated sets");
|
||||||
delete $1;
|
delete $1;
|
||||||
}
|
}
|
||||||
|
|
@ -368,10 +409,17 @@ acceptance-cond: IDENTIFIER '(' acc-set ')'
|
||||||
| acceptance-cond '&' acceptance-cond
|
| acceptance-cond '&' acceptance-cond
|
||||||
| acceptance-cond '|' acceptance-cond
|
| acceptance-cond '|' acceptance-cond
|
||||||
{
|
{
|
||||||
error(@1, "this implementation does not support "
|
if (!res.ignore_more_acc)
|
||||||
"disjunction in acceptance conditions");
|
error(@2, "this implementation does not support "
|
||||||
|
"disjunction in acceptance conditions");
|
||||||
}
|
}
|
||||||
| BOOLEAN
|
| 't'
|
||||||
|
| 'f'
|
||||||
|
{
|
||||||
|
if (!res.ignore_more_acc)
|
||||||
|
error(@$, "this implementation does not support "
|
||||||
|
"false acceptance");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
body: states
|
body: states
|
||||||
|
|
@ -429,12 +477,31 @@ trans-label_opt: { res.has_trans_label = false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acc-sig_opt: { $$ = spot::acc_cond::mark_t(0U); }
|
acc-sig_opt:
|
||||||
| '{' acc-sets '}' { $$ = $2; }
|
{
|
||||||
acc-sets: { $$ = spot::acc_cond::mark_t(0U); }
|
$$ = spot::acc_cond::mark_t(0U);
|
||||||
|
}
|
||||||
|
| '{' acc-sets '}'
|
||||||
|
{
|
||||||
|
$$ = $2;
|
||||||
|
if (res.ignore_acc && !res.ignore_acc_silent)
|
||||||
|
{
|
||||||
|
error(@$, "ignoring acceptance sets because of "
|
||||||
|
"missing acceptance condition");
|
||||||
|
// Emit this message only once.
|
||||||
|
res.ignore_acc_silent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc-sets:
|
||||||
|
{
|
||||||
|
$$ = spot::acc_cond::mark_t(0U);
|
||||||
|
}
|
||||||
| acc-sets acc-set
|
| acc-sets acc-set
|
||||||
{
|
{
|
||||||
$$ = $1 | res.h->aut->acc().mark($2);
|
if (res.ignore_acc || $2 == -1U)
|
||||||
|
$$ = spot::acc_cond::mark_t(0U);
|
||||||
|
else
|
||||||
|
$$ = $1 | res.h->aut->acc().mark($2);
|
||||||
}
|
}
|
||||||
edges: | edges edge
|
edges: | edges edge
|
||||||
edge: trans-label_opt state-num acc-sig_opt
|
edge: trans-label_opt state-num acc-sig_opt
|
||||||
|
|
@ -487,10 +554,11 @@ namespace spot
|
||||||
r.h = std::make_shared<spot::hoa_aut>();
|
r.h = std::make_shared<spot::hoa_aut>();
|
||||||
r.h->aut = make_tgba_digraph(dict);
|
r.h->aut = make_tgba_digraph(dict);
|
||||||
r.env = &env;
|
r.env = &env;
|
||||||
hoayy::parser parser(error_list, r);
|
hoayy::parser parser(error_list, r, last_loc);
|
||||||
parser.set_debug_level(debug);
|
parser.set_debug_level(debug);
|
||||||
if (parser.parse())
|
if (parser.parse())
|
||||||
r.h->aut = nullptr;
|
r.h->aut = nullptr;
|
||||||
|
last_loc = r.last_loc;
|
||||||
if (!r.h->aut)
|
if (!r.h->aut)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (r.start != -1)
|
if (r.start != -1)
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ namespace spot
|
||||||
|
|
||||||
class SPOT_API hoa_stream_parser
|
class SPOT_API hoa_stream_parser
|
||||||
{
|
{
|
||||||
|
spot::location last_loc;
|
||||||
public:
|
public:
|
||||||
hoa_stream_parser(const std::string& filename);
|
hoa_stream_parser(const std::string& filename);
|
||||||
~hoa_stream_parser();
|
~hoa_stream_parser();
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ set -e
|
||||||
expecterr()
|
expecterr()
|
||||||
{
|
{
|
||||||
cat >$1.exp
|
cat >$1.exp
|
||||||
../ltl2tgba -XH $1 2>$1.err && exit 1
|
../../bin/autfilt $1 2>$1.err && exit 1
|
||||||
test $? = 2
|
test $? = 2
|
||||||
cat $1.err
|
cat $1.err
|
||||||
diff $1.err $1.exp
|
diff $1.err $1.exp
|
||||||
|
|
@ -100,8 +100,8 @@ Acceptance: 0 t
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
expecterr input <<EOF
|
expecterr input <<EOF
|
||||||
input:4.1-3: redeclaration of APs...
|
input:4.1-3: ignoring this redeclaration of APs...
|
||||||
input:2.1-5: ... previously defined here.
|
input:2.1-5: ... previously declared here.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >input <<EOF
|
cat >input <<EOF
|
||||||
|
|
@ -110,6 +110,8 @@ AP: 1 "a"
|
||||||
States: 0
|
States: 0
|
||||||
Start: 1
|
Start: 1
|
||||||
--BODY--
|
--BODY--
|
||||||
|
State: 0 {0 1}
|
||||||
|
[0] 0 {0}
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -117,13 +119,18 @@ expecterr input <<EOF
|
||||||
input:4.8: state number is larger than state count...
|
input:4.8: state number is larger than state count...
|
||||||
input:3.1-9: ... declared here.
|
input:3.1-9: ... declared here.
|
||||||
input:1.1-4.8: missing 'Acceptance:' header
|
input:1.1-4.8: missing 'Acceptance:' header
|
||||||
|
input:6.8: state number is larger than state count...
|
||||||
|
input:3.1-9: ... declared here.
|
||||||
|
input:6.10-14: ignoring acceptance sets because of missing acceptance condition
|
||||||
|
input:7.5: state number is larger than state count...
|
||||||
|
input:3.1-9: ... declared here.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >input <<EOF
|
cat >input <<EOF
|
||||||
HOA: v1
|
HOA: v1
|
||||||
AP: 1 "a"
|
AP: 1 "a"
|
||||||
Start: 0
|
Start: 1
|
||||||
States: 0
|
States: 1
|
||||||
--BODY--
|
--BODY--
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -131,6 +138,7 @@ EOF
|
||||||
expecterr input <<EOF
|
expecterr input <<EOF
|
||||||
input:3.1-8: initial state number is larger than state count...
|
input:3.1-8: initial state number is larger than state count...
|
||||||
input:4.1-9: ... declared here.
|
input:4.1-9: ... declared here.
|
||||||
|
input:1.1-4.9: missing 'Acceptance:' header
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >input <<EOF
|
cat >input <<EOF
|
||||||
|
|
@ -144,7 +152,7 @@ Acceptance: 1 Inf(0)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
expecterr input <<EOF
|
expecterr input <<EOF
|
||||||
input:5.1-13: redefinition of the acceptance condition...
|
input:5.1-13: ignoring this redefinition of the acceptance condition...
|
||||||
input:2.1-13: ... previously defined here.
|
input:2.1-13: ... previously defined here.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -179,20 +187,14 @@ State: 0 {0}
|
||||||
State: 1 {0}
|
State: 1 {0}
|
||||||
[t] 1
|
[t] 1
|
||||||
--END--
|
--END--
|
||||||
EOF
|
|
||||||
|
|
||||||
expecterr input <<EOF
|
|
||||||
input:5.19: number is larger than the count of acceptance sets...
|
|
||||||
input:5.1-13: ... declared here.
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat >input <<EOF
|
|
||||||
HOA: v2
|
HOA: v2
|
||||||
--BODY--
|
--BODY--
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
expecterr input <<EOF
|
expecterr input <<EOF
|
||||||
input:1.1-7: unsupported version of the HOA format
|
input:5.19: number is larger than the count of acceptance sets...
|
||||||
input:1.1-7: missing 'Acceptance:' header
|
input:5.1-13: ... declared here.
|
||||||
|
input:12.1-7: unsupported version of the HOA format
|
||||||
|
input:12.1-7: missing 'Acceptance:' header
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue