parse_aut: simplify the interface
* src/parseaut/public.hh, src/parseaut/parseaut.yy, src/parseaut/fmterror.cc: Add a raise_errors options. Remove the parse_strict() method. Store parse errors and filename in the output parsed_aut to simplify usage. * doc/org/tut20.org, doc/org/tut21.org, doc/org/tut30.org, src/bin/autfilt.cc, src/bin/common_hoaread.cc, src/bin/dstar2tgba.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc, src/tests/complementation.cc, src/tests/ikwiad.cc, src/tests/ltlcross3.test, src/tests/ltldo.test, wrap/python/spot.py, wrap/python/tests/parsetgba.py: Adjust usage. * NEWS: Mention the changes.
This commit is contained in:
parent
3d5d160635
commit
dee73ee342
18 changed files with 228 additions and 215 deletions
25
NEWS
25
NEWS
|
|
@ -22,11 +22,11 @@ New in spot 1.99.4a (not yet released)
|
||||||
|
|
||||||
Library:
|
Library:
|
||||||
|
|
||||||
* Rename dtgba_complement() to dtwa_complement(), rename the header
|
* dtgba_complement() was renamed to dtwa_complement(), moved to
|
||||||
as complement.hh, and restrict the purpose of this function to
|
complement.hh, and its purpose was restricted to just completing
|
||||||
just complete the automaton and complement its acceptance
|
the automaton and complementing its acceptance condition. Any
|
||||||
condition. Any further acceptance condition transformation
|
further acceptance condition transformation can be done with
|
||||||
can be done with to_generalized_buchi() or remove_fin().
|
to_generalized_buchi() or remove_fin().
|
||||||
|
|
||||||
* The remove_fin() has learnt how to better deal with automata that
|
* The remove_fin() has learnt how to better deal with automata that
|
||||||
are declared as weak. This code was previously in
|
are declared as weak. This code was previously in
|
||||||
|
|
@ -37,7 +37,7 @@ New in spot 1.99.4a (not yet released)
|
||||||
acceptance. The most visible effect is in the output of "ltl2tgba
|
acceptance. The most visible effect is in the output of "ltl2tgba
|
||||||
-s XXXa": it used to have 5 accepting states, it now has only one.
|
-s XXXa": it used to have 5 accepting states, it now has only one.
|
||||||
(Changing removing acceptance of those 4 states has no effect on
|
(Changing removing acceptance of those 4 states has no effect on
|
||||||
the language, but it speedup some algorithms like NDFS-based
|
the language, but it speeds up some algorithms like NDFS-based
|
||||||
emptiness checks, as discussed in our Spin'15 paper.)
|
emptiness checks, as discussed in our Spin'15 paper.)
|
||||||
|
|
||||||
* The HOA parser will diagnose any version that is not v1, unless it
|
* The HOA parser will diagnose any version that is not v1, unless it
|
||||||
|
|
@ -47,6 +47,12 @@ New in spot 1.99.4a (not yet released)
|
||||||
make it easier to introduce new options. One such new option is
|
make it easier to introduce new options. One such new option is
|
||||||
"trust_hoa": when true (the default) supported properties declared
|
"trust_hoa": when true (the default) supported properties declared
|
||||||
in HOA files are trusted even if they cannot be easily be verified.
|
in HOA files are trusted even if they cannot be easily be verified.
|
||||||
|
Another option "raise_errors" now replaces the method
|
||||||
|
automaton_stream_parser::parse_strict().
|
||||||
|
|
||||||
|
* The output of the automaton parser now include the list of parse
|
||||||
|
errors (that does not have to be passed as a parameters) and the
|
||||||
|
input filename (making the output of error message easier).
|
||||||
|
|
||||||
* renamings:
|
* renamings:
|
||||||
ltl_simplifier -> tl_simplifier
|
ltl_simplifier -> tl_simplifier
|
||||||
|
|
@ -54,9 +60,10 @@ New in spot 1.99.4a (not yet released)
|
||||||
tgba_sub_statistics::sub_transitions -> twa_sub_statistics::transitions
|
tgba_sub_statistics::sub_transitions -> twa_sub_statistics::transitions
|
||||||
tgba_run -> twa_run
|
tgba_run -> twa_run
|
||||||
reduce_run -> twa_run::reduce
|
reduce_run -> twa_run::reduce
|
||||||
replay_twa_run -> twa_run::replay
|
replay_tgba_run -> twa_run::replay
|
||||||
print_twa_run -> operator<<
|
print_tgba_run -> operator<<
|
||||||
twa_run_to_tgba -> twa_run::as_twa
|
tgba_run_to_tgba -> twa_run::as_twa
|
||||||
|
format_parse_aut_errors -> parsed_aut::format_errors
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,16 +124,15 @@ State: 4
|
||||||
* C++
|
* C++
|
||||||
|
|
||||||
Parsing an automaton is almost similar to [[file:tut01.org][parsing an LTL formula]]. The
|
Parsing an automaton is almost similar to [[file:tut01.org][parsing an LTL formula]]. The
|
||||||
=parse_aut()= function takes a filename, a reference to a
|
=parse_aut()= function takes a filename and a BDD dictionary (to be
|
||||||
=parse_aut_error_list= object to populate (should errors be found) and
|
discussed later on this page). It returns a shared pointer to a
|
||||||
a BDD dictionary (to be discussed later on this page). It returns a
|
structure that has a couple of important fields: =aborted= is a
|
||||||
shared pointer to a structure that has two fields: =aborted= is a
|
|
||||||
Boolean telling if the input automaton was voluntarily aborted (a
|
Boolean telling if the input automaton was voluntarily aborted (a
|
||||||
feature of [[file:hoa.org][the HOA format]]), and =aut= is the actual automaton. The
|
feature of [[file:hoa.org][the HOA format]]), =errors= is a list of syntax errors that
|
||||||
shared pointer returned by =parse_aut()= might be null (in which case
|
occurred while parsing the automaton (printing these errors is up to
|
||||||
the the =parse_aut_error_list= is guaranteed not to be empty), but
|
you), and =aut= is the actual automaton. The parser usually tries to
|
||||||
since the parser performs some error recovery it is likely that an
|
recover from errors, so =aut= may not be null even if =errors= is
|
||||||
automaton is returned even in the presence of parse errors.
|
non-empty.
|
||||||
|
|
||||||
#+BEGIN_SRC C++ :results verbatim :exports both
|
#+BEGIN_SRC C++ :results verbatim :exports both
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -144,10 +143,9 @@ automaton is returned even in the presence of parse errors.
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string input = "tut20.never";
|
std::string input = "tut20.never";
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
||||||
spot::parsed_aut_ptr pa = parse_aut(input, pel, dict);
|
spot::parsed_aut_ptr pa = parse_aut(input, dict);
|
||||||
if (spot::format_parse_aut_errors(std::cerr, input, pel))
|
if (pa->format_errors(std::cerr))
|
||||||
return 1;
|
return 1;
|
||||||
// This cannot occur when reading a never claim, but
|
// This cannot occur when reading a never claim, but
|
||||||
// it could while reading a HOA file.
|
// it could while reading a HOA file.
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,8 @@ corresponding BDD variable number, and then use for instance
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string input = "tut21.hoa";
|
std::string input = "tut21.hoa";
|
||||||
spot::parse_aut_error_list pel;
|
spot::parsed_aut_ptr pa = parse_aut(input, spot::make_bdd_dict());
|
||||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
if (pa->format_errors(std::cerr))
|
||||||
spot::parsed_aut_ptr pa = parse_aut(input, pel, dict);
|
|
||||||
if (spot::format_parse_aut_errors(std::cerr, input, pel))
|
|
||||||
return 1;
|
return 1;
|
||||||
// This cannot occur when reading a never claim, but
|
// This cannot occur when reading a never claim, but
|
||||||
// it could while reading a HOA file.
|
// it could while reading a HOA file.
|
||||||
|
|
|
||||||
|
|
@ -240,10 +240,8 @@ automaton to process.
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string input = "tut30.hoa";
|
std::string input = "tut30.hoa";
|
||||||
spot::parse_aut_error_list pel;
|
spot::parsed_aut_ptr pa = parse_aut(input, spot::make_bdd_dict());
|
||||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
if (pa->format_errors(std::cerr))
|
||||||
spot::parsed_aut_ptr pa = parse_aut(input, pel, dict);
|
|
||||||
if (spot::format_parse_aut_errors(std::cerr, input, pel))
|
|
||||||
return 1;
|
return 1;
|
||||||
if (pa->aborted)
|
if (pa->aborted)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -676,27 +676,22 @@ namespace
|
||||||
int
|
int
|
||||||
process_file(const char* filename)
|
process_file(const char* filename)
|
||||||
{
|
{
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||||
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
while (!abort_run)
|
while (!abort_run)
|
||||||
{
|
{
|
||||||
pel.clear();
|
auto haut = hp.parse(opt->dict);
|
||||||
auto haut = hp.parse(pel, opt->dict);
|
if (!haut->aut && haut->errors.empty())
|
||||||
if (!haut && pel.empty())
|
|
||||||
break;
|
break;
|
||||||
if (spot::format_parse_aut_errors(std::cerr, filename, pel))
|
if (haut->format_errors(std::cerr))
|
||||||
err = 2;
|
err = 2;
|
||||||
if (!haut)
|
if (!haut->aut)
|
||||||
error(2, 0, "failed to read automaton from %s", filename);
|
error(2, 0, "failed to read automaton from %s", filename);
|
||||||
else if (haut->aborted)
|
else if (haut->aborted)
|
||||||
err = std::max(err, aborted(haut, filename));
|
err = std::max(err, aborted(haut, filename));
|
||||||
else
|
else
|
||||||
process_automaton(haut, filename);
|
process_automaton(haut, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,13 @@ spot::automaton_parser_options opt_parse;
|
||||||
spot::twa_graph_ptr
|
spot::twa_graph_ptr
|
||||||
read_automaton(const char* filename, spot::bdd_dict_ptr& dict)
|
read_automaton(const char* filename, spot::bdd_dict_ptr& dict)
|
||||||
{
|
{
|
||||||
spot::parse_aut_error_list pel;
|
auto p = spot::parse_aut(filename, dict,
|
||||||
auto p = spot::parse_aut(filename, pel, dict,
|
|
||||||
spot::default_environment::instance(),
|
spot::default_environment::instance(),
|
||||||
opt_parse);
|
opt_parse);
|
||||||
if (spot::format_parse_aut_errors(std::cerr, filename, pel)
|
if (p->format_errors(std::cerr))
|
||||||
|| !p || p->aborted)
|
|
||||||
error(2, 0, "failed to read automaton from %s", filename);
|
error(2, 0, "failed to read automaton from %s", filename);
|
||||||
|
if (p->aborted)
|
||||||
|
error(2, 0, "failed to read automaton from %s (--ABORT-- read)", filename);
|
||||||
return std::move(p->aut);
|
return std::move(p->aut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,27 +171,22 @@ namespace
|
||||||
int
|
int
|
||||||
process_file(const char* filename)
|
process_file(const char* filename)
|
||||||
{
|
{
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||||
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
while (!abort_run)
|
while (!abort_run)
|
||||||
{
|
{
|
||||||
pel.clear();
|
auto haut = hp.parse(spot::make_bdd_dict());
|
||||||
auto haut = hp.parse(pel, spot::make_bdd_dict());
|
if (!haut->aut && haut->errors.empty())
|
||||||
if (!haut && pel.empty())
|
|
||||||
break;
|
break;
|
||||||
if (spot::format_parse_aut_errors(std::cerr, filename, pel))
|
if (haut->format_errors(std::cerr))
|
||||||
err = 2;
|
err = 2;
|
||||||
if (!haut)
|
if (!haut->aut)
|
||||||
error(2, 0, "failed to read automaton from %s", filename);
|
error(2, 0, "failed to read automaton from %s", filename);
|
||||||
else if (haut->aborted)
|
else if (haut->aborted)
|
||||||
err = std::max(err, aborted(haut, filename));
|
err = std::max(err, aborted(haut, filename));
|
||||||
else
|
else
|
||||||
process_automaton(haut, filename);
|
process_automaton(haut, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -547,28 +547,17 @@ namespace
|
||||||
problem = false;
|
problem = false;
|
||||||
es = 0;
|
es = 0;
|
||||||
|
|
||||||
spot::parse_aut_error_list pel;
|
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||||
std::string filename = output.val()->name();
|
|
||||||
auto aut = spot::parse_aut(filename, pel, dict,
|
|
||||||
spot::default_environment::instance(),
|
spot::default_environment::instance(),
|
||||||
opt_parse);
|
opt_parse);
|
||||||
if (!pel.empty())
|
if (!aut->errors.empty())
|
||||||
{
|
{
|
||||||
status_str = "parse error";
|
status_str = "parse error";
|
||||||
problem = true;
|
problem = true;
|
||||||
es = -1;
|
es = -1;
|
||||||
std::ostream& err = global_error();
|
std::ostream& err = global_error();
|
||||||
err << "error: failed to parse the produced automaton.\n";
|
err << "error: failed to parse the produced automaton.\n";
|
||||||
spot::format_parse_aut_errors(err, filename, pel);
|
aut->format_errors(err);
|
||||||
end_error();
|
|
||||||
res = nullptr;
|
|
||||||
}
|
|
||||||
else if (!aut)
|
|
||||||
{
|
|
||||||
status_str = "empty output";
|
|
||||||
problem = true;
|
|
||||||
es = -1;
|
|
||||||
global_error() << "error: empty output.\n";
|
|
||||||
end_error();
|
end_error();
|
||||||
res = nullptr;
|
res = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,25 +168,15 @@ namespace
|
||||||
else if (output.val())
|
else if (output.val())
|
||||||
{
|
{
|
||||||
problem = false;
|
problem = false;
|
||||||
|
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
std::string filename = output.val()->name();
|
|
||||||
auto aut = spot::parse_aut(filename, pel, dict,
|
|
||||||
spot::default_environment::instance(),
|
spot::default_environment::instance(),
|
||||||
opt_parse);
|
opt_parse);
|
||||||
if (!pel.empty())
|
if (!aut->errors.empty())
|
||||||
{
|
{
|
||||||
problem = true;
|
problem = true;
|
||||||
std::cerr << "error: failed to parse the automaton "
|
std::cerr << "error: failed to parse the automaton "
|
||||||
"produced by \"" << cmd << "\".\n";
|
"produced by \"" << cmd << "\".\n";
|
||||||
spot::format_parse_aut_errors(std::cerr, filename, pel);
|
aut->format_errors(std::cerr);
|
||||||
res = nullptr;
|
|
||||||
}
|
|
||||||
else if (!aut)
|
|
||||||
{
|
|
||||||
problem = true;
|
|
||||||
std::cerr << "error: command \"" << cmd
|
|
||||||
<< "\" produced an empty output.\n";
|
|
||||||
res = nullptr;
|
res = nullptr;
|
||||||
}
|
}
|
||||||
else if (aut->aborted)
|
else if (aut->aborted)
|
||||||
|
|
|
||||||
|
|
@ -23,18 +23,16 @@
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
bool
|
bool
|
||||||
format_parse_aut_errors(std::ostream& os,
|
parsed_aut::format_errors(std::ostream& os)
|
||||||
const std::string& filename,
|
|
||||||
parse_aut_error_list& error_list)
|
|
||||||
{
|
{
|
||||||
bool printed = false;
|
bool printed = false;
|
||||||
spot::parse_aut_error_list::iterator it;
|
spot::parse_aut_error_list::iterator it;
|
||||||
for (it = error_list.begin(); it != error_list.end(); ++it)
|
for (auto& err : errors)
|
||||||
{
|
{
|
||||||
if (filename != "-")
|
if (!filename.empty() && filename != "-")
|
||||||
os << filename << ':';
|
os << filename << ':';
|
||||||
os << it->first << ": ";
|
os << err.first << ": ";
|
||||||
os << it->second << std::endl;
|
os << err.second << std::endl;
|
||||||
printed = true;
|
printed = true;
|
||||||
}
|
}
|
||||||
return printed;
|
return printed;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
%name-prefix "hoayy"
|
%name-prefix "hoayy"
|
||||||
%debug
|
%debug
|
||||||
%error-verbose
|
%error-verbose
|
||||||
%lex-param { spot::parse_aut_error_list& error_list }
|
%lex-param { PARSE_ERROR_LIST }
|
||||||
%define api.location.type "spot::location"
|
%define api.location.type "spot::location"
|
||||||
|
|
||||||
%code requires
|
%code requires
|
||||||
|
|
@ -46,6 +46,10 @@
|
||||||
extern "C" int strverscmp(const char *s1, const char *s2);
|
extern "C" int strverscmp(const char *s1, const char *s2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Work around Bison not letting us write
|
||||||
|
// %lex-param { res.h->errors }
|
||||||
|
#define PARSE_ERROR_LIST res.h->errors
|
||||||
|
|
||||||
inline namespace hoayy_support
|
inline namespace hoayy_support
|
||||||
{
|
{
|
||||||
typedef std::map<int, bdd> map_t;
|
typedef std::map<int, bdd> map_t;
|
||||||
|
|
@ -142,7 +146,6 @@ extern "C" int strverscmp(const char *s1, const char *s2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%parse-param {spot::parse_aut_error_list& error_list}
|
|
||||||
%parse-param {result_& res}
|
%parse-param {result_& res}
|
||||||
%parse-param {spot::location initial_loc}
|
%parse-param {spot::location initial_loc}
|
||||||
|
|
||||||
|
|
@ -1433,7 +1436,7 @@ nc-formula: nc-formula-or-ident
|
||||||
here.end.column = here.begin.column + j.first.end.column - 1;
|
here.end.column = here.begin.column + j.first.end.column - 1;
|
||||||
here.begin.line += j.first.begin.line - 1;
|
here.begin.line += j.first.begin.line - 1;
|
||||||
here.begin.column += j.first.begin.column - 1;
|
here.begin.column += j.first.begin.column - 1;
|
||||||
error_list.emplace_back(here, j.second);
|
res.h->errors.emplace_back(here, j.second);
|
||||||
}
|
}
|
||||||
bdd cond = bddfalse;
|
bdd cond = bddfalse;
|
||||||
if (f)
|
if (f)
|
||||||
|
|
@ -1614,7 +1617,7 @@ lbtt-guard: STRING
|
||||||
here.end.column = here.begin.column + j.first.end.column - 1;
|
here.end.column = here.begin.column + j.first.end.column - 1;
|
||||||
here.begin.line += j.first.begin.line - 1;
|
here.begin.line += j.first.begin.line - 1;
|
||||||
here.begin.column += j.first.begin.column - 1;
|
here.begin.column += j.first.begin.column - 1;
|
||||||
error_list.emplace_back(here, j.second);
|
res.h->errors.emplace_back(here, j.second);
|
||||||
}
|
}
|
||||||
if (!f)
|
if (!f)
|
||||||
{
|
{
|
||||||
|
|
@ -1679,7 +1682,7 @@ void
|
||||||
hoayy::parser::error(const location_type& location,
|
hoayy::parser::error(const location_type& location,
|
||||||
const std::string& message)
|
const std::string& message)
|
||||||
{
|
{
|
||||||
error_list.emplace_back(location, message);
|
res.h->errors.emplace_back(location, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static spot::acc_cond::acc_code
|
static spot::acc_cond::acc_code
|
||||||
|
|
@ -1867,15 +1870,14 @@ static void fix_properties(result_& r)
|
||||||
r.h->aut->prop_state_based_acc();
|
r.h->aut->prop_state_based_acc();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_version(const result_& r,
|
static void check_version(const result_& r)
|
||||||
spot::parse_aut_error_list& error_list)
|
|
||||||
{
|
{
|
||||||
if (r.h->type != spot::parsed_aut_type::HOA)
|
if (r.h->type != spot::parsed_aut_type::HOA)
|
||||||
return;
|
return;
|
||||||
auto& v = r.format_version;
|
auto& v = r.format_version;
|
||||||
if (v.size() < 2 || v[0] != 'v' || v[1] < '1' || v[1] > '9')
|
if (v.size() < 2 || v[0] != 'v' || v[1] < '1' || v[1] > '9')
|
||||||
{
|
{
|
||||||
error_list.emplace_front(r.format_version_loc, "unknown HOA version");
|
r.h->errors.emplace_front(r.format_version_loc, "unknown HOA version");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const char* beg = &v[1];
|
const char* beg = &v[1];
|
||||||
|
|
@ -1883,17 +1885,18 @@ static void check_version(const result_& r,
|
||||||
long int vers = strtol(beg, &end, 10);
|
long int vers = strtol(beg, &end, 10);
|
||||||
if (vers != 1)
|
if (vers != 1)
|
||||||
{
|
{
|
||||||
error_list.emplace_front(r.format_version_loc, "unsupported HOA version");
|
r.h->errors.emplace_front(r.format_version_loc,
|
||||||
|
"unsupported HOA version");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
constexpr const char supported[] = "1";
|
constexpr const char supported[] = "1";
|
||||||
if (strverscmp(supported, beg) < 0 && !error_list.empty())
|
if (strverscmp(supported, beg) < 0 && !r.h->errors.empty())
|
||||||
{
|
{
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << "we can read HOA v" << supported
|
s << "we can read HOA v" << supported
|
||||||
<< " but this file uses " << v << "; this might "
|
<< " but this file uses " << v << "; this might "
|
||||||
<< "cause the following errors";
|
<< "cause the following errors";
|
||||||
error_list.emplace_front(r.format_version_loc, s.str());
|
r.h->errors.emplace_front(r.format_version_loc, s.str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1930,19 +1933,31 @@ namespace spot
|
||||||
hoayyclose();
|
hoayyclose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void raise_parse_error(const parsed_aut_ptr& pa)
|
||||||
|
{
|
||||||
|
if (pa->aborted)
|
||||||
|
pa->errors.emplace_back(pa->loc, "parsing aborted");
|
||||||
|
if (!pa->errors.empty())
|
||||||
|
{
|
||||||
|
std::ostringstream s;
|
||||||
|
if (pa->format_errors(s))
|
||||||
|
throw parse_error(s.str());
|
||||||
|
}
|
||||||
|
// It is possible that pa->aut == nullptr if we reach the end of a
|
||||||
|
// stream. It is not necessarily an error.
|
||||||
|
}
|
||||||
|
|
||||||
parsed_aut_ptr
|
parsed_aut_ptr
|
||||||
automaton_stream_parser::parse(parse_aut_error_list& error_list,
|
automaton_stream_parser::parse(const bdd_dict_ptr& dict,
|
||||||
const bdd_dict_ptr& dict,
|
|
||||||
environment& env)
|
environment& env)
|
||||||
{
|
{
|
||||||
restart:
|
restart:
|
||||||
result_ r;
|
result_ r;
|
||||||
r.opts = opts_;
|
r.opts = opts_;
|
||||||
r.h = std::make_shared<spot::parsed_aut>();
|
r.h = std::make_shared<spot::parsed_aut>(filename_);
|
||||||
r.h->aut = make_twa_graph(dict);
|
r.h->aut = make_twa_graph(dict);
|
||||||
r.env = &env;
|
r.env = &env;
|
||||||
hoayy::parser parser(error_list, r, last_loc);
|
hoayy::parser parser(r, last_loc);
|
||||||
static bool env_debug = !!getenv("SPOT_DEBUG_PARSER");
|
static bool env_debug = !!getenv("SPOT_DEBUG_PARSER");
|
||||||
parser.set_debug_level(opts_.debug || env_debug);
|
parser.set_debug_level(opts_.debug || env_debug);
|
||||||
hoayyreset();
|
hoayyreset();
|
||||||
|
|
@ -1957,7 +1972,7 @@ namespace spot
|
||||||
// Bison 3.0.2 lacks a += operator for locations.
|
// Bison 3.0.2 lacks a += operator for locations.
|
||||||
r.h->loc = r.h->loc + e.pos;
|
r.h->loc = r.h->loc + e.pos;
|
||||||
}
|
}
|
||||||
check_version(r, error_list);
|
check_version(r);
|
||||||
last_loc = r.h->loc;
|
last_loc = r.h->loc;
|
||||||
last_loc.step();
|
last_loc.step();
|
||||||
if (r.h->aborted)
|
if (r.h->aborted)
|
||||||
|
|
@ -1966,8 +1981,10 @@ namespace spot
|
||||||
goto restart;
|
goto restart;
|
||||||
return r.h;
|
return r.h;
|
||||||
}
|
}
|
||||||
|
if (opts_.raise_errors)
|
||||||
|
raise_parse_error(r.h);
|
||||||
if (!r.h->aut)
|
if (!r.h->aut)
|
||||||
return nullptr;
|
return r.h;
|
||||||
if (r.state_names)
|
if (r.state_names)
|
||||||
r.h->aut->set_named_prop("state-names", r.state_names);
|
r.h->aut->set_named_prop("state-names", r.state_names);
|
||||||
fix_acceptance(r);
|
fix_acceptance(r);
|
||||||
|
|
@ -1976,27 +1993,34 @@ namespace spot
|
||||||
return r.h;
|
return r.h;
|
||||||
};
|
};
|
||||||
|
|
||||||
twa_graph_ptr
|
parsed_aut_ptr
|
||||||
automaton_stream_parser::parse_strict(const bdd_dict_ptr& dict,
|
parse_aut(const std::string& filename, const bdd_dict_ptr& dict,
|
||||||
environment& env)
|
environment& env, automaton_parser_options opts)
|
||||||
{
|
{
|
||||||
parse_aut_error_list pel;
|
auto localopts = opts;
|
||||||
auto a = parse(pel, dict, env);
|
localopts.raise_errors = false;
|
||||||
|
parsed_aut_ptr pa;
|
||||||
if (!pel.empty())
|
try
|
||||||
{
|
{
|
||||||
std::ostringstream s;
|
automaton_stream_parser p(filename, localopts);
|
||||||
if (format_parse_aut_errors(s, filename_, pel))
|
pa = p.parse(dict, env);
|
||||||
throw parse_error(s.str());
|
|
||||||
}
|
}
|
||||||
if (!a)
|
catch (std::runtime_error& e)
|
||||||
return nullptr;
|
{
|
||||||
|
if (opts.raise_errors)
|
||||||
if (a->aborted)
|
throw;
|
||||||
throw parse_error("parsing aborted");
|
parsed_aut_ptr pa = std::make_shared<spot::parsed_aut>(filename);
|
||||||
|
pa->errors.emplace_back(spot::location(), e.what());
|
||||||
return a->aut;
|
return pa;
|
||||||
|
}
|
||||||
|
if (!pa->aut && pa->errors.empty())
|
||||||
|
pa->errors.emplace_back(pa->loc, "no automaton read (empty input?)");
|
||||||
|
if (opts.raise_errors)
|
||||||
|
raise_parse_error(pa);
|
||||||
|
return pa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
|
|
|
||||||
|
|
@ -45,70 +45,58 @@ namespace spot
|
||||||
|
|
||||||
enum class parsed_aut_type { HOA, NeverClaim, LBTT, DRA, DSA, Unknown };
|
enum class parsed_aut_type { HOA, NeverClaim, LBTT, DRA, DSA, Unknown };
|
||||||
|
|
||||||
/// \brief Temporary encoding of an omega automaton produced by
|
/// \brief Result of the automaton parser
|
||||||
/// the parser.
|
struct SPOT_API parsed_aut final
|
||||||
struct SPOT_API parsed_aut
|
|
||||||
{
|
{
|
||||||
// Transition structure of the automaton.
|
/// \brief The parsed automaton.
|
||||||
// This is encoded as a TGBA without acceptance condition.
|
///
|
||||||
|
/// May be null if the parser reached the end of the stream or a
|
||||||
|
/// serious error. In the latter case, \c errors is non-empty.
|
||||||
twa_graph_ptr aut;
|
twa_graph_ptr aut;
|
||||||
|
/// Whether an HOA file was termined with <code>--ABORT</code>
|
||||||
bool aborted = false;
|
bool aborted = false;
|
||||||
|
/// Location of the automaton in the stream.
|
||||||
spot::location loc;
|
spot::location loc;
|
||||||
|
/// Format of the automaton.
|
||||||
parsed_aut_type type = parsed_aut_type::Unknown;
|
parsed_aut_type type = parsed_aut_type::Unknown;
|
||||||
|
/// Name of the stream (used for displaying syntax errors)
|
||||||
|
const std::string filename;
|
||||||
|
/// \brief Syntax errors that occurred during parsing.
|
||||||
|
///
|
||||||
|
/// Note that the parser does not print any diagnostic.
|
||||||
|
/// Deciding how to output those errors is up to you.
|
||||||
|
parse_aut_error_list errors;
|
||||||
|
|
||||||
|
parsed_aut(const std::string& str)
|
||||||
|
: filename(str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/// \brief Format diagnostics produced by spot::parse_aut.
|
||||||
|
/// \param os Where diagnostics should be output.
|
||||||
|
/// \return \c true iff any diagnostic was output.
|
||||||
|
bool format_errors(std::ostream& os);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<parsed_aut> parsed_aut_ptr;
|
typedef std::shared_ptr<parsed_aut> parsed_aut_ptr;
|
||||||
typedef std::shared_ptr<const parsed_aut> const_parsed_aut_ptr;
|
typedef std::shared_ptr<const parsed_aut> const_parsed_aut_ptr;
|
||||||
|
|
||||||
struct automaton_parser_options
|
struct automaton_parser_options final
|
||||||
{
|
{
|
||||||
bool ignore_abort = false; ///< Skip aborted automata
|
bool ignore_abort = false; ///< Skip aborted automata
|
||||||
bool debug = false; ///< Run the parser in debug mode?
|
bool debug = false; ///< Run the parser in debug mode?
|
||||||
bool trust_hoa = true; ///< Trust properties in HOA files
|
bool trust_hoa = true; ///< Trust properties in HOA files
|
||||||
|
bool raise_errors = false; ///< Raise errors as exceptions.
|
||||||
};
|
};
|
||||||
|
|
||||||
class SPOT_API automaton_stream_parser
|
/// \brief Parse a stream of automata
|
||||||
{
|
|
||||||
spot::location last_loc;
|
|
||||||
std::string filename_;
|
|
||||||
automaton_parser_options opts_;
|
|
||||||
public:
|
|
||||||
automaton_stream_parser(const std::string& filename,
|
|
||||||
automaton_parser_options opts = {});
|
|
||||||
// Read from an already open file descriptor.
|
|
||||||
// Use filename in error messages.
|
|
||||||
automaton_stream_parser(int fd, const std::string& filename,
|
|
||||||
automaton_parser_options opts = {});
|
|
||||||
// Read from a buffer
|
|
||||||
automaton_stream_parser(const char* data,
|
|
||||||
const std::string& filename,
|
|
||||||
automaton_parser_options opts = {});
|
|
||||||
~automaton_stream_parser();
|
|
||||||
parsed_aut_ptr parse(parse_aut_error_list& error_list,
|
|
||||||
const bdd_dict_ptr& dict,
|
|
||||||
environment& env =
|
|
||||||
default_environment::instance());
|
|
||||||
// Raises a parse_error on any syntax error
|
|
||||||
twa_graph_ptr parse_strict(const bdd_dict_ptr& dict,
|
|
||||||
environment& env =
|
|
||||||
default_environment::instance());
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Build a spot::twa_graph from a HOA file or a neverclaim.
|
|
||||||
/// \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 opts Additional options to pass to the parser.
|
|
||||||
/// \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
|
/// This object should be constructed for a given stream (a file, a
|
||||||
/// return a non zero value even if it encountered error during the
|
/// file descriptor, or a raw buffer), and then it parse() method
|
||||||
/// parsing of \a filename. If you want to make sure \a filename
|
/// may be called in a loop to parse each automaton in the stream.
|
||||||
/// was parsed succesfully, check \a error_list for emptiness.
|
///
|
||||||
|
/// Several input formats are supported, and automatically
|
||||||
|
/// recognized: HOA, LBTT, DSTAR, or neverclaim. We recommend
|
||||||
|
/// using the HOA format, because it is the most general.
|
||||||
///
|
///
|
||||||
/// The specification of the HOA format can be found at
|
/// The specification of the HOA format can be found at
|
||||||
/// http://adl.github.io/hoaf/
|
/// http://adl.github.io/hoaf/
|
||||||
|
|
@ -119,37 +107,78 @@ namespace spot
|
||||||
/// tool that produce Büchi automata in the form of a neverclaim,
|
/// tool that produce Büchi automata in the form of a neverclaim,
|
||||||
/// but is not understood by this parser, please report it to
|
/// but is not understood by this parser, please report it to
|
||||||
/// spot@lrde.epita.fr.
|
/// spot@lrde.epita.fr.
|
||||||
|
class SPOT_API automaton_stream_parser final
|
||||||
|
{
|
||||||
|
spot::location last_loc;
|
||||||
|
std::string filename_;
|
||||||
|
automaton_parser_options opts_;
|
||||||
|
public:
|
||||||
|
/// \brief Parse from a file opened file descriptor.
|
||||||
|
///
|
||||||
|
/// \param filename The file to read from.
|
||||||
|
/// \param opts Parser options.
|
||||||
|
automaton_stream_parser(const std::string& filename,
|
||||||
|
automaton_parser_options opts = {});
|
||||||
|
|
||||||
|
/// \brief Parse from an already opened file descriptor.
|
||||||
|
///
|
||||||
|
/// \param fd The file descriptor to read from.
|
||||||
|
/// \param filename What to display in error messages.
|
||||||
|
/// \param opts Parser options.
|
||||||
|
automaton_stream_parser(int fd, const std::string& filename,
|
||||||
|
automaton_parser_options opts = {});
|
||||||
|
|
||||||
|
/// \brief Parse from a buffer
|
||||||
|
///
|
||||||
|
/// \param data The buffer to read from.
|
||||||
|
/// \param filename What to display in error messages.
|
||||||
|
/// \param opts Parser options.
|
||||||
|
automaton_stream_parser(const char* data,
|
||||||
|
const std::string& filename,
|
||||||
|
automaton_parser_options opts = {});
|
||||||
|
|
||||||
|
~automaton_stream_parser();
|
||||||
|
|
||||||
|
/// \brief Parse the next automaton in the stream.
|
||||||
|
///
|
||||||
|
/// Note that the parser usually tries to recover from errors. It
|
||||||
|
/// can return an automaton even if it encountered an error during
|
||||||
|
/// parsing. If you want to make sure the input was parsed
|
||||||
|
/// successfully, make sure \c errors is empty and \c aborted is
|
||||||
|
/// false in the result. (Testing \c aborted is obviously
|
||||||
|
/// superfluous if the parser is configured to skip aborted
|
||||||
|
/// automata.)
|
||||||
|
///
|
||||||
|
/// The \c aut field of the result can be null in two conditions:
|
||||||
|
/// some serious error occurred (in this case \c errors is non
|
||||||
|
/// empty), or the end of the stream was reached.
|
||||||
|
///
|
||||||
|
/// \warning This function is not reentrant.
|
||||||
|
parsed_aut_ptr parse(const bdd_dict_ptr& dict,
|
||||||
|
environment& env =
|
||||||
|
default_environment::instance());
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Read the first spot::twa_graph from a file.
|
||||||
|
/// \param filename The name of the file to parse.
|
||||||
|
/// \param dict The BDD dictionary where to use.
|
||||||
|
/// \param env The environment of atomic proposition into which parsing
|
||||||
|
/// should take place.
|
||||||
|
/// \param opts Additional options to pass to the parser.
|
||||||
|
/// \return A pointer to a \c parsed_aut structure.
|
||||||
|
///
|
||||||
|
/// This is a wrapper around spot::automaton_stream_parser that returns
|
||||||
|
/// the first automaton of the file. Empty inputs are reported as
|
||||||
|
/// syntax errors, so the \c aut field of the result is guaranteed not
|
||||||
|
/// to be null if \c errors is empty. (This is unlike
|
||||||
|
/// automaton_stream_parser::parse() where a null \c aut denots the
|
||||||
|
/// end of a stream.)
|
||||||
///
|
///
|
||||||
/// \warning This function is not reentrant.
|
/// \warning This function is not reentrant.
|
||||||
inline parsed_aut_ptr
|
SPOT_API parsed_aut_ptr
|
||||||
parse_aut(const std::string& filename,
|
parse_aut(const std::string& filename,
|
||||||
parse_aut_error_list& error_list,
|
|
||||||
const bdd_dict_ptr& dict,
|
const bdd_dict_ptr& dict,
|
||||||
environment& env = default_environment::instance(),
|
environment& env = default_environment::instance(),
|
||||||
automaton_parser_options opts = {})
|
automaton_parser_options opts = {});
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
automaton_stream_parser p(filename, opts);
|
|
||||||
return p.parse(error_list, dict, env);
|
|
||||||
}
|
|
||||||
catch (std::runtime_error& e)
|
|
||||||
{
|
|
||||||
error_list.emplace_back(spot::location(), e.what());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Format diagnostics produced by spot::parse_aut.
|
|
||||||
/// \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::parse while
|
|
||||||
/// parsing \a ltl_string.
|
|
||||||
/// \return \c true iff any diagnostic was output.
|
|
||||||
SPOT_API bool
|
|
||||||
format_parse_aut_errors(std::ostream& os,
|
|
||||||
const std::string& filename,
|
|
||||||
parse_aut_error_list& error_list);
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,9 +120,8 @@ int main(int argc, char* argv[])
|
||||||
if (print_automaton || print_safra)
|
if (print_automaton || print_safra)
|
||||||
{
|
{
|
||||||
spot::environment& env(spot::default_environment::instance());
|
spot::environment& env(spot::default_environment::instance());
|
||||||
spot::parse_aut_error_list pel;
|
auto h = spot::parse_aut(file, dict, env);
|
||||||
auto h = spot::parse_aut(file, pel, dict, env);
|
if (h->format_errors(std::cerr))
|
||||||
if (spot::format_parse_aut_errors(std::cerr, file, pel))
|
|
||||||
return 2;
|
return 2;
|
||||||
spot::twa_graph_ptr a = h->aut;
|
spot::twa_graph_ptr a = h->aut;
|
||||||
|
|
||||||
|
|
@ -176,10 +175,8 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spot::parse_aut_error_list pel;
|
auto h = spot::parse_aut(file, dict);
|
||||||
spot::environment& env(spot::default_environment::instance());
|
if (h->format_errors(std::cerr))
|
||||||
auto h = spot::parse_aut(file, pel, dict, env);
|
|
||||||
if (spot::format_parse_aut_errors(std::cerr, file, pel))
|
|
||||||
return 2;
|
return 2;
|
||||||
a = h->aut;
|
a = h->aut;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -577,13 +577,10 @@ checked_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
tm.start("reading -P's argument");
|
tm.start("reading -P's argument");
|
||||||
|
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
spot::automaton_parser_options opts;
|
spot::automaton_parser_options opts;
|
||||||
opts.debug = debug_opt;
|
opts.debug = debug_opt;
|
||||||
auto daut = spot::parse_aut(argv[formula_index] + 2, pel,
|
auto daut = spot::parse_aut(argv[formula_index] + 2, dict, env, opts);
|
||||||
dict, env, opts);
|
if (daut->format_errors(std::cerr))
|
||||||
if (spot::format_parse_aut_errors(std::cerr,
|
|
||||||
argv[formula_index] + 2, pel))
|
|
||||||
return 2;
|
return 2;
|
||||||
daut->aut->merge_edges();
|
daut->aut->merge_edges();
|
||||||
system_aut = daut->aut;
|
system_aut = daut->aut;
|
||||||
|
|
@ -929,13 +926,12 @@ checked_main(int argc, char** argv)
|
||||||
|
|
||||||
if (from_file)
|
if (from_file)
|
||||||
{
|
{
|
||||||
spot::parse_aut_error_list pel;
|
|
||||||
tm.start("parsing hoa");
|
tm.start("parsing hoa");
|
||||||
spot::automaton_parser_options opts;
|
spot::automaton_parser_options opts;
|
||||||
opts.debug = debug_opt;
|
opts.debug = debug_opt;
|
||||||
auto daut = spot::parse_aut(input, pel, dict, env, opts);
|
auto daut = spot::parse_aut(input, dict, env, opts);
|
||||||
tm.stop("parsing hoa");
|
tm.stop("parsing hoa");
|
||||||
if (spot::format_parse_aut_errors(std::cerr, input, pel))
|
if (daut->format_errors(std::cerr))
|
||||||
return 2;
|
return 2;
|
||||||
daut->aut->merge_edges();
|
daut->aut->merge_edges();
|
||||||
a = daut->aut;
|
a = daut->aut;
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ check_csv out.csv
|
||||||
|
|
||||||
# Diagnose empty automata, and make sure %% is correctly replaced by %
|
# Diagnose empty automata, and make sure %% is correctly replaced by %
|
||||||
run 1 ../../bin/ltlcross ': %f >%O; echo %%>foo' -f a 2>stderr
|
run 1 ../../bin/ltlcross ': %f >%O; echo %%>foo' -f a 2>stderr
|
||||||
test 2 = `grep -c 'error: empty output.' stderr`
|
test 2 = `grep -c ':.*empty input' stderr`
|
||||||
cat foo
|
cat foo
|
||||||
cat >expected<<EOF
|
cat >expected<<EOF
|
||||||
%
|
%
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ diff output expected
|
||||||
|
|
||||||
$ltldo ': %s; true>%O' -f GFa 2>stderr && exit 1
|
$ltldo ': %s; true>%O' -f GFa 2>stderr && exit 1
|
||||||
test $? = 2
|
test $? = 2
|
||||||
grep 'error: command ".*" produced an empty output.' stderr
|
grep ':.*empty input' stderr
|
||||||
grep 'ltldo: aborting' stderr
|
grep 'ltldo: aborting' stderr
|
||||||
|
|
||||||
$ltldo '{name} foo/bar/ltl2baextended' -f GFa 2>stderr && exit 1
|
$ltldo '{name} foo/bar/ltl2baextended' -f GFa 2>stderr && exit 1
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,7 @@ def automata(*sources, timeout=None, ignore_abort=True,
|
||||||
o.debug = debug
|
o.debug = debug
|
||||||
o.ignore_abort = ignore_abort
|
o.ignore_abort = ignore_abort
|
||||||
o.trust_hoa = trust_hoa
|
o.trust_hoa = trust_hoa
|
||||||
|
o.raise_errors = True
|
||||||
for filename in sources:
|
for filename in sources:
|
||||||
try:
|
try:
|
||||||
p = None
|
p = None
|
||||||
|
|
@ -409,7 +410,7 @@ def automata(*sources, timeout=None, ignore_abort=True,
|
||||||
a = True
|
a = True
|
||||||
while a:
|
while a:
|
||||||
# This returns None when we reach the end of the file.
|
# This returns None when we reach the end of the file.
|
||||||
a = p.parse_strict(_bdd_dict)
|
a = p.parse(_bdd_dict).aut
|
||||||
if a:
|
if a:
|
||||||
yield a
|
yield a
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,12 @@ out = open(filename, 'w+')
|
||||||
out.write(contents)
|
out.write(contents)
|
||||||
out.close()
|
out.close()
|
||||||
|
|
||||||
p = spot.empty_parse_aut_error_list()
|
a = spot.parse_aut(filename, spot.make_bdd_dict())
|
||||||
a = spot.parse_aut(filename, p, spot.make_bdd_dict())
|
|
||||||
|
|
||||||
assert not p
|
assert not a.errors
|
||||||
|
|
||||||
spot.print_dot(spot.get_cout(), a.aut)
|
spot.print_dot(spot.get_cout(), a.aut)
|
||||||
|
|
||||||
del p
|
|
||||||
del a
|
del a
|
||||||
|
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue