Commit 22f442f7 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

parsetl: change the interface to return a parsed_formula

This gets the interface of all the functions parsing formula in line
with the interface of the automaton parser: both return a "parsed_*"
object (parsed_formula or parsed_automaton) that contains the said
object and its list of errors.  Doing so avoid having to declare the
parse_error_list in advance.

* spot/tl/parse.hh, spot/parsetl/parsetl.yy: Do the change.
* spot/parsetl/fmterror.cc: Adjust the error printer.
* NEWS: Document it.
* bin/common_finput.cc, bin/common_finput.hh, bin/ltlcross.cc,
bin/ltldo.cc, bin/ltlfilt.cc, doc/org/tut01.org, doc/org/tut02.org,
doc/org/tut10.org, doc/org/tut20.org, python/ajax/spotcgi.in,
python/spot/impl.i, spot/parseaut/parseaut.yy, tests/core/checkpsl.cc,
tests/core/checkta.cc, tests/core/consterm.cc, tests/core/emptchk.cc,
tests/core/equalsf.cc, tests/core/ikwiad.cc, tests/core/kind.cc,
tests/core/length.cc, tests/core/ltlprod.cc, tests/core/ltlrel.cc,
tests/core/randtgba.cc, tests/core/readltl.cc, tests/core/reduc.cc,
tests/core/safra.cc, tests/core/syntimpl.cc, tests/core/tostring.cc,
tests/ltsmin/modelcheck.cc, tests/python/alarm.py,
tests/python/interdep.py, tests/python/ltl2tgba.py,
tests/python/ltlparse.py: Adjust all uses.
parent cf4f58c3
......@@ -102,6 +102,32 @@ New in spot 1.99.7a (not yet released)
* The twa::transition_annotation() and
twa::compute_support_conditions() methods have been removed.
* The interface for all functions parsing formulas (LTL, PSL, SERE,
etc.) has been changed to use an interface similar to the one used
for parsing automata. These function now return a parsed_formula
object that includes both a formula and a list of syntax errors.
Typically a function written as
spot::formula read(std::string input)
{
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
exit(2);
return f;
}
should be updated to
spot::formula read(std::string input)
{
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
exit(2);
return pf.f;
}
Python:
* The ltsmin interface has been binded in Python. It also
......
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2012, 2013, 2014, 2015, 2016 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
......@@ -76,14 +76,14 @@ parse_opt_finput(int key, char* arg, struct argp_state*)
return 0;
}
spot::formula
parse_formula(const std::string& s, spot::parse_error_list& pel)
spot::parsed_formula
parse_formula(const std::string& s)
{
if (lbt_input)
return spot::parse_prefix_ltl(s, pel);
return spot::parse_prefix_ltl(s);
else
return spot::parse_infix_psl
(s, pel, spot::default_environment::instance(), false, lenient);
(s, spot::default_environment::instance(), false, lenient);
}
job_processor::job_processor()
......@@ -108,17 +108,16 @@ job_processor::process_string(const std::string& input,
const char* filename,
int linenum)
{
spot::parse_error_list pel;
auto f = parse_formula(input, pel);
auto pf = parse_formula(input);
if (!f || !pel.empty())
if (!pf.f || !pf.errors.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
pf.format_errors(std::cerr);
return 1;
}
return process_formula(f, filename, linenum);
return process_formula(pf.f, filename, linenum);
}
int
......
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
// Copyright (C) 2012, 2013, 2015, 2016 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -44,8 +44,7 @@ extern const struct argp finput_argp;
int parse_opt_finput(int key, char* arg, struct argp_state* state);
spot::formula
parse_formula(const std::string& s, spot::parse_error_list& error_list);
spot::parsed_formula parse_formula(const std::string& s);
class job_processor
......
......@@ -837,16 +837,15 @@ namespace
const char* filename,
int linenum)
{
spot::parse_error_list pel;
spot::formula f = parse_formula(input, pel);
if (!f || !pel.empty())
auto pf = parse_formula(input);
if (!pf.f || !pf.errors.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
pf.format_errors(std::cerr);
return 1;
}
auto f = pf.f;
int res = process_formula(f, filename, linenum);
......
......@@ -237,19 +237,18 @@ namespace
const char* filename,
int linenum)
{
spot::parse_error_list pel;
spot::formula f = parse_formula(input, pel);
spot::parsed_formula pf = parse_formula(input);
if (!f || !pel.empty())
if (!pf.f || !pf.errors.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
pf.format_errors(std::cerr);
return 1;
}
inputf = input;
process_formula(f, filename, linenum);
process_formula(pf.f, filename, linenum);
return 0;
}
......
......@@ -288,11 +288,10 @@ static std::string unabbreviate;
static spot::formula
parse_formula_arg(const std::string& input)
{
spot::parse_error_list pel;
spot::formula f = parse_formula(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = parse_formula(input);
if (pf.format_errors(std::cerr))
error(2, 0, "parse error when parsing an argument");
return f;
return pf.f;
}
static int
......@@ -482,20 +481,19 @@ namespace
process_string(const std::string& input,
const char* filename = nullptr, int linenum = 0)
{
spot::parse_error_list pel;
spot::formula f = parse_formula(input, pel);
spot::parsed_formula pf = parse_formula(input);
if (!f || pel.size() > 0)
if (!pf.f || !pf.errors.empty())
{
if (!ignore_errors)
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
pf.format_errors(std::cerr);
}
if (error_style == skip_errors)
std::cout << input << std::endl;
std::cout << input << '\n';
else
assert(error_style == drop_errors);
check_cout();
......@@ -503,7 +501,7 @@ namespace
}
try
{
return process_formula(f, filename, linenum);
return process_formula(pf.f, filename, linenum);
}
catch (const std::runtime_error& e)
{
......
......@@ -119,11 +119,10 @@ Here is how to call the infix parser explicitly:
int main()
{
std::string input = "[]<>p0 || <>[]p1";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
std::cout << f << '\n';
std::cout << pf.f << '\n';
return 0;
}
#+END_SRC
......@@ -131,26 +130,27 @@ Here is how to call the infix parser explicitly:
#+RESULTS:
: GFp0 | FGp1
So =parse_infix_psl()= processes =input=, and stores any diagnostic in
=pel=, which is a list of pairs associating each error to a location.
You could iterate over that list to print it by yourself as you wish,
or you can call =format_parse_errors()= to do that for you. Note that
as its name implies, this parser can read more than LTL formulas (the
fragment of PSL we support is basically LTL extended with regular
expressions).
Note that as its name implies, this parser can read more than LTL
formulas: the fragment of PSL we support is basically LTL extended
with regular expressions.
If =pel= is empty, =format_parse_errors()= will do nothing and return
false.
The =parse_infix_psl()= function processes =input=, and returns a
=spot::parsed_formula= object. In addition to the =spot::formula= we
desire (stored as the =spot::parsed_formula::f= attribute), the
=spot::parsed_formula= also stores any diagnostic collected during the
parsing. Those diagnostics are stored in the
=spot::parsed_formula::errors= attribute, but they can conveniently be
printed by calling the =spot::parsed::format_errors()= method: this
method returns true if and only if a diagnostic was output, so this is
usually used to abort the program with an error status as above.
If =pel= is non empty, =format_parse_errors()= will display the errors
messages and return true. In the above code, we have decided to
aborts the execution in this case.
However the parser usually tries to do some error recovery. For
instance if you have input =(a U b))= the parser will complain about
the extra parenthesis (=pel= not empty), but it will still return an
=f= that is equivalent to =a U b=. So you could decide to continue
with the "fixed" formula if you wish. Here is an example:
The parser usually tries to do some error recovery, so the =f=
attribute can be non-null even if some parsing errors where returned.
For instance if you have input =(a U b))= the parser will complain
about the extra parenthesis, but it will still return a formula that
is equivalent to =a U b=. So you could decide to continue with the
"fixed" formula if you wish. Here is an example:
#+BEGIN_SRC C++ :results verbatim :exports both
#include <string>
......@@ -161,14 +161,13 @@ with the "fixed" formula if you wish. Here is an example:
int main()
{
std::string input = "(a U b))";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
spot::parsed_formula pf = spot::parse_infix_psl(input);
// Use std::cout instead of std::cerr because we can only
// show the output of std::cout in this documentation.
(void) spot::format_parse_errors(std::cout, input, pel);
if (f == nullptr)
(void) pf.format_errors(std::cout);
if (pf.f == nullptr)
return 1;
std::cout << "Parsed formula: " << f << '\n';
std::cout << "Parsed formula: " << pf.f << '\n';
return 0;
}
#+END_SRC
......@@ -185,8 +184,8 @@ with the "fixed" formula if you wish. Here is an example:
: Parsed formula: a U b
The formula =f= is only returned as null when the parser really cannot
recover anything.
The formula =pf.f= would only be returned as null when the parser
really cannot recover anything.
** Calling the prefix parser explicitly
......@@ -202,10 +201,10 @@ of =parse_infix_psl()=.
int main()
{
std::string input = "& & G p0 p1 p2";
spot::parse_error_list pel;
spot::formula f = spot::parse_prefix_ltl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_prefix_ltl(input);
if (pf.format_errors(std::cerr))
return 1;
spot::formula f = pf.f;
print_latex_psl(std::cout, f) << '\n';
print_lbt_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, f, true) << '\n';
......@@ -246,11 +245,10 @@ For instance, let's see what happens if a PSL formulas is passed to
int main()
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
print_spin_ltl(std::cout, f) << '\n';
print_spin_ltl(std::cout, pf.f) << '\n';
return 0;
}
#+END_SRC
......@@ -276,10 +274,10 @@ The first is to simply diagnose non-LTL formulas.
int main()
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
spot::formula f = pf.f;
if (!f.is_ltl_formula())
{
std::cerr << "Only LTL formulas are supported.\n";
......@@ -306,10 +304,10 @@ prepared to reject the formula any way. In our example, we are lucky
int main()
{
std::string input = "{a*;b}<>->(a U (b & GF c))";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
spot::formula f = pf.f;
if (!f.is_ltl_formula())
{
spot::tl_simplifier simp;
......
......@@ -85,10 +85,10 @@ destructor.
int main()
{
std::string input = "\"Proc@Here\" U (\"var > 10\" | \"var < 4\")";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
spot::formula f = pf.f;
spot::relabeling_map m;
f = spot::relabel(f, spot::Pnn, &m);
for (auto& i: m)
......
......@@ -137,13 +137,12 @@ never claim is done via the =print_never_claim= function.
int main()
{
std::string input = "[]<>p0 || <>[]p1";
spot::parse_error_list pel;
spot::formula f = spot::parse_infix_psl(input, pel);
if (spot::format_parse_errors(std::cerr, input, pel))
spot::parsed_formula pf = spot::parse_infix_psl(input);
if (pf.format_errors(std::cerr))
return 1;
spot::translator trans;
trans.set_type(spot::postprocessor::BA);
spot::twa_graph_ptr aut = trans.run(f);
spot::twa_graph_ptr aut = trans.run(pf.f);
print_never_claim(std::cout, aut) << '\n';
return 0;
}
......@@ -154,23 +153,23 @@ never claim is done via the =print_never_claim= function.
never {
T0_init:
if
:: ((p1)) -> goto accept_S0
:: ((true)) -> goto T0_init
:: ((p0)) -> goto accept_S2
:: (p1) -> goto accept_S0
:: (true) -> goto T0_init
:: (p0) -> goto accept_S2
fi;
accept_S0:
if
:: ((p1)) -> goto accept_S0
:: (p1) -> goto accept_S0
fi;
accept_S2:
if
:: ((p0)) -> goto accept_S2
:: ((!(p0))) -> goto T0_S3
:: (p0) -> goto accept_S2
:: (!(p0)) -> goto T0_S3
fi;
T0_S3:
if
:: ((p0)) -> goto accept_S2
:: ((!(p0))) -> goto T0_S3
:: (p0) -> goto accept_S2
:: (!(p0)) -> goto T0_S3
fi;
}
#+end_example
......
......@@ -122,7 +122,7 @@ State: 4
* C++
Parsing an automaton is almost similar to [[file:tut01.org][parsing an LTL formula]]. The
Parsing an automaton is similar to [[file:tut01.org][parsing an LTL formula]]. The
=parse_aut()= function takes a filename and a BDD dictionary (to be
discussed later on this page). It returns a shared pointer to a
structure that has a couple of important fields: =aborted= is a
......
......@@ -435,19 +435,18 @@ for g in form.getlist('g'):
formula = form.getfirst('f', '')
env = spot.default_environment.instance()
pel = spot.empty_parse_error_list()
f = spot.parse_infix_psl(formula, pel, env)
pf = spot.parse_infix_psl(formula, env)
if pel:
if pf.errors:
# Try the LBT parser in case someone is throwing LBT formulas at us.
pel2 = spot.empty_parse_error_list()
g = spot.parse_prefix_ltl(formula, pel2, env)
if pel2:
pg = spot.parse_prefix_ltl(formula, env)
if pg.errors:
unbufprint('<div class="parse-error">')
err = spot.format_parse_errors(spot.get_cout(), formula, pel)
err = pf.format_errors(spot.get_cout())
unbufprint('</div>')
f = pf.f
else:
f = g
f = pg.f
# Do not continue if we could not parse anything sensible.
if not f:
......
......@@ -692,20 +692,6 @@ bool fnode_instances_check()
return spot::fnode::instances_check();
}
spot::parse_error_list
empty_parse_error_list()
{
parse_error_list l;
return l;
}
spot::parse_aut_error_list
empty_parse_aut_error_list()
{
parse_aut_error_list l;
return l;
}
spot::twa_graph_ptr
ensure_digraph(const spot::twa_ptr& a)
{
......
......@@ -1633,10 +1633,9 @@ nc-formula: nc-formula-or-ident
auto i = res.fcache.find(*$1);
if (i == res.fcache.end())
{
spot::parse_error_list pel;
auto f = spot::parse_infix_boolean(*$1, pel, *res.env,
debug_level(), true);
for (auto& j: pel)
auto pf = spot::parse_infix_boolean(*$1, *res.env, debug_level(),
true);
for (auto& j: pf.errors)
{
// Adjust the diagnostic to the current position.
spot::location here = @1;
......@@ -1647,8 +1646,9 @@ nc-formula: nc-formula-or-ident
res.h->errors.emplace_back(here, j.second);
}
bdd cond = bddfalse;
if (f)
cond = spot::formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
if (pf.f)
cond = spot::formula_to_bdd(pf.f,
res.h->aut->get_dict(), res.h->aut);
$$ = (res.fcache[*$1] = cond).id();
}
else
......@@ -1814,16 +1814,15 @@ lbtt-acc: { $$ = 0U; }
}
lbtt-guard: STRING
{
spot::parse_error_list pel;
auto f = spot::parse_prefix_ltl(*$1, pel, *res.env);
if (!f || !pel.empty())
auto pf = spot::parse_prefix_ltl(*$1, *res.env);
if (!pf.f || !pf.errors.empty())
{
std::string s = "failed to parse guard: ";
s += *$1;
error(@$, s);
}
if (!pel.empty())
for (auto& j: pel)
if (!pf.errors.empty())
for (auto& j: pf.errors)
{
// Adjust the diagnostic to the current position.
spot::location here = @1;
......@@ -1833,13 +1832,13 @@ lbtt-guard: STRING
here.begin.column += j.first.begin.column - 1;
res.h->errors.emplace_back(here, j.second);
}
if (!f)
if (!pf.f)
{
res.cur_label = bddtrue;
}
else
{
if (!f.is_boolean())
if (!pf.f.is_boolean())
{
error(@$,
"non-Boolean transition label (replaced by true)");
......@@ -1848,7 +1847,7 @@ lbtt-guard: STRING
else
{
res.cur_label =
formula_to_bdd(f, res.h->aut->get_dict(), res.h->aut);
formula_to_bdd(pf.f, res.h->aut->get_dict(), res.h->aut);
}
}
delete $1;
......
......@@ -72,11 +72,10 @@ namespace spot
const parse_error_list& error_list)
{
bool printed = false;
parse_error_list::const_iterator it;
for (it = error_list.begin(); it != error_list.end(); ++it)
for (auto it: error_list)
{
os << ">>> " << ltl_string << std::endl;
const location& l = it->first;
os << ">>> " << ltl_string << '\n';
const location& l = it.first;
unsigned n = 1;
for (; n < 4 + l.begin.column; ++n)
......@@ -86,7 +85,7 @@ namespace spot
++n;
for (; n < 4 + l.end.column; ++n)
os << '^';
os << std::endl << it->second << std::endl << std::endl;
os << '\n' << it.second << "\n\n";
printed = true;
}
return printed;
......@@ -94,19 +93,17 @@ namespace spot
}
bool
format_parse_errors(std::ostream& os,
const std::string& ltl_string,
const parse_error_list& error_list)
parsed_formula::format_errors(std::ostream& os)
{
if (utf8::is_valid(ltl_string.begin(), ltl_string.end()))
if (utf8::is_valid(input.begin(), input.end()))
{
parse_error_list fixed = error_list;
fix_utf8_locations(ltl_string, fixed);
return format_parse_errors_aux(os, ltl_string, fixed);
parse_error_list fixed = errors;
fix_utf8_locations(input, fixed);
return format_parse_errors_aux(os, input, fixed);
}
else
{
return format_parse_errors_aux(os, ltl_string, error_list);
return format_parse_errors_aux(os, input, errors);
}
}
}
/* -*- coding: utf-8 -*-
** Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
** Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Laboratoire
** de Recherche et Développement de l'Epita (LRDE).
** Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
** Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
......@@ -120,25 +120,24 @@ using namespace spot;
return nullptr;
}
spot::parse_error_list suberror;
formula f;
spot::parsed_formula pf;
switch (type)
{
case parser_sere:
f = spot::parse_infix_sere(str, suberror, env, debug, true);
pf = spot::parse_infix_sere(str, env, debug, true);
break;
case parser_bool:
f = spot::parse_infix_boolean(str, suberror, env, debug, true);
pf = spot::parse_infix_boolean(str, env, debug, true);
break;
case parser_ltl:
f = spot::parse_infix_psl(str, suberror, env, debug, true);
pf = spot::parse_infix_psl(str, env, debug, true);
break;
}
if (suberror.empty())
return f;
if (pf.errors.empty())
return pf.f;
f = env.require(str);
auto f = env.require(str);
if (!f)
{
std::string s = "atomic proposition `";
......@@ -993,69 +992,65 @@ tlyy::parser::error(const location_type& location, const std::string& message)
namespace spot
{
formula
parsed_formula
parse_infix_psl(const std::string& ltl_string,
parse_error_list& error_list,