move spot/bin/ and spot/tests/ up by one level

* spot/bin/: Move...
* bin/: ... here.
* spot/tests/: Move...
* tests/: ... here.
* Makefile.am, README, bench/stutter/Makefile.am,
bench/stutter/stutter_invariance_formulas.cc, doc/Makefile.am,
configure.ac, debian/rules, spot/Makefile.am, spot/ltsmin/Makefile.am,
spot/ltsmin/kripke.test, spot/sanity/style.test, python/tests/run.in:
Adjust.
This commit is contained in:
Alexandre Duret-Lutz 2015-12-27 12:16:06 +01:00
parent ff4837f4f2
commit 134dfc73de
220 changed files with 35 additions and 30 deletions

16
bin/.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
autfilt
dstar2tgba
genltl
ltl2tgba
ltl2tgta
ltlcross
ltldo
ltlfilt
ltlgrind
randaut
randltl
spot-x
*.a
*.1
*.7
lck-*

90
bin/Makefile.am Normal file
View file

@ -0,0 +1,90 @@
## -*- coding: utf-8 -*-
## 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.
##
## 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/>.
SUBDIRS = . man
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS) \
-I$(top_builddir)/lib -I$(top_srcdir)/lib
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
LDADD = \
libcommon.a \
$(top_builddir)/lib/libgnu.la \
$(top_builddir)/spot/libspot.la
noinst_LIBRARIES = libcommon.a
libcommon_a_SOURCES = \
common_aoutput.cc \
common_aoutput.hh \
common_conv.hh \
common_conv.cc \
common_cout.hh \
common_cout.cc \
common_file.cc \
common_file.hh \
common_finput.cc \
common_finput.hh \
common_hoaread.cc \
common_hoaread.hh \
common_output.cc \
common_output.hh \
common_post.cc \
common_post.hh \
common_range.cc \
common_range.hh \
common_r.cc \
common_r.hh \
common_setup.cc \
common_setup.hh \
common_sys.hh \
common_trans.cc \
common_trans.hh
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
# is automatically updated).
noinst_PROGRAMS = spot-x
autfilt_SOURCES = autfilt.cc
ltlfilt_SOURCES = ltlfilt.cc
genltl_SOURCES = genltl.cc
randaut_SOURCES = randaut.cc
randltl_SOURCES = randltl.cc
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)
EXTRA_DIST = options.py

52
bin/README Normal file
View file

@ -0,0 +1,52 @@
This directory contains the source of some command-line tools that
expose some of Spot's algorithms to Unix users.
Man pages are generated from the --help output of each tool,
supplemented by any text in the man/*.x files. Usually the extra text
contains either some bibliographical references, some formal
definitions or some examples that are too long for --help. Having a
few short examples at the end of --help is good.
This directory also build some non-installed binaries, like spot-x,
whose purpose is just to generate a man-page with the same format as
the other man pages (this includes keeping the version number
up-to-date).
There is also a script called 'options.py' that summerizes how the
different short options are used among the tools.
Routines that are shared by multiple command-line tools are stored in
files called common_*.{cc,hh}.
Recommendations when adding new tools or features:
--------------------------------------------------
- Tools should be designed to work on multiple inputs (e.g., read
different outputs from multiple files, and accept many inputs from
the same file, including stdin). They should also all be designed
to produce several outputs, usually one per input. This way they
can be piped one onto the other easily.
- When naming an option, seek inspiration from the POSIX standard, or
from GNU extensions. For instance ltlfilt and autfilt both have a
-v option to invert the filter; this is inspired from grep's -v
option. The long version of this option (--invert-match) is also
the same as in grep.
- When adding a new option, implement only the --long-option by
default. Do not add a short version unless
(1) you are sure it will be frequently used interactively
(if it is only used in scripts, then a long option is enough)
(2) this option can be shared by multiple tools.
- As much as possible, use the same option names across tools. Use
the script options.py in this directory to check what short options
are used. It's OK if the same short option correspond to different
long names in the various tools, as long as the intent is similar.
For instance -n has different long options depending on the tool:
autfilt -n N means --max-count=N
randltl -n N means --formulas=N
randaut -n N means --automata=N
but in all cases, the intent is to specify the number of items
to output.

782
bin/autfilt.cc Normal file
View file

@ -0,0 +1,782 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 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 <limits>
#include <set>
#include <memory>
#include <argp.h>
#include "error.h"
#include "common_setup.hh"
#include "common_finput.hh"
#include "common_cout.hh"
#include "common_aoutput.hh"
#include "common_range.hh"
#include "common_post.hh"
#include "common_conv.hh"
#include "common_hoaread.hh"
#include <spot/twaalgos/product.hh>
#include <spot/twaalgos/isdet.hh>
#include <spot/twaalgos/stutter.hh>
#include <spot/twaalgos/isunamb.hh>
#include <spot/misc/optionmap.hh>
#include <spot/misc/timer.hh>
#include <spot/misc/random.hh>
#include <spot/parseaut/public.hh>
#include <spot/tl/exclusive.hh>
#include <spot/twaalgos/remprop.hh>
#include <spot/twaalgos/randomize.hh>
#include <spot/twaalgos/are_isomorphic.hh>
#include <spot/twaalgos/canonicalize.hh>
#include <spot/twaalgos/mask.hh>
#include <spot/twaalgos/sepsets.hh>
#include <spot/twaalgos/stripacc.hh>
#include <spot/twaalgos/remfin.hh>
#include <spot/twaalgos/cleanacc.hh>
#include <spot/twaalgos/dtwasat.hh>
#include <spot/twaalgos/complement.hh>
#include <spot/twaalgos/strength.hh>
static const char argp_program_doc[] ="\
Convert, transform, and filter omega-automata.\v\
Exit status:\n\
0 if some automata were output\n\
1 if no automata were output (no match)\n\
2 if any error has been reported";
// Keep this list sorted
enum {
OPT_ACC_SETS = 256,
OPT_ARE_ISOMORPHIC,
OPT_CLEAN_ACC,
OPT_CNF_ACC,
OPT_COMPLEMENT,
OPT_COMPLEMENT_ACC,
OPT_COUNT,
OPT_DECOMPOSE_STRENGTH,
OPT_DESTUT,
OPT_DNF_ACC,
OPT_EDGES,
OPT_EXCLUSIVE_AP,
OPT_GENERIC,
OPT_INSTUT,
OPT_INTERSECT,
OPT_IS_COMPLETE,
OPT_IS_DETERMINISTIC,
OPT_IS_EMPTY,
OPT_IS_UNAMBIGUOUS,
OPT_IS_TERMINAL,
OPT_IS_WEAK,
OPT_IS_INHERENTLY_WEAK,
OPT_KEEP_STATES,
OPT_MASK_ACC,
OPT_MERGE,
OPT_PRODUCT_AND,
OPT_PRODUCT_OR,
OPT_RANDOMIZE,
OPT_REM_AP,
OPT_REM_DEAD,
OPT_REM_UNREACH,
OPT_REM_FIN,
OPT_SAT_MINIMIZE,
OPT_SEED,
OPT_SEP_SETS,
OPT_SIMPLIFY_EXCLUSIVE_AP,
OPT_STATES,
OPT_STRIPACC,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Input:", 1 },
{ "file", 'F', "FILENAME", 0,
"process the automaton in FILENAME", 0 },
/**************************************************/
{ "count", 'c', nullptr, 0, "print only a count of matched automata", 3 },
{ "max-count", 'n', "NUM", 0, "output at most NUM automata", 3 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Transformations:", 5 },
{ "merge-transitions", OPT_MERGE, nullptr, 0,
"merge transitions with same destination and acceptance", 0 },
{ "product", OPT_PRODUCT_AND, "FILENAME", 0,
"build the product with the automaton in FILENAME "
"to intersect languages", 0 },
{ "product-and", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
{ "product-or", OPT_PRODUCT_OR, "FILENAME", 0,
"build the product with the automaton in FILENAME "
"to sum languages", 0 },
{ "randomize", OPT_RANDOMIZE, "s|t", OPTION_ARG_OPTIONAL,
"randomize states and transitions (specify 's' or 't' to "
"randomize only states or transitions)", 0 },
{ "instut", OPT_INSTUT, "1|2", OPTION_ARG_OPTIONAL,
"allow more stuttering (two possible algorithms)", 0 },
{ "destut", OPT_DESTUT, nullptr, 0, "allow less stuttering", 0 },
{ "mask-acc", OPT_MASK_ACC, "NUM[,NUM...]", 0,
"remove all transitions in specified acceptance sets", 0 },
{ "strip-acceptance", OPT_STRIPACC, nullptr, 0,
"remove the acceptance condition and all acceptance sets", 0 },
{ "keep-states", OPT_KEEP_STATES, "NUM[,NUM...]", 0,
"only keep specified states. The first state will be the new "\
"initial state. Implies --remove-unreachable-states.", 0 },
{ "dnf-acceptance", OPT_DNF_ACC, nullptr, 0,
"put the acceptance condition in Disjunctive Normal Form", 0 },
{ "cnf-acceptance", OPT_CNF_ACC, nullptr, 0,
"put the acceptance condition in Conjunctive Normal Form", 0 },
{ "remove-fin", OPT_REM_FIN, nullptr, 0,
"rewrite the automaton without using Fin acceptance", 0 },
{ "cleanup-acceptance", OPT_CLEAN_ACC, nullptr, 0,
"remove unused acceptance sets from the automaton", 0 },
{ "complement", OPT_COMPLEMENT, nullptr, 0,
"complement each automaton (currently support only deterministic "
"automata)", 0 },
{ "complement-acceptance", OPT_COMPLEMENT_ACC, nullptr, 0,
"complement the acceptance condition (without touching the automaton)",
0 },
{ "decompose-strength", OPT_DECOMPOSE_STRENGTH, "t|w|s", 0,
"extract the (t) terminal, (w) weak, or (s) strong part of an automaton"
" (letters may be combined to combine more strengths in the output)", 0 },
{ "exclusive-ap", OPT_EXCLUSIVE_AP, "AP,AP,...", 0,
"if any of those APs occur in the automaton, restrict all edges to "
"ensure two of them may not be true at the same time. Use this option "
"multiple times to declare independent groups of exclusive "
"propositions.", 0 },
{ "simplify-exclusive-ap", OPT_SIMPLIFY_EXCLUSIVE_AP, nullptr, 0,
"if --exclusive-ap is used, assume those AP groups are actually exclusive"
" in the system to simplify the expression of transition labels (implies "
"--merge-transitions)", 0 },
{ "remove-ap", OPT_REM_AP, "AP[=0|=1][,AP...]", 0,
"remove atomic propositions either by existential quantification, or "
"by assigning them 0 or 1", 0 },
{ "remove-unreachable-states", OPT_REM_UNREACH, nullptr, 0,
"remove states that are unreachable from the initial state", 0 },
{ "remove-dead-states", OPT_REM_DEAD, nullptr, 0,
"remove states that are unreachable, or that cannot belong to an "
"infinite path", 0 },
{ "separate-sets", OPT_SEP_SETS, nullptr, 0,
"if both Inf(x) and Fin(x) appear in the acceptance condition, replace "
"Fin(x) by a new Fin(y) and adjust the automaton", 0 },
{ "sat-minimize", OPT_SAT_MINIMIZE, "options", OPTION_ARG_OPTIONAL,
"minimize the automaton using a SAT solver (only work for deterministic"
" automata)", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Filtering options:", 6 },
{ "are-isomorphic", OPT_ARE_ISOMORPHIC, "FILENAME", 0,
"keep automata that are isomorphic to the automaton in FILENAME", 0 },
{ "isomorphic", 0, nullptr, OPTION_ALIAS | OPTION_HIDDEN, nullptr, 0 },
{ "unique", 'u', nullptr, 0,
"do not output the same automaton twice (same in the sense that they "\
"are isomorphic)", 0 },
{ "is-complete", OPT_IS_COMPLETE, nullptr, 0,
"keep complete automata", 0 },
{ "is-deterministic", OPT_IS_DETERMINISTIC, nullptr, 0,
"keep deterministic automata", 0 },
{ "is-empty", OPT_IS_EMPTY, nullptr, 0,
"keep automata with an empty language", 0 },
{ "is-unambiguous", OPT_IS_UNAMBIGUOUS, nullptr, 0,
"keep only unambiguous automata", 0 },
{ "is-terminal", OPT_IS_TERMINAL, nullptr, 0,
"keep only terminal automata", 0 },
{ "is-weak", OPT_IS_WEAK, nullptr, 0,
"keep only weak automata", 0 },
{ "is-inherently-weak", OPT_IS_INHERENTLY_WEAK, nullptr, 0,
"keep only inherently weak automata", 0 },
{ "intersect", OPT_INTERSECT, "FILENAME", 0,
"keep automata whose languages have an non-empty intersection with"
" the automaton from FILENAME", 0 },
{ "invert-match", 'v', nullptr, 0, "select non-matching automata", 0 },
{ "states", OPT_STATES, "RANGE", 0,
"keep automata whose number of states are in RANGE", 0 },
{ "edges", OPT_EDGES, "RANGE", 0,
"keep automata whose number of edges are in RANGE", 0 },
{ "acc-sets", OPT_ACC_SETS, "RANGE", 0,
"keep automata whose number of acceptance sets are in RANGE", 0 },
RANGE_DOC_FULL,
{ nullptr, 0, nullptr, 0,
"If any option among --small, --deterministic, or --any is given, "
"then the simplification level defaults to --high unless specified "
"otherwise. If any option among --low, --medium, or --high is given, "
"then the simplification goal defaults to --small unless specified "
"otherwise. If none of those options are specified, then autfilt "
"acts as is --any --low were given: these actually disable the "
"simplification routines.", 22 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ "extra-options", 'x', "OPTS", 0,
"fine-tuning options (see spot-x (7))", 0 },
{ "seed", OPT_SEED, "INT", 0,
"seed for the random number generator (0)", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const struct argp_child children[] =
{
{ &hoaread_argp, 0, nullptr, 0 },
{ &aoutput_argp, 0, nullptr, 0 },
{ &aoutput_io_format_argp, 0, nullptr, 4 },
{ &post_argp_disabled, 0, nullptr, 0 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
typedef std::set<std::vector<tr_t>> unique_aut_t;
static long int match_count = 0;
static spot::option_map extra_options;
static bool randomize_st = false;
static bool randomize_tr = false;
static int opt_seed = 0;
// We want all these variable to be destroyed when we exit main, to
// make sure it happens before all other global variables (like the
// atomic propositions maps) are destroyed. Otherwise we risk
// accessing deleted stuff.
static struct opt_t
{
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
spot::twa_graph_ptr product_and = nullptr;
spot::twa_graph_ptr product_or = nullptr;
spot::twa_graph_ptr intersect = nullptr;
spot::twa_graph_ptr are_isomorphic = nullptr;
std::unique_ptr<spot::isomorphism_checker>
isomorphism_checker = nullptr;
std::unique_ptr<unique_aut_t> uniq = nullptr;
}* opt;
static bool opt_merge = false;
static bool opt_is_complete = false;
static bool opt_is_deterministic = false;
static bool opt_is_unambiguous = false;
static bool opt_is_terminal = false;
static bool opt_is_weak = false;
static bool opt_is_inherently_weak = false;
static bool opt_invert = false;
static range opt_states = { 0, std::numeric_limits<int>::max() };
static range opt_edges = { 0, std::numeric_limits<int>::max() };
static range opt_accsets = { 0, std::numeric_limits<int>::max() };
static int opt_max_count = -1;
static bool opt_destut = false;
static char opt_instut = 0;
static bool opt_is_empty = false;
static bool opt_stripacc = false;
static bool opt_dnf_acc = false;
static bool opt_cnf_acc = false;
static bool opt_rem_fin = false;
static bool opt_clean_acc = false;
static bool opt_complement = false;
static bool opt_complement_acc = false;
static char* opt_decompose_strength = nullptr;
static spot::acc_cond::mark_t opt_mask_acc = 0U;
static std::vector<bool> opt_keep_states = {};
static unsigned int opt_keep_states_initial = 0;
static spot::exclusive_ap excl_ap;
static spot::remove_ap rem_ap;
static bool opt_simplify_exclusive_ap = false;
static bool opt_rem_dead = false;
static bool opt_rem_unreach = false;
static bool opt_sep_sets = false;
static const char* opt_sat_minimize = nullptr;
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'c':
automaton_format = Count;
break;
case 'F':
jobs.emplace_back(arg, true);
break;
case 'n':
opt_max_count = to_pos_int(arg);
break;
case 'u':
opt->uniq =
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
break;
case 'v':
opt_invert = true;
break;
case 'x':
{
const char* opt = extra_options.parse_options(arg);
if (opt)
error(2, 0, "failed to parse --options near '%s'", opt);
}
break;
case OPT_ACC_SETS:
opt_accsets = parse_range(arg, 0, std::numeric_limits<int>::max());
break;
case OPT_ARE_ISOMORPHIC:
opt->are_isomorphic = read_automaton(arg, opt->dict);
break;
case OPT_CLEAN_ACC:
opt_clean_acc = true;
break;
case OPT_CNF_ACC:
opt_dnf_acc = false;
opt_cnf_acc = true;
break;
case OPT_COMPLEMENT:
opt_complement = true;
break;
case OPT_COMPLEMENT_ACC:
opt_complement_acc = true;
break;
case OPT_DECOMPOSE_STRENGTH:
opt_decompose_strength = arg;
break;
case OPT_DESTUT:
opt_destut = true;
break;
case OPT_DNF_ACC:
opt_dnf_acc = true;
opt_cnf_acc = false;
break;
case OPT_EDGES:
opt_edges = parse_range(arg, 0, std::numeric_limits<int>::max());
break;
case OPT_EXCLUSIVE_AP:
excl_ap.add_group(arg);
break;
case OPT_INSTUT:
if (!arg || (arg[0] == '1' && arg[1] == 0))
opt_instut = 1;
else if (arg[0] == '2' && arg[1] == 0)
opt_instut = 2;
else
error(2, 0, "unknown argument for --instut: %s", arg);
break;
case OPT_INTERSECT:
opt->intersect = read_automaton(arg, opt->dict);
break;
case OPT_IS_COMPLETE:
opt_is_complete = true;
break;
case OPT_IS_DETERMINISTIC:
opt_is_deterministic = true;
break;
case OPT_IS_EMPTY:
opt_is_empty = true;
break;
case OPT_IS_INHERENTLY_WEAK:
opt_is_inherently_weak = true;
break;
case OPT_IS_UNAMBIGUOUS:
opt_is_unambiguous = true;
break;
case OPT_IS_TERMINAL:
opt_is_terminal = true;
break;
case OPT_IS_WEAK:
opt_is_weak = true;
break;
case OPT_MERGE:
opt_merge = true;
break;
case OPT_MASK_ACC:
{
for (auto res : to_longs(arg))
{
if (res < 0)
error(2, 0, "acceptance sets should be non-negative:"
" --mask-acc=%ld", res);
if (static_cast<unsigned long>(res)
> sizeof(spot::acc_cond::mark_t::value_t))
error(2, 0, "this implementation does not support that many"
" acceptance sets: --mask-acc=%ld", res);
opt_mask_acc.set(res);
}
break;
}
case OPT_KEEP_STATES:
{
std::vector<long> values = to_longs(arg);
if (!values.empty())
opt_keep_states_initial = values[0];
for (auto res : values)
{
if (res < 0)
error(2, 0, "state ids should be non-negative:"
" --mask-acc=%ld", res);
// We don't know yet how many states the automata contain.
if (opt_keep_states.size() <= static_cast<unsigned long>(res))
opt_keep_states.resize(res + 1, false);
opt_keep_states[res] = true;
}
opt_rem_unreach = true;
break;
}
case OPT_PRODUCT_AND:
{
auto a = read_automaton(arg, opt->dict);
if (!opt->product_and)
opt->product_and = std::move(a);
else
opt->product_and = spot::product(std::move(opt->product_and),
std::move(a));
}
break;
case OPT_PRODUCT_OR:
{
auto a = read_automaton(arg, opt->dict);
if (!opt->product_or)
opt->product_or = std::move(a);
else
opt->product_or = spot::product_or(std::move(opt->product_or),
std::move(a));
}
break;
case OPT_RANDOMIZE:
if (arg)
{
for (auto p = arg; *p; ++p)
switch (*p)
{
case 's':
randomize_st = true;
break;
case 't':
randomize_tr = true;
break;
default:
error(2, 0, "unknown argument for --randomize: '%c'", *p);
}
}
else
{
randomize_tr = true;
randomize_st = true;
}
break;
case OPT_REM_AP:
rem_ap.add_ap(arg);
break;
case OPT_REM_DEAD:
opt_rem_dead = true;
break;
case OPT_REM_FIN:
opt_rem_fin = true;
break;
case OPT_REM_UNREACH:
opt_rem_unreach = true;
break;
case OPT_SAT_MINIMIZE:
opt_sat_minimize = arg ? arg : "";
break;
case OPT_SEED:
opt_seed = to_int(arg);
break;
case OPT_SEP_SETS:
opt_sep_sets = true;
break;
case OPT_SIMPLIFY_EXCLUSIVE_AP:
opt_simplify_exclusive_ap = true;
opt_merge = true;
break;
case OPT_STATES:
opt_states = parse_range(arg, 0, std::numeric_limits<int>::max());
break;
case OPT_STRIPACC:
opt_stripacc = true;
break;
case ARGP_KEY_ARG:
jobs.emplace_back(arg, true);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
namespace
{
class hoa_processor: public job_processor
{
private:
spot::postprocessor& post;
automaton_printer printer;
public:
hoa_processor(spot::postprocessor& post)
: post(post), printer(aut_input)
{
}
int
process_formula(spot::formula, const char*, int)
{
SPOT_UNREACHABLE();
}
int
process_automaton(const spot::const_parsed_aut_ptr& haut,
const char* filename)
{
spot::stopwatch sw;
sw.start();
// If --stats or --name is used, duplicate the automaton so we
// never modify the original automaton (e.g. with
// merge_edges()) and the statistics about it make sense.
auto aut = ((automaton_format == Stats) || opt_name)
? spot::make_twa_graph(haut->aut, spot::twa::prop_set::all())
: haut->aut;
// Preprocessing.
if (opt_stripacc)
spot::strip_acceptance_here(aut);
if (opt_merge)
aut->merge_edges();
if (opt_clean_acc || opt_rem_fin)
cleanup_acceptance_here(aut);
if (opt_sep_sets)
separate_sets_here(aut);
if (opt_complement_acc)
aut->set_acceptance(aut->acc().num_sets(),
aut->get_acceptance().complement());
if (opt_complement)
{
if (!spot::is_deterministic(aut))
{
std::cerr << filename << ':'
<< haut->loc << (": --complement currently supports "
"only deterministic automata\n");
exit(2);
}
aut = spot::dtwa_complement(aut);
}
if (opt_rem_fin)
aut = remove_fin(aut);
if (opt_dnf_acc)
aut->set_acceptance(aut->acc().num_sets(),
aut->get_acceptance().to_dnf());
if (opt_cnf_acc)
aut->set_acceptance(aut->acc().num_sets(),
aut->get_acceptance().to_cnf());
// Filters.
bool matched = true;
matched &= opt_states.contains(aut->num_states());
matched &= opt_edges.contains(aut->num_edges());
matched &= opt_accsets.contains(aut->acc().num_sets());
if (opt_is_complete)
matched &= is_complete(aut);
if (opt_is_deterministic)
matched &= is_deterministic(aut);
if (opt_is_deterministic)
matched &= is_deterministic(aut);
else if (opt_is_unambiguous)
matched &= is_unambiguous(aut);
if (opt_is_terminal)
matched &= is_terminal_automaton(aut);
else if (opt_is_weak)
matched &= is_weak_automaton(aut);
else if (opt_is_inherently_weak)
matched &= is_inherently_weak_automaton(aut);
if (opt->are_isomorphic)
matched &= opt->isomorphism_checker->is_isomorphic(aut);
if (opt_is_empty)
matched &= aut->is_empty();
if (opt->intersect)
matched &= !spot::product(aut, opt->intersect)->is_empty();
// Drop or keep matched automata depending on the --invert option
if (matched == opt_invert)
return 0;
// Postprocessing.
if (opt_mask_acc)
aut = mask_acc_sets(aut, opt_mask_acc & aut->acc().all_sets());
if (!excl_ap.empty())
aut = excl_ap.constrain(aut, opt_simplify_exclusive_ap);
if (!rem_ap.empty())
aut = rem_ap.strip(aut);
if (opt_destut)
aut = spot::closure(std::move(aut));
if (opt_instut == 1)
aut = spot::sl(std::move(aut));
else if (opt_instut == 2)
aut = spot::sl2(std::move(aut));
if (!opt_keep_states.empty())
aut = mask_keep_states(aut, opt_keep_states, opt_keep_states_initial);
if (opt_rem_dead)
aut->purge_dead_states();
else if (opt_rem_unreach)
aut->purge_unreachable_states();
if (opt->product_and)
aut = spot::product(std::move(aut), opt->product_and);
if (opt->product_or)
aut = spot::product_or(std::move(aut), opt->product_or);
if (opt_decompose_strength)
{
aut = decompose_strength(aut, opt_decompose_strength);
if (!aut)
return 0;
}
if (opt_sat_minimize)
{
aut = spot::sat_minimize(aut, opt_sat_minimize, sbacc);
if (!aut)
return 0;
}
aut = post.run(aut, nullptr);
if (randomize_st || randomize_tr)
spot::randomize(aut, randomize_st, randomize_tr);
const double conversion_time = sw.stop();
if (opt->uniq)
{
auto tmp =
spot::canonicalize(make_twa_graph(aut,
spot::twa::prop_set::all()));
if (!opt->uniq->emplace(tmp->edge_vector().begin() + 1,
tmp->edge_vector().end()).second)
return 0;
}
++match_count;
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
if (opt_max_count >= 0 && match_count >= opt_max_count)
abort_run = true;
return 0;
}
int
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
{
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
return 2;
}
int
process_file(const char* filename)
{
auto hp = spot::automaton_stream_parser(filename, opt_parse);
int err = 0;
while (!abort_run)
{
auto haut = hp.parse(opt->dict);
if (!haut->aut && haut->errors.empty())
break;
if (haut->format_errors(std::cerr))
err = 2;
if (!haut->aut)
error(2, 0, "failed to read automaton from %s", filename);
else if (haut->aborted)
err = std::max(err, aborted(haut, filename));
else
process_automaton(haut, filename);
}
return err;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[FILENAMES...]",
argp_program_doc, children, nullptr, nullptr };
try
{
// This will ensure that all objects stored in this struct are
// destroyed before global variables.
opt_t o;
opt = &o;
// Disable post-processing as much as possible by default.
level = spot::postprocessor::Low;
pref = spot::postprocessor::Any;
type = spot::postprocessor::Generic;
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (level_set && !pref_set)
pref = spot::postprocessor::Small;
if (pref_set && !level_set)
level = spot::postprocessor::High;
if (jobs.empty())
jobs.emplace_back("-", true);
if (opt->are_isomorphic)
{
if (opt_merge)
opt->are_isomorphic->merge_edges();
opt->isomorphism_checker = std::unique_ptr<spot::isomorphism_checker>
(new spot::isomorphism_checker(opt->are_isomorphic));
}
spot::srand(opt_seed);
spot::postprocessor post(&extra_options);
post.set_pref(pref | comp | sbacc);
post.set_type(type);
post.set_level(level);
hoa_processor processor(post);
if (processor.run())
return 2;
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
catch (const std::invalid_argument& e)
{
error(2, 0, "%s", e.what());
}
if (automaton_format == Count)
std::cout << match_count << std::endl;
return !match_count;
}

368
bin/common_aoutput.cc Normal file
View file

@ -0,0 +1,368 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 "error.h"
#include "argmatch.h"
#include "common_aoutput.hh"
#include "common_post.hh"
#include "common_cout.hh"
#include <spot/twa/bddprint.hh>
#include <spot/twaalgos/dot.hh>
#include <spot/twaalgos/lbtt.hh>
#include <spot/twaalgos/hoa.hh>
#include <spot/twaalgos/neverclaim.hh>
#include <spot/twaalgos/stutter.hh>
#include <spot/twaalgos/isunamb.hh>
#include <spot/twaalgos/strength.hh>
automaton_format_t automaton_format = Dot;
static const char* opt_dot = nullptr;
static const char* opt_never = nullptr;
static const char* hoa_opt = nullptr;
static const char* opt_lbtt = nullptr;
const char* opt_name = nullptr;
static const char* opt_output = nullptr;
static const char* stats = "";
enum check_type
{
check_unambiguous = (1 << 0),
check_stutter = (1 << 1),
check_strength = (1 << 2),
check_all = -1U,
};
static char const *const check_args[] =
{
"unambiguous",
"stutter-invariant", "stuttering-invariant",
"stutter-insensitive", "stuttering-insensitive",
"stutter-sensitive", "stuttering-sensitive",
"strength", "weak", "terminal",
"all",
nullptr
};
static check_type const check_types[] =
{
check_unambiguous,
check_stutter, check_stutter,
check_stutter, check_stutter,
check_stutter, check_stutter,
check_strength, check_strength, check_strength,
check_all
};
ARGMATCH_VERIFY(check_args, check_types);
unsigned opt_check = 0U;
enum {
OPT_DOT = 1,
OPT_LBTT,
OPT_NAME,
OPT_STATS,
OPT_CHECK,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output format:", 3 },
{ "dot", OPT_DOT, "1|a|b|B|c|e|f(FONT)|h|n|N|o|r|R|s|t|v|+INT",
OPTION_ARG_OPTIONAL,
"GraphViz's format (default). Add letters for "
"(1) force numbered states, "
"(a) acceptance display, (b) acceptance sets as bullets, "
"(B) bullets except for Büchi/co-Büchi automata, "
"(c) force circular nodes, (e) force elliptic nodes, "
"(f(FONT)) use FONT, (h) horizontal layout, "
"(v) vertical layout, (n) with name, (N) without name, "
"(o) ordered transitions, "
"(r) rainbow colors for acceptance sets, "
"(R) color acceptance sets by Inf/Fin, (s) with SCCs, "
"(t) force transition-based acceptance, "
"(+INT) add INT to all set numbers", 0 },
{ "hoaf", 'H', "i|l|m|s|t|v", OPTION_ARG_OPTIONAL,
"Output the automaton in HOA format. Add letters to select "
"(i) use implicit labels for complete deterministic automata, "
"(s) prefer state-based acceptance when possible [default], "
"(t) force transition-based acceptance, "
"(m) mix state and transition-based acceptance, "
"(k) use state labels when possible, "
"(l) single-line output, "
"(v) verbose properties", 0 },
{ "lbtt", OPT_LBTT, "t", OPTION_ARG_OPTIONAL,
"LBTT's format (add =t to force transition-based acceptance even"
" on Büchi automata)", 0 },
{ "name", OPT_NAME, "FORMAT", 0,
"set the name of the output automaton", 0 },
{ "output", 'o', "FORMAT", 0,
"send output to a file named FORMAT instead of standard output. The"
" first automaton sent to a file truncates it unless FORMAT starts"
" with '>>'.", 0 },
{ "quiet", 'q', nullptr, 0, "suppress all normal output", 0 },
{ "spin", 's', "6|c", OPTION_ARG_OPTIONAL, "Spin neverclaim (implies --ba)."
" Add letters to select (6) Spin's 6.2.4 style, (c) comments on states",
0 },
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output "
"(ignored with --lbtt or --spin)", 0 },
{ "stats", OPT_STATS, "FORMAT", 0,
"output statistics about the automaton", 0 },
{ "check", OPT_CHECK, "PROP", OPTION_ARG_OPTIONAL,
"test for the additional property PROP and output the result "
"in the HOA format (implies -H). PROP may be any prefix of "
"'all' (default), 'unambiguous', 'stutter-invariant', or 'strength'.",
0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp aoutput_argp = { options, parse_opt_aoutput, nullptr, nullptr,
nullptr, nullptr, nullptr };
// Those can be overridden by individual tools. E.g. randaut has no
// notion of input file, so %F and %L represent something else.
char F_doc[32] = "name of the input file";
char L_doc[32] = "location in the input file";
static const argp_option io_options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
"the following interpreted sequences (capitals for input,"
" minuscules for output):", 4 },
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
{ "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"name of the automaton", 0 },
{ "%S, %s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of states", 0 },
{ "%E, %e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of edges", 0 },
{ "%T, %t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of transitions", 0 },
{ "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of acceptance sets", 0 },
{ "%G, %g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"acceptance condition (in HOA syntax)", 0 },
{ "%C, %c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of SCCs", 0 },
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of nondeterministic states in output", 0 },
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"1 if the output is deterministic, 0 otherwise", 0 },
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"1 if the output is complete, 0 otherwise", 0 },
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"processing time (excluding parsing) in seconds", 0 },
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"one word accepted by the output automaton", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp aoutput_io_format_argp = { io_options, nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr };
static const argp_option o_options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
"the following interpreted sequences:", 4 },
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
{ "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"name of the automaton", 0 },
{ "%s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of states", 0 },
{ "%e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of edges", 0 },
{ "%t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of transitions", 0 },
{ "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of acceptance sets", 0 },
{ "%g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"acceptance condition (in HOA syntax)", 0 },
{ "%c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of SCCs", 0 },
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"number of nondeterministic states in output", 0 },
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"1 if the output is deterministic, 0 otherwise", 0 },
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"1 if the output is complete, 0 otherwise", 0 },
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"processing time (excluding parsing) in seconds", 0 },
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"one word accepted by the output automaton", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp aoutput_o_format_argp = { o_options,
nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr };
int parse_opt_aoutput(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case '8':
spot::enable_utf8();
break;
case 'H':
automaton_format = Hoa;
hoa_opt = arg;
break;
case 'q':
automaton_format = Quiet;
break;
case 's':
automaton_format = Spin;
if (type != spot::postprocessor::Monitor)
type = spot::postprocessor::BA;
if (arg)
opt_never = arg;
break;
case 'o':
opt_output = arg;
break;
case OPT_CHECK:
automaton_format = Hoa;
if (arg)
opt_check |= XARGMATCH("--check", arg, check_args, check_types);
else
opt_check |= check_all;
break;
case OPT_DOT:
automaton_format = Dot;
opt_dot = arg;
break;
case OPT_LBTT:
automaton_format = Lbtt;
opt_lbtt = arg;
// This test could be removed when more options are added,
// because print_lbtt will raise an exception anyway. The
// error message is slightly better in the current way.
if (arg && (arg[0] != 't' || arg[1] != 0))
error(2, 0, "unknown argument for --lbtt: '%s'", arg);
break;
case OPT_NAME:
opt_name = arg;
break;
case OPT_STATS:
if (!*arg)
error(2, 0, "empty format string for --stats");
stats = arg;
automaton_format = Stats;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
automaton_printer::automaton_printer(stat_style input)
: statistics(std::cout, stats, input),
namer(name, opt_name, input),
outputnamer(outputname, opt_output, input)
{
if (automaton_format == Count && opt_output)
throw std::runtime_error
("options --output and --count are incompatible");
}
void
automaton_printer::print(const spot::twa_graph_ptr& aut,
spot::formula f,
// Input location for errors and statistics.
const char* filename,
int loc,
// Time and input automaton for statistics
double time,
const spot::const_parsed_aut_ptr& haut)
{
if (opt_check)
{
if (opt_check & check_stutter)
spot::check_stutter_invariance(aut, f);
if (opt_check & check_unambiguous)
spot::check_unambiguous(aut);
if (opt_check & check_strength)
spot::check_strength(aut);
}
// Name the output automaton.
if (opt_name)
{
name.str("");
namer.print(haut, aut, f, filename, loc, time);
aut->set_named_prop("automaton-name", new std::string(name.str()));
}
std::ostream* out = &std::cout;
if (opt_output)
{
outputname.str("");
outputnamer.print(haut, aut, f, filename, loc, time);
std::string fname = outputname.str();
auto p = outputfiles.emplace(fname, nullptr);
if (p.second)
p.first->second.reset(new output_file(fname.c_str()));
out = &p.first->second->ostream();
}
// Output it.
switch (automaton_format)
{
case Count:
case Quiet:
// Do not output anything.
break;
case Dot:
spot::print_dot(*out, aut, opt_dot);
break;
case Lbtt:
spot::print_lbtt(*out, aut, opt_lbtt);
break;
case Hoa:
spot::print_hoa(*out, aut, hoa_opt) << '\n';
break;
case Spin:
spot::print_never_claim(*out, aut, opt_never);
break;
case Stats:
statistics.set_output(*out);
statistics.print(haut, aut, f, filename, loc, time) << '\n';
break;
}
flush_cout();
}
void automaton_printer::add_stat(char c, const spot::printable* p)
{
namer.declare(c, p);
statistics.declare(c, p);
outputnamer.declare(c, p);
}

235
bin/common_aoutput.hh Normal file
View file

@ -0,0 +1,235 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 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/>.
#pragma once
#include "common_sys.hh"
#include <argp.h>
#include <memory>
#include <spot/parseaut/public.hh>
#include <spot/twaalgos/stats.hh>
#include <spot/twaalgos/sccinfo.hh>
#include <spot/twaalgos/gtec/gtec.hh>
#include <spot/twaalgos/word.hh>
#include <spot/twaalgos/isdet.hh>
#include "common_file.hh"
// Format for automaton output
enum automaton_format_t {
Dot,
Lbtt,
Spin,
Stats,
Hoa,
Quiet,
Count,
};
// The format to use in output_automaton()
extern automaton_format_t automaton_format;
// Set to the argument of --name, else nullptr.
extern const char* opt_name;
// Output options
extern const struct argp aoutput_argp;
// help text for %F and %L
extern char F_doc[32];
extern char L_doc[32];
// FORMAT help text
extern const struct argp aoutput_io_format_argp;
extern const struct argp aoutput_o_format_argp;
// Parse output options
int parse_opt_aoutput(int key, char* arg, struct argp_state* state);
enum stat_style { no_input, aut_input, ltl_input };
/// \brief prints various statistics about a TGBA
///
/// This object can be configured to display various statistics
/// about a TGBA. Some %-sequence of characters are interpreted in
/// the format string, and replaced by the corresponding statistics.
class hoa_stat_printer: protected spot::stat_printer
{
public:
hoa_stat_printer(std::ostream& os, const char* format,
stat_style input = no_input)
: spot::stat_printer(os, format)
{
if (input == aut_input)
{
declare('A', &haut_acc_);
declare('C', &haut_scc_);
declare('E', &haut_edges_);
declare('G', &haut_gen_acc_);
declare('M', &haut_name_);
declare('S', &haut_states_);
declare('T', &haut_trans_);
}
declare('F', &filename_);
declare('L', &location_);
if (input != ltl_input)
declare('f', &filename_); // Override the formula printer.
declare('m', &aut_name_);
declare('w', &aut_word_);
}
using spot::formater::declare;
using spot::formater::set_output;
/// \brief print the configured statistics.
///
/// The \a f argument is not needed if the Formula does not need
/// to be output.
std::ostream&
print(const spot::const_parsed_aut_ptr& haut,
const spot::const_twa_graph_ptr& aut,
spot::formula f,
const char* filename, int loc, double run_time)
{
filename_ = filename ? filename : "";
if (loc >= 0 && has('L'))
{
std::ostringstream os;
os << loc;
location_ = os.str();
}
if (haut)
{
if (loc < 0 && has('L'))
{
std::ostringstream os;
os << haut->loc;
location_ = os.str();
}
if (has('T'))
{
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
haut_states_ = s.states;
haut_edges_ = s.edges;
haut_trans_ = s.transitions;
}
else if (has('E'))
{
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
haut_states_ = s.states;
haut_edges_ = s.edges;
}
if (has('M'))
{
auto n = haut->aut->get_named_prop<std::string>("automaton-name");
if (n)
haut_name_ = *n;
else
haut_name_.val().clear();
}
if (has('S'))
haut_states_ = haut->aut->num_states();
if (has('A'))
haut_acc_ = haut->aut->acc().num_sets();
if (has('C'))
haut_scc_ = spot::scc_info(haut->aut).scc_count();
if (has('G'))
{
std::ostringstream os;
os << haut->aut->get_acceptance();
haut_gen_acc_ = os.str();
}
}
if (has('m'))
{
auto n = aut->get_named_prop<std::string>("automaton-name");
if (n)
aut_name_ = *n;
else
aut_name_.val().clear();
}
if (has('w'))
{
auto res = spot::couvreur99(aut)->check();
if (res)
{
auto run = res->accepting_run();
assert(run);
spot::twa_word w(run->reduce());
w.simplify();
std::ostringstream out;
out << w;
aut_word_ = out.str();
}
else
{
aut_word_.val().clear();
}
}
return this->spot::stat_printer::print(aut, f, run_time);
}
private:
spot::printable_value<const char*> filename_;
spot::printable_value<std::string> location_;
spot::printable_value<std::string> haut_name_;
spot::printable_value<std::string> aut_name_;
spot::printable_value<std::string> aut_word_;
spot::printable_value<std::string> haut_gen_acc_;
spot::printable_value<unsigned> haut_states_;
spot::printable_value<unsigned> haut_edges_;
spot::printable_value<unsigned> haut_trans_;
spot::printable_value<unsigned> haut_acc_;
spot::printable_value<unsigned> haut_scc_;
};
class automaton_printer
{
hoa_stat_printer statistics;
std::ostringstream name;
hoa_stat_printer namer;
std::ostringstream outputname;
hoa_stat_printer outputnamer;
std::map<std::string, std::unique_ptr<output_file>> outputfiles;
public:
automaton_printer(stat_style input = no_input);
void
print(const spot::twa_graph_ptr& aut,
spot::formula f = nullptr,
// Input location for errors and statistics.
const char* filename = nullptr,
int loc = -1,
// Time and input automaton for statistics
double time = 0.0,
const spot::const_parsed_aut_ptr& haut = nullptr);
void add_stat(char c, const spot::printable* p);
};

88
bin/common_conv.cc Normal file
View file

@ -0,0 +1,88 @@
// -*- 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_conv.hh"
#include <cstdlib>
#include "error.h"
int
to_int(const char* s)
{
char* endptr;
int res = strtol(s, &endptr, 10);
if (*endptr)
error(2, 0, "failed to parse '%s' as an integer.", s);
return res;
}
int
to_pos_int(const char* s)
{
int res = to_int(s);
if (res < 0)
error(2, 0, "%d is not positive", res);
return res;
}
unsigned
to_unsigned (const char *s)
{
char* endptr;
unsigned res = strtoul(s, &endptr, 10);
if (*endptr)
error(2, 0, "failed to parse '%s' as an unsigned integer.", s);
return res;
}
float
to_float(const char* s)
{
char* endptr;
float res = strtof(s, &endptr);
if (*endptr)
error(2, 0, "failed to parse '%s' as a float.", s);
return res;
}
float
to_probability(const char* s)
{
float res = to_float(s);
if (res < 0.0 || res > 1.0)
error(2, 0, "%f is not between 0 and 1.", res);
return res;
}
std::vector<long>
to_longs(const char* arg)
{
std::vector<long> res;
while (*arg)
{
char* endptr;
long value = strtol(arg, &endptr, 10);
if (endptr == arg)
error(2, 0, "failed to parse '%s' as an integer.", arg);
res.push_back(value);
while (*endptr == ' ' || *endptr == ',')
++endptr;
arg = endptr;
}
return res;
}

32
bin/common_conv.hh Normal file
View file

@ -0,0 +1,32 @@
// -*- 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/>.
#pragma once
#include "common_sys.hh"
#include <spot/twa/twagraph.hh>
int to_int(const char* s);
int to_pos_int(const char* s);
unsigned to_unsigned (const char *s);
float to_float(const char* s);
float to_probability(const char* s);
// Parse the comma or space seperate string of numbers.
std::vector<long> to_longs(const char* s);

37
bin/common_cout.cc Normal file
View file

@ -0,0 +1,37 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012 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 "common_cout.hh"
#include <iostream>
#include "error.h"
void check_cout()
{
// Make sure we abort if we can't write to std::cout anymore
// (like disk full or broken pipe with SIGPIPE ignored).
if (!std::cout)
error(2, 0, "error writing to standard output");
}
void flush_cout()
{
std::cout.flush();
check_cout();
}

23
bin/common_cout.hh Normal file
View file

@ -0,0 +1,23 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012 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/>.
#pragma once
void check_cout();
void flush_cout();

43
bin/common_file.cc Normal file
View file

@ -0,0 +1,43 @@
// -*- 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_file.hh"
#include <error.h>
#include <iostream>
output_file::output_file(const char* name)
{
std::ios_base::openmode mode = std::ios_base::trunc;
if (name[0] == '>' && name[1] == '>')
{
mode = std::ios_base::app;
append_ = true;
name += 2;
}
if (name[0] == '-' && name[1] == 0)
{
os_ = &std::cout;
return;
}
of_ = new std::ofstream(name, mode);
if (!*of_)
error(2, errno, "cannot open '%s'", name);
os_ = of_;
}

51
bin/common_file.hh Normal file
View file

@ -0,0 +1,51 @@
// -*- 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/>.
#pragma once
#include "common_sys.hh"
#include <iosfwd>
#include <fstream>
class output_file
{
std::ostream* os_;
std::ofstream* of_ = nullptr;
bool append_ = false;
public:
// Open a file for output. "-" is interpreted as stdout.
// Names that start with ">>" are opened for append.
// The function calls error() on... error.
output_file(const char* name);
~output_file()
{
delete of_;
}
bool append() const
{
return append_;
}
std::ostream& ostream()
{
return *os_;
}
};

356
bin/common_finput.cc Normal file
View file

@ -0,0 +1,356 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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_finput.hh"
#include "error.h"
#include <fstream>
#include <cstring>
enum {
OPT_LBT = 1,
OPT_LENIENT,
};
jobs_t jobs;
bool lbt_input = false;
static bool lenient = false;
static const argp_option options[] =
{
{ nullptr, 0, nullptr, 0, "Input options:", 1 },
{ "formula", 'f', "STRING", 0, "process the formula STRING", 0 },
{ "file", 'F', "FILENAME[/COL]", 0,
"process each line of FILENAME as a formula; if COL is a "
"positive integer, assume a CSV file and read column COL; use "
"a negative COL to drop the first line of the CSV file", 0 },
{ "lbt-input", OPT_LBT, nullptr, 0,
"read all formulas using LBT's prefix syntax", 0 },
{ "lenient", OPT_LENIENT, nullptr, 0,
"parenthesized blocks that cannot be parsed as subformulas "
"are considered as atomic properties", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp finput_argp = { options, parse_opt_finput,
nullptr, nullptr, nullptr,
nullptr, nullptr };
int
parse_opt_finput(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'f':
jobs.emplace_back(arg, false);
break;
case 'F':
jobs.emplace_back(arg, true);
break;
case OPT_LBT:
lbt_input = true;
break;
case OPT_LENIENT:
lenient = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
spot::formula
parse_formula(const std::string& s, spot::parse_error_list& pel)
{
if (lbt_input)
return spot::parse_prefix_ltl(s, pel);
else
return spot::parse_infix_psl
(s, pel, spot::default_environment::instance(), false, lenient);
}
job_processor::job_processor()
: abort_run(false), real_filename(nullptr),
col_to_read(0), prefix(nullptr), suffix(nullptr)
{
}
job_processor::~job_processor()
{
if (real_filename)
free(real_filename);
if (prefix)
free(prefix);
if (suffix)
free(suffix);
}
int
job_processor::process_string(const std::string& input,
const char* filename,
int linenum)
{
spot::parse_error_list pel;
auto f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
return 1;
}
return process_formula(f, filename, linenum);
}
int
job_processor::process_stream(std::istream& is,
const char* filename)
{
int error = 0;
int linenum = 1;
std::string line;
// Discard the first line of a CSV file if requested.
if (col_to_read < 0)
{
std::getline(is, line);
col_to_read = -col_to_read;
++linenum;
}
// Each line of the file and send them to process_string,
// optionally extracting a column of a CSV file.
while (!abort_run && std::getline(is, line))
if (!line.empty())
{
if (col_to_read == 0)
{
error |= process_string(line, filename, linenum++);
}
else // We are reading column COL_TO_READ in a CSV file.
{
// If the line we have read contains an odd number
// of double-quotes, then it is an incomplete CSV line
// that should be completed by the next lines.
unsigned dquotes = 0;
std::string fullline;
unsigned csvlines = 0;
do
{
++csvlines;
size_t s = line.size();
for (unsigned i = 0; i < s; ++i)
dquotes += line[i] == '"';
if (fullline.empty())
fullline = line;
else
(fullline += '\n') += line;
if (!(dquotes &= 1))
break;
}
while (std::getline(is, line));
if (dquotes)
error_at_line(2, errno, filename, linenum,
"mismatched double-quote, "
"reached EOF while parsing this line");
// Now that we have a full CSV line, extract the right
// column.
const char* str = fullline.c_str();
const char* col1_start = str;
// Delimiters for the extracted column.
const char* coln_start = str;
const char* coln_end = nullptr;
// The current column. (1-based)
int colnum = 1;
// Whether we are parsing a double-quoted string.
bool instring = false;
// Note that RFC 4180 has strict rules about
// double-quotes: ① if a field is double-quoted, the first
// and last characters of the field should be
// double-quotes; ② if a field contains a double-quote
// then it should be double quoted, and the occurrences
// of double-quotes should be doubled. Therefore a CSV file
// may no contain a line such as:
// foo,bar"ba""z",12
// Tools have different interpretation of such a line.
// For instance Python's pandas.read_csv() function will
// load the second field verbatim as the string 'bar"ba""z"',
// while R's read.csv() function will load it as the
// string 'barba"z'. We use this second interpretation, because
// it also makes it possible to parse CSVs fields formatted
// with leading spaces (often for cosmetic purpose). When
// extracting the second field of
// foo, "ba""z", 12
// we will return ' baz' and the leading space will be ignored
// by our LTL formula parser.
while (*str)
{
switch (*str)
{
case '"':
// Doubled double-quotes are used to escape
// double-quotes.
if (instring && str[1] == '"')
++str;
else
instring = !instring;
break;
case ',':
if (!instring)
{
if (col_to_read == colnum)
coln_end = str;
++colnum;
if (col_to_read == colnum)
coln_start = str + 1;
}
break;
}
// Once we have the end delimiter for our target
// column, we have all we need.
if (coln_end)
break;
++str;
}
if (!*str)
{
if (colnum != col_to_read)
// Skip this line as it has no enough columns.
continue;
else
// The target columns ends at the end of the line.
coln_end = str;
}
// Skip the line if it has an empty field.
if (coln_start == coln_end)
continue;
// save the contents before and after that columns for the
// %< and %> escapes (ignoring the trailing and leading
// commas).
prefix = (col_to_read != 1) ?
strndup(col1_start, coln_start - col1_start - 1) : nullptr;
suffix = (*coln_end != 0) ? strdup(coln_end + 1) : nullptr;
std::string field(coln_start, coln_end);
// Remove double-quotes if any.
if (field.find('"') != std::string::npos)
{
unsigned dst = 0;
bool instring = false;
for (; coln_start != coln_end; ++coln_start)
if (*coln_start == '"')
// A doubled double-quote instead a double-quoted
// string is an escaped double-quote.
if (instring && coln_start[1] == '"')
field[dst++] = *++coln_start;
else
instring = !instring;
else
field[dst++] = *coln_start;
field.resize(dst);
}
error |= process_string(field, filename, linenum);
linenum += csvlines;
if (prefix)
{
free(prefix);
prefix = nullptr;
}
if (suffix)
{
free(suffix);
suffix = nullptr;
}
}
}
return error;
}
int
job_processor::process_file(const char* filename)
{
// Special case for stdin.
if (filename[0] == '-' && filename[1] == 0)
return process_stream(std::cin, filename);
errno = 0;
std::ifstream input(filename);
if (input)
return process_stream(input, filename);
int saved_errno = errno;
// If we have a filename like "foo/NN" such
// that:
// ① foo/NN is not a file (already the case),
// ② NN is a number > 0,
// ③ foo is a file,
// then it means we want to open foo as
// a CSV file and process column NN.
if (const char* slash = strrchr(filename, '/'))
{
char* end;
errno = 0;
long int col = strtol(slash + 1, &end, 10);
// strtol ate all remaining characters and NN is positive
if (errno == 0 && !*end && col != 0)
{
col_to_read = col;
if (real_filename)
free(real_filename);
real_filename = strndup(filename, slash - filename);
// Special case for stdin.
if (real_filename[0] == '-' && real_filename[1] == 0)
return process_stream(std::cin, real_filename);
std::ifstream input(real_filename);
if (input)
return process_stream(input, real_filename);
error(2, errno, "cannot open '%s' nor '%s'",
filename, real_filename);
}
}
error(2, saved_errno, "cannot open '%s'", filename);
return -1;
}
int
job_processor::run()
{
int error = 0;
for (auto& j: jobs)
{
if (!j.file_p)
error |= process_string(j.str);
else
error |= process_file(j.str);
if (abort_run)
break;
}
return error;
}

80
bin/common_finput.hh Normal file
View file

@ -0,0 +1,80 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 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/>.
#pragma once
#include "common_sys.hh"
#include <argp.h>
#include <vector>
#include <spot/tl/parse.hh>
struct job
{
const char* str;
bool file_p; // true if str is a filename, false if it is a formula
job(const char* str, bool file_p)
: str(str), file_p(file_p)
{
}
};
typedef std::vector<job> jobs_t;
extern jobs_t jobs;
extern bool lbt_input;
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);
class job_processor
{
protected:
bool abort_run; // Set to true in process_formula() to abort run().
public:
job_processor();
virtual ~job_processor();
virtual int
process_formula(spot::formula f,
const char* filename = nullptr, int linenum = 0) = 0;
virtual int
process_string(const std::string& str,
const char* filename = nullptr, int linenum = 0);
virtual int
process_stream(std::istream& is, const char* filename);
virtual int
process_file(const char* filename);
virtual int
run();
char* real_filename;
long int col_to_read;
char* prefix;
char* suffix;
};

90
bin/common_hoaread.cc Normal file
View file

@ -0,0 +1,90 @@
// -*- 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_hoaread.hh"
#include "argmatch.h"
#include "error.h"
enum
{
OPT_TRUST_HOA = 1,
};
static const argp_option options[] =
{
{ "trust-hoa", OPT_TRUST_HOA, "BOOL", 0,
"If False, properties listed in HOA files are ignored, "
"unless they can be easily verified. If True (the default) "
"any supported property is trusted.", 1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
spot::automaton_parser_options opt_parse;
spot::twa_graph_ptr
read_automaton(const char* filename, spot::bdd_dict_ptr& dict)
{
auto p = spot::parse_aut(filename, dict,
spot::default_environment::instance(),
opt_parse);
if (p->format_errors(std::cerr))
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);
}
static bool parse_bool(const char* opt, const char* arg)
{
enum bool_type { bool_false, bool_true };
static char const *const bool_args[] =
{
"false", "no", "0",
"true", "yes", "1",
nullptr
};
static bool_type const bool_types[] =
{
bool_false, bool_false, bool_false,
bool_true, bool_true, bool_true,
};
ARGMATCH_VERIFY(bool_args, bool_types);
bool_type bt = XARGMATCH(opt, arg, bool_args, bool_types);
return bt == bool_true;
}
static int
parse_opt_hoaread(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case OPT_TRUST_HOA:
opt_parse.trust_hoa = parse_bool("--trust-hoa", arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp hoaread_argp = { options, parse_opt_hoaread,
nullptr, nullptr, nullptr,
nullptr, nullptr };

34
bin/common_hoaread.hh Normal file
View file

@ -0,0 +1,34 @@
// -*- 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/>.
#pragma once
#include "common_sys.hh"
#include <argp.h>
#include <spot/parseaut/public.hh>
extern const struct argp hoaread_argp;
extern spot::automaton_parser_options opt_parse;
spot::twa_graph_ptr
read_automaton(const char* filename, spot::bdd_dict_ptr& dict);

319
bin/common_output.cc Normal file
View file

@ -0,0 +1,319 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 "common_output.hh"
#include <iostream>
#include <sstream>
#include <spot/tl/print.hh>
#include <spot/misc/formater.hh>
#include <spot/misc/escape.hh>
#include "common_cout.hh"
#include "error.h"
enum {
OPT_CSV = 1,
OPT_FORMAT,
OPT_LATEX,
OPT_SPOT,
OPT_WRING,
};
output_format_t output_format = spot_output;
bool full_parenth = false;
bool escape_csv = false;
char output_terminator = '\n';
static const argp_option options[] =
{
{ "full-parentheses", 'p', nullptr, 0,
"output fully-parenthesized formulas", -20 },
{ "spin", 's', nullptr, 0, "output in Spin's syntax", -20 },
{ "spot", OPT_SPOT, nullptr, 0, "output in Spot's syntax (default)", -20 },
{ "lbt", 'l', nullptr, 0, "output in LBT's syntax", -20 },
{ "wring", OPT_WRING, nullptr, 0, "output in Wring's syntax", -20 },
{ "utf8", '8', nullptr, 0, "output using UTF-8 characters", -20 },
{ "latex", OPT_LATEX, nullptr, 0, "output using LaTeX macros", -20 },
{ "csv-escape", OPT_CSV, nullptr, 0,
"quote the formula for use in a CSV file", -20 },
{ "format", OPT_FORMAT, "FORMAT", 0,
"specify how each line should be output (default: \"%f\")", -20 },
{ "output", 'o', "FORMAT", 0,
"send output to a file named FORMAT instead of standard output. The"
" first formula sent to a file truncates it unless FORMAT starts"
" with '>>'.", 0 },
{ "zero-terminated-output", '0', nullptr, 0,
"separate output formulas with \\0 instead of \\n "
"(for use with xargs -0)", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp output_argp = { options, parse_opt_output,
nullptr, nullptr, nullptr,
nullptr, nullptr };
static
void
report_not_ltl(spot::formula f,
const char* filename, int linenum, const char* syn)
{
std::string s = spot::str_psl(f);
static const char msg[] =
"formula '%s' cannot be written %s's syntax because it is not LTL";
if (filename)
error_at_line(2, 0, filename, linenum, msg, s.c_str(), syn);
else
error(2, 0, msg, s.c_str(), syn);
}
std::ostream&
stream_formula(std::ostream& out,
spot::formula f, const char* filename, int linenum)
{
switch (output_format)
{
case lbt_output:
if (f.is_ltl_formula())
spot::print_lbt_ltl(out, f);
else
report_not_ltl(f, filename, linenum, "LBT");
break;
case spot_output:
spot::print_psl(out, f, full_parenth);
break;
case spin_output:
if (f.is_ltl_formula())
spot::print_spin_ltl(out, f, full_parenth);
else
report_not_ltl(f, filename, linenum, "Spin");
break;
case wring_output:
if (f.is_ltl_formula())
spot::print_wring_ltl(out, f);
else
report_not_ltl(f, filename, linenum, "Wring");
break;
case utf8_output:
spot::print_utf8_psl(out, f, full_parenth);
break;
case latex_output:
spot::print_latex_psl(out, f, full_parenth);
break;
case count_output:
case quiet_output:
break;
}
return out;
}
static void
stream_escapable_formula(std::ostream& os,
spot::formula f,
const char* filename, int linenum)
{
if (escape_csv)
{
std::ostringstream out;
stream_formula(out, f, filename, linenum);
os << '"';
spot::escape_rfc4180(os, out.str());
os << '"';
}
else
{
stream_formula(os, f, filename, linenum);
}
}
namespace
{
struct formula_with_location
{
spot::formula f;
const char* filename;
int line;
const char* prefix;
const char* suffix;
};
class printable_formula:
public spot::printable_value<const formula_with_location*>
{
public:
printable_formula&
operator=(const formula_with_location* new_val)
{
val_ = new_val;
return *this;
}
virtual void
print(std::ostream& os, const char*) const
{
stream_escapable_formula(os, val_->f, val_->filename, val_->line);
}
};
class formula_printer: protected spot::formater
{
public:
formula_printer(std::ostream& os, const char* format)
: format_(format)
{
declare('f', &fl_);
declare('F', &filename_);
declare('L', &line_);
declare('<', &prefix_);
declare('>', &suffix_);
set_output(os);
}
std::ostream&
print(const formula_with_location& fl)
{
fl_ = &fl;
filename_ = fl.filename ? fl.filename : "";
line_ = fl.line;
prefix_ = fl.prefix ? fl.prefix : "";
suffix_ = fl.suffix ? fl.suffix : "";
return format(format_);
}
private:
const char* format_;
printable_formula fl_;
spot::printable_value<const char*> filename_;
spot::printable_value<int> line_;
spot::printable_value<const char*> prefix_;
spot::printable_value<const char*> suffix_;
};
}
static formula_printer* format = nullptr;
static std::ostringstream outputname;
static formula_printer* outputnamer = nullptr;
static std::map<std::string, std::unique_ptr<output_file>> outputfiles;
int
parse_opt_output(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case '0':
output_terminator = 0;
break;
case '8':
output_format = utf8_output;
break;
case 'l':
output_format = lbt_output;
break;
case 'o':
outputnamer = new formula_printer(outputname, arg);
break;
case 'p':
full_parenth = true;
break;
case 's':
output_format = spin_output;
break;
case OPT_CSV:
escape_csv = true;
break;
case OPT_LATEX:
output_format = latex_output;
break;
case OPT_SPOT:
output_format = spot_output;
break;
case OPT_WRING:
output_format = wring_output;
break;
case OPT_FORMAT:
delete format;
format = new formula_printer(std::cout, arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static void
output_formula(std::ostream& out,
spot::formula f,
const char* filename = nullptr, int linenum = 0,
const char* prefix = nullptr, const char* suffix = nullptr)
{
if (!format)
{
if (prefix)
out << prefix << ',';
stream_escapable_formula(out, f, filename, linenum);
if (suffix)
out << ',' << suffix;
}
else
{
formula_with_location fl = { f, filename, linenum, prefix, suffix };
format->print(fl);
}
}
void
::printable_formula::print(std::ostream& os, const char*) const
{
output_formula(os, val_);
}
void
output_formula_checked(spot::formula f,
const char* filename, int linenum,
const char* prefix, const char* suffix)
{
if (output_format == count_output)
{
if (outputnamer)
throw std::runtime_error
("options --output and --count are incompatible");
return;
}
if (output_format == quiet_output)
return;
std::ostream* out = &std::cout;
if (outputnamer)
{
outputname.str("");
formula_with_location fl = { f, filename, linenum, prefix, suffix };
outputnamer->print(fl);
std::string fname = outputname.str();
auto p = outputfiles.emplace(fname, nullptr);
if (p.second)
p.first->second.reset(new output_file(fname.c_str()));
out = &p.first->second->ostream();
}
output_formula(*out, f, filename, linenum, prefix, suffix);
*out << output_terminator;
// Make sure we abort if we can't write to std::cout anymore
// (like disk full or broken pipe with SIGPIPE ignored).
check_cout();
}

90
bin/common_output.hh Normal file
View file

@ -0,0 +1,90 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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/>.
#pragma once
#include "common_sys.hh"
#include <argp.h>
#include <map>
#include <memory>
#include <spot/tl/formula.hh>
#include <spot/twaalgos/stats.hh>
#include "common_output.hh"
#include "common_file.hh"
enum output_format_t { spot_output, spin_output, utf8_output,
lbt_output, wring_output, latex_output,
quiet_output, count_output };
extern output_format_t output_format;
extern bool full_parenth;
extern bool escape_csv;
extern const struct argp output_argp;
int parse_opt_output(int key, char* arg, struct argp_state* state);
// Low-level output
std::ostream&
stream_formula(std::ostream& out,
spot::formula f, const char* filename, int linenum);
void output_formula_checked(spot::formula f,
const char* filename = nullptr, int linenum = 0,
const char* prefix = nullptr,
const char* suffix = nullptr);
class printable_formula:
public spot::printable_value<spot::formula>
{
public:
printable_formula&
operator=(spot::formula new_val)
{
val_ = new_val;
return *this;
}
virtual void
print(std::ostream& os, const char*) const;
};
class aut_stat_printer: protected spot::stat_printer
{
public:
aut_stat_printer(std::ostream& os, const char* format)
: spot::stat_printer(os, format)
{
declare('f', &formula_); // Override the formula printer.
}
using spot::formater::set_output;
std::ostream&
print(const spot::const_twa_graph_ptr& aut,
spot::formula f = nullptr,
double run_time = -1.)
{
formula_ = f;
return this->spot::stat_printer::print(aut, f, run_time);
}
printable_formula formula_;
};

166
bin/common_post.cc Normal file
View file

@ -0,0 +1,166 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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_post.hh"
#include "common_r.hh"
#include "common_aoutput.hh"
#include "error.h"
spot::postprocessor::output_type type = spot::postprocessor::TGBA;
spot::postprocessor::output_pref pref = spot::postprocessor::Small;
spot::postprocessor::output_pref comp = spot::postprocessor::Any;
spot::postprocessor::output_pref sbacc = spot::postprocessor::Any;
spot::postprocessor::optimization_level level = spot::postprocessor::High;
bool level_set = false;
bool pref_set = false;
enum {
OPT_GENERIC = 1,
OPT_HIGH,
OPT_LOW,
OPT_MEDIUM,
OPT_SMALL,
OPT_TGBA,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
{ "tgba", OPT_TGBA, nullptr, 0,
"Transition-based Generalized Büchi Automaton (default)", 0 },
{ "ba", 'B', nullptr, 0,
"Büchi Automaton (implies -S)", 0 },
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
"of the given property)", 0 },
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
{ "state-based-acceptance", 'S', nullptr, 0,
"define the acceptance using states", 0 },
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata (default)", 0 },
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
"or deterministic", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
{ "high", OPT_HIGH, nullptr, 0,
"all available optimizations (slow, default)", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const argp_option options_disabled[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
{ "generic", OPT_GENERIC, nullptr, 0,
"any acceptance is allowed (default)", 0 },
{ "tgba", OPT_TGBA, nullptr, 0,
"Transition-based Generalized Büchi Automaton", 0 },
{ "ba", 'B', nullptr, 0,
"Büchi Automaton (with state-based acceptance)", 0 },
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
"of the given property)", 0 },
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
{ "state-based-acceptance", 'S', nullptr, 0,
"define the acceptance using states", 0 },
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata", 0 },
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
"or deterministic", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
{ "high", OPT_HIGH, nullptr, 0,
"all available optimizations (slow)", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static int
parse_opt_post(int key, char*, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'a':
pref = spot::postprocessor::Any;
pref_set = true;
break;
case 'B':
type = spot::postprocessor::BA;
break;
case 'C':
comp = spot::postprocessor::Complete;
break;
case 'D':
pref = spot::postprocessor::Deterministic;
pref_set = true;
break;
case 'M':
type = spot::postprocessor::Monitor;
break;
case 'S':
sbacc = spot::postprocessor::SBAcc;
break;
case OPT_GENERIC:
type = spot::postprocessor::Generic;
break;
case OPT_HIGH:
level = spot::postprocessor::High;
simplification_level = 3;
level_set = true;
break;
case OPT_LOW:
level = spot::postprocessor::Low;
simplification_level = 1;
level_set = true;
break;
case OPT_MEDIUM:
level = spot::postprocessor::Medium;
simplification_level = 2;
level_set = true;
break;
case OPT_SMALL:
pref = spot::postprocessor::Small;
pref_set = true;
break;
case OPT_TGBA:
if (automaton_format == Spin)
error(2, 0, "--spin and --tgba are incompatible");
type = spot::postprocessor::TGBA;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp post_argp = { options, parse_opt_post,
nullptr, nullptr, nullptr, nullptr, nullptr };
const struct argp post_argp_disabled = { options_disabled, parse_opt_post,
nullptr, nullptr, nullptr,
nullptr, nullptr };

37
bin/common_post.hh Normal file
View file

@ -0,0 +1,37 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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/>.
#pragma once
#include "common_sys.hh"
#include <spot/twaalgos/postproc.hh>
#include <argp.h>
extern const struct argp post_argp; // postprocessing enabled
extern const struct argp post_argp_disabled; // postprocessing disabled
extern spot::postprocessor::output_type type;
extern spot::postprocessor::output_pref pref;
extern spot::postprocessor::output_pref comp;
extern spot::postprocessor::output_pref sbacc;
extern spot::postprocessor::optimization_level level;
// True if --low, --medium, or --high has been given
extern bool level_set;
// True if --any, --small, or --deterministic has been given
extern bool pref_set;

40
bin/common_r.cc Normal file
View file

@ -0,0 +1,40 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 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 "error.h"
#include "common_r.hh"
int simplification_level = 0;
void
parse_r(const char* arg)
{
if (!arg)
{
simplification_level = 3;
return;
}
if (arg[1] == 0 && arg[0] >= '0' && arg[0] <= '3')
{
simplification_level = arg[0] - '0';
return;
}
error(2, 0, "invalid simplification level '%s'", arg);
}

47
bin/common_r.hh Normal file
View file

@ -0,0 +1,47 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 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/>.
#pragma once
#include "common_sys.hh"
#include <spot/tl/simplify.hh>
#define OPT_R 'r'
#define DECLARE_OPT_R \
{ "simplify", OPT_R, "LEVEL", OPTION_ARG_OPTIONAL, \
"simplify formulas according to LEVEL (see below); LEVEL is " \
"set to 3 if omitted", 0 }
#define LEVEL_DOC(g) \
{ nullptr, 0, nullptr, 0, \
"The simplification LEVEL may be set as follows.", g }, \
{ " 0", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
"No rewriting", 0 }, \
{ " 1", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
"basic rewritings and eventual/universal rules", 0 }, \
{ " 2", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
"additional syntactic implication rules", 0 }, \
{ " 3", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
"better implications using containment", 0 }
extern int simplification_level;
void parse_r(const char* arg);
spot::tl_simplifier_options simplifier_options();

81
bin/common_range.cc Normal file
View file

@ -0,0 +1,81 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2014 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 "error.h"
#include "common_range.hh"
#include <iostream>
#include <cstdlib>
// The range should have the form INT..INT or INT:INT, with
// "42" standing for "42..42",
// "..42" meaning "missing_left..42".
// and "42.." meaning "42..missing_right".
//
// As an exception, if missing_right is 0, then missing right bounds
// are disallowed.
range
parse_range(const char* str, int missing_left, int missing_right)
{
range res;
char* end;
res.min = strtol(str, &end, 10);
if (end == str)
{
// No leading number. It's OK as long as the string is not
// empty.
if (!*end)
error(1, 0, "invalid empty range");
res.min = missing_left;
}
if (!*end)
{
// Only one number.
res.max = res.min;
}
else
{
// Skip : or ..
if (end[0] == ':')
++end;
else if (end[0] == '.' && end[1] == '.')
end += 2;
if (!*end && missing_right != 0)
{
res.max = missing_right;
}
else
{
// Parse the next integer.
char* end2;
res.max = strtol(end, &end2, 10);
if (end == end2)
error(1, 0, "invalid range '%s' (missing end?)", str);
if (*end2)
error(1, 0, "invalid range '%s' (trailing garbage?)", str);
}
}
if (res.min < 0 || res.max < 0)
error(1, 0, "invalid range '%s': values must be positive", str);
return res;
}

50
bin/common_range.hh Normal file
View file

@ -0,0 +1,50 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2014, 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/>.
#pragma once
#define RANGE_DOC \
{ nullptr, 0, nullptr, 0, \
"RANGE may have one of the following forms: 'INT', " \
"'INT..INT', or '..INT'.\nIn the latter case, the missing number " \
"is assumed to be 1.", 0 }
#define RANGE_DOC_FULL \
{ nullptr, 0, nullptr, 0, \
"RANGE may have one of the following forms: 'INT', " \
"'INT..INT', '..INT', or 'INT..'", 0 }
struct range
{
int min;
int max;
bool contains(int val)
{
return val >= min && val <= max;
}
};
// INT, INT..INT, ..INT, or INT..
//
// The missing_left and missing_right argument gives the default bound
// values. Additionally, if missing_right == 0, then the INT.. form
// is disallowed.
range parse_range(const char* str,
int missing_left = 1, int missing_right = 0);

146
bin/common_setup.cc Normal file
View file

@ -0,0 +1,146 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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_setup.hh"
#include "argp.h"
#include <cstdlib>
#include <iostream>
#include <signal.h>
#include <sys/wait.h>
#include <spot/misc/tmpfile.hh>
const char* argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
static void
display_version(FILE *stream, struct argp_state*)
{
fputs(program_name, stream);
fputs(" (" PACKAGE_STRING ")\n\
\n\
Copyright (C) 2015 Laboratoire de Recherche et Développement de l'Epita.\n\
License GPLv3+: \
GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
This is free software: you are free to change and redistribute it.\n\
There is NO WARRANTY, to the extent permitted by law.\n", stream);
}
#ifdef HAVE_SIGACTION
static void sig_handler(int sig)
{
spot::cleanup_tmpfiles();
// Send the signal again, this time to the default handler, so that
// we return a meaningful error code.
raise(sig);
}
static void setup_sig_handler()
{
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
// Catch termination signals, so we can cleanup temporary files.
sigaction(SIGALRM, &sa, nullptr);
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGPIPE, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
}
#else
# define setup_sig_handler() while (0);
#endif
void
setup(char** argv)
{
// Simplify the program name, because argp() uses it to report
// errors and display help text.
set_program_name(argv[0]);
argv[0] = const_cast<char*>(program_name);
argp_program_version_hook = display_version;
argp_err_exit_status = 2;
std::ios_base::sync_with_stdio(false);
setup_sig_handler();
}
// argp's default behavior of offering -? for --help is just too silly.
// I mean, come on, why not also add -* to Darwinise more shell users?
// We disable this option as well as -V (because --version don't need
// a short version).
enum {
OPT_HELP = 1,
OPT_USAGE,
OPT_VERSION,
};
static const argp_option options[] =
{
{ "version", OPT_VERSION, nullptr, 0, "print program version", -1 },
{ "help", OPT_HELP, nullptr, 0, "print this help", -1 },
// We support this option just in case, but we don't advertise it.
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const argp_option options_hidden[] =
{
{ "version", OPT_VERSION, nullptr, OPTION_HIDDEN,
"print program version", -1 },
{ "help", OPT_HELP, nullptr, OPTION_HIDDEN, "print this help", -1 },
// We support this option just in case, but we don't advertise it.
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static int
parse_opt_misc(int key, char*, struct argp_state* state)
{
// This switch is alphabetically-ordered.
switch (key)
{
case OPT_HELP:
argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
break;
case OPT_USAGE:
argp_state_help(state, state->out_stream,
ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
break;
case OPT_VERSION:
display_version(state->out_stream, state);
exit(0);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp misc_argp = { options, parse_opt_misc,
nullptr, nullptr, nullptr, nullptr, nullptr };
const struct argp misc_argp_hidden = { options_hidden, parse_opt_misc,
nullptr, nullptr, nullptr,
nullptr, nullptr };

28
bin/common_setup.hh Normal file
View file

@ -0,0 +1,28 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 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/>.
#pragma once
#include "common_sys.hh"
#include "progname.h"
void setup(char** progname);
extern const struct argp misc_argp;
extern const struct argp misc_argp_hidden;

24
bin/common_sys.hh Normal file
View file

@ -0,0 +1,24 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012 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/>.
#pragma once
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

440
bin/common_trans.cc Normal file
View file

@ -0,0 +1,440 @@
// -*- 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 <iomanip>
#include "error.h"
#include <spot/tl/print.hh>
#include "common_conv.hh"
#include <spot/misc/escape.hh>
// A set of tools for which we know the correct output
static struct shorthands_t
{
const char* prefix;
const char* suffix;
}
shorthands[] = {
{ "lbt", " <%L>%O" },
{ "ltl2ba", " -f %s>%O" },
{ "ltl2dstar", " --output-format=hoa %L %O"},
{ "ltl2tgba", " -H %f>%O" },
{ "ltl3ba", " -f %s>%O" },
{ "ltl3dra", " -f %s>%O" },
{ "modella", " %L %O" },
{ "spin", " -f %s>%O" },
};
static void show_shorthands()
{
std::cout
<< ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n"
"the following words, then the string on the right is appended.\n\n");
for (auto& s: shorthands)
std::cout << " "
<< std::left << std::setw(12) << s.prefix
<< s.suffix << '\n';
std::cout
<< ("\nAny {name} and directory component is skipped for the purpose of\n"
"matching those prefixes. So for instance\n"
" '{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
"will changed into\n"
" '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %L %O'\n");
}
translator_spec::translator_spec(const char* spec)
: spec(spec), cmd(spec), name(spec)
{
if (*cmd == '{')
{
// 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;
}
}
}
// If there is no % in the string, look for a known
// command from our shorthand list. If we find it,
// add the suffix.
bool allocated = false;
if (!strchr(cmd, '%'))
{
// Skip any leading directory name.
auto basename = cmd;
auto pos = cmd;
while (*pos)
{
if (*pos == '/')
basename = pos + 1;
else if (*pos == ' ')
break;
++pos;
}
// Match a shorthand.
for (auto& p: shorthands)
{
int n = strlen(p.prefix);
if (strncmp(basename, p.prefix, n) == 0)
{
int m = strlen(p.suffix);
int q = strlen(cmd);
char* tmp = static_cast<char*>(malloc(q + m + 1));
strcpy(tmp, cmd);
strcpy(tmp + q, p.suffix);
cmd = tmp;
allocated = true;
break;
}
}
}
if (!allocated)
cmd = strdup(cmd);
}
translator_spec::translator_spec(const translator_spec& other)
: spec(other.spec), cmd(other.cmd), name(other.name)
{
if (name != spec)
name = strdup(name);
if (cmd != spec)
cmd = strdup(cmd);
}
translator_spec::~translator_spec()
{
if (name != spec)
free(const_cast<char*>(name));
if (cmd != spec)
free(const_cast<char*>(cmd));
}
std::vector<translator_spec> translators;
void
quoted_string::print(std::ostream& os, const char*) const
{
spot::quote_shell_string(os, val().c_str());
}
printable_result_filename::printable_result_filename()
{
val_ = nullptr;
}
printable_result_filename::~printable_result_filename()
{
delete val_;
}
void printable_result_filename::reset(unsigned n)
{
translator_num = n;
}
void printable_result_filename::cleanup()
{
delete val_;
val_ = nullptr;
}
void
printable_result_filename::print(std::ostream& os, const char*) const
{
char prefix[30];
snprintf(prefix, sizeof prefix, "lcr-o%u-", translator_num);
const_cast<printable_result_filename*>(this)->val_ =
spot::create_tmpfile(prefix);
spot::quote_shell_string(os, val()->name());
}
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);
declare('O', &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['O'] ||
// backward-compatibility
has['D'] || has['N'] || has['T'] || has['H']))
error(2, 0, "no output %%-sequence in '%s'.\n Use "
"%%O to indicate where the automaton is output.",
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(spot::formula f, unsigned serial)
{
if (has('f') || has('F'))
string_ltl_spot = spot::str_psl(f, true);
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;
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, "received signal %d before starting child", 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, nullptr);
// Catch termination signals, so we can kill the subprocess.
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
}
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, nullptr);
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;
}
#endif // ENABLE_TIMEOUT
enum {
OPT_LIST = 1,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 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 },
{ "list-shorthands", OPT_LIST, nullptr, 0,
"list availabled shorthands to use in COMMANDFMT", 0},
/**************************************************/
{ nullptr, 0, nullptr, 0,
"COMMANDFMT should specify input and output arguments using the "
"following character sequences:", 3 },
{ "%f,%s,%l,%w", 0, nullptr, 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, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula as a file in Spot, Spin, LBT, or Wring's syntax", 0 },
{ "%O", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the automaton is output in HOA, never claim, LBTT, or ltl2dstar's "
"format", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "a single %", 0 },
{ nullptr, 0, nullptr, 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 },
{ nullptr, 0, nullptr, 0, nullptr, 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;
case OPT_LIST:
show_shorthands();
exit(0);
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
nullptr, nullptr, nullptr };

117
bin/common_trans.hh Normal file
View file

@ -0,0 +1,117 @@
// -*- 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/>.
#pragma once
#include "common_sys.hh"
#include <vector>
#include <argp.h>
#include <spot/misc/formater.hh>
#include <spot/misc/tmpfile.hh>
#include <spot/twa/twagraph.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;
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(spot::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

202
bin/dstar2tgba.cc Normal file
View file

@ -0,0 +1,202 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 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 <memory>
#include <argp.h>
#include "error.h"
#include "common_setup.hh"
#include "common_finput.hh"
#include "common_cout.hh"
#include "common_aoutput.hh"
#include "common_post.hh"
#include "common_file.hh"
#include "common_hoaread.hh"
#include <spot/twaalgos/dot.hh>
#include <spot/twaalgos/lbtt.hh>
#include <spot/twaalgos/hoa.hh>
#include <spot/twaalgos/neverclaim.hh>
#include <spot/twaalgos/stats.hh>
#include <spot/twaalgos/totgba.hh>
#include <spot/twa/bddprint.hh>
#include <spot/misc/optionmap.hh>
#include <spot/misc/timer.hh>
#include <spot/parseaut/public.hh>
#include <spot/twaalgos/sccinfo.hh>
static const char argp_program_doc[] ="\
Convert automata with any acceptance condition into variants of \
Büchi automata.\n\nThis reads automata into any supported format \
(HOA, LBTT, ltl2dstar, never claim) and outputs a \
Transition-based Generalized Büchi Automata in GraphViz's format by default. \
Each supplied file may contain multiple automata.";
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Input:", 1 },
{ "file", 'F', "FILENAME", 0,
"process the automaton in FILENAME", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ "extra-options", 'x', "OPTS", 0,
"fine-tuning options (see spot-x (7))", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const struct argp_child children[] =
{
{ &hoaread_argp, 0, nullptr, 0 },
{ &aoutput_argp, 0, nullptr, 0 },
{ &aoutput_io_format_argp, 0, nullptr, 4 },
{ &post_argp, 0, nullptr, 0 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
static spot::option_map extra_options;
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'F':
jobs.emplace_back(arg, true);
break;
case 'x':
{
const char* opt = extra_options.parse_options(arg);
if (opt)
error(2, 0, "failed to parse --options near '%s'", opt);
}
break;
case ARGP_KEY_ARG:
jobs.emplace_back(arg, true);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
namespace
{
class dstar_processor: public job_processor
{
public:
spot::postprocessor& post;
automaton_printer printer;
dstar_processor(spot::postprocessor& post)
: post(post), printer(aut_input)
{
}
int
process_formula(spot::formula, const char*, int)
{
SPOT_UNREACHABLE();
}
int
process_automaton(const spot::const_parsed_aut_ptr& haut,
const char* filename)
{
spot::stopwatch sw;
sw.start();
auto nba = spot::to_generalized_buchi(haut->aut);
auto aut = post.run(nba, nullptr);
const double conversion_time = sw.stop();
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
flush_cout();
return 0;
}
int
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
{
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
return 2;
}
int
process_file(const char* filename)
{
auto hp = spot::automaton_stream_parser(filename, opt_parse);
int err = 0;
while (!abort_run)
{
auto haut = hp.parse(spot::make_bdd_dict());
if (!haut->aut && haut->errors.empty())
break;
if (haut->format_errors(std::cerr))
err = 2;
if (!haut->aut)
error(2, 0, "failed to read automaton from %s", filename);
else if (haut->aborted)
err = std::max(err, aborted(haut, filename));
else
process_automaton(haut, filename);
}
return err;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[FILENAMES...]",
argp_program_doc, children, nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (jobs.empty())
jobs.emplace_back("-", true);
spot::postprocessor post(&extra_options);
post.set_pref(pref | comp | sbacc);
post.set_type(type);
post.set_level(level);
try
{
dstar_processor processor(post);
if (processor.run())
return 2;
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
return 0;
}

834
bin/genltl.cc Normal file
View file

@ -0,0 +1,834 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 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/>.
// Families defined here come from the following papers:
//
// @InProceedings{cichon.09.depcos,
// author = {Jacek Cicho{\'n} and Adam Czubak and Andrzej Jasi{\'n}ski},
// title = {Minimal {B\"uchi} Automata for Certain Classes of {LTL} Formulas},
// booktitle = {Proceedings of the Fourth International Conference on
// Dependability of Computer Systems},
// pages = {17--24},
// year = 2009,
// publisher = {IEEE Computer Society},
// }
//
// @InProceedings{geldenhuys.06.spin,
// author = {Jaco Geldenhuys and Henri Hansen},
// title = {Larger Automata and Less Work for LTL Model Checking},
// booktitle = {Proceedings of the 13th International SPIN Workshop},
// year = {2006},
// pages = {53--70},
// series = {Lecture Notes in Computer Science},
// volume = {3925},
// publisher = {Springer}
// }
//
// @InProceedings{gastin.01.cav,
// author = {Paul Gastin and Denis Oddoux},
// title = {Fast {LTL} to {B\"u}chi Automata Translation},
// booktitle = {Proceedings of the 13th International Conference on
// Computer Aided Verification (CAV'01)},
// pages = {53--65},
// year = 2001,
// editor = {G. Berry and H. Comon and A. Finkel},
// volume = {2102},
// series = {Lecture Notes in Computer Science},
// address = {Paris, France},
// publisher = {Springer-Verlag}
// }
//
// @InProceedings{rozier.07.spin,
// author = {Kristin Y. Rozier and Moshe Y. Vardi},
// title = {LTL Satisfiability Checking},
// booktitle = {Proceedings of the 12th International SPIN Workshop on
// Model Checking of Software (SPIN'07)},
// pages = {149--167},
// year = {2007},
// volume = {4595},
// series = {Lecture Notes in Computer Science},
// publisher = {Springer-Verlag}
// }
#include "common_sys.hh"
#include <iostream>
#include <fstream>
#include <argp.h>
#include <cstdlib>
#include "error.h"
#include <vector>
#include "common_setup.hh"
#include "common_output.hh"
#include "common_range.hh"
#include <cassert>
#include <iostream>
#include <sstream>
#include <set>
#include <string>
#include <cstdlib>
#include <cstring>
#include <spot/tl/formula.hh>
#include <spot/tl/relabel.hh>
using namespace spot;
const char argp_program_doc[] ="\
Generate temporal logic formulas from predefined scalable patterns.";
enum {
OPT_AND_F = 1,
OPT_AND_FG,
OPT_AND_GF,
OPT_CCJ_ALPHA,
OPT_CCJ_BETA,
OPT_CCJ_BETA_PRIME,
OPT_GH_Q,
OPT_GH_R,
OPT_GO_THETA,
OPT_OR_FG,
OPT_OR_G,
OPT_OR_GF,
OPT_R_LEFT,
OPT_R_RIGHT,
OPT_RV_COUNTER,
OPT_RV_COUNTER_CARRY,
OPT_RV_COUNTER_CARRY_LINEAR,
OPT_RV_COUNTER_LINEAR,
OPT_U_LEFT,
OPT_U_RIGHT,
LAST_CLASS,
};
const char* const class_name[LAST_CLASS] =
{
"and-f",
"and-fg",
"and-gf",
"ccj-alpha",
"ccj-beta",
"ccj-beta-prime",
"gh-q",
"gh-r",
"go-theta",
"or-fg",
"or-g",
"or-gf",
"or-r-left",
"or-r-right",
"rv-counter",
"rv-counter-carry",
"rv-counter-carry-linear",
"rv-counter-linear",
"u-left",
"u-right",
};
#define OPT_ALIAS(o) { #o, 0, nullptr, OPTION_ALIAS, nullptr, 0 }
static const argp_option options[] =
{
/**************************************************/
// Keep this alphabetically sorted (expect for aliases).
{ nullptr, 0, nullptr, 0, "Pattern selection:", 1},
// J. Geldenhuys and H. Hansen (Spin'06): Larger automata and less
// work for LTL model checking.
{ "and-f", OPT_AND_F, "RANGE", 0, "F(p1)&F(p2)&...&F(pn)", 0 },
OPT_ALIAS(gh-e),
{ "and-fg", OPT_AND_FG, "RANGE", 0, "FG(p1)&FG(p2)&...&FG(pn)", 0 },
{ "and-gf", OPT_AND_GF, "RANGE", 0, "GF(p1)&GF(p2)&...&GF(pn)", 0 },
OPT_ALIAS(ccj-phi),
OPT_ALIAS(gh-c2),
{ "ccj-alpha", OPT_CCJ_ALPHA, "RANGE", 0,
"F(p1&F(p2&F(p3&...F(pn)))) & F(q1&F(q2&F(q3&...F(qn))))", 0 },
{ "ccj-beta", OPT_CCJ_BETA, "RANGE", 0,
"F(p&X(p&X(p&...X(p)))) & F(q&X(q&X(q&...X(q))))", 0 },
{ "ccj-beta-prime", OPT_CCJ_BETA_PRIME, "RANGE", 0,
"F(p&(Xp)&(XXp)&...(X...X(p))) & F(q&(Xq)&(XXq)&...(X...X(q)))", 0 },
{ "gh-q", OPT_GH_Q, "RANGE", 0,
"(F(p1)|G(p2))&(F(p2)|G(p3))&... &(F(pn)|G(p{n+1}))", 0 },
{ "gh-r", OPT_GH_R, "RANGE", 0,
"(GF(p1)|FG(p2))&(GF(p2)|FG(p3))&... &(GF(pn)|FG(p{n+1}))", 0},
{ "go-theta", OPT_GO_THETA, "RANGE", 0,
"!((GF(p1)&GF(p2)&...&GF(pn)) -> G(q->F(r)))", 0 },
{ "or-fg", OPT_OR_FG, "RANGE", 0, "FG(p1)|FG(p2)|...|FG(pn)", 0 },
OPT_ALIAS(ccj-xi),
{ "or-g", OPT_OR_G, "RANGE", 0, "G(p1)|G(p2)|...|G(pn)", 0 },
OPT_ALIAS(gh-s),
{ "or-gf", OPT_OR_GF, "RANGE", 0, "GF(p1)|GF(p2)|...|GF(pn)", 0 },
OPT_ALIAS(gh-c1),
{ "r-left", OPT_R_LEFT, "RANGE", 0, "(((p1 R p2) R p3) ... R pn)", 0 },
{ "r-right", OPT_R_RIGHT, "RANGE", 0, "(p1 R (p2 R (... R pn)))", 0 },
{ "rv-counter", OPT_RV_COUNTER, "RANGE", 0,
"n-bit counter", 0 },
{ "rv-counter-carry", OPT_RV_COUNTER_CARRY, "RANGE", 0,
"n-bit counter w/ carry", 0 },
{ "rv-counter-carry-linear", OPT_RV_COUNTER_CARRY_LINEAR, "RANGE", 0,
"n-bit counter w/ carry (linear size)", 0 },
{ "rv-counter-linear", OPT_RV_COUNTER_LINEAR, "RANGE", 0,
"n-bit counter (linear size)", 0 },
{ "u-left", OPT_U_LEFT, "RANGE", 0, "(((p1 U p2) U p3) ... U pn)", 0 },
OPT_ALIAS(gh-u),
{ "u-right", OPT_U_RIGHT, "RANGE", 0, "(p1 U (p2 U (... U pn)))", 0 },
OPT_ALIAS(gh-u2),
OPT_ALIAS(go-phi),
RANGE_DOC,
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
"the following interpreted sequences:", -19 },
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula (in the selected syntax)", 0 },
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the name of the pattern", 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the argument of the pattern", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
struct job
{
int pattern;
struct range range;
};
typedef std::vector<job> jobs_t;
static jobs_t jobs;
const struct argp_child children[] =
{
{ &output_argp, 0, nullptr, -20 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
static void
enqueue_job(int pattern, const char* range_str)
{
job j;
j.pattern = pattern;
j.range = parse_range(range_str);
jobs.push_back(j);
}
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case OPT_AND_F:
case OPT_AND_FG:
case OPT_AND_GF:
case OPT_CCJ_ALPHA:
case OPT_CCJ_BETA:
case OPT_CCJ_BETA_PRIME:
case OPT_GH_Q:
case OPT_GH_R:
case OPT_GO_THETA:
case OPT_OR_FG:
case OPT_OR_G:
case OPT_OR_GF:
case OPT_R_LEFT:
case OPT_R_RIGHT:
case OPT_RV_COUNTER:
case OPT_RV_COUNTER_CARRY:
case OPT_RV_COUNTER_CARRY_LINEAR:
case OPT_RV_COUNTER_LINEAR:
case OPT_U_LEFT:
case OPT_U_RIGHT:
enqueue_job(key, arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
#define G_(x) formula::G(x)
#define F_(x) formula::F(x)
#define X_(x) formula::X(x)
#define Not_(x) formula::Not(x)
#define Implies_(x, y) formula::Implies((x), (y))
#define Equiv_(x, y) formula::Equiv((x), (y))
#define And_(x, y) formula::And({(x), (y)})
#define Or_(x, y) formula::Or({(x), (y)})
#define U_(x, y) formula::U((x), (y))
// F(p_1 & F(p_2 & F(p_3 & ... F(p_n))))
static formula
E_n(std::string name, int n)
{
if (n <= 0)
return formula::tt();
formula result = nullptr;
for (; n > 0; --n)
{
std::ostringstream p;
p << name << n;
formula f = formula::ap(p.str());
if (result)
result = And_(f, result);
else
result = f;
result = F_(result);
}
return result;
}
// p & X(p & X(p & ... X(p)))
static formula
phi_n(std::string name, int n)
{
if (n <= 0)
return formula::tt();
formula result = nullptr;
formula p = formula::ap(name);
for (; n > 0; --n)
{
if (result)
result = And_(p, X_(result));
else
result = p;
}
return result;
}
static formula
N_n(std::string name, int n)
{
return formula::F(phi_n(name, n));
}
// p & X(p) & XX(p) & XXX(p) & ... X^n(p)
static formula
phi_prime_n(std::string name, int n)
{
if (n <= 0)
return formula::tt();
formula result = nullptr;
formula p = formula::ap(name);
for (; n > 0; --n)
{
if (result)
{
p = X_(p);
result = And_(result, p);
}
else
{
result = p;
}
}
return result;
}
static formula
N_prime_n(std::string name, int n)
{
return F_(phi_prime_n(name, n));
}
// GF(p_1) & GF(p_2) & ... & GF(p_n) if conj == true
// GF(p_1) | GF(p_2) | ... | GF(p_n) if conj == false
static formula
GF_n(std::string name, int n, bool conj = true)
{
if (n <= 0)
return conj ? formula::tt() : formula::ff();
formula result = nullptr;
op o = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
formula f = G_(F_(formula::ap(p.str())));
if (result)
result = formula::multop(o, {f, result});
else
result = f;
}
return result;
}
// FG(p_1) | FG(p_2) | ... | FG(p_n) if conj == false
// FG(p_1) & FG(p_2) & ... & FG(p_n) if conj == true
static formula
FG_n(std::string name, int n, bool conj = false)
{
if (n <= 0)
return conj ? formula::tt() : formula::ff();
formula result = nullptr;
op o = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
formula f = F_(G_(formula::ap(p.str())));
if (result)
result = formula::multop(o, {f, result});
else
result = f;
}
return result;
}
// (((p1 OP p2) OP p3)...OP pn) if right_assoc == false
// (p1 OP (p2 OP (p3 OP (... pn) if right_assoc == true
static formula
bin_n(std::string name, int n, op o, bool right_assoc = false)
{
if (n <= 0)
n = 1;
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << (right_assoc ? (n + 1 - i) : i);
formula f = formula::ap(p.str());
if (!result)
result = f;
else if (right_assoc)
result = formula::binop(o, f, result);
else
result = formula::binop(o, result, f);
}
return result;
}
// (GF(p1)|FG(p2))&(GF(p2)|FG(p3))&...&(GF(pn)|FG(p{n+1}))"
static formula
R_n(std::string name, int n)
{
if (n <= 0)
return formula::tt();
formula pi;
{
std::ostringstream p;
p << name << 1;
pi = formula::ap(p.str());
}
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
formula gf = G_(F_(pi));
std::ostringstream p;
p << name << i + 1;
pi = formula::ap(p.str());
formula fg = F_(G_(pi));
formula f = Or_(gf, fg);
if (result)
result = And_(f, result);
else
result = f;
}
return result;
}
// (F(p1)|G(p2))&(F(p2)|G(p3))&...&(F(pn)|G(p{n+1}))"
static formula
Q_n(std::string name, int n)
{
if (n <= 0)
return formula::tt();
formula pi;
{
std::ostringstream p;
p << name << 1;
pi = formula::ap(p.str());
}
formula result = nullptr;
for (int i = 1; i <= n; ++i)
{
formula f = F_(pi);
std::ostringstream p;
p << name << i + 1;
pi = formula::ap(p.str());
formula g = G_(pi);
f = Or_(f, g);
if (result)
result = And_(f, result);
else
result = f;
}
return result;
}
// OP(p1) | OP(p2) | ... | OP(Pn) if conj == false
// OP(p1) & OP(p2) & ... & OP(Pn) if conj == true
static formula
combunop_n(std::string name, int n, op o, bool conj = false)
{
if (n <= 0)
return conj ? formula::tt() : formula::ff();
formula result = nullptr;
op cop = conj ? op::And : op::Or;
for (int i = 1; i <= n; ++i)
{
std::ostringstream p;
p << name << i;
formula f = formula::unop(o, formula::ap(p.str()));
if (result)
result = formula::multop(cop, {f, result});
else
result = f;
}
return result;
}
// !((GF(p1)&GF(p2)&...&GF(pn))->G(q -> F(r)))
// From "Fast LTL to Büchi Automata Translation" [gastin.01.cav]
static formula
fair_response(std::string p, std::string q, std::string r, int n)
{
formula fair = GF_n(p, n);
formula resp = G_(Implies_(formula::ap(q), F_(formula::ap(r))));
return Not_(Implies_(fair, resp));
}
// Builds X(X(...X(p))) with n occurrences of X.
static formula
X_n(formula p, int n)
{
assert(n >= 0);
formula res = p;
while (n--)
res = X_(res);
return res;
}
// Based on LTLcounter.pl from Kristin Rozier.
// http://shemesh.larc.nasa.gov/people/kyr/benchmarking_scripts/
static formula
ltl_counter(std::string bit, std::string marker, int n, bool linear)
{
formula b = formula::ap(bit);
formula neg_b = Not_(b);
formula m = formula::ap(marker);
formula neg_m = Not_(m);
std::vector<formula> res(4);
// The marker starts with "1", followed by n-1 "0", then "1" again,
// n-1 "0", etc.
if (!linear)
{
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
std::vector<formula> v(n);
for (int i = 0; i + 1 < n; ++i)
v[i] = X_n(neg_m, i + 1);
v[n - 1] = X_n(m, n);
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
}
else
{
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
formula p = m;
for (int i = n - 1; i > 0; --i)
p = And_(neg_m, X_(p));
res[0] = And_(m, G_(Implies_(m, X_(p))));
}
// All bits are initially zero.
if (!linear)
{
// !b & X(!b) & XX(!b) [if n = 3]
std::vector<formula> v2(n);
for (int i = 0; i < n; ++i)
v2[i] = X_n(neg_b, i);
res[1] = formula::And(std::move(v2));
}
else
{
// !b & X(!b & X(!b)) [if n = 3]
formula p = neg_b;
for (int i = n - 1; i > 0; --i)
p = And_(neg_b, X_(p));
res[1] = p;
}
#define AndX_(x, y) (linear ? X_(And_((x), (y))) : And_(X_(x), X_(y)))
// If the least significant bit is 0, it will be 1 at the next time,
// and other bits stay the same.
formula Xnm1_b = X_n(b, n - 1);
formula Xn_b = X_(Xnm1_b);
res[2] = G_(Implies_(And_(m, neg_b),
AndX_(Xnm1_b, U_(And_(Not_(m), Equiv_(b, Xn_b)), m))));
// From the least significant bit to the first 0, all the bits
// are flipped on the next value. Remaining bits are identical.
formula Xnm1_negb = X_n(neg_b, n - 1);
formula Xn_negb = X_(Xnm1_negb);
res[3] = G_(Implies_(And_(m, b),
AndX_(Xnm1_negb,
U_(And_(And_(b, neg_m), Xn_negb),
Or_(m, And_(And_(neg_m, neg_b),
AndX_(Xnm1_b,
U_(And_(neg_m,
Equiv_(b, Xn_b)),
m))))))));
return formula::And(std::move(res));
}
static formula
ltl_counter_carry(std::string bit, std::string marker,
std::string carry, int n, bool linear)
{
formula b = formula::ap(bit);
formula neg_b = Not_(b);
formula m = formula::ap(marker);
formula neg_m = Not_(m);
formula c = formula::ap(carry);
formula neg_c = Not_(c);
std::vector<formula> res(6);
// The marker starts with "1", followed by n-1 "0", then "1" again,
// n-1 "0", etc.
if (!linear)
{
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
std::vector<formula> v(n);
for (int i = 0; i + 1 < n; ++i)
v[i] = X_n(neg_m, i + 1);
v[n - 1] = X_n(m, n);
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
}
else
{
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
formula p = m;
for (int i = n - 1; i > 0; --i)
p = And_(neg_m, X_(p));
res[0] = And_(m, G_(Implies_(m, X_(p))));
}
// All bits are initially zero.
if (!linear)
{
// !b & X(!b) & XX(!b) [if n = 3]
std::vector<formula> v2(n);
for (int i = 0; i < n; ++i)
v2[i] = X_n(neg_b, i);
res[1] = formula::And(std::move(v2));
}
else
{
// !b & X(!b & X(!b)) [if n = 3]
formula p = neg_b;
for (int i = n - 1; i > 0; --i)
p = And_(neg_b, X_(p));
res[1] = p;
}
formula Xn_b = X_n(b, n);
formula Xn_negb = X_n(neg_b, n);
// If m is 1 and b is 0 then c is 0 and n steps later b is 1.
res[2] = G_(Implies_(And_(m, neg_b), And_(neg_c, Xn_b)));
// If m is 1 and b is 1 then c is 1 and n steps later b is 0.
res[3] = G_(Implies_(And_(m, b), And_(c, Xn_negb)));
if (!linear)
{
// If there's no carry, then all of the bits stay the same n steps later.
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
And_(X_(Not_(c)), Equiv_(X_(b), X_(Xn_b)))));
// If there's a carry, then add one: flip the bits of b and
// adjust the carry.
res[5] = G_(Implies_(c, And_(Implies_(X_(neg_b),
And_(X_(neg_c), X_(Xn_b))),
Implies_(X_(b),
And_(X_(c), X_(Xn_negb))))));
}
else
{
// If there's no carry, then all of the bits stay the same n steps later.
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
X_(And_(Not_(c), Equiv_(b, Xn_b)))));
// If there's a carry, then add one: flip the bits of b and
// adjust the carry.
res[5] = G_(Implies_(c, X_(And_(Implies_(neg_b, And_(neg_c, Xn_b)),
Implies_(b, And_(c, Xn_negb))))));
}
return formula::And(std::move(res));
}
static void
output_pattern(int pattern, int n)
{
formula f = nullptr;
switch (pattern)
{
// Keep this alphabetically-ordered!
case OPT_AND_F:
f = combunop_n("p", n, op::F, true);
break;
case OPT_AND_FG:
f = FG_n("p", n, true);
break;
case OPT_AND_GF:
f = GF_n("p", n, true);
break;
case OPT_CCJ_ALPHA:
f = formula::And({E_n("p", n), E_n("q", n)});
break;
case OPT_CCJ_BETA:
f = formula::And({N_n("p", n), N_n("q", n)});
break;
case OPT_CCJ_BETA_PRIME:
f = formula::And({N_prime_n("p", n), N_prime_n("q", n)});
break;
case OPT_GH_Q:
f = Q_n("p", n);
break;
case OPT_GH_R:
f = R_n("p", n);
break;
case OPT_GO_THETA:
f = fair_response("p", "q", "r", n);
break;
case OPT_OR_FG:
f = FG_n("p", n, false);
break;
case OPT_OR_G:
f = combunop_n("p", n, op::G, false);
break;
case OPT_OR_GF:
f = GF_n("p", n, false);
break;
case OPT_R_LEFT:
f = bin_n("p", n, op::R, false);
break;
case OPT_R_RIGHT:
f = bin_n("p", n, op::R, true);
break;
case OPT_RV_COUNTER_CARRY:
f = ltl_counter_carry("b", "m", "c", n, false);
break;
case OPT_RV_COUNTER_CARRY_LINEAR:
f = ltl_counter_carry("b", "m", "c", n, true);
break;
case OPT_RV_COUNTER:
f = ltl_counter("b", "m", n, false);
break;
case OPT_RV_COUNTER_LINEAR:
f = ltl_counter("b", "m", n, true);
break;
case OPT_U_LEFT:
f = bin_n("p", n, op::U, false);
break;
case OPT_U_RIGHT:
f = bin_n("p", n, op::U, true);
break;
default:
error(100, 0, "internal error: pattern not implemented");
}
// Make sure we use only "p42"-style of atomic propositions
// in lbt's output.
if (output_format == lbt_output && !f.has_lbt_atomic_props())
f = relabel(f, Pnn);
output_formula_checked(f, class_name[pattern - 1], n);
}
static void
run_jobs()
{
for (auto& j: jobs)
{
int inc = (j.range.max < j.range.min) ? -1 : 1;
int n = j.range.min;
for (;;)
{
output_pattern(j.pattern, n);
if (n == j.range.max)
break;
n += inc;
}
}
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, nullptr, argp_program_doc,
children, nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (jobs.empty())
error(1, 0, "Nothing to do. Try '%s --help' for more information.",
program_name);
run_jobs();
return 0;
}

180
bin/ltl2tgba.cc Normal file
View file

@ -0,0 +1,180 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 <argp.h>
#include "error.h"
#include "common_setup.hh"
#include "common_r.hh"
#include "common_cout.hh"
#include "common_finput.hh"
#include "common_output.hh"
#include "common_aoutput.hh"
#include "common_post.hh"
#include <spot/tl/formula.hh>
#include <spot/tl/print.hh>
#include <spot/twaalgos/translate.hh>
#include <spot/misc/optionmap.hh>
#include <spot/misc/timer.hh>
static const char argp_program_doc[] ="\
Translate linear-time formulas (LTL/PSL) into Büchi automata.\n\n\
By default it will apply all available optimizations to output \
the smallest Transition-based Generalized Büchi Automata, \
in GraphViz's format.\n\
If multiple formulas are supplied, several automata will be output.";
static const argp_option options[] =
{
/**************************************************/
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula, in Spot's syntax", 4 },
/**************************************************/
{ "unambiguous", 'U', nullptr, 0, "output unambiguous automata", 2 },
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ "extra-options", 'x', "OPTS", 0,
"fine-tuning options (see spot-x (7))", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp_child children[] =
{
{ &finput_argp, 0, nullptr, 1 },
{ &aoutput_argp, 0, nullptr, 0 },
{ &aoutput_o_format_argp, 0, nullptr, 0 },
{ &post_argp, 0, nullptr, 0 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
static spot::option_map extra_options;
static spot::postprocessor::output_pref unambig = 0;
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'U':
unambig = spot::postprocessor::Unambiguous;
break;
case 'x':
{
const char* opt = extra_options.parse_options(arg);
if (opt)
error(2, 0, "failed to parse --options near '%s'", opt);
}
break;
case ARGP_KEY_ARG:
// FIXME: use stat() to distinguish filename from string?
jobs.emplace_back(arg, false);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
namespace
{
class trans_processor: public job_processor
{
public:
spot::translator& trans;
automaton_printer printer;
trans_processor(spot::translator& trans)
: trans(trans), printer(ltl_input)
{
}
int
process_formula(spot::formula f,
const char* filename = nullptr, int linenum = 0)
{
// This should not happen, because the parser we use can only
// read PSL/LTL formula, but since our formula type can
// represent more than PSL formula, let's make this
// future-proof.
if (!f.is_psl_formula())
{
std::string s = spot::str_psl(f);
error_at_line(2, 0, filename, linenum,
"formula '%s' is not an LTL or PSL formula",
s.c_str());
}
spot::stopwatch sw;
sw.start();
auto aut = trans.run(&f);
const double translation_time = sw.stop();
printer.print(aut, f, filename, linenum, translation_time, nullptr);
return 0;
}
};
}
int
main(int argc, char** argv)
{
// By default we name automata using the formula.
opt_name = "%f";
setup(argv);
const argp ap = { options, parse_opt, "[FORMULA...]",
argp_program_doc, children, nullptr, nullptr };
simplification_level = 3;
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (jobs.empty())
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
program_name);
spot::translator trans(&extra_options);
trans.set_pref(pref | comp | sbacc | unambig);
trans.set_type(type);
trans.set_level(level);
try
{
trans_processor processor(trans);
if (processor.run())
return 2;
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
return 0;
}

242
bin/ltl2tgta.cc Normal file
View file

@ -0,0 +1,242 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 <fstream>
#include <argp.h>
#include "error.h"
#include "common_setup.hh"
#include "common_r.hh"
#include "common_cout.hh"
#include "common_finput.hh"
#include "common_post.hh"
#include <spot/tl/parse.hh>
#include <spot/tl/print.hh>
#include <spot/tl/simplify.hh>
#include <spot/twaalgos/dot.hh>
#include <spot/twaalgos/ltl2tgba_fm.hh>
#include <spot/twaalgos/translate.hh>
#include <spot/twa/bddprint.hh>
#include <spot/taalgos/tgba2ta.hh>
#include <spot/taalgos/dot.hh>
#include <spot/taalgos/minimize.hh>
#include <spot/misc/optionmap.hh>
const char argp_program_doc[] ="\
Translate linear-time formulas (LTL/PSL) into Testing Automata.\n\n\
By default it outputs a transition-based generalized Testing Automaton \
the smallest Transition-based Generalized Büchi Automata, \
in GraphViz's format. The input formula is assumed to be \
stuttering-insensitive.";
enum {
OPT_GTA = 1,
OPT_INIT,
OPT_SPLV,
OPT_SPNO,
OPT_TA,
OPT_TGTA,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Automaton type:", 1 },
{ "tgta", OPT_TGTA, nullptr, 0,
"Transition-based Generalized Testing Automaton (default)", 0 },
{ "ta", OPT_TA, nullptr, 0, "Testing Automaton", 0 },
{ "gta", OPT_GTA, nullptr, 0, "Generalized Testing Automaton", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Options for TA and GTA creation:", 3 },
{ "single-pass-lv", OPT_SPLV, nullptr, 0,
"add an artificial livelock state to obtain a single-pass (G)TA", 0 },
{ "single-pass", OPT_SPNO, nullptr, 0,
"create a single-pass (G)TA without artificial livelock state", 0 },
{ "multiple-init", OPT_INIT, nullptr, 0,
"do not create the fake initial state", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output options:", 4 },
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ "extra-options", 'x', "OPTS", 0,
"fine-tuning options (see spot-x (7))", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp_child children[] =
{
{ &finput_argp, 0, nullptr, 1 },
{ &post_argp, 0, nullptr, 20 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
enum ta_types { TGTA, GTA, TA };
ta_types ta_type = TGTA;
bool utf8 = false;
const char* stats = "";
spot::option_map extra_options;
bool opt_with_artificial_initial_state = true;
bool opt_single_pass_emptiness_check = false;
bool opt_with_artificial_livelock = false;
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case '8':
spot::enable_utf8();
break;
case 'B':
type = spot::postprocessor::BA;
break;
case 'x':
{
const char* opt = extra_options.parse_options(arg);
if (opt)
error(2, 0, "failed to parse --options near '%s'", opt);
}
break;
case OPT_TGTA:
ta_type = TGTA;
type = spot::postprocessor::TGBA;
break;
case OPT_GTA:
ta_type = GTA;
type = spot::postprocessor::TGBA;
break;
case OPT_TA:
ta_type = TA;
type = spot::postprocessor::BA;
break;
case OPT_INIT:
opt_with_artificial_initial_state = false;
break;
case OPT_SPLV:
opt_with_artificial_livelock = true;
break;
case OPT_SPNO:
opt_single_pass_emptiness_check = true;
break;
case ARGP_KEY_ARG:
// FIXME: use stat() to distinguish filename from string?
jobs.emplace_back(arg, false);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
namespace
{
class trans_processor: public job_processor
{
public:
spot::translator& trans;
trans_processor(spot::translator& trans)
: trans(trans)
{
}
int
process_formula(spot::formula f,
const char* filename = nullptr, int linenum = 0)
{
auto aut = trans.run(&f);
// This should not happen, because the parser we use can only
// read PSL/LTL formula, but since our formula type can
// represent more than PSL formula, let's make this
// future-proof.
if (!f.is_psl_formula())
{
std::string s = spot::str_psl(f);
error_at_line(2, 0, filename, linenum,
"formula '%s' is not an LTL or PSL formula",
s.c_str());
}
bdd ap_set = atomic_prop_collect_as_bdd(f, aut);
if (ta_type != TGTA)
{
auto testing_automaton =
tgba_to_ta(aut, ap_set, type == spot::postprocessor::BA,
opt_with_artificial_initial_state,
opt_single_pass_emptiness_check,
opt_with_artificial_livelock);
if (level != spot::postprocessor::Low)
testing_automaton = spot::minimize_ta(testing_automaton);
spot::print_dot(std::cout, testing_automaton);
}
else
{
auto tgta = tgba_to_tgta(aut, ap_set);
if (level != spot::postprocessor::Low)
tgta = spot::minimize_tgta(tgta);
spot::print_dot(std::cout, tgta->get_ta());
}
flush_cout();
return 0;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[FORMULA...]",
argp_program_doc, children, nullptr, nullptr };
simplification_level = 3;
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (jobs.empty())
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
program_name);
spot::translator trans(&extra_options);
trans.set_pref(pref | comp | sbacc);
trans.set_type(type);
trans.set_level(level);
trans_processor processor(trans);
if (processor.run())
return 2;
return 0;
}

1479
bin/ltlcross.cc Normal file

File diff suppressed because it is too large Load diff

337
bin/ltldo.cc Normal file
View file

@ -0,0 +1,337 @@
// -*- 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 <sys/wait.h>
#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 "common_hoaread.hh"
#include <spot/tl/relabel.hh>
#include <spot/misc/bareword.hh>
#include <spot/misc/timer.hh>
#include <spot/twaalgos/lbtt.hh>
#include <spot/twaalgos/relabel.hh>
#include <spot/twaalgos/totgba.hh>
#include <spot/parseaut/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[] =
{
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const argp_option more_o_format[] =
{
{ "%R", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"serial number of the formula translated", 0 },
{ "%T", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"tool used for translation", 0 },
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"formula translated", 0 },
{ nullptr, 0, nullptr, 0, nullptr, 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, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
return &more_o_format_argp;
}
const struct argp_child children[] =
{
{ &hoaread_argp, 0, "Parsing of automata:", 3 },
{ &finput_argp, 0, nullptr, 1 },
{ &trans_argp, 0, nullptr, 3 },
{ &aoutput_argp, 0, nullptr, 4 },
{ build_percent_list(), 0, nullptr, 5 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 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::twa_graph_ptr
translate(unsigned int translator_num, bool& problem, double& duration)
{
output.reset(translator_num);
std::ostringstream command;
format(command, translators[translator_num].cmd);
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::twa_graph_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 if (output.val())
{
problem = false;
auto aut = spot::parse_aut(output.val()->name(), dict,
spot::default_environment::instance(),
opt_parse);
if (!aut->errors.empty())
{
problem = true;
std::cerr << "error: failed to parse the automaton "
"produced by \"" << cmd << "\".\n";
aut->format_errors(std::cerr);
res = nullptr;
}
else if (aut->aborted)
{
problem = true;
std::cerr << "error: command \"" << cmd
<< "\" aborted its output.\n";
res = nullptr;
}
else
{
res = aut->aut;
}
}
else // No automaton output
{
problem = false;
res = nullptr;
}
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::parse_error_list pel;
spot::formula f = parse_formula(input, pel);
if (!f || !pel.empty())
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
return 1;
}
inputf = input;
process_formula(f, filename, linenum);
return 0;
}
int
process_formula(spot::formula f,
const char* filename = nullptr, int linenum = 0)
{
std::unique_ptr<spot::relabeling_map> relmap;
// If atomic propositions are incompatible with one of the
// output, relabel the formula.
if ((!f.has_lbt_atomic_props() &&
(runner.has('l') || runner.has('L') || runner.has('T')))
|| (!f.has_spin_atomic_props() &&
(runner.has('s') || runner.has('S'))))
{
relmap.reset(new spot::relabeling_map);
f = spot::relabel(f, spot::Pnn, relmap.get());
}
static unsigned round = 1;
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)
{
if (relmap)
relabel_here(aut, relmap.get());
aut = post.run(aut, f);
cmdname = translators[t].name;
roundval = round;
printer.print(aut, f, filename, linenum, translation_time,
nullptr);
};
}
spot::cleanup_tmpfiles();
++round;
return 0;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
argp_program_doc, children, nullptr, nullptr };
// Disable post-processing as much as possible by default.
level = spot::postprocessor::Low;
pref = spot::postprocessor::Any;
type = spot::postprocessor::Generic;
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
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 | sbacc);
post.set_type(type);
post.set_level(level);
try
{
processor p(post);
if (p.run())
return 2;
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
return 0;
}

665
bin/ltlfilt.cc Normal file
View file

@ -0,0 +1,665 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 <cstdlib>
#include <string>
#include <iostream>
#include <fstream>
#include <argp.h>
#include <cstring>
#include "error.h"
#include "common_setup.hh"
#include "common_finput.hh"
#include "common_output.hh"
#include "common_cout.hh"
#include "common_conv.hh"
#include "common_r.hh"
#include <spot/misc/hash.hh>
#include <spot/tl/simplify.hh>
#include <spot/tl/length.hh>
#include <spot/tl/relabel.hh>
#include <spot/tl/unabbrev.hh>
#include <spot/tl/remove_x.hh>
#include <spot/tl/apcollect.hh>
#include <spot/tl/exclusive.hh>
#include <spot/tl/print.hh>
#include <spot/twaalgos/ltl2tgba_fm.hh>
#include <spot/twaalgos/minimize.hh>
#include <spot/twaalgos/strength.hh>
#include <spot/twaalgos/stutter.hh>
const char argp_program_doc[] ="\
Read a list of formulas and output them back after some optional processing.\v\
Exit status:\n\
0 if some formulas were output (skipped syntax errors do not count)\n\
1 if no formulas were output (no match)\n\
2 if any error has been reported";
enum {
OPT_AP_N = 256,
OPT_BOOLEAN,
OPT_BOOLEAN_TO_ISOP,
OPT_BSIZE_MAX,
OPT_BSIZE_MIN,
OPT_DEFINE,
OPT_DROP_ERRORS,
OPT_EQUIVALENT_TO,
OPT_EXCLUSIVE_AP,
OPT_EVENTUAL,
OPT_GUARANTEE,
OPT_IGNORE_ERRORS,
OPT_IMPLIED_BY,
OPT_IMPLY,
OPT_LTL,
OPT_NEGATE,
OPT_NNF,
OPT_OBLIGATION,
OPT_RELABEL,
OPT_RELABEL_BOOL,
OPT_REMOVE_WM,
OPT_REMOVE_X,
OPT_SAFETY,
OPT_SIZE_MAX,
OPT_SIZE_MIN,
OPT_SKIP_ERRORS,
OPT_STUTTER_INSENSITIVE,
OPT_SYNTACTIC_GUARANTEE,
OPT_SYNTACTIC_OBLIGATION,
OPT_SYNTACTIC_PERSISTENCE,
OPT_SYNTACTIC_RECURRENCE,
OPT_SYNTACTIC_SAFETY,
OPT_SYNTACTIC_SI,
OPT_UNABBREVIATE,
OPT_UNIVERSAL,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Error handling:", 2 },
{ "skip-errors", OPT_SKIP_ERRORS, nullptr, 0,
"output erroneous lines as-is without processing", 0 },
{ "drop-errors", OPT_DROP_ERRORS, nullptr, 0,
"discard erroneous lines (default)", 0 },
{ "ignore-errors", OPT_IGNORE_ERRORS, nullptr, 0,
"do not report syntax errors", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Transformation options:", 3 },
{ "negate", OPT_NEGATE, nullptr, 0, "negate each formula", 0 },
{ "nnf", OPT_NNF, nullptr, 0,
"rewrite formulas in negative normal form", 0 },
{ "relabel", OPT_RELABEL, "abc|pnn", OPTION_ARG_OPTIONAL,
"relabel all atomic propositions, alphabetically unless " \
"specified otherwise", 0 },
{ "relabel-bool", OPT_RELABEL_BOOL, "abc|pnn", OPTION_ARG_OPTIONAL,
"relabel Boolean subexpressions, alphabetically unless " \
"specified otherwise", 0 },
{ "define", OPT_DEFINE, "FILENAME", OPTION_ARG_OPTIONAL,
"when used with --relabel or --relabel-bool, output the relabeling map "
"using #define statements", 0 },
{ "remove-wm", OPT_REMOVE_WM, nullptr, 0,
"rewrite operators W and M using U and R (this is an alias for "
"--unabbreviate=WM)", 0 },
{ "boolean-to-isop", OPT_BOOLEAN_TO_ISOP, nullptr, 0,
"rewrite Boolean subformulas as irredundant sum of products "
"(implies at least -r1)", 0 },
{ "remove-x", OPT_REMOVE_X, nullptr, 0,
"remove X operators (valid only for stutter-insensitive properties)",
0 },
{ "unabbreviate", OPT_UNABBREVIATE, "STR", OPTION_ARG_OPTIONAL,
"remove all occurrences of the operators specified by STR, which "
"must be a substring of \"eFGiMRW^\", where 'e', 'i', and '^' stand "
"respectively for <->, ->, and xor. If no argument is passed, "
"the subset \"eFGiMW^\" is used.", 0 },
{ "exclusive-ap", OPT_EXCLUSIVE_AP, "AP,AP,...", 0,
"if any of those APs occur in the formula, add a term ensuring "
"two of them may not be true at the same time. Use this option "
"multiple times to declare independent groups of exclusive "
"propositions.", 0 },
DECLARE_OPT_R,
LEVEL_DOC(4),
/**************************************************/
{ nullptr, 0, nullptr, 0,
"Filtering options (matching is done after transformation):", 5 },
{ "ltl", OPT_LTL, nullptr, 0,
"match only LTL formulas (no PSL operator)", 0 },
{ "boolean", OPT_BOOLEAN, nullptr, 0, "match Boolean formulas", 0 },
{ "eventual", OPT_EVENTUAL, nullptr, 0, "match pure eventualities", 0 },
{ "universal", OPT_UNIVERSAL, nullptr, 0,
"match purely universal formulas", 0 },
{ "syntactic-safety", OPT_SYNTACTIC_SAFETY, nullptr, 0,
"match syntactic-safety formulas", 0 },
{ "syntactic-guarantee", OPT_SYNTACTIC_GUARANTEE, nullptr, 0,
"match syntactic-guarantee formulas", 0 },
{ "syntactic-obligation", OPT_SYNTACTIC_OBLIGATION, nullptr, 0,
"match syntactic-obligation formulas", 0 },
{ "syntactic-recurrence", OPT_SYNTACTIC_RECURRENCE, nullptr, 0,
"match syntactic-recurrence formulas", 0 },
{ "syntactic-persistence", OPT_SYNTACTIC_PERSISTENCE, nullptr, 0,
"match syntactic-persistence formulas", 0 },
{ "syntactic-stutter-invariant", OPT_SYNTACTIC_SI, nullptr, 0,
"match stutter-invariant formulas syntactically (LTL-X or siPSL)", 0 },
{ "nox", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
{ "safety", OPT_SAFETY, nullptr, 0,
"match safety formulas (even pathological)", 0 },
{ "guarantee", OPT_GUARANTEE, nullptr, 0,
"match guarantee formulas (even pathological)", 0 },
{ "obligation", OPT_OBLIGATION, nullptr, 0,
"match obligation formulas (even pathological)", 0 },
{ "size-max", OPT_SIZE_MAX, "INT", 0,
"match formulas with size <= INT", 0 },
{ "size-min", OPT_SIZE_MIN, "INT", 0,
"match formulas with size >= INT", 0 },
{ "bsize-max", OPT_BSIZE_MAX, "INT", 0,
"match formulas with Boolean size <= INT", 0 },
{ "bsize-min", OPT_BSIZE_MIN, "INT", 0,
"match formulas with Boolean size >= INT", 0 },
{ "implied-by", OPT_IMPLIED_BY, "FORMULA", 0,
"match formulas implied by FORMULA", 0 },
{ "imply", OPT_IMPLY, "FORMULA", 0,
"match formulas implying FORMULA", 0 },
{ "equivalent-to", OPT_EQUIVALENT_TO, "FORMULA", 0,
"match formulas equivalent to FORMULA", 0 },
{ "stutter-insensitive", OPT_STUTTER_INSENSITIVE, nullptr, 0,
"match stutter-insensitive LTL formulas", 0 },
{ "stutter-invariant", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
{ "ap", OPT_AP_N, "N", 0,
"match formulas which use exactly N atomic propositions", 0 },
{ "invert-match", 'v', nullptr, 0, "select non-matching formulas", 0},
{ "unique", 'u', nullptr, 0,
"drop formulas that have already been output (not affected by -v)", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
{ "count", 'c', nullptr, 0, "print only a count of matched formulas", 0 },
{ "quiet", 'q', nullptr, 0, "suppress all normal output", 0 },
{ "max-count", 'n', "NUM", 0, "output at most NUM formulas", 0 },
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "\
"the following interpreted sequences:", -19 },
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula (in the selected syntax)", 0 },
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the name of the input file", 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the original line number in the input file", 0 },
{ "%<", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the part of the line before the formula if it "
"comes from a column extracted from a CSV file", 0 },
{ "%>", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the part of the line after the formula if it "
"comes from a column extracted from a CSV file", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp_child children[] =
{
{ &finput_argp, 0, nullptr, 1 },
{ &output_argp, 0, nullptr, -20 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
static bool one_match = false;
enum error_style_t { drop_errors, skip_errors };
static error_style_t error_style = drop_errors;
static bool ignore_errors = false;
static bool nnf = false;
static bool negate = false;
static bool boolean_to_isop = false;
static bool unique = false;
static bool psl = false;
static bool ltl = false;
static bool invert = false;
static bool boolean = false;
static bool universal = false;
static bool eventual = false;
static bool syntactic_safety = false;
static bool syntactic_guarantee = false;
static bool syntactic_obligation = false;
static bool syntactic_recurrence = false;
static bool syntactic_persistence = false;
static bool syntactic_si = false;
static bool safety = false;
static bool guarantee = false;
static bool obligation = false;
static int size_min = -1;
static int size_max = -1;
static int bsize_min = -1;
static int bsize_max = -1;
enum relabeling_mode { NoRelabeling = 0, ApRelabeling, BseRelabeling };
static relabeling_mode relabeling = NoRelabeling;
static spot::relabeling_style style = spot::Abc;
static bool remove_x = false;
static bool stutter_insensitive = false;
static bool ap = false;
static unsigned ap_n = 0;
static int opt_max_count = -1;
static long int match_count = 0;
static spot::exclusive_ap excl_ap;
static std::unique_ptr<output_file> output_define = nullptr;
static std::string unabbreviate;
static spot::formula implied_by = nullptr;
static spot::formula imply = nullptr;
static spot::formula equivalent_to = nullptr;
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))
error(2, 0, "parse error when parsing an argument");
return f;
}
static int
parse_opt(int key, char* arg, struct argp_state*)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'c':
output_format = count_output;
break;
case 'n':
opt_max_count = to_pos_int(arg);
break;
case 'q':
output_format = quiet_output;
break;
case OPT_R:
parse_r(arg);
break;
case 'u':
unique = true;
break;
case 'v':
invert = true;
break;
case ARGP_KEY_ARG:
// FIXME: use stat() to distinguish filename from string?
jobs.emplace_back(arg, true);
break;
case OPT_BOOLEAN:
boolean = true;
break;
case OPT_BOOLEAN_TO_ISOP:
boolean_to_isop = true;
break;
case OPT_BSIZE_MIN:
bsize_min = to_int(arg);
break;
case OPT_BSIZE_MAX:
bsize_max = to_int(arg);
break;
case OPT_DEFINE:
output_define.reset(new output_file(arg ? arg : "-"));
break;
case OPT_DROP_ERRORS:
error_style = drop_errors;
break;
case OPT_EVENTUAL:
eventual = true;
break;
case OPT_EQUIVALENT_TO:
{
if (equivalent_to)
error(2, 0, "only one --equivalent-to option can be given");
equivalent_to = parse_formula_arg(arg);
break;
}
case OPT_EXCLUSIVE_AP:
excl_ap.add_group(arg);
break;
case OPT_GUARANTEE:
guarantee = obligation = true;
break;
case OPT_IGNORE_ERRORS:
ignore_errors = true;
break;
case OPT_IMPLIED_BY:
{
spot::formula i = parse_formula_arg(arg);
// a→c∧b→c ≡ (ab)→c
implied_by = spot::formula::Or({implied_by, i});
break;
}
case OPT_IMPLY:
{
// a→b∧a→c ≡ a→(b∧c)
spot::formula i = parse_formula_arg(arg);
imply = spot::formula::And({imply, i});
break;
}
case OPT_LTL:
ltl = true;
break;
case OPT_NEGATE:
negate = true;
break;
case OPT_NNF:
nnf = true;
break;
case OPT_OBLIGATION:
obligation = true;
break;
case OPT_RELABEL:
case OPT_RELABEL_BOOL:
relabeling = (key == OPT_RELABEL_BOOL ? BseRelabeling : ApRelabeling);
if (!arg || !strncasecmp(arg, "abc", 6))
style = spot::Abc;
else if (!strncasecmp(arg, "pnn", 4))
style = spot::Pnn;
else
error(2, 0, "invalid argument for --relabel%s: '%s'",
(key == OPT_RELABEL_BOOL ? "-bool" : ""),
arg);
break;
case OPT_REMOVE_WM:
unabbreviate += "MW";
break;
case OPT_REMOVE_X:
remove_x = true;
break;
case OPT_SAFETY:
safety = obligation = true;
break;
case OPT_SIZE_MIN:
size_min = to_int(arg);
break;
case OPT_SIZE_MAX:
size_max = to_int(arg);
break;
case OPT_SKIP_ERRORS:
error_style = skip_errors;
break;
case OPT_STUTTER_INSENSITIVE:
stutter_insensitive = true;
break;
case OPT_UNABBREVIATE:
if (arg)
unabbreviate += arg;
else
unabbreviate += spot::default_unabbrev_string;
break;
case OPT_AP_N:
ap = true;
ap_n = to_int(arg);
break;
case OPT_SYNTACTIC_SAFETY:
syntactic_safety = true;
break;
case OPT_SYNTACTIC_GUARANTEE:
syntactic_guarantee = true;
break;
case OPT_SYNTACTIC_OBLIGATION:
syntactic_obligation = true;
break;
case OPT_SYNTACTIC_RECURRENCE:
syntactic_recurrence = true;
break;
case OPT_SYNTACTIC_PERSISTENCE:
syntactic_persistence = true;
break;
case OPT_SYNTACTIC_SI:
syntactic_si = true;
break;
case OPT_UNIVERSAL:
universal = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
typedef
std::unordered_set<spot::formula> fset_t;
namespace
{
class ltl_processor: public job_processor
{
public:
spot::tl_simplifier& simpl;
fset_t unique_set;
spot::relabeling_map relmap;
ltl_processor(spot::tl_simplifier& simpl)
: simpl(simpl)
{
}
int
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);
if (!f || pel.size() > 0)
{
if (!ignore_errors)
{
if (filename)
error_at_line(0, 0, filename, linenum, "parse error:");
spot::format_parse_errors(std::cerr, input, pel);
}
if (error_style == skip_errors)
std::cout << input << std::endl;
else
assert(error_style == drop_errors);
check_cout();
return !ignore_errors;
}
try
{
return process_formula(f, filename, linenum);
}
catch (const std::runtime_error& e)
{
error_at_line(2, 0, filename, linenum, "%s", e.what());
SPOT_UNREACHABLE();
}
}
int
process_formula(spot::formula f,
const char* filename = nullptr, int linenum = 0)
{
if (opt_max_count >= 0 && match_count >= opt_max_count)
{
abort_run = true;
return 0;
}
if (negate)
f = spot::formula::Not(f);
if (remove_x)
{
// If simplification are enabled, we do them before and after.
if (simplification_level)
f = simpl.simplify(f);
f = spot::remove_x(f);
}
if (simplification_level || boolean_to_isop)
f = simpl.simplify(f);
if (nnf)
f = simpl.negative_normal_form(f);
switch (relabeling)
{
case ApRelabeling:
{
relmap.clear();
f = spot::relabel(f, style, &relmap);
break;
}
case BseRelabeling:
{
relmap.clear();
f = spot::relabel_bse(f, style, &relmap);
break;
}
case NoRelabeling:
break;
}
if (!unabbreviate.empty())
f = spot::unabbreviate(f, unabbreviate.c_str());
if (!excl_ap.empty())
f = excl_ap.constrain(f);
bool matched = true;
matched &= !ltl || f.is_ltl_formula();
matched &= !psl || f.is_psl_formula();
matched &= !boolean || f.is_boolean();
matched &= !universal || f.is_universal();
matched &= !eventual || f.is_eventual();
matched &= !syntactic_safety || f.is_syntactic_safety();
matched &= !syntactic_guarantee || f.is_syntactic_guarantee();
matched &= !syntactic_obligation || f.is_syntactic_obligation();
matched &= !syntactic_recurrence || f.is_syntactic_recurrence();
matched &= !syntactic_persistence || f.is_syntactic_persistence();
matched &= !syntactic_si || f.is_syntactic_stutter_invariant();
matched &= !ap || atomic_prop_collect(f)->size() == ap_n;
if (matched && (size_min > 0 || size_max >= 0))
{
int l = spot::length(f);
matched &= (size_min <= 0) || (l >= size_min);
matched &= (size_max < 0) || (l <= size_max);
}
if (matched && (bsize_min > 0 || bsize_max >= 0))
{
int l = spot::length_boolone(f);
matched &= (bsize_min <= 0) || (l >= bsize_min);
matched &= (bsize_max < 0) || (l <= bsize_max);
}
matched &= !implied_by || simpl.implication(implied_by, f);
matched &= !imply || simpl.implication(f, imply);
matched &= !equivalent_to || simpl.are_equivalent(f, equivalent_to);
matched &= !stutter_insensitive || spot::is_stutter_invariant(f);
// Match obligations and subclasses using WDBA minimization.
// Because this is costly, we compute it later, so that we don't
// have to compute it on formulas that have been discarded for
// other reasons.
if (matched && obligation)
{
auto aut = ltl_to_tgba_fm(f, simpl.get_dict());
auto min = minimize_obligation(aut, f);
assert(min);
if (aut == min)
{
// Not an obligation
matched = false;
}
else
{
matched &= !guarantee || is_terminal_automaton(min);
matched &= !safety || is_safety_mwdba(min);
}
}
matched ^= invert;
if (unique && !unique_set.insert(f).second)
matched = false;
if (matched)
{
if (output_define
&& output_format != count_output
&& output_format != quiet_output)
{
// Sort the formulas alphabetically.
std::map<std::string, spot::formula> m;
for (auto& p: relmap)
m.emplace(str_psl(p.first), p.second);
for (auto& p: m)
stream_formula(output_define->ostream()
<< "#define " << p.first << " (",
p.second, filename, linenum) << ")\n";
}
one_match = true;
output_formula_checked(f, filename, linenum, prefix, suffix);
++match_count;
}
return 0;
}
};
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]",
argp_program_doc, children, nullptr, nullptr };
try
{
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
if (jobs.empty())
jobs.emplace_back("-", 1);
if (boolean_to_isop && simplification_level == 0)
simplification_level = 1;
spot::tl_simplifier_options opt(simplification_level);
opt.boolean_to_isop = boolean_to_isop;
spot::tl_simplifier simpl(opt);
ltl_processor processor(simpl);
if (processor.run())
return 2;
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
catch (const std::invalid_argument& e)
{
error(2, 0, "%s", e.what());
}
if (output_format == count_output)
std::cout << match_count << std::endl;
return one_match ? 0 : 1;
}

199
bin/ltlgrind.cc Normal file
View file

@ -0,0 +1,199 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 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 <argp.h>
#include "error.h"
#include "common_setup.hh"
#include "common_finput.hh"
#include "common_output.hh"
#include "common_conv.hh"
#include <spot/tl/mutation.hh>
enum {
OPT_AP2CONST = 1,
OPT_SIMPLIFY_BOUNDS,
OPT_REMOVE_MULTOP_OPERANDS,
OPT_REMOVE_OPS,
OPT_SPLIT_OPS,
OPT_REWRITE_OPS,
OPT_REMOVE_ONE_AP,
OPT_SORT,
};
static unsigned mutation_nb = 1;
static unsigned max_output = -1U;
static unsigned opt_all = spot::Mut_All;
static unsigned mut_opts = 0;
static bool opt_sort = false;
static const char * argp_program_doc =
"List formulas that are similar to but simpler than a given formula.";
static const argp_option options[] = {
{ nullptr, 0, nullptr, 0,
"Mutation rules (all enabled unless those options are used):", 15},
{ "ap-to-const", OPT_AP2CONST, nullptr, 0,
"atomic propositions are replaced with true/false", 15 },
{ "remove-one-ap", OPT_REMOVE_ONE_AP, nullptr, 0,
"all occurrences of an atomic proposition are replaced with another " \
"atomic proposition used in the formula", 15 },
{ "remove-multop-operands", OPT_REMOVE_MULTOP_OPERANDS, nullptr, 0,
"remove one operand from multops", 15 },
{ "remove-ops", OPT_REMOVE_OPS, nullptr, 0,
"replace unary/binary operators with one of their operands", 15 },
{ "split-ops", OPT_SPLIT_OPS, nullptr, 0,
"when an operator can be expressed as a conjunction/disjunction using "
"simpler operators, each term of the conjunction/disjunction is a "
"mutation. e.g. a <-> b can be written as ((a & b) | (!a & !b)) or as "
"((a -> b) & (b -> a)) so those four terms can be a mutation of a <-> b",
0 },
{ "rewrite-ops", OPT_REWRITE_OPS, nullptr, 0,
"rewrite operators that have a semantically simpler form: a U b becomes "
"a W b, etc.", 0 },
{ "simplify-bounds", OPT_SIMPLIFY_BOUNDS, nullptr, 0,
"on a bounded unary operator, decrement one of the bounds, or set min to "
"0 or max to unbounded", 15 },
{ nullptr, 0, nullptr, 0, "Output options:", 20 },
{ "max-count", 'n', "NUM", 0, "maximum number of mutations to output", 20 },
{ "mutations", 'm', "NUM", 0, "number of mutations to apply to the "
"formulae (default: 1)", 0 },
{ "sort", OPT_SORT, nullptr, 0, "sort the result by formula size", 0 },
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
"the following interpreted sequences:", 21 },
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula (in the selected syntax)", 0 },
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the name of the input file", 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the original line number in the input file", 0 },
{ "%<", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the part of the line before the formula if it "
"comes from a column extracted from a CSV file", 0 },
{ "%>", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the part of the line after the formula if it "
"comes from a column extracted from a CSV file", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{nullptr, 0, nullptr, 0, "Miscellaneous options:", -1},
{nullptr, 0, nullptr, 0, nullptr, 0}
};
static const argp_child children[] = {
{ &finput_argp, 0, nullptr, 10 },
{ &output_argp, 0, nullptr, 20 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
namespace
{
class mutate_processor: public job_processor
{
public:
int
process_formula(spot::formula f, const char* filename = nullptr,
int linenum = 0)
{
auto mutations =
spot::mutate(f, mut_opts, max_output, mutation_nb, opt_sort);
for (auto g: mutations)
output_formula_checked(g, filename, linenum, prefix, suffix);
return 0;
}
};
}
static int
parse_opt(int key, char* arg, struct argp_state*)
{
switch (key)
{
case 'm':
mutation_nb = to_unsigned(arg);
break;
case 'n':
max_output = to_int(arg);
break;
case ARGP_KEY_ARG:
// FIXME: use stat() to distinguish filename from string?
jobs.emplace_back(arg, true);
break;
case OPT_AP2CONST:
opt_all = 0;
mut_opts |= spot::Mut_Ap2Const;
break;
case OPT_REMOVE_ONE_AP:
opt_all = 0;
mut_opts |= spot::Mut_Remove_One_Ap;
break;
case OPT_REMOVE_MULTOP_OPERANDS:
opt_all = 0;
mut_opts |= spot::Mut_Remove_Multop_Operands;
break;
case OPT_REMOVE_OPS:
opt_all = 0;
mut_opts |= spot::Mut_Remove_Ops;
break;
case OPT_SPLIT_OPS:
opt_all = 0;
mut_opts |= spot::Mut_Split_Ops;
break;
case OPT_REWRITE_OPS:
opt_all = 0;
mut_opts |= spot::Mut_Rewrite_Ops;
break;
case OPT_SIMPLIFY_BOUNDS:
opt_all = 0;
mut_opts |= spot::Mut_Simplify_Bounds;
break;
case OPT_SORT:
opt_sort = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int
main(int argc, char* argv[])
{
setup(argv);
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]", argp_program_doc,
children, nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
mut_opts |= opt_all;
if (jobs.empty())
jobs.push_back(job("-", true));
mutate_processor processor;
if (processor.run())
return 2;
return 0;
}

79
bin/man/Makefile.am Normal file
View file

@ -0,0 +1,79 @@
## -*- coding: utf-8 -*-
## 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.
##
## 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/>.
common_dep = $(top_srcdir)/configure.ac
x_to_1 = $(top_builddir)/tools/x-to-1
convman = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
"$(PERL)" "$(top_srcdir)/tools/help2man -N -L 'en_US.UTF-8'"
convman7 = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N -L 'en_US.UTF-8'"
dist_man1_MANS = \
autfilt.1 \
dstar2tgba.1 \
genltl.1 \
ltl2tgba.1 \
ltl2tgta.1 \
ltlcross.1 \
ltldo.1 \
ltlfilt.1 \
ltlgrind.1 \
randaut.1 \
randltl.1
dist_man7_MANS = \
spot-x.7
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
dstar2tgba.1: $(common_dep) $(srcdir)/dstar2tgba.x $(srcdir)/../dstar2tgba.cc
$(convman) ../dstar2tgba$(EXEEXT) $(srcdir)/dstar2tgba.x $@
ltl2tgba.1: $(common_dep) $(srcdir)/ltl2tgba.x $(srcdir)/../ltl2tgba.cc
$(convman) ../ltl2tgba$(EXEEXT) $(srcdir)/ltl2tgba.x $@
ltl2tgta.1: $(common_dep) $(srcdir)/ltl2tgta.x $(srcdir)/../ltl2tgta.cc
$(convman) ../ltl2tgta$(EXEEXT) $(srcdir)/ltl2tgta.x $@
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 $@
genltl.1: $(common_dep) $(srcdir)/genltl.x $(srcdir)/../genltl.cc
$(convman) ../genltl$(EXEEXT) $(srcdir)/genltl.x $@
randltl.1: $(common_dep) $(srcdir)/randltl.x $(srcdir)/../randltl.cc
$(convman) ../randltl$(EXEEXT) $(srcdir)/randltl.x $@
randaut.1: $(common_dep) $(srcdir)/randaut.x $(srcdir)/../randaut.cc
$(convman) ../randaut$(EXEEXT) $(srcdir)/randaut.x $@
spot-x.7: $(common_dep) $(srcdir)/spot-x.x $(srcdir)/../spot-x.cc
$(convman7) ../spot-x$(EXEEXT) $(srcdir)/spot-x.x $@
ltlgrind.1: $(common_dep) $(srcdir)/ltlgrind.x $(srcdir)/../ltlgrind.cc
$(convman) ../ltlgrind$(EXEEXT) $(srcdir)/ltlgrind.x $@

44
bin/man/autfilt.x Normal file
View file

@ -0,0 +1,44 @@
.\" -*- coding: utf-8 -*-
[NAME]
autfilt \- filter, convert, and transform omega-automata
[DESCRIPTION]
.\" Add any additional description here
[BIBLIOGRAPHY]
The following papers are related to some of the transformations implemented
in autfilt.
.TP
\(bu
Etienne Renault, Alexandre Duret-Lutz, Fabrice Kordon, and Denis Poitrenaud:
Strength-based decomposition of the property Büchi automaton for faster
model checking. Proceedings of TACAS'13. LNCS 7795.
The \fB\-\-strength\-decompose\fR option implements the definitions
given in the above paper.
.TP
\(bu
František Blahoudek, Alexandre Duret-Lutz, Vojtčech Rujbr, and Jan Strejček:
On refinement of Büchi automata for explicit model checking.
Proceedings of SPIN'15. LNCS 9232.
That paper gives the motivation for options \fB\-\-exclusive\-ap\fR
and \fB\-\-simplify\-exclusive\-ap\fR.
.TP
\(bu
Thibaud Michaud and Alexandre Duret-Lutz:
Practical stutter-invariance checks for ω-regular languages.
Proceedings of SPIN'15. LNCS 9232.
Describes the algorithms used by the \fB\-\-destut\fR and
\fB\-\-instut\fR options. These options correpond respectively to
cl() and sl() in the paper.
.TP
\(bu
Souheib Baarir and Alexandre Duret-Lutz: SAT-based minimization of
deterministic ω-automata. Proceedings of LPAR'15 (a.k.a LPAR-20).
LNCS 9450.
Describes the \fB\-\-sat\-minimize\fR option.
[SEE ALSO]
.BR spot-x (7)
.BR dstar2tgba (1)

107
bin/man/dstar2tgba.x Normal file
View file

@ -0,0 +1,107 @@
[NAME]
dstar2tgba \- convert automata into Büchi automata
[HISTORY]
.B dstar2tgba
was introduced in Spot 1.2 as a command that reads automata
in
.BR ltl2dstar 's
format, and converts them into TGBA. At this time it was
the only command-line tool being able to read automata.
.PP
In Spot 1.99.1 the
.B autfilt
command was introduced, but could only read automata
in the HOA format, or in
.BR lbtt 's
format, or as never claims. So
.B dstar2tgba
was still the only way to process automata
in
.BR ltl2dstar 's
format.
.PP
In Spot 1.99.4 the parser for
.BR ltl2dstar 's
format was finally merged with the parser
used by
.B autfilt
for reading the other format. This implies not only
that
.B autfilt
can now read
.BR ltl2dstar's
format, but also that
.B dstar2tgba
can read the other formats as well.
Nowadays, the command
.PP
.in +4n
.nf
.ft C
% dstar2tgba some files
.fi
.PP
can be used as a shorthand for
.PP
.in +4n
.nf
.ft C
% autfilt \-\-tgba \-\-high \-\-small some files
.fi
.PP
The name
.B dstar2tgba
is kept for backward compatibility and because it is used
in at least one published paper, but naming this tool
.B aut2tgba
would make more sense.
[BIBLIOGRAPHY]
.TP
1.
<http://www.ltl2dstar.de/docs/ltl2dstar.html>
Documents the output format of ltl2dstar.
.TP
2.
Chistof Löding: Mehods for the Transformation of ω-Automata:
Complexity and Connection to Second Order Logic. Diploma Thesis.
University of Kiel. 1998.
Describes various tranformations from non-deterministic Rabin and
Streett automata to Büchi automata. Slightly optimized variants of
these transformations are used by dstar2tgba for the general cases.
.TP
3.
Sriram C. Krishnan, Anuj Puri, and Robert K. Brayton: Deterministic
ω-automata vis-a-vis Deterministic Büchi Automata. ISAAC'94.
Explains how to preserve the determinism of Rabin and Streett automata
when the property can be repreted by a Deterministic automaton.
dstar2tgba implements this for the Rabin case only. In other words,
translating a deterministic Rabin automaton with dstar2tgba will
produce a deterministic TGBA or BA if such a automaton exists.
.TP
4.
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
LNCS 8461.
Explains the SAT-based minimization techniques that can be used (on
request only) by dstar2tgba to minimize deterministic Büchi automata.
.TP
5.
Souheib Baarir and Alexandre Duret-Lutz: SAT-based minimization of
deterministic ω-automata. Proceedings of LPAR'15 (a.k.a LPAR-20).
LNCS 9450.
Extends the previous paper by allowing arbitrary acceptance
conditions.
[SEE ALSO]
.BR spot-x (7),
.BR autfilt (1)

31
bin/man/genltl.x Normal file
View file

@ -0,0 +1,31 @@
[NAME]
genltl \- generate LTL formulas from scalable patterns
[DESCRIPTION]
.\" Add any additional description here
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite the following paper:
.TP
\(bu
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
.PP
Prefixes used in pattern names refer to the following papers:
.TP
gh
J. Geldenhuys and H. Hansen: Larger automata and less
work for LTL model checking. Proceedings of Spin'06. LNCS 3925.
.TP
ccj
J. Cichoń, A. Czubak, and A. Jasiński (DepCoS'09): Minimal Büchi
Automata for Certain Classes of LTL Formulas. Proceedings of DepCoS'09.
.TP
go
P. Gastin and D. Oddoux: Fast LTL to Büchi Automata Translation.
Proceedings of CAV'01. LNCS 2102.
.TP
rv
K. Rozier and M. Vardi: LTL Satisfiability Checking.
Proceedings of Spin'07. LNCS 4595.
[SEE ALSO]
.BR randltl (1)

202
bin/man/ltl2tgba.x Normal file
View file

@ -0,0 +1,202 @@
.\" -*- coding: utf-8 -*-
[NAME]
ltl2tgba \- translate LTL/PSL formulas into Büchi automata
[NOTE ON TGBA]
TGBA stands for Transition-based Generalized Büchi Automaton. The
name was coined by Dimitra Giannakopoulou and Flavio Lerda in their
FORTE'02 paper (From States to Transitions: Improving Translation of
LTL Formulae to Büchi Automata), although similar automata have been
used under different names long before that.
.PP
As its name implies a TGBA uses a generalized Büchi acceptance
condition, meanings that a run of the automaton is accepted iff it
visits ininitely often multiple acceptance sets, and it also uses
transition-based acceptance, i.e., those acceptance sets are sets of
transitions. TGBA are often more consise than traditional Büchi
automata. For instance the LTL formula \f(CWGFa & GFb\fR can be
translated into a single-state TGBA while a traditional Büchi
automaton would need 3 states. Compare
.PP
.in +4n
.nf
.ft C
% ltl2tgba 'GFa & GFb'
.fi
.PP
with
.PP
.in +4n
.ft C
.nf
% ltl2tgba --ba 'GFa & GFb'
.fi
.PP
In the dot output produced by the above commands, the membership of
the transitions to the various acceptance sets is denoted using names
in braces. The actuall names do not really matter as they may be
produced by the translation algorithm or altered by any latter
postprocessing.
.PP
When the \fB\-\-ba\fR option is used to request a Büchi automaton, Spot
builds a TGBA with a single acceptance set, and in which for any state
either all outgoing transitions are accepting (this is equivalent to
the state being accepting) or none of them are. Double circles are
used to highlight accepting states in the output, but the braces
denoting the accepting transitions are still shown because the
underling structure really is a TGBA.
[NOTE ON LBTT'S FORMAT]
.UR http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html
LBTT's format
.UE
has support for both transition-based and state based generalized acceptance.
.PP
Because Spot uses transition-based generalized Büchi automata
internally, it will normally use the transition-based flavor of that
format, indicated with a 't' flag after the number of acceptance sets.
For instance:
.PP
.in +4n
.ft C
.nf
% ltl2tgba --lbtt 'GFp0 & GFp1 & FGp2'
2 2t // 2 states, 2 transition-based acceptance sets
0 1 // state 0: initial
0 -1 t // trans. to state 0, no acc., label: true
1 -1 | & p0 p2 & p1 p2 // trans. to state 1, no acc., label: (p0&p2)|(p1&p2)
-1 // end of state 0
1 0 // state 1: not initial
1 0 1 -1 & & p0 p1 p2 // trans. to state 1, acc. 0 and 1, label: p0&p1&p2
1 0 -1 & & p1 p2 ! p0 // trans. to state 1, acc. 0, label: !p0&p1&p2
1 1 -1 & & p0 p2 ! p1 // trans. to state 1, acc. 1, label: p0&!p1&p2
1 -1 & & p2 ! p0 ! p1 // trans. to state 1, no acc., label: !p0&!p1&p2
-1 // end if state 1
.fi
.PP
Here, the two acceptance sets are represented by the numbers 0 and 1,
and they each contain two transitions (the first transition of state 1
belongs to both sets).
.PP
When both \fB\-\-ba\fR and \fB\-\-lbtt\fR options are used,
the state-based flavor of
the format is used instead. Note that the LBTT format supports
generalized acceptance conditions on states, but Spot only use this
format for Büchi automata, where there is always only one acceptance
set. Unlike in the LBTT documentation, we do not use the
optional '\fBs\fR' flag to indicate the state-based acceptance, this way our
output is also compatible with that of
.UR http://www.tcs.hut.fi/Software/maria/tools/lbt/
LBT
.UE .
.PP
.in +4n
.ft C
.nf
% ltl2tgba --ba --lbtt FGp0
2 1 // 2 states, 1 (state-based) accepance set
0 1 -1 // state 0: initial, non-accepting
0 t // trans. to state 0, label: true
1 p0 // trans. to state 1, label: p0
-1 // end of state 0
1 0 0 -1 // state 1: not initial, in acceptance set 0
1 p0 // trans. to state 0, label: p0
-1 // end if state 1
.fi
.PP
You can force ltl2tgba to use the transition-based flavor of the
format even for Büchi automaton using \fB\-\-lbtt=t\fR.
.PP
.in +4n
.ft C
.nf
% ltl2tgba --ba --lbtt=t FGp0
2 1t // 2 states, 1 transition-based accepance set.
0 1 // state 0: initial
0 -1 t // trans. to state 0, no acc., label: true
1 -1 p0 // trans. to state 1, no acc., label: p0
-1 // end of state 0
1 0 // state 1: not initial
1 0 -1 p0 // trans. to state 1, acc. 0, label: p0
-1 // end if state 1
.fi
.PP
When representing a Büchi automaton using transition-based acceptance,
all transitions leaving accepting states are put into the acceptance set.
.PP
A final note concerns the name of the atomic propositions. The
original LBTT and LBT formats require these atomic propositions to
have names such as '\fBp0\fR', '\fBp32\fR', ... We extend the format to accept
atomic proposition with arbitrary names that do not conflict with
LBT's operators (e.g. '\fBi\fR' is the symbol of the implication operator so
it may not be used as an atomic proposition), or as double-quoted
strings. Spot will always output atomic-proposition that do not match
\fBp[0-9]+\fR as double-quoted strings.
.PP
.in +4n
.ft C
.nf
% ltl2tgba --lbtt 'GFa & GFb'
1 2t
0 1
0 0 1 -1 & "a" "b"
0 0 -1 & "b" ! "a"
0 1 -1 & "a" ! "b"
0 -1 & ! "b" ! "a"
-1
.fi
[NOTE ON GENERATING MONITORS]
The monitors generated with option \fB\-M\fR are finite state automata
used to reject finite words that cannot be extended to infinite words
compatible with the supplied formula. The idea is that the monitor
should progress alongside the system, and can only make decisions
based on the finite prefix read so far.
.PP
Monitors can be seen as Büchi automata in which all recognized runs are
accepting. As such, the only infinite words they can reject are those
are not recognized, i.e., infinite words that start with a bad prefix.
.PP
Because of this limited expressiveness, a monitor for some given LTL
or PSL formula may accept a larger language than the one specified by
the formula. For instance a monitor for the LTL formula \f(CWa U b\fR
will reject (for instance) any word starting with \f(CW!a&!b\fR as
there is no way such a word can validate the formula, but it will not
reject a finite prefix repeating only \f(CWa&!b\fR as such a prefix
could be extented in a way that is comptible with \f(CWa U b\fR.
.PP
For more information about monitors, we refer the readers to the
following two papers (the first paper describes the construction of
the second paper in a more concise way):
.TP
\(bu
Deian Tabakov and Moshe Y. Vardi: Optimized Temporal Monitors for SystemC.
Proceedings of RV'10. LNCS 6418.
.TP
\(bu
Marcelo dAmorim and Grigoire Roşu: Efficient monitoring of
ω-languages. Proceedings of CAV'05. LNCS 3576.
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite one of the following papers:
.TP
\(bu
Alexandre Duret-Lutz: LTL translation improvements in Spot 1.0.
Int. J. on Critical Computer-Based Systems, 5(1/2):31--54, March 2014.
.TP
\(bu
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
.TP
\(bu
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
and Jan Strejček: Compositional approach to suspension and other
improvements to LTL translation. Proceedings of SPIN'13. LNCS 7976.
.TP
\(bu
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
LNCS 8461.
[SEE ALSO]
.BR spot-x (7)

14
bin/man/ltl2tgta.x Normal file
View file

@ -0,0 +1,14 @@
[NAME]
ltl2tgta \- translate LTL/PSL formulas into Testing Automata
[DESCRIPTION]
.\" Add any additional description here
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite the following paper:
.TP
\(bu
Ala Eddine Ben Salem, Alexandre Duret-Lutz, and Fabrice Kordon: Model
checking using generalized testing automata. Transactions on Petri
Nets and Other Models of Concurrency (ToPNoC VI), 7400:94-112, 2012.
[SEE ALSO]
.BR spot-x (7)

270
bin/man/ltlcross.x Normal file
View file

@ -0,0 +1,270 @@
.\" -*- coding: utf-8 -*-
[NAME]
ltlcross \- cross-compare LTL/PSL translators to Büchi automata
[EXAMPLES]
The following commands compare never claims produced by
.BR ltl2tgba (1),
.BR spin (1),
and
.B lbt
for 100 random formulas, using a timeout of 2 minutes. Because
.B ltlcross
knows those tools, there is no need to specify their input and
output. A trace of the execution of the two tools, including any
potential issue detected, is reported on standard error, while
statistics are written to \f(CWresults.json\fR.
.PP
.in +4n
.nf
.ft C
% randltl \-n100 \-\-tree\-size=20..30 a b c | \e
ltlcross \-T120 ltl2tgba spin lbt \-\-json=results.json
.fi
.PP
The next command compares
.BR lbt ,
.BR ltl3ba ,
and
.BR ltl2tgba (1)
on a set of formulas saved in file \f(CWinput.ltl\fR.
Statistics are again writen
as CSV into \f(CWresults.csv\fR. This examples specify the
input and output for each tool, to show how this can be done.
Note the use of \f(CW%L\fR to indicate that the formula passed t
for the formula in
.BR spin (1)'s
format, and \f(CW%f\fR for the
formula in Spot's format. Each of these tool produces an
automaton in a different format (respectively, LBTT's format,
Spin's never claims, and HOA format), but Spot's parser can
distinguish and understand these three formats.
.PP
.in +4n
.nf
.ft C
% ltlcross \-F input.ltl \-\-csv=results.csv \e
'lbt <%L >%O' \e
'ltl3ba \-f %s >%O' \e
'ltl2tgba \-H %f >%O'
.fi
.PP
Rabin or Streett automata output by
.B ltl2dstar
in its historical format can be read from a
file specified with \f(CW%D\fR instead of \f(CW%O\fR. For instance:
.PP
.in +4n
.nf
.ft C
% ltlcross \-F input.ltl \e
'ltl2dstar \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
'ltl2dstar \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
.fi
.PP
However, we now recommand to use the HOA output of
.BR ltl2dstar ,
as supported since version 0.5.2:
.PP
.in +4n
.nf
.ft C
% ltlcross \-F input.ltl \e
'ltl2dstar \-\-output\-format=hoa \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
'ltl2dstar \-\-output\-format=hoa \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
.fi
.PP
In more recent versions of ltl2dstar, \fB\-\-output\-format=hoa\fR can
be abbreviated \fB-H\fR.
[ENVIRONMENT VARIABLES]
.TP
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
These variables control in which directory temporary files (e.g.,
those who contain the input and output when interfacing with
translators) are created. \fBTMPDIR\fR is only read if
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
variables exist, or if their value is empty, files are created in the
current directory.
.TP
\fBSPOT_TMPKEEP\fR
When this variable is defined, temporary files are not removed.
This is mostly useful for debugging.
[OUTPUT DATA]
The following columns are output in the CSV or JSON files.
.TP
\fBformula\fR
The formula translated.
.TP
\fBtool\fR
The tool used to translate this formula. This is either the value of the
full \fICOMMANDFMT\fR string specified on the command-line, or,
if \fICOMMANDFMT\fR has the form \f(CW{\fISHORTNAME\fR\f(CW}\fR\fICMD\fR,
the value of \fISHORTNAME\fR.
.TP
\fBexit_status\fR, \fBexit_code\fR
Information about how the execution of the translator went. If the
option \fB\-\-omit\-missing\fR is given, these two columns are omitted
and only the lines corresponding to successful translation are output.
Otherwise, \fBexit_status\fR is a string that can take the following
values:
.RS
.TP
\f(CW"ok"\fR
The translator ran succesfully (this does not imply that the produced
automaton is correct) and ltlcross could parse the resulting
automaton. In this case \fBexit_code\fR is always 0.
.TP
\f(CW"timeout"\fR
The translator ran for more than the number of seconds
specified with the \fB\-\-timeout\fR option. In this
case \fBexit_code\fR is always -1.
.TP
\f(CW"exit code"\fR
The translator terminated with a non-zero exit code.
\fBexit_code\fR contains that value.
.TP
\f(CW"signal"\fR
The translator terminated with a signal.
\fBexit_code\fR contains that signal's number.
.TP
\f(CW"parse error"\fR
The translator terminated normally, but ltlcross could not
parse its output. In this case \fBexit_code\fR is always -1.
.TP
\f(CW"no output"\fR
The translator terminated normally, but without creating the specified
output file. In this case \fBexit_code\fR is always -1.
.RE
.TP
\fBtime\fR
A floating point number giving the run time of the translator in seconds.
This is reported for all executions, even failling ones.
.PP
Unless the \fB\-\-omit\-missing\fR option is used, data for all the
following columns might be missing.
.TP
\fBstates\fR, \fBedges\fR, \fBtransitions\fR, \fBacc\fR
The number of states, edges, transitions, and acceptance sets in the
translated automaton. Column \fBedges\fR counts the number of edges
(labeled by Boolean formulas) in the automaton seen as a graph, while
\fBtransitions\fR counts the number of assignment-labeled transitions
that might have been merged into a formula-labeled edge. For instance
an edge labeled by \f(CWtrue\fR will be counted as 2^3=8 transitions if
the automaton mention 3 atomic propositions.
.PP
If the translator produced a Streett or Rabin automaton, these columns
contains the size of a TGBA (or BA) produced by ltlcross from that
Streett or Rabin automaton. Check \fBin_states\fR, \fBin_edges\fR,
\fBin_transitions\fR, and \fBin_acc\fR for statistics about the actual
input automaton.
.TP
\fBscc\fR, \fBnonacc_scc\fR, \fBterminal_scc\fR, \fBweak_scc\fR, \fBstrong_scc\fR
The number of strongly connected components in the automaton. The
\fBscc\fR column gives the total number, while the other columns only
count the SCCs that are non-accepting (a.k.a. transiant), terminal
(recognizes and accepts all words), weak (do not recognize all words,
but accepts all recognized words), or strong (accept some words, but
reject some recognized words).
.TP
\fBnondet_states\fR, \fBnondet_aut\fR
The number of nondeterministic states, and a Boolean indicating whether the
automaton is nondeterministic.
.TP
\fBterminal_aut\fR, \fBweak_aut\fR, \fBstrong_aut\fR
Three Boolean used to indicate whether the automaton is terminal (no
weak nor strong SCCs), weak (some weak SCCs but no strong SCCs), or strong
(some strong SCCs).
.TP
\fBproduct_states\fR, \fBproduct_transitions\fR, \fBproduct_scc\fR
Size of the product between the translated automaton and a randomly
generated state-space. For a given formula, the same state-space is
of course used the result of each translator. When the
\fB\-\-products\fR=\fIN\fR option is used, these values are averaged
over the \fIN\fR products performed.
[DEPRECATED OUTPUT SPECIFIERS]
Until version 1.2.6, the output of translators was specifed using the
following escape sequences.
.PP
.TP
%N
An output file containing a never claim.
.TP
%T
An output file in \fBlbtt\fR's format.
.TP
%D
An output file in \fBltl2dstar\fR's format.
.PP
Some development versions leading to 1.99.1 also featured
.PP
.TP
%H
An output file in the HOA format.
.PP
Different specifiers were needed because Spot implemented
different parsers for these formats. Nowadays, these parsers
have been merged into a single parser that is able to
distinguish these four formats automatically.
.B ltlcross
officially supports only one output specifier:
.TP
%O
An output file in either \fBlbtt\fR's format, \fBltl2dstar\fR's format,
as a never claim, or in the HOA format
.P
For backward compatibility, the sequences %D, %H, %N, and %T, are
still supported (as aliases for %O), but are deprecated.
[SEE ALSO]
.BR randltl (1),
.BR genltl (1),
.BR ltlfilt (1),
.BR ltl2tgba (1),
.BR ltldo (1)
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite the following paper:
.PP
.TP
\(bu
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
.PP
.B ltlcross
is a Spot-based reimplementation of a tool called LBTT. LBTT
was developped by Heikki Tauriainen at the Helsinki University of
Technology. The main motivation for the reimplementation was to
support PSL, and output more statistics about the translations.
.PP
The sanity checks performed on the result of each translator (by
either LBTT or ltlcross) are described in the following paper:
.PP
.TP
\(bu
H. Tauriainen and K. Heljanko: Testing LTL formula translation into
Büchi automata. Int. J. on Software Tools for Technology Transfer.
Volume 4, number 1, October 2002.
.PP
LBTT did not implement Test 2 described in this paper. ltlcross
implements a slight variation: when an automaton produced by some
translator is deterministic, its complement is built and used for
additional cross-comparisons with other tools. If the translation P1
of the positive formula and the translation N1 of the negative formula
both yield deterministic automata (this may only happen for obligation
properties) then the emptiness check of Comp(P1)*Comp(N1) is
equivalent to Test 2 of Tauriainen and Heljanko. If only one
automaton is deterministic, say P1, it can still be used to check we
can be used to check the result of another translators, for instance
checking the emptiness of Comp(P1)*P2.
.PP
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.
.PP
Another major difference with LBTT is that ltlcross is
not restricted to generalized Büchi acceptance. It supports
Rabin and Streett automata via ltl2dstar's format, and automata
with arbitrary acceptance conditions via the HOA format.

8
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)

55
bin/man/ltlfilt.x Normal file
View file

@ -0,0 +1,55 @@
[NAME]
ltlfilt \- filter files or lists of LTL/PSL formulas
[DESCRIPTION]
.\" Add any additional description here
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite the following paper:
.TP
\(bu
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
.PP
The following papers describe algorithms used by ltlfilt:
.TP
\(bu
Kousha Etessami: A note on a question of Peled and Wilke regarding
stutter-invariant LTL. Information Processing Letters 75(6): 261-263
(2000).
Describes the transformation behind the \fB\-\-remove\-x\fR option.
.TP
\(bu
Thibaud Michaud and Alexandre Duret-Lutz:
Practical stutter-invariance checks for ω-regular languages.
Proceedings of SPIN'15. LNCS 9232.
Describes the algorithm used by \fB\-\-stutter\-insensitive\fR option.
.TP
\(bu
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
Powerset Construction for Restricted Classes of
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
Describes the checks implemented by the \fB\-\-safety\fR,
\fB\-\-guarantee\fR, and \fB\-\-obligation\fR options.
.TP
\(bu
Ivana Černá, Radek Pelánek: Relating Hierarchy of Temporal Properties
to Model Checking. Proceedings of MFCS'03. LNCS 2747.
Describes the syntactic LTL classes matched by the
\fB\-\-syntactic\-safety\fR, \fB\-\-syntactic\-guarantee\fR,
\fB\-\-syntactic\-obligation\fR options,
\fB\-\-syntactic\-persistence\fR, and \fB\-\-syntactic\-recurrence\fR
options.
.TP
\(bu
Kousha Etessami, Gerard J. Holzmann: Optimizing Büchi
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 ltldo (1)

6
bin/man/ltlgrind.x Normal file
View file

@ -0,0 +1,6 @@
[NAME]
ltlgrind \- list mutations of a formula.
[DESCRIPTION]
.\" Add any additional description here
[SEE ALSO]
.BR ltlcross (1)

7
bin/man/randaut.x Normal file
View file

@ -0,0 +1,7 @@
[NAME]
randaut \- generate random automata
[DESCRIPTION]
.\" Add any additional description here
[SEE ALSO]
.BR randltl (1),
.BR autfilt (1)

15
bin/man/randltl.x Normal file
View file

@ -0,0 +1,15 @@
[NAME]
randltl \- generate random LTL/PSL formulas
[DESCRIPTION]
.\" Add any additional description here
[BIBLIOGRAPHY]
If you would like to give a reference to this tool in an article,
we suggest you cite the following paper:
.TP
\(bu
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
Proceedings of ATVA'13. LNCS 8172.
[SEE ALSO]
.BR genltl (1),
.BR ltlfilt (1),
.BR randaut (1)

123
bin/man/spot-x.x Normal file
View file

@ -0,0 +1,123 @@
[NAME]
spot-x \- Common fine-tuning options.
[SYNOPSIS]
.B \-\-extra-options STRING
.br
.B \-x STRING
[DESCRIPTION]
.\" Add any additional description here
[ENVIRONMENT VARIABLES]
.TP
\fBSPOT_DOTDEFAULT\fR
Whenever the \f(CW--dot\fR option is used without argument (even
implicitely), the contents of this variable is used as default
argument. If you have some default setting in \fBSPOT_DOTDEFAULT\fR
but want to alter them temporarily for one call, use
\f(CW--dot=.yyy\fR: the dot character will be replaced by the contents
of the \f(CWSPOT_DOTDEFAULT\fR environment variable.
.TP
\fBSPOT_DOTEXTRA\fR
The contents of this variable is added to any dot output, immediately
before the first state is output. This makes it easy to override
global attributes of the graph.
.TP
\fBSPOT_SATLOG\fR
If set to a filename, the SAT-based minimization routines will append
statistics about each iteration to the named file. Each line lists
the following comma-separated values: requested number of states,
number of reachable states in the output, number of edges in the
output, number of transitions in the output, number of variables in
the SAT problem, number of clauses in the SAT problem, user time for
encoding the SAT problem, system time for encoding the SAT problem,
user time for solving the SAT problem, system time for solving the SAT
problem.
.TP
\fBSPOT_SATSOLVER\fR
If set, this variable should indicate how to call a SAT\-solver. This
is used by the sat\-minimize option described above. The default
value is \f(CW"glucose -verb=0 -model %I >%O"\fR, it is correct for
glucose version 3.0 (for older versions, remove the \fCW(-model\fR
option). The escape sequences \f(CW%I\fR and \f(CW%O\fR respectively
denote the names of the input and output files. These temporary files
are created in the directory specified by \fBSPOT_TMPDIR\fR or
\fBTMPDIR\fR (see below). The SAT-solver should follow the convention
of the SAT Competition for its input and output format.
.TP
\fBSPOT_STREETT_CONV_MIN\fR
The number of Streett pairs above which conversion from Streett
acceptance to generalized-Büchi acceptance should be made with a
dedicated algorithm. By default this is 3, i.e., if a Streett
automaton with 3 acceptance pairs or more has to be converted into
generalized-Büchi, the dedicated algorithm is used. This algorithm is
close to the classical conversion from Streett to Büchi, but with
several tweaks. When this algorithm is not used, the standard
"Fin-removal" approach is used instead: first the acceptance condition
is converted into disjunctive normal form (DNF), then Fin acceptance
is removed like for Rabin automata, yielding a disjuction of
generalized Büchi acceptance, and the result is finally converted into
conjunctive normal form (CNF) to obtain a generalized Büchi
acceptance. Both algorithms have a worst-case size that is
exponential in the number of Streett pairs, but in practice the
dedicated algorithm works better for most Streett automata with 3 or
more pairs (and many 2-pair Streett automata as well, but the
difference here is less clear). Setting this variable to 0 will
disable the dedicated algorithm. Setting it to 1 will enable it for
all Streett automata, however we do not recommand setting it to less
than 2, because the "Fin-removal" approach is better for single-pair
Streett automata.
.TP
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
These variables control in which directory temporary files (e.g.,
those who contain the input and output when interfacing with
translators) are created. \fBTMPDIR\fR is only read if
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
variables exist, or if their value is empty, files are created in the
current directory.
.TP
\fBSPOT_TMPKEEP\fR
When this variable is defined, temporary files are not removed.
This is mostly useful for debugging.
[BIBLIOGRAPHY]
.TP
1.
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
Powerset Construction for Restricted Classes of
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
Describes the WDBA-minimization algorithm implemented in Spot. The
algorithm used for the tba-det options is also a generalization (to
TBA instead of BA) of what they describe in sections 3.2 and 3.3.
.TP
2.
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
Jan Strejček: Compositional Approach to Suspension and Other
Improvements to LTL Translation. Proceedings of SPIN'13. LNCS 7976.
Describes the compositional suspension, the simulation-based
reductions, and the SCC-based simplifications.
.TP
3.
Rüdiger Ehlers: Minimising Deterministic Büchi Automata Precisely using
SAT Solving. Proceedings of SAT'10. LNCS 6175.
Our SAT-based minimization procedures are generalizations of this
paper to deal with TBA or TGBA.
[SEE ALSO]
.BR ltl2tgba (1)
.BR ltl2tgta (1)
.BR dstar2tgba (1)
.BR autfilt (1)

71
bin/options.py Executable file
View file

@ -0,0 +1,71 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2014 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/>.
# Run all binaries, and collect the long option associated to each
# short option for easy comparison.
# This script should work with both Python 2 and 3.
from sys import stdout as out
import re
import subprocess
with open('Makefile.am', 'r') as mf:
lines = mf.read()
lines = re.sub('\s*\\\\\s*', ' ', lines)
bin_programs = re.search('bin_PROGRAMS\s*=([\w \t]*)', lines).group(1).split()
optre = re.compile('(-\w), (--[\w=-]+)')
d = {}
for tool in bin_programs:
args = ('./' + tool, '--help')
try:
popen = subprocess.Popen(args, stdout=subprocess.PIPE)
except OSError:
print("Cannot execute " + tool + ", is it compiled?")
exit(1)
popen.wait()
output = popen.communicate()[0].decode('utf-8')
for match in optre.finditer(output):
shortname, longname = match.group(1), match.group(2)
if not shortname in d:
d[shortname] = { longname: tool }
elif not longname in d[shortname]:
d[shortname][longname] = tool
else:
w = ('%29s' % '') + d[shortname][longname]
w = w[w.rfind('\n') + 1 : -1]
if len(w + ' ' + tool) < 80:
d[shortname][longname] += ' ' + tool
else:
d[shortname][longname] += '\n%29s%s' % ('', tool)
# The lambda function works around the fact that x might be an str or
# a unicode object depending on the Python implementation.
for shortname in sorted(d, key=lambda x: x.lower()):
out.write(shortname)
first=''
for longname in sorted(d[shortname]):
out.write('%s %-24s %s\n' % (first, longname, d[shortname][longname]))
first=' '

397
bin/randaut.cc Normal file
View file

@ -0,0 +1,397 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 <iostream>
#include <fstream>
#include <argp.h>
#include <cstdlib>
#include <sstream>
#include <iterator>
#include "error.h"
#include "argmatch.h"
#include "common_setup.hh"
#include "common_range.hh"
#include "common_cout.hh"
#include "common_aoutput.hh"
#include "common_conv.hh"
#include <spot/misc/timer.hh>
#include <spot/misc/random.hh>
#include <spot/twa/bddprint.hh>
#include <spot/twaalgos/randomgraph.hh>
#include <spot/twaalgos/canonicalize.hh>
const char argp_program_doc[] = "\
Generate random connected automata.\n\n\
The automata are built over the atomic propositions named by PROPS...\n\
or, if N is a nonnegative number, using N arbitrary names.\n\
If the density is set to D, and the number of states to Q, the degree\n\
of each state follows a normal distribution with mean 1+(Q-1)D and\n\
variance (Q-1)D(1-D). In particular, for D=0 all states have a single\n\
successor, while for D=1 all states are interconnected.\v\
Examples:\n\
\n\
This builds a random neverclaim with 4 states and labeled using the two\n\
atomic propositions \"a\" and \"b\":\n\
% randaut --spin -Q4 a b\n\
\n\
This builds three random, complete, and deterministic TGBA with 5 to 10\n\
states, 1 to 3 acceptance sets, and three atomic propositions:\n\
% randaut -n3 -D -H -Q5..10 -A1..3 3\n\
\n\
Build 3 random, complete, and deterministic Rabin automata\n\
with 2 to 3 acceptance pairs, state-based acceptance, 8 states, \n\
a high density of edges, and 3 to 4 atomic propositions:\n\
% randaut -n3 -D -H -Q8 -d.8 -S -A 'Rabin 2..3' 3..4\n\
";
enum {
OPT_SEED = 1,
OPT_COLORED,
};
static const argp_option options[] =
{
/**************************************************/
{ nullptr, 0, nullptr, 0, "Generation:", 1 },
{ "acceptance", 'A', "ACCEPTANCE", 0,
"specify the acceptance type of the automaton", 0 },
{ "acc-probability", 'a', "FLOAT", 0,
"probability that an edge belongs to one acceptance set (0.2)", 0 },
{ "automata", 'n', "INT", 0, "number of automata to output (1)\n"\
"use a negative value for unbounded generation", 0 },
{ "ba", 'B', nullptr, 0,
"build a Buchi automaton (implies --acceptance=Buchi --state-acc)", 0 },
{ "colored", OPT_COLORED, nullptr, 0,
"build an automaton in which each edge (or state if combined with "
"-S) belong to a single acceptance set", 0 },
{ "density", 'd', "FLOAT", 0, "density of the edges (0.2)", 0 },
{ "deterministic", 'D', nullptr, 0,
"build a complete, deterministic automaton ", 0 },
{ "unique", 'u', nullptr, 0,
"do not output the same automaton twice (same in the sense that they "\
"are isomorphic)", 0 },
{ "seed", OPT_SEED, "INT", 0,
"seed for the random number generator (0)", 0 },
{ "states", 'Q', "RANGE", 0, "number of states to output (10)", 0 },
{ "state-based-acceptance", 'S', nullptr, 0,
"used state-based acceptance", 0 },
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
RANGE_DOC,
{ nullptr, 0, nullptr, 0, "ACCEPTANCE may be either a RANGE (in which case "
"generalized Büchi is assumed), or an arbitrary acceptance formula "
"such as 'Fin(0)|Inf(1)&Fin(2)' in the same syntax as in the HOA "
"format, or one of the following patterns:\n"
" none\n"
" all\n"
" Buchi\n"
" co-Buchi\n"
" generalized-Buchi RANGE\n"
" generalized-co-Buchi RANGE\n"
" Rabin RANGE\n"
" Streett RANGE\n"
" generalized-Rabin INT RANGE RANGE ... RANGE\n"
" parity (min|max|rand) (odd|even|rand) RANGE\n"
" random RANGE\n"
" random RANGE PROBABILITY\n"
"The random acceptance condition uses each set only once, "
"unless a probability (to reuse the set again every time it is used) "
"is given.", 2 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
static const struct argp_child children[] =
{
{ &aoutput_argp, 0, nullptr, 3 },
{ &aoutput_o_format_argp, 0, nullptr, 4 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
static const char* opt_acceptance = nullptr;
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
typedef std::set<std::vector<tr_t>> unique_aut_t;
static spot::atomic_prop_set aprops;
static range ap_count_given = {-1, -2}; // Must be two different negative val
static int opt_seed = 0;
static const char* opt_seed_str = "0";
static int opt_automata = 1;
static range opt_states = { 10, 10 };
static float opt_density = 0.2;
static range opt_acc_sets = { -1, 0 };
static float opt_acc_prob = 0.2;
static bool opt_deterministic = false;
static bool opt_state_acc = false;
static bool opt_colored = false;
static bool ba_wanted = false;
static bool generic_wanted = false;
static bool gba_wanted = false;
static std::unique_ptr<unique_aut_t> opt_uniq = nullptr;
static void
ba_options()
{
opt_acc_sets = { 1, 1 };
opt_state_acc = true;
}
// Range should have the form 12..34 or 12:34, maybe with spaces. The
// characters between '.' and ':' include all digits plus '/', but the
// parser will later choke on '/' if it is used, so let's not worry
// here.
static bool
looks_like_a_range(const char* str)
{
while (*str == ' ' || (*str >= '.' && *str <= ':'))
++str;
return !*str;
}
static int
parse_opt(int key, char* arg, struct argp_state* as)
{
// This switch is alphabetically-ordered.
switch (key)
{
case '8':
spot::enable_utf8();
break;
case 'a':
opt_acc_prob = to_float(arg);
if (opt_acc_prob < 0.0 || opt_acc_prob > 1.0)
error(2, 0, "probability of acceptance set membership "
"should be between 0.0 and 1.0");
break;
case 'A':
if (looks_like_a_range(arg))
{
opt_acc_sets = parse_range(arg);
if (opt_acc_sets.min > opt_acc_sets.max)
std::swap(opt_acc_sets.min, opt_acc_sets.max);
if (opt_acc_sets.min < 0)
error(2, 0, "number of acceptance sets should be positive");
gba_wanted = true;
}
else
{
opt_acceptance = arg;
generic_wanted = true;
}
break;
case 'B':
ba_options();
ba_wanted = true;
break;
case 'd':
opt_density = to_float(arg);
if (opt_density < 0.0 || opt_density > 1.0)
error(2, 0, "density should be between 0.0 and 1.0");
break;
case 'D':
opt_deterministic = true;
break;
case 'n':
opt_automata = to_int(arg);
break;
case 'Q':
opt_states = parse_range(arg);
if (opt_states.min > opt_states.max)
std::swap(opt_states.min, opt_states.max);
if (opt_states.min == 0)
error(1, 0, "cannot build an automaton with 0 states");
break;
case 'S':
opt_state_acc = true;
break;
case 'u':
opt_uniq =
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
break;
case OPT_COLORED:
opt_colored = true;
break;
case OPT_SEED:
opt_seed = to_int(arg);
opt_seed_str = arg;
break;
case ARGP_KEY_ARG:
// If this is the unique non-option argument, it can
// be a number of atomic propositions to build.
//
// argp reorganizes argv[] so that options always come before
// non-options. So if as->argc == as->next we know this is the
// last non-option argument, and if aprops.empty() we know this
// is the also the first one.
if (aprops.empty() && as->argc == as->next && looks_like_a_range(arg))
{
ap_count_given = parse_range(arg);
// Create the set once if the count is fixed.
if (ap_count_given.min == ap_count_given.max)
aprops = spot::create_atomic_prop_set(ap_count_given.min);
break;
}
aprops.insert(spot::formula::ap(arg));
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int
main(int argc, char** argv)
{
strcpy(F_doc, "seed number");
strcpy(L_doc, "automaton number");
setup(argv);
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
children, nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
// running 'randaut 0' is one way to generate automata using no
// atomic propositions so do not complain in that case.
if (aprops.empty() && ap_count_given.max < 0)
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
program_name);
if (generic_wanted && automaton_format == Spin)
error(2, 0, "--spin implies --ba so should not be used with --acceptance");
if (generic_wanted && ba_wanted)
error(2, 0, "--acceptance and --ba may not be used together");
if (automaton_format == Spin && opt_acc_sets.max > 1)
error(2, 0, "--spin is incompatible with --acceptance=%d..%d",
opt_acc_sets.min, opt_acc_sets.max);
if (ba_wanted && opt_acc_sets.min != 1 && opt_acc_sets.max != 1)
error(2, 0, "--ba is incompatible with --acceptance=%d..%d",
opt_acc_sets.min, opt_acc_sets.max);
if (ba_wanted && generic_wanted)
error(2, 0, "--ba is incompatible with --acceptance=%s", opt_acceptance);
if (automaton_format == Spin)
ba_options();
if (opt_colored && opt_acc_sets.min == -1 && !generic_wanted)
error(2, 0, "--colored requires at least one acceptance set; "
"use --acceptance");
if (opt_colored && opt_acc_sets.min == 0)
error(2, 0, "--colored requires at least one acceptance set; "
"fix the range of --acceptance");
if (opt_acc_sets.min == -1)
opt_acc_sets.min = 0;
try
{
spot::srand(opt_seed);
auto d = spot::make_bdd_dict();
automaton_printer printer;
constexpr unsigned max_trials = 10000;
unsigned trials = max_trials;
int automaton_num = 0;
for (;;)
{
spot::stopwatch sw;
sw.start();
if (ap_count_given.max > 0
&& ap_count_given.min != ap_count_given.max)
{
int c = spot::rrand(ap_count_given.min, ap_count_given.max);
aprops = spot::create_atomic_prop_set(c);
}
int size = opt_states.min;
if (size != opt_states.max)
size = spot::rrand(size, opt_states.max);
int accs = opt_acc_sets.min;
if (accs != opt_acc_sets.max)
accs = spot::rrand(accs, opt_acc_sets.max);
spot::acc_cond::acc_code code;
if (opt_acceptance)
{
code = spot::acc_cond::acc_code(opt_acceptance);
accs = code.used_sets().max_set();
if (opt_colored && accs == 0)
error(2, 0, "--colored requires at least one acceptance set; "
"fix the range of --acceptance");
}
auto aut =
spot::random_graph(size, opt_density, &aprops, d,
accs, opt_acc_prob, 0.5,
opt_deterministic, opt_state_acc,
opt_colored);
if (opt_acceptance)
aut->set_acceptance(accs, code);
if (opt_uniq)
{
auto tmp = spot::canonicalize
(make_twa_graph(aut, spot::twa::prop_set::all()));
std::vector<tr_t> trans(tmp->edge_vector().begin() + 1,
tmp->edge_vector().end());
if (!opt_uniq->emplace(trans).second)
{
--trials;
if (trials == 0)
error(2, 0, "failed to generate a new unique automaton"
" after %d trials", max_trials);
continue;
}
trials = max_trials;
}
auto runtime = sw.stop();
printer.print(aut, nullptr,
opt_seed_str, automaton_num, runtime, nullptr);
++automaton_num;
if (opt_automata > 0 && automaton_num >= opt_automata)
break;
}
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
}

317
bin/randltl.cc Normal file
View file

@ -0,0 +1,317 @@
// -*- coding: utf-8 -*-
// 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.
//
// 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 <iostream>
#include <fstream>
#include <argp.h>
#include <cstdlib>
#include <iterator>
#include "error.h"
#include "common_setup.hh"
#include "common_output.hh"
#include "common_range.hh"
#include "common_r.hh"
#include "common_conv.hh"
#include <sstream>
#include <spot/tl/randomltl.hh>
#include <spot/tl/simplify.hh>
#include <spot/misc/random.hh>
#include <spot/misc/optionmap.hh>
const char argp_program_doc[] ="\
Generate random temporal logic formulas.\n\n\
The formulas are built over the atomic propositions named by PROPS...\n\
or, if N is a nonnegative number, using N arbitrary names.\v\
Examples:\n\
\n\
The following generates 10 random LTL formulas over the propositions a, b,\n\
and c, with the default tree-size, and all available operators.\n\
% randltl -n10 a b c\n\
\n\
If you do not mind about the name of the atomic propositions, just give\n\
a number instead:\n\
% randltl -n10 3\n\
\n\
You can disable or favor certain operators by changing their priority.\n\
The following disables xor, implies, and equiv, and multiply the probability\n\
of X to occur by 10.\n\
% randltl --ltl-priorities='xor=0, implies=0, equiv=0, X=10' -n10 a b c\n\
";
enum {
OPT_BOOLEAN_PRIORITIES = 1,
OPT_DUMP_PRIORITIES,
OPT_DUPS,
OPT_LTL_PRIORITIES,
OPT_PSL_PRIORITIES,
OPT_SEED,
OPT_SERE_PRIORITIES,
OPT_TREE_SIZE,
OPT_WF,
};
static const argp_option options[] =
{
// Keep this alphabetically sorted (expect for aliases).
/**************************************************/
{ nullptr, 0, nullptr, 0, "Type of formula to generate:", 1 },
{ "boolean", 'B', nullptr, 0, "generate Boolean formulas", 0 },
{ "ltl", 'L', nullptr, 0, "generate LTL formulas (default)", 0 },
{ "sere", 'S', nullptr, 0, "generate SERE", 0 },
{ "psl", 'P', nullptr, 0, "generate PSL formulas", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Generation:", 2 },
{ "weak-fairness", OPT_WF, nullptr, 0,
"append some weak-fairness conditions", 0 },
{ "formulas", 'n', "INT", 0, "number of formulas to output (1)\n"\
"use a negative value for unbounded generation", 0 },
{ "seed", OPT_SEED, "INT", 0,
"seed for the random number generator (0)", 0 },
{ "tree-size", OPT_TREE_SIZE, "RANGE", 0,
"tree size of the formulas generated, before mandatory "\
"trivial simplifications (15)", 0 },
{ "allow-dups", OPT_DUPS, nullptr, 0,
"allow duplicate formulas to be output", 0 },
DECLARE_OPT_R,
RANGE_DOC,
LEVEL_DOC(3),
/**************************************************/
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
{ "dump-priorities", OPT_DUMP_PRIORITIES, nullptr, 0,
"show current priorities, do not generate any formula", 0 },
{ "ltl-priorities", OPT_LTL_PRIORITIES, "STRING", 0,
"set priorities for LTL formulas", 0 },
{ "sere-priorities", OPT_SERE_PRIORITIES, "STRING", 0,
"set priorities for SERE formulas", 0 },
{ "boolean-priorities", OPT_BOOLEAN_PRIORITIES, "STRING", 0,
"set priorities for Boolean formulas", 0 },
{ nullptr, 0, nullptr, 0, "STRING should be a comma-separated list of "
"assignments, assigning integer priorities to the tokens "
"listed by --dump-priorities.", 0 },
/**************************************************/
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
"the following interpreted sequences:", -19 },
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the formula (in the selected syntax)", 0 },
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"the (serial) number of the formula", 0 },
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
"a single %", 0 },
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp_child children[] =
{
{ &output_argp, 0, nullptr, -20 },
{ &misc_argp, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
spot::atomic_prop_set aprops;
static int output = OUTPUTLTL;
static char* opt_pL = nullptr;
static char* opt_pS = nullptr;
static char* opt_pB = nullptr;
static bool opt_dump_priorities = false;
static int opt_formulas = 1;
static int opt_seed = 0;
static range opt_tree_size = { 15, 15 };
static bool opt_unique = true;
static bool opt_wf = false;
static bool ap_count_given = false;
static int
parse_opt(int key, char* arg, struct argp_state* as)
{
// This switch is alphabetically-ordered.
switch (key)
{
case 'B':
output = OUTPUTBOOL;
break;
case 'L':
output = OUTPUTLTL;
break;
case 'n':
opt_formulas = to_int(arg);
break;
case 'P':
output = OUTPUTPSL;
break;
case OPT_R:
parse_r(arg);
break;
case 'S':
output = OUTPUTSERE;
break;
case OPT_BOOLEAN_PRIORITIES:
opt_pB = arg;
break;
case OPT_DUPS:
opt_unique = false;
break;
case OPT_LTL_PRIORITIES:
opt_pL = arg;
break;
case OPT_DUMP_PRIORITIES:
opt_dump_priorities = true;
break;
// case OPT_PSL_PRIORITIES: break;
case OPT_SERE_PRIORITIES:
opt_pS = arg;
break;
case OPT_SEED:
opt_seed = to_int(arg);
break;
case OPT_TREE_SIZE:
opt_tree_size = parse_range(arg);
if (opt_tree_size.min > opt_tree_size.max)
std::swap(opt_tree_size.min, opt_tree_size.max);
break;
case OPT_WF:
opt_wf = true;
break;
case ARGP_KEY_ARG:
// If this is the unique non-option argument, it can
// be a number of atomic propositions to build.
//
// argp reorganizes argv[] so that options always come before
// non-options. So if as->argc == as->next we know this is the
// last non-option argument, and if aprops.empty() we know this
// is the also the first one.
if (aprops.empty() && as->argc == as->next)
{
char* endptr;
int res = strtol(arg, &endptr, 10);
if (!*endptr && res >= 0) // arg is a number
{
ap_count_given = true;
aprops = spot::create_atomic_prop_set(res);
break;
}
}
aprops.insert(spot::default_environment::instance().require(arg));
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
children, nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
// running 'randltl 0' is one way to generate formulas using no
// atomic propositions so do not complain in that case.
if (aprops.empty() && !ap_count_given)
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
program_name);
spot::srand(opt_seed);
try
{
spot::randltlgenerator rg
(aprops,
[&] (){
spot::option_map opts;
opts.set("output", output);
opts.set("tree_size_min", opt_tree_size.min);
opts.set("tree_size_max", opt_tree_size.max);
opts.set("wf", opt_wf);
opts.set("seed", opt_seed);
opts.set("simplification_level", simplification_level);
return opts;
}(), opt_pL, opt_pS, opt_pB);
if (opt_dump_priorities)
{
switch (output)
{
case OUTPUTLTL:
std::cout <<
"Use --ltl-priorities to set the following LTL priorities:\n";
rg.dump_ltl_priorities(std::cout);
break;
case OUTPUTBOOL:
std::cout <<
"Use --boolean-priorities to set the following Boolean "
"formula priorities:\n";
rg.dump_bool_priorities(std::cout);
break;
case OUTPUTPSL:
std::cout <<
"Use --ltl-priorities to set the following LTL priorities:\n";
rg.dump_psl_priorities(std::cout);
// Fall through.
case OUTPUTSERE:
std::cout <<
"Use --sere-priorities to set the following SERE priorities:\n";
rg.dump_sere_priorities(std::cout);
std::cout <<
"Use --boolean-priorities to set the following Boolean "
"formula priorities:\n";
rg.dump_sere_bool_priorities(std::cout);
break;
default:
error(2, 0, "internal error: unknown type of output");
}
exit(0);
}
while (opt_formulas < 0 || opt_formulas--)
{
static int count = 0;
spot::formula f = rg.next();
if (!f)
{
error(2, 0, "failed to generate a new unique formula after %d " \
"trials", MAX_TRIALS);
}
else
{
output_formula_checked(f, nullptr, ++count);
}
};
}
catch (const std::runtime_error& e)
{
error(2, 0, "%s", e.what());
}
catch (const std::invalid_argument& e)
{
error(2, 0, "%s", e.what());
}
return 0;
}

161
bin/spot-x.cc Normal file
View file

@ -0,0 +1,161 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 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 <cstdlib>
#include <argp.h>
#include "common_setup.hh"
const char argp_program_doc[] ="\
Common fine-tuning options for binaries built with Spot.\n\
\n\
The argument of -x or --extra-options is a comma-separated list of KEY=INT \
assignments that are passed to the post-processing routines (they may \
be passed to other algorithms in the future). These options are \
mostly used for benchmarking and debugging purpose. KEYR (without any \
value) is a shorthand for KEY=1, while !KEY is a shorthand for KEY=0.";
#define DOC(NAME, TXT) NAME, 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, TXT, 0
static const argp_option options[] =
{
{ nullptr, 0, nullptr, 0, "Translation options:", 0 },
{ DOC("comp-susp", "Set to 1 to enable compositional suspension, \
as described in our SPIN'13 paper (see Bibliography below). Set to 2, \
to build only the skeleton TGBA without composing it. Set to 0 (the \
default) to disable.") },
{ DOC("early-susp", "When set to 1, start compositional suspension on \
the transitions that enter accepting SCCs, and not only on the transitions \
inside accepting SCCs. This option defaults to 0, and is only used when \
comp-susp=1.") },
{ DOC("skel-simul", "Default to 1. Set to 0 to disable simulation \
on the skeleton automaton during compositional suspension. Only used when \
comp-susp=1.") },
{ DOC("skel-wdba", "Set to 0 to disable WDBA \
minimization on the skeleton automaton during compositional suspension. \
Set to 1 always WDBA-minimize the skeleton . Set to 2 to keep the WDBA \
only if it is smaller than the original skeleton. This option is only \
used when comp-susp=1 and default to 1 or 2 depending on whether --small \
or --deterministic is specified.") },
{ nullptr, 0, nullptr, 0, "Postprocessing options:", 0 },
{ DOC("scc-filter", "Set to 1 (the default) to enable \
SCC-pruning and acceptance simplification at the beginning of \
post-processing. Transitions that are outside of accepting SCC are \
removed from accepting sets, except those that enter into an accepting \
SCC. Set to 2 to remove even these entering transition from the \
accepting sets. Set to 0 to disable this SCC-pruning and acceptance \
simpification pass.") },
{ DOC("degen-reset", "If non-zero (the default), the \
degeneralization algorithm will reset its level any time it exits \
an SCC.") },
{ DOC("degen-lcache", "If non-zero (the default is 1), whenever the \
degeneralization algorithm enters an SCC on a state that has already \
been associated to a level elsewhere, it should reuse that level. \
Different values can be used to select which level to reuse: 1 always \
uses the first level created, 2 uses the minimum level seen so far, and \
3 uses the maximum level seen so far. The \"lcache\" stands for \
\"level cache\".") },
{ DOC("degen-order", "If non-zero, the degeneralization algorithm \
will compute an independent degeneralization order for each SCC it \
processes. This is currently disabled by default.") },
{ DOC("degen-lskip", "If non-zero (the default), the degeneralization \
algorithm will skip as much levels as possible for each transition. This \
is enabled by default as it very often reduce the number of resulting \
states. A consequence of skipping levels is that the degeneralized \
automaton tends to have smaller cycles around the accepting states. \
Disabling skipping will produce automata with large cycles, and often \
with more states.") },
{ DOC("degen-lowinit", "Whenever the degeneralization algorihm enters \
a new SCC (or starts from the initial state), it starts on some level L that \
is compatible with all outgoing transitions. If degen-lowinit is zero \
(the default) and the corresponding state (in the generalized automaton) \
has an accepting self-loop, then level L is replaced by the accepting \
level, as it might favor finding accepting cycles earlier. If \
degen-lowinit is non-zero, then level L is always used without looking \
for the presence of an accepting self-loop.") },
{ DOC("simul", "Set to 0 to disable simulation-based reductions. \
Set to 1 to use only direct simulation. Set to 2 to use only reverse \
simulation. Set to 3 to iterate both direct and reverse simulations. \
The default is 3, except when option --low is specified, in which case \
the default is 1.") },
{ DOC("ba-simul", "Set to 0 to disable simulation-based reductions \
on the Büchi automaton (i.e., after degeneralization has been performed). \
Set to 1 to use only direct simulation. Set to 2 to use only reverse \
simulation. Set to 3 to iterate both direct and reverse simulations. \
The default is 3 in --high mode, and 0 otherwise.") },
{ DOC("wdba-minimize", "Set to 0 to disable WDBA-minimization. \
Enabled by default.") },
{ DOC("tba-det", "Set to 1 to attempt a powerset determinization \
if the TGBA is not already deterministic. Doing so will degeneralize \
the automaton. This is disabled by default, unless sat-minimize is set.") },
{ DOC("sat-minimize",
"Set to 1 to enable SAT-based minimization of deterministic \
TGBA: it starts with the number of states of the input, and iteratively \
tries to find a deterministic TGBA with one less state. Set to 2 to perform \
a binary search instead. Disabled (0) by default. The sat solver to use \
can be set with the SPOT_SATSOLVER environment variable (see below). By \
default the procedure looks for a TGBA with the same number of acceptance \
set; this can be changed with the sat-acc option, or of course by using -B \
to construct a Büchi automaton. Enabling SAT-based minimization will \
also enable tba-det.") },
{ DOC("sat-states",
"When this is set to some positive integer, the SAT-based \
minimization will attempt to construct a TGBA with the given number of \
states. It may however return an automaton with less states if some of \
these are unreachable or useless. Setting sat-states automatically \
enables sat-minimize, but no iteration is performed. If no equivalent \
automaton could be constructed with the given number of states, the original \
automaton is returned.") },
{ DOC("sat-acc",
"When this is set to some positive integer, the SAT-based will \
attempt to construct a TGBA with the given number of acceptance sets. \
states. It may however return an automaton with less acceptance sets if \
some of these are useless. Setting sat-acc automatically \
sets sat-minimize to 1 if not set differently.") },
{ DOC("state-based",
"Set to 1 to instruct the SAT-minimization procedure to produce \
a TGBA where all outgoing transition of a state have the same acceptance \
sets. By default this is only enabled when option -B is used.") },
{ nullptr, 0, nullptr, 0, nullptr, 0 }
};
const struct argp_child children[] =
{
{ &misc_argp_hidden, 0, nullptr, -1 },
{ nullptr, 0, nullptr, 0 }
};
int
main(int argc, char** argv)
{
setup(argv);
const argp ap = { options, nullptr, nullptr, argp_program_doc, children,
nullptr, nullptr };
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
exit(err);
std::cerr << "This binary serves no purpose other than generating"
<< " the spot-x.7 manpage.\n";
return 1;
}