ltldo: new binary

* src/bin/common_trans.cc, src/bin/common_trans.hh: New files,
extracted from...
* src/bin/ltlcross.cc: ... here, so that ltldo can use them.
* src/bin/ltldo.cc: New file.
* src/bin/Makefile.am: Adjust.
* src/bin/common_aoutput.cc, src/bin/common_aoutput.hh: Make
it possible to add new statistics.
* doc/org/ltldo.org: New file.
* doc/Makefile.am, doc/org/tools.org: Adjust.
* src/bin/man/ltldo.x: New file.
* src/bin/man/Makefile.am: Adjust.
* src/bin/man/ltlcross.x, src/bin/man/ltlfilt.x: Mention ltldo(1).
* src/tgbatest/ltldo.test, src/tgbatest/ltldo2.test: New files.
* src/tgbatest/Makefile.am: Add them.
* NEWS: Mention ltldo.
This commit is contained in:
Alexandre Duret-Lutz 2015-01-26 19:28:11 +01:00
parent e5294aac21
commit 16a8c03143
20 changed files with 1294 additions and 392 deletions

4
NEWS
View file

@ -20,6 +20,10 @@ New in spot 1.99a (not yet released)
- autfilt is a new tool that converts/filters/transforms a
stream of automata.
- ltldo is a new tool that run LTL/PSL formulas through other
tools, but uses Spot's command-line interfaces for specifying
input and output.
- "ltlfilt --stutter-invariant" will now work with PSL formula.
The implementation is actually much more efficient
than the previous implementation that was only for LTL.

View file

@ -69,6 +69,7 @@ ORG_FILES = \
org/ltl2tgba.org \
org/ltl2tgta.org \
org/ltlcross.org \
org/ltldo.org \
org/ltlfilt.org \
org/ltlgrind.org \
org/oaut.org \

1
doc/org/.gitignore vendored
View file

@ -11,3 +11,4 @@ gfagfb
bogus
bogus-grind
example.hoa
sample.ltl

275
doc/org/ltldo.org Normal file
View file

@ -0,0 +1,275 @@
# -*- coding: utf-8 -*-
#+TITLE: =ltldo=
#+SETUPFILE: setup.org
#+HTML_LINK_UP: tools.html
This tool is a wrapper for tools that read LTL/PSL formulas and
(optionally) output automata.
It reads formulas specified using the [[file:ioltl.org][common options for specifying
input]] and passes each formula to a tool (or a list of tools) specified
using options similar to those of [[file:ltlcross.org][=ltlcross=]]. In case that tool
returns an automaton, the resulting automaton is read back by =ltldo=
and is finally output as specified using the [[file:oaut.org][common options for
outputing automata]].
In effect, =ltldo= wraps the I/O interface of the Spot tools on top of
any other tool.
* Example: computing statistics for =ltl3ba=
As a motivating example, consider a scenario where we want to run
[[http://sourceforge.net/projects/ltl3ba/][=ltl3ba=]] on a set of 10 formulas stored in a file. For each formula
we would like to compute compute the number of states and edges in the
Büchi automaton produced by =ltl3ba=.
Here is the input file:
#+BEGIN_SRC sh :results verbatim :exports code
cat >sample.ltl <<EOF
1
1 U a
!(!((a U Gb) U b) U GFa)
(b <-> Xc) xor Fb
FXb R (a R (1 U b))
Ga
G(!(c | (a & (a W Gb))) M Xa)
GF((b R !a) U (Xc M 1))
G(Xb | Gc)
XG!F(a xor Gb)
EOF
#+END_SRC
#+RESULTS:
We will first implement this scenario without =ltldo=.
A first problem that the input is not in the correct syntax: although
=ltl3ba= understands =G= and =F=, it does not support =xor= or =M=,
and requires the Boolean operators =||= and =&&=. This syntax
issue can be fixed by processing the input with [[file:ltlfilt.org][=ltlfilt -s=]].
A second problem is that =ltl3ba= (at least version 1.1.1) can only
process one formula at a time. So we'll need to call =ltl3ba= in a
loop.
Finally, one way to compute the size of the resulting Büchi automaton
is to pipe the output of =ltl3ba= through [[file:autfilt.org][=autfilt=]].
Here is how the shell command could look like:
#+BEGIN_SRC sh :results verbatim :exports both
ltlfilt -F sample.ltl -s |
while read f; do
ltl3ba -f "$f" | autfilt --stats="$f,%s,%t"
done
#+END_SRC
#+RESULTS:
#+begin_example
true,1,1
true U a,2,4
!(!((a U []b) U b) U []<>a),2,4
(((!b && !Xc) || (b && Xc)) && !<>b) || (<>b && !((!b && !Xc) || (b && Xc))),7,21
<>Xb V (a V (true U b)),6,28
[]a,1,1
[](Xa U (Xa && !(c || (a && ([]b V (a || []b)))))),1,0
[]<>((b V !a) U (true U Xc)),2,4
[](Xb || []c),3,11
X[]!<>((a && ![]b) || (!a && []b)),4,10
#+end_example
Using =ltldo= the above command can be reduced to this:
#+BEGIN_SRC sh :results verbatim :exports both
ltldo -F sample.ltl 'ltl3ba -f %s>%N' --stats='%f,%s,%t'
#+END_SRC
#+RESULTS:
#+begin_example
1,1,1
1 U a,2,4
!(!((a U Gb) U b) U GFa),2,4
(b <-> Xc) xor Fb,7,21
FXb R (a R (1 U b)),6,28
Ga,1,1
G(!(c | (a & (a W Gb))) M Xa),1,0
GF((b R !a) U (Xc M 1)),2,4
G(Xb | Gc),3,11
XG!F(a xor Gb),4,10
#+end_example
Note that the formulas look different in both cases, because in the
=while= loop the formula printed has already been processed with
=ltlfilt=, while =ltldo= emits the input string untouched.
* Example: running =spin= and producing HOA
Here is another example, where we use Spin to produce two automata in
the [[http://adl.github.io/hoaf/][HOA format]]. Spin has no support for HOA, but =ltldo= simply
converts the never claim produced by =spin= into this format.
#+BEGIN_SRC sh :results verbatim :exports both
ltldo -f a -f GFa 'spin -f %s>%N' -H
#+END_SRC
#+RESULTS:
#+begin_example
HOA: v1
States: 2
Start: 0
AP: 1 "a"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc deterministic
--BODY--
State: 0 {0}
[0] 1
State: 1 {0}
[t] 1
--END--
HOA: v1
States: 2
Start: 0
AP: 1 "a"
acc-name: Buchi
Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc complete
--BODY--
State: 0
[0] 1
[t] 0
State: 1 {0}
[t] 0
--END--
#+end_example
* Syntax for specifying tools to call
The syntax for specifying how a tool should be called is the same as
in [[file:ltlcross.org][=ltlcross=]]. Namely, the following sequences are available.
#+BEGIN_SRC sh :results verbatim :exports results
ltldo --help | sed -n '/character sequences:/,/^$/p' | sed '1d;$d'
#+END_SRC
#+RESULTS:
: %f,%s,%l,%w the formula as a (quoted) string in Spot, Spin,
: LBT, or Wring's syntax
: %F,%S,%L,%W the formula as a file in Spot, Spin, LBT, or
: Wring's syntax
: %N,%T,%D,%H the automaton is output as a Never claim, or in
: LBTT's, in LTL2DSTAR's, or in the HOA format
Contrarily to =ltlcross=, it this not mandatory to specify an output filename
using one of the sequence for that later lines. For instance we could
simply run a formula though =echo= to compare different output syntaxes:
#+BEGIN_SRC sh :results verbatim :exports both
ltldo -f 'p0 U p1' -f 'GFp0' 'echo %f, %s, %l, %w'
#+END_SRC
#+RESULTS:
: (p0) U (p1), (p0) U (p1), U p0 p1, (p0=1) U (p1=1)
: (G(F(p0))), ([](<>(p0))), G F p0, (G(F(p0=1)))
In this case (i.e., when the command does not specify any output
filename), =ltldo= will not output anything.
As will =ltlcross=, multiple commands can be given, and they will be
executed on each formula in the same order.
A typical use-case is to compare statistics of different tools:
#+BEGIN_SRC sh :results verbatim :exports both
ltldo -F sample.ltl 'spin -f %s>%N' 'ltl3ba -f %s>%N' --stats=%T,%f,%s,%e
#+END_SRC
#+RESULTS:
#+begin_example
spin -f %s>%N,1,2,2
ltl3ba -f %s>%N,1,1,1
spin -f %s>%N,1 U a,2,3
ltl3ba -f %s>%N,1 U a,2,3
spin -f %s>%N,!(!((a U Gb) U b) U GFa),23,86
ltl3ba -f %s>%N,!(!((a U Gb) U b) U GFa),2,3
spin -f %s>%N,(b <-> Xc) xor Fb,12,23
ltl3ba -f %s>%N,(b <-> Xc) xor Fb,7,11
spin -f %s>%N,FXb R (a R (1 U b)),28,176
ltl3ba -f %s>%N,FXb R (a R (1 U b)),6,20
spin -f %s>%N,Ga,1,1
ltl3ba -f %s>%N,Ga,1,1
spin -f %s>%N,G(!(c | (a & (a W Gb))) M Xa),15,51
ltl3ba -f %s>%N,G(!(c | (a & (a W Gb))) M Xa),1,0
spin -f %s>%N,GF((b R !a) U (Xc M 1)),12,60
ltl3ba -f %s>%N,GF((b R !a) U (Xc M 1)),2,4
spin -f %s>%N,G(Xb | Gc),4,8
ltl3ba -f %s>%N,G(Xb | Gc),3,5
spin -f %s>%N,XG!F(a xor Gb),8,21
ltl3ba -f %s>%N,XG!F(a xor Gb),4,7
#+end_example
Here we used =%T= to output the name of the tool used to translate the
formula =%f= as an automaton with =%s= states and =%e= edges.
If you feel that =%T= has too much clutter, you can give each tool
a shorter name by prefixing its command with ={name}=.
In the following example, we moved the formula used on its own line
using the trick that the command =echo %f= will not be subject to
=--stats= (since it does not declare any output automaton).
#+BEGIN_SRC sh :results verbatim :exports both
ltldo -F sample.ltl --stats=%T,%s,%e \
'echo "#" %f' '{spin}spin -f %s>%N' '{ltl3ba}ltl3ba -f %s>%N'
#+END_SRC
#+RESULTS:
#+begin_example
# (1)
spin,2,2
ltl3ba,1,1
# (1) U (a)
spin,2,3
ltl3ba,2,3
# (!((!(((a) U (G(b))) U (b))) U (G(F(a)))))
spin,23,86
ltl3ba,2,3
# ((b) <-> (X(c))) xor (F(b))
spin,12,23
ltl3ba,7,11
# (F(X(b))) R ((a) R ((1) U (b)))
spin,28,176
ltl3ba,6,20
# (G(a))
spin,1,1
ltl3ba,1,1
# (G((!((c) | ((a) & ((a) W (G(b)))))) M (X(a))))
spin,15,51
ltl3ba,1,0
# (G(F(((b) R (!(a))) U ((X(c)) M (1)))))
spin,12,60
ltl3ba,2,4
# (G((X(b)) | (G(c))))
spin,4,8
ltl3ba,3,5
# (X(G(!(F((a) xor (G(b)))))))
spin,8,21
ltl3ba,4,7
#+end_example
Much more readable!
* Controlling and measuring time
The run time of each command can be restricted with the =-T NUM=
option. The argument is the maximum number of seconds that each
command is allowed to run.
When a timeout occurs a warning is printed on stderr, and no automaton
(or statistic) is output by =ltdo= for this specific pair of
command/formula. The processing then continue with other formulas and
tools. Timeouts are not considered as errors, so they have no effect
on the exit status of =ltldo=.
For each command (that does not terminate with a timeout) the runtime
can be printed using the =%r= escape sequence. This makes =ltldo= an
alternative to [[file:ltlcross.org][=ltlcross=]] for running benchmarks without any
verification.

View file

@ -48,6 +48,8 @@ corresponding commands are hidden.
Büchi automata.
- [[file:randaut.org][=randaut=]] Generate random automata.
- [[file:autfilt.org][=autfilt=]] Filter and convert automata.
- [[file:ltldo.org][=ltldo=]] Run LTL/PSL formulas through other tools using common [[file:ioltl.org][input]]
and [[file:oaut.org][output]] interfaces.
* Advanced use-cases

1
src/bin/.gitignore vendored
View file

@ -4,6 +4,7 @@ genltl
ltl2tgba
ltl2tgta
ltlcross
ltldo
ltlfilt
ltlgrind
randaut

View file

@ -44,10 +44,22 @@ libcommon_a_SOURCES = \
common_r.hh \
common_setup.cc \
common_setup.hh \
common_sys.hh
common_sys.hh \
common_trans.cc \
common_trans.hh
bin_PROGRAMS = autfilt ltlfilt genltl randaut randltl ltl2tgba \
ltl2tgta ltlcross dstar2tgba ltlgrind
bin_PROGRAMS = \
autfilt \
dstar2tgba \
genltl \
ltl2tgba \
ltl2tgta \
ltlcross \
ltldo \
ltlfilt \
ltlgrind \
randaut \
randltl
# Dummy program used just to generate man/spot-x.7 in a way that is
# consistent with the other man pages (e.g., with a version number that
@ -63,6 +75,7 @@ ltl2tgba_SOURCES = ltl2tgba.cc
ltl2tgta_SOURCES = ltl2tgta.cc
ltlcross_SOURCES = ltlcross.cc
ltlgrind_SOURCES = ltlgrind.cc
ltldo_SOURCES = ltldo.cc
dstar2tgba_SOURCES = dstar2tgba.cc
spot_x_SOURCES = spot-x.cc
ltlcross_LDADD = $(LDADD) $(LIB_GETHRXTIME)

View file

@ -23,6 +23,7 @@
#include "common_aoutput.hh"
#include "common_post.hh"
#include "common_cout.hh"
#include "common_post.hh"
#include "tgba/bddprint.hh"
@ -259,3 +260,9 @@ automaton_printer::print(const spot::tgba_digraph_ptr& aut,
}
flush_cout();
}
void automaton_printer::add_stat(char c, const spot::printable* p)
{
namer.declare(c, p);
statistics.declare(c, p);
}

View file

@ -97,6 +97,8 @@ public:
declare('w', &aut_word_);
}
using spot::formater::declare;
/// \brief print the configured statistics.
///
/// The \a f argument is not needed if the Formula does not need
@ -218,6 +220,8 @@ public:
// Time and input automaton for statistics
double time = 0.0,
const spot::const_hoa_aut_ptr& haut = nullptr);
void add_stat(char c, const spot::printable* p);
};

381
src/bin/common_trans.cc Normal file
View file

@ -0,0 +1,381 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "common_trans.hh"
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include "error.h"
#include "ltlvisit/tostring.hh"
#include "ltlvisit/lbt.hh"
#include "common_conv.hh"
translator_spec::translator_spec(const char* spec)
: spec(spec), cmd(spec), name(spec)
{
if (*cmd != '{')
return;
// Match the closing '}'
const char* pos = cmd;
unsigned count = 1;
while (*++pos)
{
if (*pos == '{')
++count;
else if (*pos == '}')
if (!--count)
{
name = strndup(cmd + 1, pos - cmd - 1);
cmd = pos + 1;
while (*cmd == ' ' || *cmd == '\t')
++cmd;
break;
}
}
}
translator_spec::translator_spec(const translator_spec& other)
: spec(other.spec), cmd(other.cmd), name(other.name)
{
if (name != spec)
name = strdup(name);
}
translator_spec::~translator_spec()
{
if (name != spec)
free(const_cast<char*>(name));
}
std::vector<translator_spec> translators;
void
quoted_string::print(std::ostream& os, const char* pos) const
{
os << '\'';
this->spot::printable_value<std::string>::print(os, pos);
os << '\'';
}
printable_result_filename::printable_result_filename()
{
val_ = 0;
}
printable_result_filename::~printable_result_filename()
{
delete val_;
}
void printable_result_filename::reset(unsigned n)
{
translator_num = n;
format = None;
}
void printable_result_filename::cleanup()
{
delete val_;
val_ = 0;
}
void
printable_result_filename::print(std::ostream& os, const char* pos) const
{
output_format old_format = format;
if (*pos == 'N')
format = Hoa; // The HOA parse also reads neverclaims
else if (*pos == 'T')
format = Lbtt;
else if (*pos == 'D')
format = Dstar;
else if (*pos == 'H')
format = Hoa;
else
SPOT_UNREACHABLE();
if (val_)
{
// It's OK to use a specifier multiple time, but it's not OK
// to mix the formats.
if (format != old_format)
error(2, 0,
"you may not mix %%D, %%H, %%N, and %%T specifiers: %s",
translators[translator_num].spec);
}
else
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-o%u-", translator_num);
const_cast<printable_result_filename*>(this)->val_
= spot::create_tmpfile(prefix);
}
os << '\'' << val_ << '\'';
}
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
bool no_output_allowed)
: dict(dict)
{
declare('f', &string_ltl_spot);
declare('s', &string_ltl_spin);
declare('l', &string_ltl_lbt);
declare('w', &string_ltl_wring);
declare('F', &filename_ltl_spot);
declare('S', &filename_ltl_spin);
declare('L', &filename_ltl_lbt);
declare('W', &filename_ltl_wring);
declare('D', &output);
declare('H', &output);
declare('N', &output);
declare('T', &output);
size_t s = translators.size();
assert(s);
for (size_t n = 0; n < s; ++n)
{
// Check that each translator uses at least one input and
// one output.
std::vector<bool> has(256);
const translator_spec& t = translators[n];
scan(t.cmd, has);
if (!(has['f'] || has['s'] || has['l'] || has['w']
|| has['F'] || has['S'] || has['L'] || has['W']))
error(2, 0, "no input %%-sequence in '%s'.\n Use "
"one of %%f,%%s,%%l,%%w,%%F,%%S,%%L,%%W to indicate how "
"to pass the formula.", t.spec);
if (!no_output_allowed
&& !(has['D'] || has['N'] || has['T'] || has['H']))
error(2, 0, "no output %%-sequence in '%s'.\n Use one of "
"%%D,%%H,%%N,%%T to indicate where the automaton is saved.",
t.spec);
// Remember the %-sequences used by all translators.
prime(t.cmd);
}
}
void
translator_runner::string_to_tmp(std::string& str, unsigned n,
std::string& tmpname)
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
spot::open_temporary_file* tmpfile = spot::create_open_tmpfile(prefix);
tmpname = tmpfile->name();
int fd = tmpfile->fd();
ssize_t s = str.size();
if (write(fd, str.c_str(), s) != s
|| write(fd, "\n", 1) != 1)
error(2, errno, "failed to write into %s", tmpname.c_str());
tmpfile->close();
}
const std::string&
translator_runner::formula() const
{
// Pick the most readable format we have...
if (!string_ltl_spot.val().empty())
return string_ltl_spot;
if (!string_ltl_spin.val().empty())
return string_ltl_spin;
if (!string_ltl_wring.val().empty())
return string_ltl_wring;
if (!string_ltl_lbt.val().empty())
return string_ltl_lbt;
SPOT_UNREACHABLE();
return string_ltl_spot;
}
void
translator_runner::round_formula(const spot::ltl::formula* f, unsigned serial)
{
if (has('f') || has('F'))
string_ltl_spot = spot::ltl::to_string(f, true);
if (has('s') || has('S'))
string_ltl_spin = spot::ltl::to_spin_string(f, true);
if (has('l') || has('L'))
string_ltl_lbt = spot::ltl::to_lbt_string(f);
if (has('w') || has('W'))
string_ltl_wring = spot::ltl::to_wring_string(f);
if (has('F'))
string_to_tmp(string_ltl_spot, serial, filename_ltl_spot);
if (has('S'))
string_to_tmp(string_ltl_spin, serial, filename_ltl_spin);
if (has('L'))
string_to_tmp(string_ltl_lbt, serial, filename_ltl_lbt);
if (has('W'))
string_to_tmp(string_ltl_wring, serial, filename_ltl_wring);
}
volatile bool timed_out = false;
unsigned timeout_count = 0;
static unsigned timeout = 0;
#if ENABLE_TIMEOUT
static volatile int alarm_on = 0;
static int child_pid = -1;
static void
sig_handler(int sig)
{
if (child_pid == 0)
error(2, 0, "child received signal %d before starting", sig);
if (sig == SIGALRM && alarm_on)
{
timed_out = true;
if (--alarm_on)
{
// Send SIGTERM to children.
kill(-child_pid, SIGTERM);
// Try again later if it didn't work. (alarm() will be reset
// if it did work and the call to wait() returns)
alarm(2);
}
else
{
// After a few gentle tries, really kill that child.
kill(-child_pid, SIGKILL);
}
}
else
{
// forward signal
kill(-child_pid, sig);
// cleanup files
spot::cleanup_tmpfiles();
// and die verbosely
error(2, 0, "received signal %d", sig);
}
}
void
setup_sig_handler()
{
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // So that wait() doesn't get aborted by SIGALRM.
sigaction(SIGALRM, &sa, 0);
// Catch termination signals, so we can kill the subprocess.
sigaction(SIGHUP, &sa, 0);
sigaction(SIGINT, &sa, 0);
sigaction(SIGQUIT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
}
int
exec_with_timeout(const char* cmd)
{
int status;
timed_out = false;
child_pid = fork();
if (child_pid == -1)
error(2, errno, "failed to fork()");
if (child_pid == 0)
{
setpgid(0, 0);
execlp("sh", "sh", "-c", cmd, (char*)0);
error(2, errno, "failed to run 'sh'");
// never reached
return -1;
}
else
{
alarm(timeout);
// Upon SIGALRM, the child will receive up to 3
// signals: SIGTERM, SIGTERM, SIGKILL.
alarm_on = 3;
int w = waitpid(child_pid, &status, 0);
alarm_on = 0;
if (w == -1)
error(2, errno, "error during wait()");
alarm(0);
}
return status;
}
static const argp_option options[] =
{
/**************************************************/
{ 0, 0, 0, 0, "Specifying translators to call:", 2 },
{ "translator", 't', "COMMANDFMT", 0,
"register one translator to call", 0 },
{ "timeout", 'T', "NUMBER", 0, "kill translators after NUMBER seconds", 0 },
/**************************************************/
{ 0, 0, 0, 0,
"COMMANDFMT should specify input and output arguments using the "
"following character sequences:", 3 },
{ "%f,%s,%l,%w", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the formula as a (quoted) string in Spot, Spin, LBT, or Wring's syntax",
0 },
{ "%F,%S,%L,%W", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the formula as a file in Spot, Spin, LBT, or Wring's syntax", 0 },
{ "%N,%T,%D,%H", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the automaton is output as a Never claim, or in LBTT's, in LTL2DSTAR's,"
" or in the HOA format", 0 },
{ 0, 0, 0, 0,
"If either %l, %L, or %T are used, any input formula that does "
"not use LBT-style atomic propositions (i.e. p0, p1, ...) will be "
"relabeled automatically.\n"
"Furthermore, if COMMANDFMT has the form \"{NAME}CMD\", then only CMD "
"will be passed to the shell, and NAME will be used to name the tool "
"in the output.", 4 },
{ 0, 0, 0, 0, 0, 0 }
};
static int parse_opt_trans(int key, char* arg, struct argp_state*)
{
switch (key)
{
case 't':
translators.push_back(arg);
break;
case 'T':
timeout = to_pos_int(arg);
#if !ENABLE_TIMEOUT
std::cerr << "warning: setting a timeout is not supported "
<< "on your platform" << std::endl;
#endif
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp trans_argp { options, parse_opt_trans, 0, 0, 0, 0, 0 };
#endif // ENABLE_TIMEOUT

123
src/bin/common_trans.hh Normal file
View file

@ -0,0 +1,123 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SPOT_BIN_COMMON_TRANS_HH
#define SPOT_BIN_COMMON_TRANS_HH
#include "common_sys.hh"
#include <vector>
#include <argp.h>
#include "misc/formater.hh"
#include "misc/tmpfile.hh"
#include "tgba/tgbagraph.hh"
extern const struct argp trans_argp;
struct translator_spec
{
// The translator command, as specified on the command-line.
// If this has the form of
// {name}cmd
// then it is split in two components.
// Otherwise, spec=cmd=name.
const char* spec;
// actual shell command (or spec)
const char* cmd;
// name of the translator (or spec)
const char* name;
translator_spec(const char* spec);
translator_spec(const translator_spec& other);
~translator_spec();
};
extern std::vector<translator_spec> translators;
struct quoted_string final: public spot::printable_value<std::string>
{
using spot::printable_value<std::string>::operator=;
void print(std::ostream& os, const char* pos) const override;
};
struct printable_result_filename final:
public spot::printable_value<spot::temporary_file*>
{
unsigned translator_num;
enum output_format { None, Lbtt, Dstar, Hoa };
mutable output_format format;
printable_result_filename();
~printable_result_filename();
void reset(unsigned n);
void cleanup();
void print(std::ostream& os, const char* pos) const override;
};
class translator_runner: protected spot::formater
{
protected:
spot::bdd_dict_ptr dict;
// Round-specific variables
quoted_string string_ltl_spot;
quoted_string string_ltl_spin;
quoted_string string_ltl_lbt;
quoted_string string_ltl_wring;
quoted_string filename_ltl_spot;
quoted_string filename_ltl_spin;
quoted_string filename_ltl_lbt;
quoted_string filename_ltl_wring;
// Run-specific variables
printable_result_filename output;
public:
using spot::formater::has;
translator_runner(spot::bdd_dict_ptr dict,
// whether we accept the absence of output
// specifier
bool no_output_allowed = false);
void string_to_tmp(std::string& str, unsigned n, std::string& tmpname);
const std::string& formula() const;
void round_formula(const spot::ltl::formula* f, unsigned serial);
};
// Disable handling of timeout on systems that miss kill() or alarm().
// For instance MinGW.
#if HAVE_KILL && HAVE_ALARM
# define ENABLE_TIMEOUT 1
#else
# define ENABLE_TIMEOUT 0
#endif
extern volatile bool timed_out;
extern unsigned timeout_count;
#if ENABLE_TIMEOUT
void setup_sig_handler();
int exec_with_timeout(const char* cmd);
#else // !ENABLE_TIMEOUT
#define exec_with_timeout(cmd) system(cmd)
#define setup_sig_handler() while (0);
#endif // !ENABLE_TIMEOUT
#endif // SPOT_BIN_COMMON_TRANS_HH

View file

@ -27,24 +27,23 @@
#include <cstdlib>
#include <cstdio>
#include <argp.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include "error.h"
#include "argmatch.h"
#include "common_setup.hh"
#include "common_cout.hh"
#include "common_conv.hh"
#include "common_trans.hh"
#include "common_finput.hh"
#include "dstarparse/public.hh"
#include "hoaparse/public.hh"
#include "ltlast/unop.hh"
#include "ltlvisit/tostring.hh"
#include "ltlvisit/apcollect.hh"
#include "ltlvisit/lbt.hh"
#include "ltlvisit/mutation.hh"
#include "ltlvisit/relabel.hh"
#include "ltlvisit/lbt.hh"
#include "tgbaalgos/lbtt.hh"
#include "tgbaalgos/product.hh"
#include "tgbaalgos/gtec/gtec.hh"
@ -63,14 +62,6 @@
#include "misc/tmpfile.hh"
#include "misc/timer.hh"
// Disable handling of timeout on systems that miss kill() or alarm().
// For instance MinGW.
#if HAVE_KILL && HAVE_ALARM
# define ENABLE_TIMEOUT 1
#else
# define ENABLE_TIMEOUT 0
#endif
const char argp_program_doc[] ="\
Call several LTL/PSL translators and cross-compare their output to detect \
bugs, or to gather statistics. The list of formulas to use should be \
@ -102,31 +93,7 @@ Exit status:\n\
static const argp_option options[] =
{
/**************************************************/
{ 0, 0, 0, 0, "Specifying translators to call:", 2 },
{ "translator", 't', "COMMANDFMT", 0,
"register one translator to call", 0 },
{ "timeout", 'T', "NUMBER", 0, "kill translators after NUMBER seconds", 0 },
/**************************************************/
{ 0, 0, 0, 0,
"COMMANDFMT should specify input and output arguments using the "
"following character sequences:", 3 },
{ "%f,%s,%l,%w", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the formula as a (quoted) string in Spot, Spin, LBT, or Wring's syntax",
0 },
{ "%F,%S,%L,%W", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the formula as a file in Spot, Spin, LBT, or Wring's syntax", 0 },
{ "%N,%T,%D,%H", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"the automaton is output as a Never claim, or in LBTT's, in LTL2DSTAR's,"
"or in the HOA format", 0 },
{ 0, 0, 0, 0,
"If either %l, %L, or %T are used, any input formula that does "
"not use LBT-style atomic propositions (i.e. p0, p1, ...) will be "
"relabeled automatically.\n"
"Furthermore, if COMMANDFMT has the form \"{NAME}CMD\", then only CMD "
"will be passed to the shell, and NAME will be used to name the tool "
"in the CSV or JSON outputs.", 0 },
/**************************************************/
{ 0, 0, 0, 0, "ltlcross behavior:", 4 },
{ 0, 0, 0, 0, "ltlcross behavior:", 5 },
{ "allow-dups", OPT_DUPS, 0, 0,
"translate duplicate formulas in input", 0 },
{ "no-checks", OPT_NOCHECKS, 0, 0,
@ -138,7 +105,7 @@ static const argp_option options[] =
"stop on first execution error or failure to pass"
" sanity checks (timeouts are OK)", 0 },
/**************************************************/
{ 0, 0, 0, 0, "State-space generation:", 5 },
{ 0, 0, 0, 0, "State-space generation:", 6 },
{ "states", OPT_STATES, "INT", 0,
"number of the states in the state-spaces (200 by default)", 0 },
{ "density", OPT_DENSITY, "FLOAT", 0,
@ -150,7 +117,7 @@ static const argp_option options[] =
"number of products to perform (1 by default), statistics will be "
"averaged unless the number is prefixed with '+'", 0 },
/**************************************************/
{ 0, 0, 0, 0, "Statistics output:", 6 },
{ 0, 0, 0, 0, "Statistics output:", 7 },
{ "json", OPT_JSON, "FILENAME", OPTION_ARG_OPTIONAL,
"output statistics as JSON in FILENAME or on standard output", 0 },
{ "csv", OPT_CSV, "FILENAME", OPTION_ARG_OPTIONAL,
@ -176,6 +143,7 @@ static const argp_option options[] =
const struct argp_child children[] =
{
{ &finput_argp, 0, 0, 1 },
{ &trans_argp, 0, 0, 0 },
{ &misc_argp, 0, 0, -1 },
{ 0, 0, 0, 0 }
};
@ -204,7 +172,6 @@ const char* reset_color = "\033[m";
unsigned states = 200;
float density = 0.1;
unsigned timeout = 0;
const char* json_output = 0;
const char* csv_output = 0;
bool want_stats = false;
@ -222,59 +189,6 @@ std::ofstream* bogus_output = 0;
std::ofstream* grind_output = 0;
bool verbose = false;
struct translator_spec
{
// The translator command, as specified on the command-line.
// If this has the form of
// {name}cmd
// then it is split in two components.
// Otherwise, spec=cmd=name.
const char* spec;
// actual shell command (or spec)
const char* cmd;
// name of the translator (or spec)
const char* name;
translator_spec(const char* spec)
: spec(spec), cmd(spec), name(spec)
{
if (*cmd != '{')
return;
// Match the closing '}'
const char* pos = cmd;
unsigned count = 1;
while (*++pos)
{
if (*pos == '{')
++count;
else if (*pos == '}')
if (!--count)
{
name = strndup(cmd + 1, pos - cmd - 1);
cmd = pos + 1;
while (*cmd == ' ' || *cmd == '\t')
++cmd;
break;
}
}
}
translator_spec(const translator_spec& other)
: spec(other.spec), cmd(other.cmd), name(other.name)
{
if (name != spec)
name = strdup(name);
}
~translator_spec()
{
if (name != spec)
free(const_cast<char*>(name));
}
};
std::vector<translator_spec> translators;
bool global_error_flag = false;
@ -473,17 +387,9 @@ parse_opt(int key, char* arg, struct argp_state*)
// This switch is alphabetically-ordered.
switch (key)
{
case 't':
case ARGP_KEY_ARG:
translators.push_back(arg);
break;
case 'T':
timeout = to_pos_int(arg);
#if !ENABLE_TIMEOUT
std::cerr << "warning: setting a timeout is not supported "
<< "on your platform" << std::endl;
#endif
break;
case OPT_BOGUS:
{
bogus_output = new std::ofstream(arg);
@ -555,292 +461,15 @@ parse_opt(int key, char* arg, struct argp_state*)
return 0;
}
static volatile bool timed_out = false;
unsigned timeout_count = 0;
#if ENABLE_TIMEOUT
static volatile int alarm_on = 0;
static int child_pid = -1;
static void
sig_handler(int sig)
{
if (child_pid == 0)
error(2, 0, "child received signal %d before starting", sig);
if (sig == SIGALRM && alarm_on)
{
timed_out = true;
if (--alarm_on)
{
// Send SIGTERM to children.
kill(-child_pid, SIGTERM);
// Try again later if it didn't work. (alarm() will be reset
// if it did work and the call to wait() returns)
alarm(2);
}
else
{
// After a few gentle tries, really kill that child.
kill(-child_pid, SIGKILL);
}
}
else
{
// forward signal
kill(-child_pid, sig);
// cleanup files
spot::cleanup_tmpfiles();
// and die verbosely
error(2, 0, "received signal %d", sig);
}
}
static void
setup_sig_handler()
{
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // So that wait() doesn't get aborted by SIGALRM.
sigaction(SIGALRM, &sa, 0);
// Catch termination signals, so we can kill the subprocess.
sigaction(SIGHUP, &sa, 0);
sigaction(SIGINT, &sa, 0);
sigaction(SIGQUIT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
}
static int
exec_with_timeout(const char* cmd)
{
int status;
timed_out = false;
child_pid = fork();
if (child_pid == -1)
error(2, errno, "failed to fork()");
if (child_pid == 0)
{
setpgid(0, 0);
execlp("sh", "sh", "-c", cmd, (char*)0);
error(2, errno, "failed to run 'sh'");
// never reached
return -1;
}
else
{
alarm(timeout);
// Upon SIGALRM, the child will receive up to 3
// signals: SIGTERM, SIGTERM, SIGKILL.
alarm_on = 3;
int w = waitpid(child_pid, &status, 0);
alarm_on = 0;
if (w == -1)
error(2, errno, "error during wait()");
alarm(0);
}
return status;
}
#else // !ENABLE_TIMEOUT
#define exec_with_timeout(cmd) system(cmd)
#define setup_sig_handler() while (0);
#endif // !ENABLE_TIMEOUT
namespace
{
struct quoted_string: public spot::printable_value<std::string>
class xtranslator_runner: public translator_runner
{
using spot::printable_value<std::string>::operator=;
void
print(std::ostream& os, const char* pos) const
{
os << '\'';
this->spot::printable_value<std::string>::print(os, pos);
os << '\'';
}
};
struct printable_result_filename:
public spot::printable_value<spot::temporary_file*>
{
unsigned translator_num;
enum output_format { None, Lbtt, Dstar, Hoa };
mutable output_format format;
printable_result_filename()
{
val_ = 0;
}
~printable_result_filename()
{
delete val_;
}
void reset(unsigned n)
{
translator_num = n;
format = None;
}
void cleanup()
{
delete val_;
val_ = 0;
}
void
print(std::ostream& os, const char* pos) const
{
output_format old_format = format;
if (*pos == 'N')
format = Hoa; // The HOA parse also reads neverclaims
else if (*pos == 'T')
format = Lbtt;
else if (*pos == 'D')
format = Dstar;
else if (*pos == 'H')
format = Hoa;
else
SPOT_UNREACHABLE();
if (val_)
{
// It's OK to use a specified multiple time, but it's not OK
// to mix the formats.
if (format != old_format)
error(2, 0,
"you may not mix %%D, %%H, %%N, and %%T specifiers: %s",
translators[translator_num].spec);
}
else
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-o%u-", translator_num);
const_cast<printable_result_filename*>(this)->val_
= spot::create_tmpfile(prefix);
}
os << '\'' << val_ << '\'';
}
};
class translator_runner: protected spot::formater
{
private:
spot::bdd_dict_ptr dict;
// Round-specific variables
quoted_string string_ltl_spot;
quoted_string string_ltl_spin;
quoted_string string_ltl_lbt;
quoted_string string_ltl_wring;
quoted_string filename_ltl_spot;
quoted_string filename_ltl_spin;
quoted_string filename_ltl_lbt;
quoted_string filename_ltl_wring;
// Run-specific variables
printable_result_filename output;
public:
using spot::formater::has;
translator_runner(spot::bdd_dict_ptr dict)
: dict(dict)
xtranslator_runner(spot::bdd_dict_ptr dict)
: translator_runner(dict)
{
declare('f', &string_ltl_spot);
declare('s', &string_ltl_spin);
declare('l', &string_ltl_lbt);
declare('w', &string_ltl_wring);
declare('F', &filename_ltl_spot);
declare('S', &filename_ltl_spin);
declare('L', &filename_ltl_lbt);
declare('W', &filename_ltl_wring);
declare('D', &output);
declare('H', &output);
declare('N', &output);
declare('T', &output);
size_t s = translators.size();
assert(s);
for (size_t n = 0; n < s; ++n)
{
// Check that each translator uses at least one input and
// one output.
std::vector<bool> has(256);
const translator_spec& t = translators[n];
scan(t.cmd, has);
if (!(has['f'] || has['s'] || has['l'] || has['w']
|| has['F'] || has['S'] || has['L'] || has['W']))
error(2, 0, "no input %%-sequence in '%s'.\n Use "
"one of %%f,%%s,%%l,%%w,%%F,%%S,%%L,%%W to indicate how "
"to pass the formula.", t.spec);
bool has_d = has['D'];
if (!(has_d || has['N'] || has['T'] || has['H']))
error(2, 0, "no output %%-sequence in '%s'.\n Use one of "
"%%D,%%H,%%N,%%T to indicate where the automaton is saved.",
t.spec);
has_sr |= has_d;
// Remember the %-sequences used by all translators.
prime(t.cmd);
}
}
void
string_to_tmp(std::string& str, unsigned n, std::string& tmpname)
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
spot::open_temporary_file* tmpfile = spot::create_open_tmpfile(prefix);
tmpname = tmpfile->name();
int fd = tmpfile->fd();
ssize_t s = str.size();
if (write(fd, str.c_str(), s) != s
|| write(fd, "\n", 1) != 1)
error(2, errno, "failed to write into %s", tmpname.c_str());
tmpfile->close();
}
const std::string&
formula() const
{
// Pick the most readable format we have...
if (!string_ltl_spot.val().empty())
return string_ltl_spot;
if (!string_ltl_spin.val().empty())
return string_ltl_spin;
if (!string_ltl_wring.val().empty())
return string_ltl_wring;
if (!string_ltl_lbt.val().empty())
return string_ltl_lbt;
SPOT_UNREACHABLE();
return string_ltl_spot;
}
void
round_formula(const spot::ltl::formula* f, unsigned serial)
{
if (has('f') || has('F'))
string_ltl_spot = spot::ltl::to_string(f, true);
if (has('s') || has('S'))
string_ltl_spin = spot::ltl::to_spin_string(f, true);
if (has('l') || has('L'))
string_ltl_lbt = spot::ltl::to_lbt_string(f);
if (has('w') || has('W'))
string_ltl_wring = spot::ltl::to_wring_string(f);
if (has('F'))
string_to_tmp(string_ltl_spot, serial, filename_ltl_spot);
if (has('S'))
string_to_tmp(string_ltl_spin, serial, filename_ltl_spin);
if (has('L'))
string_to_tmp(string_ltl_lbt, serial, filename_ltl_lbt);
if (has('W'))
string_to_tmp(string_ltl_wring, serial, filename_ltl_wring);
has_sr = has('D');
}
spot::const_tgba_digraph_ptr
@ -1230,7 +859,7 @@ namespace
class processor: public job_processor
{
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
translator_runner runner;
xtranslator_runner runner;
fset_t unique_set;
public:
processor():

362
src/bin/ltldo.cc Normal file
View file

@ -0,0 +1,362 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "common_sys.hh"
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include "error.h"
#include "common_setup.hh"
#include "common_cout.hh"
#include "common_conv.hh"
#include "common_finput.hh"
#include "common_aoutput.hh"
#include "common_post.hh"
#include "common_trans.hh"
#include "ltlvisit/tostring.hh"
#include "misc/timer.hh"
#include "tgbaalgos/lbtt.hh"
#include "hoaparse/public.hh"
#include "dstarparse/public.hh"
const char argp_program_doc[] ="\
Run LTL/PSL formulas through another program, performing conversion\n\
of input and output as required.";
static const argp_option options[] =
{
{ 0, 0, 0, 0, "Miscellaneous options:", -1 },
{ 0, 0, 0, 0, 0, 0 }
};
static const argp_option more_o_format[] =
{
{ "%R", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"serial number of the formula translated", 0 },
{ "%T", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"tool used for translation", 0 },
{ "%f", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
"formula translated", 0 },
{ 0, 0, 0, 0, 0, 0 }
};
// This is not very elegant, but we need to add the above %-escape
// sequences to those of aoutput_o_fromat_argp for the --help output.
// So far I've failed to instruct argp to merge those two lists into a
// single block.
static const struct argp*
build_percent_list()
{
const argp_option* iter = aoutput_o_format_argp.options;
unsigned count = 0;
while (iter->name || iter->doc)
{
++count;
++iter;
}
unsigned s = count * sizeof(argp_option);
argp_option* d =
static_cast<argp_option*>(malloc(sizeof(more_o_format) + s));
memcpy(d, aoutput_o_format_argp.options, s);
memcpy(d + count, more_o_format, sizeof(more_o_format));
static const struct argp more_o_format_argp =
{ d, 0, 0, 0, 0, 0, 0 };
return &more_o_format_argp;
}
const struct argp_child children[] =
{
{ &finput_argp, 0, 0, 1 },
{ &trans_argp, 0, 0, 3 },
{ &aoutput_argp, 0, 0, 4 },
{ build_percent_list(), 0, 0, 5 },
{ &misc_argp, 0, 0, -1 },
{ 0, 0, 0, 0 }
};
static int
parse_opt(int key, char* arg, struct argp_state*)
{
switch (key)
{
case ARGP_KEY_ARG:
translators.push_back(arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
namespace
{
class xtranslator_runner: public translator_runner
{
public:
xtranslator_runner(spot::bdd_dict_ptr dict)
: translator_runner(dict, true)
{
}
spot::tgba_digraph_ptr
translate(unsigned int translator_num, bool& problem, double& duration)
{
output.reset(translator_num);
std::ostringstream command;
format(command, translators[translator_num].cmd);
//assert(output.format != printable_result_filename::None);
std::string cmd = command.str();
//std::cerr << "Running [" << l << translator_num << "]: "
// << cmd << std::endl;
spot::stopwatch sw;
sw.start();
int es = exec_with_timeout(cmd.c_str());
duration = sw.stop();
spot::tgba_digraph_ptr res = nullptr;
if (timed_out)
{
problem = false; // A timeout is considered benign
std::cerr << "warning: timeout during execution of command \""
<< cmd << "\"\n";
++timeout_count;
}
else if (WIFSIGNALED(es))
{
problem = true;
es = WTERMSIG(es);
std::cerr << "error: execution of command \"" << cmd
<< "\" terminated by signal " << es << ".\n";
}
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
{
problem = true;
es = WEXITSTATUS(es);
std::cerr << "error: execution of command \"" << cmd
<< "\" returned exit code " << es << ".\n";
}
else
{
problem = false;
switch (output.format)
{
case printable_result_filename::Lbtt:
{
std::string error;
std::ifstream f(output.val()->name());
if (!f)
{
problem = true;
std::cerr << "error: could not open " << output.val()
<< " after running \"" << cmd << "\".\n";
}
else
{
res = spot::lbtt_parse(f, error, dict);
if (!res)
{
problem = true;
std::cerr << "error: failed to parse output of \""
<< cmd << "\" in LBTT format:\n"
<< "error: " << error << '\n';
}
}
break;
}
case printable_result_filename::Dstar:
{
spot::dstar_parse_error_list pel;
std::string filename = output.val()->name();
auto aut = spot::dstar_parse(filename, pel, dict);
if (!pel.empty())
{
problem = true;
std::cerr << "error: failed to parse the output of \""
<< cmd << "\" as a DSTAR automaton.\n";
spot::format_dstar_parse_errors(std::cerr, filename, pel);
res = nullptr;
}
else
{
res = dstar_to_tgba(aut);
}
break;
}
case printable_result_filename::Hoa: // Will also read neverclaims
{
spot::hoa_parse_error_list pel;
std::string filename = output.val()->name();
auto aut = spot::hoa_parse(filename, pel, dict);
if (!pel.empty())
{
problem = true;
std::cerr << "error: failed to parse the automaton "
"produced by \"" << cmd << "\".\n";
spot::format_hoa_parse_errors(std::cerr, filename, pel);
res = nullptr;
}
else if (aut->aborted)
{
problem = true;
std::cerr << "error: command \"" << cmd
<< "\" aborted its output.\n";
res = nullptr;
}
else
{
res = aut->aut;
}
}
break;
case printable_result_filename::None:
problem = false;
res = nullptr;
break;
}
}
output.cleanup();
return res;
}
};
class processor: public job_processor
{
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
xtranslator_runner runner;
automaton_printer printer;
spot::postprocessor& post;
spot::printable_value<std::string> cmdname;
spot::printable_value<unsigned> roundval;
spot::printable_value<std::string> inputf;
public:
processor(spot::postprocessor& post)
: runner(dict), post(post)
{
printer.add_stat('T', &cmdname);
printer.add_stat('R', &roundval);
printer.add_stat('f', &inputf);
}
~processor()
{
}
int
process_string(const std::string& input,
const char* filename,
int linenum)
{
spot::ltl::parse_error_list pel;
const spot::ltl::formula* f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::ltl::format_parse_errors(std::cerr, input, pel);
if (f)
f->destroy();
return 1;
}
inputf = input;
process_formula(f, filename, linenum);
f->destroy();
return 0;
}
int
process_formula(const spot::ltl::formula* f,
const char* filename = 0, int linenum = 0)
{
static unsigned round = 0;
runner.round_formula(f, round);
unsigned ts = translators.size();
for (unsigned t = 0; t < ts; ++t)
{
bool problem;
double translation_time;
auto aut = runner.translate(t, problem, translation_time);
if (problem)
error_at_line(2, 0, filename, linenum, "aborting here");
if (aut)
{
aut = post.run(aut, f);
cmdname = translators[t].name;
roundval = round;
printer.print(aut, f, filename, linenum, translation_time,
nullptr);
};
}
++round;
return 0;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
argp_program_doc, children, 0, 0 };
// Disable post-processing as much as possible by default.
level = spot::postprocessor::Low;
pref = spot::postprocessor::Any;
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, 0, 0))
exit(err);
if (jobs.empty())
jobs.emplace_back("-", 1);
if (translators.empty())
error(2, 0, "No translator to run? Run '%s --help' for usage.",
program_name);
setup_sig_handler();
spot::postprocessor post;
post.set_pref(pref | comp);
post.set_type(type);
post.set_level(level);
processor p(post);
if (p.run())
return 2;
return 0;
}

View file

@ -1,6 +1,6 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement
## de l'Epita (LRDE).
## Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
## Développement de l'Epita (LRDE).
##
## This file is part of Spot, a model checking library.
##
@ -29,6 +29,7 @@ dist_man1_MANS = \
ltl2tgba.1 \
ltl2tgta.1 \
ltlcross.1 \
ltldo.1 \
ltlfilt.1 \
ltlgrind.1 \
randaut.1 \
@ -54,6 +55,9 @@ ltl2tgta.1: $(common_dep) $(srcdir)/ltl2tgta.x $(srcdir)/../ltl2tgta.cc
ltlcross.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltlcross.cc
$(convman) ../ltlcross$(EXEEXT) $(srcdir)/ltlcross.x $@
ltldo.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltldo.cc
$(convman) ../ltldo$(EXEEXT) $(srcdir)/ltldo.x $@
ltlfilt.1: $(common_dep) $(srcdir)/ltlfilt.x $(srcdir)/../ltlfilt.cc
$(convman) ../ltlfilt$(EXEEXT) $(srcdir)/ltlfilt.x $@

View file

@ -192,7 +192,8 @@ over the \fIN\fR products performed.
.BR randltl (1),
.BR genltl (1),
.BR ltlfilt (1),
.BR ltl2tgba (1)
.BR ltl2tgba (1),
.BR ltldo (1)
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
@ -230,5 +231,3 @@ checking the emptiness of Comp(P1)*P2.
Our implementation will detect and reports problems (like
inconsistencies between two translations) but unlike LBTT it does not
offer an interactive mode to investigate such problems.

8
src/bin/man/ltldo.x Normal file
View file

@ -0,0 +1,8 @@
[NAME]
ltldo \- run LTL/PSL formulas through other tools
[SEE ALSO]
.BR randltl (1),
.BR genltl (1),
.BR ltlfilt (1),
.BR ltl2tgba (1),
.BR ltldo (1)

View file

@ -10,7 +10,7 @@ we suggest you cite the following paper:
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
.PP
The following papers describes algorithms used by ltlfilt:
The following papers describe algorithms used by ltlfilt:
.TP
\(bu
Kousha Etessami: A note on a question of Peled and Wilke regarding
@ -44,4 +44,5 @@ Automata. Proceedings of CONCUR'00. LNCS 1877.
Describe the syntactic LTL classes matched by \fB\-\-eventual\fR, and
\fB\-\-universal\fR.
[SEE ALSO]
.BR randltl (1)
.BR randltl (1),
.BR ltldo (1)

View file

@ -83,6 +83,8 @@ TESTS = \
hoaparse.test \
dstar.test \
readsave.test \
ltldo.test \
ltldo2.test \
maskacc.test \
simdet.test \
sim2.test \

54
src/tgbatest/ltldo.test Executable file
View file

@ -0,0 +1,54 @@
#!/bin/sh
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Laboratoire de Recherche et
# Développement de l'Epita (LRDE).
#
# This file is part of Spot, a model checking library.
#
# Spot is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Spot is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
. ./defs
set -e
ltldo=../../bin/ltldo
ltl2tgba=../../bin/ltl2tgba
genltl=../../bin/genltl
run 0 $ltldo -f a -f 'a&b' -t 'echo %f,%s' >output
cat >expected <<EOF
(a),(a)
(a) & (b),(a) && (b)
EOF
diff output expected
# Test timeouts. This test should take 2*2 seconds.
$genltl --or-g=1..2 |
run 0 $ltldo -F- -t 'sleep 10; echo %f' -T1 -t 'sleep 10; echo %f' \
>output 2>stderr
test -z "`cat output`"
test 4 = `grep -c warning: stderr`
$genltl --and-gf=1..3 |
run 0 $ltldo "{tgba}$ltl2tgba %f -H >%H" "{ba}$ltl2tgba >%N %f -s" \
--stats="%T,%R,%f,%s,%t,%e" >output
cat output
cat >expected <<EOF
tgba,0,GFp1,1,2,2
ba,0,GFp1,2,4,4
tgba,1,GFp1 & GFp2,1,4,4
ba,1,GFp1 & GFp2,3,12,8
tgba,2,GFp1 & GFp2 & GFp3,1,8,8
ba,2,GFp1 & GFp2 & GFp3,4,32,13
EOF

31
src/tgbatest/ltldo2.test Executable file
View file

@ -0,0 +1,31 @@
#!/bin/sh
# -*- coding: utf-8 -*-
# Copyright (C) 2015 Laboratoire de Recherche et
# Développement de l'Epita (LRDE).
#
# This file is part of Spot, a model checking library.
#
# Spot is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Spot is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
. ./defs
set -e
ltldo=../../bin/ltldo
genltl=../../bin/genltl
test -n "$LTL2BA" || exit 77
$genltl --or-g=1..2 |
run 0 $ltldo '{ltl2ba}ltl2ba -f %s>%H' >output
test 2 = `grep -c digraph output`