Commit 1d962f79 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

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.
parent 392c527d
...@@ -343,13 +343,14 @@ namespace ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ')' ...@@ -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; } ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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" ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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} ...@@ -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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment