common_trans: allow rewriting operators

Part of #168.

* spot/misc/formater.cc: Adjust to support bracketed options.
* bin/common_trans.hh, bin/common_trans.cc: Use that to
support rewriting operators.
* doc/org/ltlcross.org, tests/core/ltldo.test: Add some examples.
* NEWS: Mention it.
This commit is contained in:
Alexandre Duret-Lutz 2016-05-01 18:37:24 +02:00
parent 925785e85f
commit d9174593c8
6 changed files with 207 additions and 80 deletions

14
NEWS
View file

@ -20,6 +20,20 @@ New in spot 2.0a (not yet released)
These differ from --ap=RANGE that only consider *declared* atomic These differ from --ap=RANGE that only consider *declared* atomic
propositions, regardless of whether they are actually used. propositions, regardless of whether they are actually used.
* ltlcross and ltldo have a new syntax to specify that an input
formula should be written in some given syntax after rewriting
some operators away. For instance the defaults arguments passed
to ltl2dstar have been changed from
--output-format=hoa %L %O
into
--output-format=hoa %[WM]L %O
where [WM] specifies that operators W and M should be rewritten
away. As a consequence running
ltldo ltl2dstar -f 'a M b'
will now work and call ltl2dstar on the equivalent formula
'b U (a & b)' instead. The operators that can be listed between
brackets are the same as those of ltlfilt --unabbreviate option.
Library: Library:
* The print_hoa() function will now output version 1.1 of the HOA * The print_hoa() function will now output version 1.1 of the HOA

View file

@ -29,6 +29,7 @@
#include "error.h" #include "error.h"
#include <spot/tl/print.hh> #include <spot/tl/print.hh>
#include <spot/tl/unabbrev.hh>
#include "common_conv.hh" #include "common_conv.hh"
#include <spot/misc/escape.hh> #include <spot/misc/escape.hh>
@ -41,11 +42,11 @@ static struct shorthands_t
shorthands[] = { shorthands[] = {
{ "lbt", " <%L>%O" }, { "lbt", " <%L>%O" },
{ "ltl2ba", " -f %s>%O" }, { "ltl2ba", " -f %s>%O" },
{ "ltl2dstar", " --output-format=hoa %L %O"}, { "ltl2dstar", " --output-format=hoa %[MW]L %O"},
{ "ltl2tgba", " -H %f>%O" }, { "ltl2tgba", " -H %f>%O" },
{ "ltl3ba", " -f %s>%O" }, { "ltl3ba", " -f %s>%O" },
{ "ltl3dra", " -f %s>%O" }, { "ltl3dra", " -f %s>%O" },
{ "modella", " %L %O" }, { "modella", " %[MWei^]L %O" },
{ "spin", " -f %s>%O" }, { "spin", " -f %s>%O" },
}; };
@ -63,7 +64,7 @@ static void show_shorthands()
"matching those prefixes. So for instance\n" "matching those prefixes. So for instance\n"
" '{DRA} ~/mytools/ltl2dstar-0.5.2'\n" " '{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
"will changed into\n" "will changed into\n"
" '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %L %O'\n"); " '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %[MR]L %O'\n");
} }
@ -153,6 +154,58 @@ quoted_string::print(std::ostream& os, const char*) const
spot::quote_shell_string(os, val().c_str()); spot::quote_shell_string(os, val().c_str());
} }
void quoted_formula::print(std::ostream& os, const char* pos) const
{
spot::formula f = val_;
if (*pos == '[')
{
++pos;
auto end = strchr(pos, ']');
auto arg = strndup(pos, end - pos);
f = spot::unabbreviate(f, arg);
free(arg);
pos = end + 1;
}
std::ostringstream ss;
std::ostream* out = &ss;
bool quoted = true;
switch (*pos)
{
case 'F':
case 'S':
case 'L':
case 'W':
out = &os;
quoted = false;
}
switch (*pos)
{
case 'f':
case 'F':
print_psl(*out, f, true);
break;
case 's':
case 'S':
print_spin_ltl(*out, f, true);
break;
case 'l':
case 'L':
print_lbt_ltl(*out, f);
break;
case 'w':
case 'W':
print_wring_ltl(*out, f);
break;
}
if (quoted)
{
std::string s = ss.str();
spot::quote_shell_string(os, s.c_str());
}
}
printable_result_filename::printable_result_filename() printable_result_filename::printable_result_filename()
{ {
val_ = nullptr; val_ = nullptr;
@ -184,19 +237,42 @@ printable_result_filename::print(std::ostream& os, const char*) const
spot::quote_shell_string(os, val()->name()); spot::quote_shell_string(os, val()->name());
} }
void
filed_formula::print(std::ostream& os, const char* pos) const
{
std::ostringstream ss;
f_.print(ss, pos);
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
}
std::string
filed_formula::string_to_tmp(const std::string str, unsigned n) const
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
spot::open_temporary_file* tmpfile = spot::create_open_tmpfile(prefix);
std::string 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();
return tmpname;
}
translator_runner::translator_runner(spot::bdd_dict_ptr dict, translator_runner::translator_runner(spot::bdd_dict_ptr dict,
bool no_output_allowed) bool no_output_allowed)
: dict(dict) : dict(dict)
{ {
declare('f', &string_ltl_spot); declare('f', &ltl_formula);
declare('s', &string_ltl_spin); declare('s', &ltl_formula);
declare('l', &string_ltl_lbt); declare('l', &ltl_formula);
declare('w', &string_ltl_wring); declare('w', &ltl_formula);
declare('F', &filename_ltl_spot); declare('F', &filename_formula);
declare('S', &filename_ltl_spin); declare('S', &filename_formula);
declare('L', &filename_ltl_lbt); declare('L', &filename_formula);
declare('W', &filename_ltl_wring); declare('W', &filename_formula);
declare('D', &output); declare('D', &output);
declare('H', &output); declare('H', &output);
declare('N', &output); declare('N', &output);
@ -229,61 +305,29 @@ translator_runner::translator_runner(spot::bdd_dict_ptr dict,
} }
} }
void std::string
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 translator_runner::formula() const
{ {
// Pick the most readable format we have... // Pick the most readable format we have...
if (!string_ltl_spot.val().empty()) if (has('f') || has('F'))
return string_ltl_spot; return spot::str_psl(ltl_formula, true);
if (!string_ltl_spin.val().empty()) if (has('s') || has('S'))
return string_ltl_spin; return spot::str_spin_ltl(ltl_formula, true);
if (!string_ltl_wring.val().empty()) if (has('l') || has('L'))
return string_ltl_wring; return spot::str_lbt_ltl(ltl_formula);
if (!string_ltl_lbt.val().empty()) if (has('w') || has('W'))
return string_ltl_lbt; return spot::str_wring_ltl(ltl_formula);
SPOT_UNREACHABLE(); SPOT_UNREACHABLE();
return string_ltl_spot; return spot::str_psl(ltl_formula, true);
} }
void void
translator_runner::round_formula(spot::formula f, unsigned serial) translator_runner::round_formula(spot::formula f, unsigned serial)
{ {
if (has('f') || has('F')) ltl_formula = f;
string_ltl_spot = spot::str_psl(f, true); filename_formula.new_round(serial);
if (has('s') || has('S'))
string_ltl_spin = spot::str_spin_ltl(f, true);
if (has('l') || has('L'))
string_ltl_lbt = spot::str_lbt_ltl(f);
if (has('w') || has('W'))
string_ltl_wring = spot::str_wring_ltl(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; volatile bool timed_out = false;
unsigned timeout_count = 0; unsigned timeout_count = 0;
@ -407,6 +451,10 @@ static const argp_option options[] =
"If either %l, %L, or %T are used, any input formula that does " "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 " "not use LBT-style atomic propositions (i.e. p0, p1, ...) will be "
"relabeled automatically.\n" "relabeled automatically.\n"
"The sequences %f,%s,%l,%w,%F,%S,%L,%W can optionally be \"infixed\""
" by a bracketed sequence of operators to unabbreviate before outputing"
" the formula. For instance %[MW]f will rewrite operators M and W"
" before outputing it.\n"
"Furthermore, if COMMANDFMT has the form \"{NAME}CMD\", then only CMD " "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 " "will be passed to the shell, and NAME will be used to name the tool "
"in the output.", 4 }, "in the output.", 4 },

View file

@ -56,6 +56,31 @@ struct quoted_string final: public spot::printable_value<std::string>
void print(std::ostream& os, const char* pos) const override; void print(std::ostream& os, const char* pos) const override;
}; };
struct quoted_formula final: public spot::printable_value<spot::formula>
{
using spot::printable_value<spot::formula>::operator=;
void print(std::ostream& os, const char* pos) const override;
};
struct filed_formula final: public spot::printable
{
filed_formula(const quoted_formula& ltl) : f_(ltl)
{
}
void print(std::ostream& os, const char* pos) const override;
void new_round(unsigned serial)
{
serial_ = serial;
}
private:
const quoted_formula& f_;
unsigned serial_;
std::string string_to_tmp(const std::string str, unsigned n) const;
};
struct printable_result_filename final: struct printable_result_filename final:
public spot::printable_value<spot::temporary_file*> public spot::printable_value<spot::temporary_file*>
{ {
@ -75,14 +100,8 @@ class translator_runner: protected spot::formater
protected: protected:
spot::bdd_dict_ptr dict; spot::bdd_dict_ptr dict;
// Round-specific variables // Round-specific variables
quoted_string string_ltl_spot; quoted_formula ltl_formula;
quoted_string string_ltl_spin; filed_formula filename_formula = ltl_formula;
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 // Run-specific variables
printable_result_filename output; printable_result_filename output;
public: public:
@ -92,8 +111,7 @@ public:
// whether we accept the absence of output // whether we accept the absence of output
// specifier // specifier
bool no_output_allowed = false); bool no_output_allowed = false);
void string_to_tmp(std::string& str, unsigned n, std::string& tmpname); std::string formula() const;
const std::string& formula() const;
void round_formula(spot::formula f, unsigned serial); void round_formula(spot::formula f, unsigned serial);
}; };

View file

@ -98,6 +98,14 @@ Performing sanity checks and gathering statistics...
No problem detected. No problem detected.
#+end_example #+end_example
To handle tools that do not support some LTL operators, the character
sequences ~%f~, ~%s~, ~%l~, ~%w~, ~%F~, ~%S~, ~%L~, and ~%W~ can be
"infixed" by a bracketed list of operators to rewrite away. For
instance if a tool reads LTL formulas from a file in LBT's syntax, but
does not support operators ~M~ (strong until) and ~W~ (weak until),
use ~%[WM]L~ instead of just ~%L~; this way operators ~W~ and ~M~ will
be rewritten using the other supported operators.
=ltlcross= can only read four kinds of output: =ltlcross= can only read four kinds of output:
- Never claims (only if they are restricted to representing an - Never claims (only if they are restricted to representing an
automaton using =if=, =goto=, and =skip= statements) such as those automaton using =if=, =goto=, and =skip= statements) such as those
@ -125,7 +133,7 @@ tools:
- '=ltl2ba -f %s >%O=' - '=ltl2ba -f %s >%O='
- '=ltl3ba -M0 -f %s >%O=' (less deterministic output, can be smaller) - '=ltl3ba -M0 -f %s >%O=' (less deterministic output, can be smaller)
- '=ltl3ba -M1 -f %s >%O=' (more deterministic output) - '=ltl3ba -M1 -f %s >%O=' (more deterministic output)
- '=modella -r12 -g -e %L %O=' - '=modella -r12 -g -e %[MWei^]L %O='
- '=/path/to/script4lbtt.py %L %O=' (script supplied by [[http://web.archive.org/web/20070214050826/http://estragon.ti.informatik.uni-kiel.de/~fritz/][ltl2nba]] for - '=/path/to/script4lbtt.py %L %O=' (script supplied by [[http://web.archive.org/web/20070214050826/http://estragon.ti.informatik.uni-kiel.de/~fritz/][ltl2nba]] for
its interface with LBTT) its interface with LBTT)
- '=ltl2tgba -s %f >%O=' (smaller output, Büchi automaton) - '=ltl2tgba -s %f >%O=' (smaller output, Büchi automaton)
@ -134,22 +142,22 @@ tools:
- '=ltl2tgba -H -D %f >%O=' (more deterministic output, TGBA) - '=ltl2tgba -H -D %f >%O=' (more deterministic output, TGBA)
- '=lbt <%L >%O=' - '=lbt <%L >%O='
- '~ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD - '~ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD
--output-format=hoa %L %O~' deterministic Rabin output in HOA, as --output-format=hoa %[MW]L %O~' deterministic Rabin output in HOA, as
supported since version 0.5.2 of =ltl2dstar=. supported since version 0.5.2 of =ltl2dstar=.
- '~ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD --automata=streett - '~ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD --automata=streett
--output-format=hoa %L %O~' deterministic Streett output in HOA, --output-format=hoa %[MW]L %O~' deterministic Streett output in HOA,
as supported since version 0.5.2 of =ltl2dstar=. as supported since version 0.5.2 of =ltl2dstar=.
- '=ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD %L %O=' (Rabin - '=ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD %[MW]L %O=' (Rabin
output in DSTAR format, as supported in older versions of output in DSTAR format, as supported in older versions of
=ltl2dstar=. =ltl2dstar=.
- '=ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD %L - | dstar2tgba - '=ltl2dstar --ltl2nba=spin:path/to/ltl2tgba@-sD %L - | dstar2tgba
-s >%O=' (external conversion from Rabin to Büchi done by -s >%O=' (external conversion from Rabin to Büchi done by
=dstar2tgba= for more reduction of the Büchi automaton than what =dstar2tgba= for more reduction of the Büchi automaton than what
=ltlcross= would provide) =ltlcross= would provide)
- '=java -jar Rabinizer.jar -ltl2dstar %F %O; mv %O.dst %O=' (Rabinizer - '=java -jar Rabinizer.jar -ltl2dstar %[MW]F %O; mv %O.dst %O=' (Rabinizer
uses the last =%O= argument as a prefix to which it always append =.dst=, uses the last =%O= argument as a prefix to which it always append =.dst=,
so we have to rename =%O.dst= as =%O= so that =ltlcross= can find the file) so we have to rename =%O.dst= as =%O= so that =ltlcross= can find the file)
- '~java -jar rabinizer3.1.jar -in=formula -silent -out=std -format=hoa -auto=tr %f >%O~' - '~java -jar rabinizer3.1.jar -in=formula -silent -out=std -format=hoa -auto=tr %[MWR]f >%O~'
(rabinizer 3.1 can output automata in the HOA format) (rabinizer 3.1 can output automata in the HOA format)
- '=ltl3dra -f %s >%O=' (The HOA format is the default for =ltl2dra=.) - '=ltl3dra -f %s >%O=' (The HOA format is the default for =ltl2dra=.)
@ -167,18 +175,18 @@ the following words, then the string on the right is appended.
lbt <%L>%O lbt <%L>%O
ltl2ba -f %s>%O ltl2ba -f %s>%O
ltl2dstar --output-format=hoa %L %O ltl2dstar --output-format=hoa %[MW]L %O
ltl2tgba -H %f>%O ltl2tgba -H %f>%O
ltl3ba -f %s>%O ltl3ba -f %s>%O
ltl3dra -f %f>%O ltl3dra -f %s>%O
modella %L %O modella %[MWei^]L %O
spin -f %s>%O spin -f %s>%O
Any {name} and directory component is skipped for the purpose of Any {name} and directory component is skipped for the purpose of
matching those prefixes. So for instance matching those prefixes. So for instance
'{DRA} ~/mytools/ltl2dstar-0.5.2' '{DRA} ~/mytools/ltl2dstar-0.5.2'
will changed into will changed into
'{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %L %O' '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %[MR]L %O'
#+end_example #+end_example
What this implies is that running =ltlcross ltl2ba ltl3ba ...= is What this implies is that running =ltlcross ltl2ba ltl3ba ...= is

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement // Copyright (C) 2012, 2013, 2016 Laboratoire de Recherche et
// de l'Epita (LRDE). // Développement de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -20,9 +20,18 @@
#include "config.h" #include "config.h"
#include <spot/misc/formater.hh> #include <spot/misc/formater.hh>
#include <iostream> #include <iostream>
#include <sstream>
namespace spot namespace spot
{ {
static void unclosed_bracket(const char* s)
{
std::ostringstream ss;
ss << '\'' << s << "' has unclosed bracket";
throw std::runtime_error(ss.str());
}
void void
formater::scan(const char* fmt, std::vector<bool>& has) const formater::scan(const char* fmt, std::vector<bool>& has) const
{ {
@ -30,6 +39,19 @@ namespace spot
if (*pos == '%') if (*pos == '%')
{ {
char c = *++pos; char c = *++pos;
// %[...]c
const char* mark = pos;
if (c == '[')
{
do
{
++pos;
if (SPOT_UNLIKELY(!*pos))
unclosed_bracket(mark - 1);
}
while (*pos != ']');
c = *++pos;
}
has[c] = true; has[c] = true;
if (!c) if (!c)
break; break;
@ -53,9 +75,25 @@ namespace spot
else else
{ {
char c = *++pos; char c = *++pos;
const char* next = pos;
// in case we have %[...]X... , we want to pass
// [...]X... to the printer, and continue after the X once
// that is done.
if (c == '[')
{
do
{
++next;
if (SPOT_UNLIKELY(*next == 0))
unclosed_bracket(pos - 1);
}
while (*next != ']');
c = *++next;
}
call_[c]->print(*output_, pos); call_[c]->print(*output_, pos);
if (!c) if (!c)
break; break;
pos = next;
} }
return *output_; return *output_;
} }

View file

@ -25,10 +25,11 @@ ltldo=ltldo
ltl2tgba=ltl2tgba ltl2tgba=ltl2tgba
genltl=genltl genltl=genltl
run 0 $ltldo -f a -f 'a&b' -t 'echo %f,%s' >output run 0 $ltldo -f a -f 'a&b' -f 'a -> b' -t 'echo %f,%s,%[ei]w' >output
cat >expected <<EOF cat >expected <<EOF
a,a a,a,a=1
(a) & (b),(a) && (b) (a) & (b),(a) && (b),(a=1) * (b=1)
(a) -> (b),(a) -> (b),(a=0) + (b=1)
EOF EOF
diff output expected diff output expected