rename src/ as spot/ and use include <spot/...>

* NEWS: Mention the change.
* src/: Rename as ...
* spot/: ... this, adjust all headers to include <spot/...> instead of
"...", and adjust all Makefile.am to search headers from the top-level
directory.
* HACKING: Add conventions about #include.
* spot/sanity/style.test: Add a few more grep to catch cases
that do not follow these conventions.
* .gitignore, Makefile.am, README, bench/stutter/Makefile.am,
bench/stutter/stutter_invariance_formulas.cc,
bench/stutter/stutter_invariance_randomgraph.cc, configure.ac,
debian/rules, doc/Doxyfile.in, doc/Makefile.am,
doc/org/.dir-locals.el.in, doc/org/g++wrap.in, doc/org/init.el.in,
doc/org/tut01.org, doc/org/tut02.org, doc/org/tut03.org,
doc/org/tut10.org, doc/org/tut20.org, doc/org/tut21.org,
doc/org/tut22.org, doc/org/tut30.org, iface/ltsmin/Makefile.am,
iface/ltsmin/kripke.test, iface/ltsmin/ltsmin.cc,
iface/ltsmin/ltsmin.hh, iface/ltsmin/modelcheck.cc,
wrap/python/Makefile.am, wrap/python/ajax/spotcgi.in,
wrap/python/spot_impl.i, wrap/python/tests/ltl2tgba.py,
wrap/python/tests/randgen.py, wrap/python/tests/run.in: Adjust.
This commit is contained in:
Alexandre Duret-Lutz 2015-12-04 19:42:23 +01:00
parent 1fddfe60ec
commit f120dd3206
529 changed files with 1308 additions and 1262 deletions

8
spot/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
.deps
Makefile
Makefile.in
.libs
libspot.la
_.cc
*.log
*.dir

49
spot/Makefile.am Normal file
View file

@ -0,0 +1,49 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2009, 2010, 2012, 2013, 2014, 2015 Laboratoire de Recherche
## et Développement de l'Epita (LRDE).
## Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
## département Systèmes Répartis Coopératifs (SRC), Université Pierre
## et Marie Curie.
##
## 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/>.
AUTOMAKE_OPTIONS = subdir-objects
# List directories in the order they must be built. Keep tests at the
# end, after building '.' (since the current directory contains
# libspot.la needed by the tests)
SUBDIRS = misc priv tl graph twa twaalgos ta taalgos kripke \
parseaut parsetl . bin tests sanity
lib_LTLIBRARIES = libspot.la
libspot_la_SOURCES =
libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined $(SYMBOLIC_LDFLAGS)
libspot_la_LIBADD = \
kripke/libkripke.la \
misc/libmisc.la \
parseaut/libparseaut.la \
parsetl/libparsetl.la \
priv/libpriv.la \
taalgos/libtaalgos.la \
ta/libta.la \
tl/libtl.la \
twaalgos/libtwaalgos.la \
twa/libtwa.la \
../lib/libgnu.la
# Dummy C++ source to cause C++ linking.
nodist_EXTRA_libspot_la_SOURCES = _.cc
_.cc:; touch $@

16
spot/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-*

87
spot/bin/Makefile.am Normal file
View file

@ -0,0 +1,87 @@
## -*- 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 ../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
spot/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.

773
spot/bin/autfilt.cc Normal file
View file

@ -0,0 +1,773 @@
// -*- 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_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 },
{ "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_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_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);
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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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;
};

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 };

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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/bin/ltlcross.cc Normal file

File diff suppressed because it is too large Load diff

337
spot/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
spot/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
spot/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
spot/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 $@

29
spot/bin/man/autfilt.x Normal file
View file

@ -0,0 +1,29 @@
.\" -*- 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.
[SEE ALSO]
.BR spot-x (7)
.BR dstar2tgba (1)

99
spot/bin/man/dstar2tgba.x Normal file
View file

@ -0,0 +1,99 @@
[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.
[SEE ALSO]
.BR spot-x (7),
.BR autfilt (1)

31
spot/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
spot/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
spot/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
spot/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
spot/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)

48
spot/bin/man/ltlfilt.x Normal file
View file

@ -0,0 +1,48 @@
[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
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
spot/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
spot/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
spot/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
spot/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
spot/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
spot/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::parse_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
spot/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
spot/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;
}

28
spot/graph/Makefile.am Normal file
View file

@ -0,0 +1,28 @@
## -*- 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/>.
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
graphdir = $(pkgincludedir)/graph
graph_HEADERS = \
graph.hh \
ngraph.hh

973
spot/graph/graph.hh Normal file
View file

@ -0,0 +1,973 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 <spot/misc/common.hh>
#include <vector>
#include <type_traits>
#include <tuple>
#include <cassert>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <type_traits>
namespace spot
{
template <typename State_Data, typename Edge_Data, bool Alternating = false>
class SPOT_API digraph;
namespace internal
{
template <typename Of, typename ...Args>
struct first_is_base_of
{
static const bool value = false;
};
template <typename Of, typename Arg1, typename ...Args>
struct first_is_base_of<Of, Arg1, Args...>
{
static const bool value =
std::is_base_of<Of, typename std::decay<Arg1>::type>::value;
};
// The boxed_label class stores Data as an attribute called
// "label" if boxed is true. It is an empty class if Data is
// void, and it simply inherits from Data if boxed is false.
//
// The data() method offers an homogeneous access to the Data
// instance.
template <typename Data, bool boxed = !std::is_class<Data>::value>
struct SPOT_API boxed_label
{
typedef Data data_t;
Data label;
template <typename... Args,
typename = typename std::enable_if<
!first_is_base_of<boxed_label, Args...>::value>::type>
boxed_label(Args&&... args)
noexcept(std::is_nothrow_constructible<Data, Args...>::value)
: label{std::forward<Args>(args)...}
{
}
// if Data is a POD type, G++ 4.8.2 wants default values for all
// label fields unless we define this default constructor here.
explicit boxed_label()
noexcept(std::is_nothrow_constructible<Data>::value)
{
}
Data& data()
{
return label;
}
const Data& data() const
{
return label;
}
bool operator<(const boxed_label& other) const
{
return label < other.label;
}
};
template <>
struct SPOT_API boxed_label<void, true>: public std::tuple<>
{
typedef std::tuple<> data_t;
std::tuple<>& data()
{
return *this;
}
const std::tuple<>& data() const
{
return *this;
}
};
template <typename Data>
struct SPOT_API boxed_label<Data, false>: public Data
{
typedef Data data_t;
template <typename... Args,
typename = typename std::enable_if<
!first_is_base_of<boxed_label, Args...>::value>::type>
boxed_label(Args&&... args)
noexcept(std::is_nothrow_constructible<Data, Args...>::value)
: Data{std::forward<Args>(args)...}
{
}
// if Data is a POD type, G++ 4.8.2 wants default values for all
// label fields unless we define this default constructor here.
explicit boxed_label()
noexcept(std::is_nothrow_constructible<Data>::value)
{
}
Data& data()
{
return *this;
}
const Data& data() const
{
return *this;
}
};
//////////////////////////////////////////////////
// State storage for digraphs
//////////////////////////////////////////////////
// We have two implementations, one with attached State_Data, and
// one without.
template <typename Edge, typename State_Data>
struct SPOT_API distate_storage final: public State_Data
{
Edge succ = 0; // First outgoing edge (used when iterating)
Edge succ_tail = 0; // Last outgoing edge (used for
// appending new edges)
template <typename... Args,
typename = typename std::enable_if<
!first_is_base_of<distate_storage, Args...>::value>::type>
distate_storage(Args&&... args)
noexcept(std::is_nothrow_constructible<State_Data, Args...>::value)
: State_Data{std::forward<Args>(args)...}
{
}
};
//////////////////////////////////////////////////
// Edge storage
//////////////////////////////////////////////////
// Again two implementation: one with label, and one without.
template <typename StateIn,
typename StateOut, typename Edge, typename Edge_Data>
struct SPOT_API edge_storage final: public Edge_Data
{
typedef Edge edge;
StateOut dst; // destination
Edge next_succ; // next outgoing edge with same
// source, or 0
StateIn src; // source
explicit edge_storage()
noexcept(std::is_nothrow_constructible<Edge_Data>::value)
: Edge_Data{}
{
}
template <typename... Args>
edge_storage(StateOut dst, Edge next_succ,
StateIn src, Args&&... args)
noexcept(std::is_nothrow_constructible<Edge_Data, Args...>::value
&& std::is_nothrow_constructible<StateOut, StateOut>::value
&& std::is_nothrow_constructible<Edge, Edge>::value)
: Edge_Data{std::forward<Args>(args)...},
dst(dst), next_succ(next_succ), src(src)
{
}
bool operator<(const edge_storage& other) const
{
if (src < other.src)
return true;
if (src > other.src)
return false;
// This might be costly if the destination is a vector
if (dst < other.dst)
return true;
if (dst > other.dst)
return false;
return this->data() < other.data();
}
bool operator==(const edge_storage& other) const
{
return src == other.src &&
dst == other.dst &&
this->data() == other.data();
}
};
//////////////////////////////////////////////////
// Edge iterator
//////////////////////////////////////////////////
// This holds a graph and a edge number that is the start of
// a list, and it iterates over all the edge_storage_t elements
// of that list.
template <typename Graph>
class SPOT_API edge_iterator:
std::iterator<std::forward_iterator_tag,
typename
std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_storage_t,
typename Graph::edge_storage_t>::type>
{
typedef
std::iterator<std::forward_iterator_tag,
typename
std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_storage_t,
typename Graph::edge_storage_t>::type>
super;
public:
typedef typename Graph::edge edge;
edge_iterator() noexcept
: g_(nullptr), t_(0)
{
}
edge_iterator(Graph* g, edge t) noexcept
: g_(g), t_(t)
{
}
bool operator==(edge_iterator o) const
{
return t_ == o.t_;
}
bool operator!=(edge_iterator o) const
{
return t_ != o.t_;
}
typename super::reference
operator*()
{
return g_->edge_storage(t_);
}
typename super::pointer
operator->()
{
return &g_->edge_storage(t_);
}
edge_iterator operator++()
{
t_ = operator*().next_succ;
return *this;
}
edge_iterator operator++(int)
{
edge_iterator ti = *this;
t_ = operator*().next_succ;
return ti;
}
operator bool() const
{
return t_;
}
edge trans() const
{
return t_;
}
protected:
Graph* g_;
edge t_;
};
template <typename Graph>
class SPOT_API killer_edge_iterator: public edge_iterator<Graph>
{
typedef edge_iterator<Graph> super;
public:
typedef typename Graph::state_storage_t state_storage_t;
typedef typename Graph::edge edge;
killer_edge_iterator(Graph* g, edge t, state_storage_t& src) noexcept
: super(g, t), src_(src), prev_(0)
{
}
killer_edge_iterator operator++()
{
prev_ = this->t_;
this->t_ = this->operator*().next_succ;
return *this;
}
killer_edge_iterator operator++(int)
{
killer_edge_iterator ti = *this;
++*this;
return ti;
}
// Erase the current edge and advance the iterator.
void erase()
{
edge next = this->operator*().next_succ;
// Update source state and previous edges
if (prev_)
{
this->g_->edge_storage(prev_).next_succ = next;
}
else
{
if (src_.succ == this->t_)
src_.succ = next;
}
if (src_.succ_tail == this->t_)
{
src_.succ_tail = prev_;
assert(next == 0);
}
// Erased edges have themselves as next_succ.
this->operator*().next_succ = this->t_;
// Advance iterator to next edge.
this->t_ = next;
++this->g_->killed_edge_;
}
protected:
state_storage_t& src_;
edge prev_;
};
//////////////////////////////////////////////////
// State OUT
//////////////////////////////////////////////////
// Fake container listing the outgoing edges of a state.
template <typename Graph>
class SPOT_API state_out
{
public:
typedef typename Graph::edge edge;
state_out(Graph* g, edge t) noexcept
: g_(g), t_(t)
{
}
edge_iterator<Graph> begin()
{
return {g_, t_};
}
edge_iterator<Graph> end()
{
return {};
}
void recycle(edge t)
{
t_ = t;
}
protected:
Graph* g_;
edge t_;
};
//////////////////////////////////////////////////
// all_trans
//////////////////////////////////////////////////
template <typename Graph>
class SPOT_API all_edge_iterator:
std::iterator<std::forward_iterator_tag,
typename
std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_storage_t,
typename Graph::edge_storage_t>::type>
{
typedef
std::iterator<std::forward_iterator_tag,
typename
std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_storage_t,
typename Graph::edge_storage_t>::type>
super;
typedef typename std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_vector_t,
typename Graph::edge_vector_t>::type
tv_t;
unsigned t_;
tv_t& tv_;
void skip_()
{
unsigned s = tv_.size();
do
++t_;
while (t_ < s && tv_[t_].next_succ == t_);
}
public:
all_edge_iterator(unsigned pos, tv_t& tv) noexcept
: t_(pos), tv_(tv)
{
skip_();
}
all_edge_iterator(tv_t& tv) noexcept
: t_(tv.size()), tv_(tv)
{
}
all_edge_iterator& operator++()
{
skip_();
return *this;
}
all_edge_iterator operator++(int)
{
all_edge_iterator old = *this;
++*this;
return old;
}
bool operator==(all_edge_iterator o) const
{
return t_ == o.t_;
}
bool operator!=(all_edge_iterator o) const
{
return t_ != o.t_;
}
typename super::reference
operator*()
{
return tv_[t_];
}
typename super::pointer
operator->()
{
return &tv_[t_];
}
};
template <typename Graph>
class SPOT_API all_trans
{
typedef typename std::conditional<std::is_const<Graph>::value,
const typename Graph::edge_vector_t,
typename Graph::edge_vector_t>::type
tv_t;
typedef all_edge_iterator<Graph> iter_t;
tv_t& tv_;
public:
all_trans(tv_t& tv) noexcept
: tv_(tv)
{
}
iter_t begin()
{
return {0, tv_};
}
iter_t end()
{
return {tv_};
}
};
}
// The actual graph implementation
template <typename State_Data, typename Edge_Data, bool Alternating>
class digraph
{
friend class internal::edge_iterator<digraph>;
friend class internal::edge_iterator<const digraph>;
friend class internal::killer_edge_iterator<digraph>;
public:
typedef internal::edge_iterator<digraph> iterator;
typedef internal::edge_iterator<const digraph> const_iterator;
static constexpr bool alternating()
{
return Alternating;
}
// Extra data to store on each state or edge.
typedef State_Data state_data_t;
typedef Edge_Data edge_data_t;
// State and edges are identified by their indices in some
// vector.
typedef unsigned state;
typedef unsigned edge;
// The type of an output state (when seen from a edge)
// depends on the kind of graph we build
typedef typename std::conditional<Alternating,
std::vector<state>,
state>::type out_state;
typedef internal::distate_storage<edge,
internal::boxed_label<State_Data>>
state_storage_t;
typedef internal::edge_storage<state, out_state, edge,
internal::boxed_label<Edge_Data>>
edge_storage_t;
typedef std::vector<state_storage_t> state_vector;
typedef std::vector<edge_storage_t> edge_vector_t;
protected:
state_vector states_;
edge_vector_t edges_;
// Number of erased edges.
unsigned killed_edge_;
public:
/// \brief construct an empty graph
///
/// Construct an empty graph, and reserve space for \a max_states
/// states and \a max_trans edges. These are not hard
/// limits, but just hints to pre-allocate a data structure that
/// may hold that much items.
digraph(unsigned max_states = 10, unsigned max_trans = 0)
: killed_edge_(0)
{
states_.reserve(max_states);
if (max_trans == 0)
max_trans = max_states * 2;
edges_.reserve(max_trans + 1);
// Edge number 0 is not used, because we use this index
// to mark the absence of a edge.
edges_.resize(1);
// This causes edge 0 to be considered as dead.
edges_[0].next_succ = 0;
}
unsigned num_states() const
{
return states_.size();
}
unsigned num_edges() const
{
return edges_.size() - killed_edge_ - 1;
}
bool valid_trans(edge t) const
{
// Erased edges have their next_succ pointing to
// themselves.
return (t < edges_.size() &&
edges_[t].next_succ != t);
}
template <typename... Args>
state new_state(Args&&... args)
{
state s = states_.size();
states_.emplace_back(std::forward<Args>(args)...);
return s;
}
template <typename... Args>
state new_states(unsigned n, Args&&... args)
{
state s = states_.size();
states_.reserve(s + n);
while (n--)
states_.emplace_back(std::forward<Args>(args)...);
return s;
}
state_storage_t&
state_storage(state s)
{
assert(s < states_.size());
return states_[s];
}
const state_storage_t&
state_storage(state s) const
{
assert(s < states_.size());
return states_[s];
}
// Do not use State_Data& as return type, because State_Data might
// be void.
typename state_storage_t::data_t&
state_data(state s)
{
assert(s < states_.size());
return states_[s].data();
}
// May not be called on states with no data.
const typename state_storage_t::data_t&
state_data(state s) const
{
assert(s < states_.size());
return states_[s].data();
}
edge_storage_t&
edge_storage(edge s)
{
assert(s < edges_.size());
return edges_[s];
}
const edge_storage_t&
edge_storage(edge s) const
{
assert(s < edges_.size());
return edges_[s];
}
typename edge_storage_t::data_t&
edge_data(edge s)
{
assert(s < edges_.size());
return edges_[s].data();
}
const typename edge_storage_t::data_t&
edge_data(edge s) const
{
assert(s < edges_.size());
return edges_[s].data();
}
template <typename... Args>
edge
new_edge(state src, out_state dst, Args&&... args)
{
assert(src < states_.size());
edge t = edges_.size();
edges_.emplace_back(dst, 0, src, std::forward<Args>(args)...);
edge st = states_[src].succ_tail;
assert(st < t || !st);
if (!st)
states_[src].succ = t;
else
edges_[st].next_succ = t;
states_[src].succ_tail = t;
return t;
}
state index_of_state(const state_storage_t& ss) const
{
assert(!states_.empty());
return &ss - &states_.front();
}
edge index_of_edge(const edge_storage_t& tt) const
{
assert(!edges_.empty());
return &tt - &edges_.front();
}
internal::state_out<digraph>
out(state src)
{
return {this, states_[src].succ};
}
internal::state_out<digraph>
out(state_storage_t& src)
{
return out(index_of_state(src));
}
internal::state_out<const digraph>
out(state src) const
{
return {this, states_[src].succ};
}
internal::state_out<const digraph>
out(state_storage_t& src) const
{
return out(index_of_state(src));
}
internal::killer_edge_iterator<digraph>
out_iteraser(state_storage_t& src)
{
return {this, src.succ, src};
}
internal::killer_edge_iterator<digraph>
out_iteraser(state src)
{
return out_iteraser(state_storage(src));
}
const state_vector& states() const
{
return states_;
}
state_vector& states()
{
return states_;
}
internal::all_trans<const digraph> edges() const
{
return edges_;
}
internal::all_trans<digraph> edges()
{
return edges_;
}
// When using this method, beware that the first entry (edge
// #0) is not a real edge, and that any edge with
// next_succ pointing to itself is an erased edge.
//
// You should probably use edges() instead.
const edge_vector_t& edge_vector() const
{
return edges_;
}
edge_vector_t& edge_vector()
{
return edges_;
}
bool is_dead_edge(unsigned t) const
{
return edges_[t].next_succ == t;
}
bool is_dead_edge(const edge_storage_t& t) const
{
return t.next_succ == index_of_edge(t);
}
// To help debugging
void dump_storage(std::ostream& o) const
{
unsigned tend = edges_.size();
for (unsigned t = 1; t < tend; ++t)
{
o << 't' << t << ": (s"
<< edges_[t].src << ", s"
<< edges_[t].dst << ") t"
<< edges_[t].next_succ << '\n';
}
unsigned send = states_.size();
for (unsigned s = 0; s < send; ++s)
{
o << 's' << s << ": t"
<< states_[s].succ << " t"
<< states_[s].succ_tail << '\n';
}
}
// Remove all dead edges. The edges_ vector is left
// in a state that is incorrect and should eventually be fixed by
// a call to chain_edges_() before any iteration on the
// successor of a state is performed.
void remove_dead_edges_()
{
if (killed_edge_ == 0)
return;
auto i = std::remove_if(edges_.begin() + 1, edges_.end(),
[this](const edge_storage_t& t) {
return this->is_dead_edge(t);
});
edges_.erase(i, edges_.end());
killed_edge_ = 0;
}
// This will invalidate all iterators, and also destroy edge
// chains. Call chain_edges_() immediately afterwards
// unless you know what you are doing.
template<class Predicate = std::less<edge_storage_t>>
void sort_edges_(Predicate p = Predicate())
{
//std::cerr << "\nbefore\n";
//dump_storage(std::cerr);
std::stable_sort(edges_.begin() + 1, edges_.end(), p);
}
// Should be called only when it is known that all edges
// with the same destination are consecutive in the vector.
void chain_edges_()
{
state last_src = -1U;
edge tend = edges_.size();
for (edge t = 1; t < tend; ++t)
{
state src = edges_[t].src;
if (src != last_src)
{
states_[src].succ = t;
if (last_src != -1U)
{
states_[last_src].succ_tail = t - 1;
edges_[t - 1].next_succ = 0;
}
while (++last_src != src)
{
states_[last_src].succ = 0;
states_[last_src].succ_tail = 0;
}
}
else
{
edges_[t - 1].next_succ = t;
}
}
if (last_src != -1U)
{
states_[last_src].succ_tail = tend - 1;
edges_[tend - 1].next_succ = 0;
}
unsigned send = states_.size();
while (++last_src != send)
{
states_[last_src].succ = 0;
states_[last_src].succ_tail = 0;
}
//std::cerr << "\nafter\n";
//dump_storage(std::cerr);
}
// Rename all the states in the edge vector. The
// edges_ vector is left in a state that is incorrect and
// should eventually be fixed by a call to chain_edges_()
// before any iteration on the successor of a state is performed.
void rename_states_(const std::vector<unsigned>& newst)
{
assert(newst.size() == states_.size());
unsigned tend = edges_.size();
for (unsigned t = 1; t < tend; t++)
{
edges_[t].dst = newst[edges_[t].dst];
edges_[t].src = newst[edges_[t].src];
}
}
void defrag_states(std::vector<unsigned>&& newst, unsigned used_states)
{
assert(newst.size() == states_.size());
assert(used_states > 0);
//std::cerr << "\nbefore defrag\n";
//dump_storage(std::cerr);
// Shift all states in states_, as indicated by newst.
unsigned send = states_.size();
for (state s = 0; s < send; ++s)
{
state dst = newst[s];
if (dst == s)
continue;
if (dst == -1U)
{
// This is an erased state. Mark all its edges as
// dead (i.e., t.next_succ should point to t for each of
// them).
auto t = states_[s].succ;
while (t)
std::swap(t, edges_[t].next_succ);
continue;
}
states_[dst] = std::move(states_[s]);
}
states_.resize(used_states);
// Shift all edges in edges_. The algorithm is
// similar to remove_if, but it also keeps the correspondence
// between the old and new index as newidx[old] = new.
unsigned tend = edges_.size();
std::vector<edge> newidx(tend);
unsigned dest = 1;
for (edge t = 1; t < tend; ++t)
{
if (is_dead_edge(t))
continue;
if (t != dest)
edges_[dest] = std::move(edges_[t]);
newidx[t] = dest;
++dest;
}
edges_.resize(dest);
killed_edge_ = 0;
// Adjust next_succ and dst pointers in all edges.
for (edge t = 1; t < dest; ++t)
{
auto& tr = edges_[t];
tr.next_succ = newidx[tr.next_succ];
tr.dst = newst[tr.dst];
tr.src = newst[tr.src];
assert(tr.dst != -1U);
}
// Adjust succ and succ_tails pointers in all states.
for (auto& s: states_)
{
s.succ = newidx[s.succ];
s.succ_tail = newidx[s.succ_tail];
}
//std::cerr << "\nafter defrag\n";
//dump_storage(std::cerr);
}
};
}

163
spot/graph/ngraph.hh Normal file
View file

@ -0,0 +1,163 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 <unordered_map>
#include <vector>
#include <spot/graph/graph.hh>
namespace spot
{
template <typename Graph,
typename State_Name,
typename Name_Hash = std::hash<State_Name>,
typename Name_Equal = std::equal_to<State_Name>>
class SPOT_API named_graph
{
protected:
Graph& g_;
public:
typedef typename Graph::state state;
typedef typename Graph::edge edge;
typedef State_Name name;
typedef std::unordered_map<name, state,
Name_Hash, Name_Equal> name_to_state_t;
name_to_state_t name_to_state;
typedef std::vector<name> state_to_name_t;
state_to_name_t state_to_name;
named_graph(Graph& g)
: g_(g)
{
}
Graph& graph()
{
return g_;
}
Graph& graph() const
{
return g_;
}
template <typename... Args>
state new_state(name n, Args&&... args)
{
auto p = name_to_state.emplace(n, 0U);
if (p.second)
{
unsigned s = g_.new_state(std::forward<Args>(args)...);
p.first->second = s;
if (state_to_name.size() < s + 1)
state_to_name.resize(s + 1);
state_to_name[s] = n;
return s;
}
return p.first->second;
}
/// \brief Give an alternate name to a state.
/// \return true iff the newname state was already existing
/// (in this case the existing newname state will be merged
/// with state s: the newname will be unreachable and without
/// successors.)
bool alias_state(state s, name newname)
{
auto p = name_to_state.emplace(newname, s);
if (!p.second)
{
// The state already exists. Change its number.
auto old = p.first->second;
p.first->second = s;
// Add the successor of OLD to those of S.
auto& trans = g_.edge_vector();
auto& states = g_.states();
trans[states[s].succ_tail].next_succ = states[old].succ;
states[s].succ_tail = states[old].succ_tail;
states[old].succ = 0;
states[old].succ_tail = 0;
// Remove all references to old in edges:
unsigned tend = trans.size();
for (unsigned t = 1; t < tend; ++t)
{
if (trans[t].src == old)
trans[t].src = s;
if (trans[t].dst == old)
trans[t].dst = s;
}
}
return !p.second;
}
state get_state(name n) const
{
return name_to_state.at(n);
}
name get_name(state s) const
{
return state_to_name.at(s);
}
bool has_state(name n) const
{
return name_to_state.find(n) != name_to_state.end();
}
const state_to_name_t& names() const
{
return state_to_name;
}
template <typename... Args>
edge
new_edge(name src, name dst, Args&&... args)
{
return g_.new_edge(get_state(src), get_state(dst),
std::forward<Args>(args)...);
}
template <typename... Args>
edge
new_edge(name src, const std::vector<State_Name>& dst, Args&&... args)
{
std::vector<State_Name> d;
d.reserve(dst.size());
for (auto n: dst)
d.push_back(get_state(n));
return g_.new_edge(get_state(src), d, std::forward<Args>(args)...);
}
template <typename... Args>
edge
new_edge(name src,
const std::initializer_list<State_Name>& dst, Args&&... args)
{
std::vector<state> d;
d.reserve(dst.size());
for (auto n: dst)
d.push_back(get_state(n));
return g_.new_edge(get_state(src), d, std::forward<Args>(args)...);
}
};
}

34
spot/kripke/Makefile.am Normal file
View file

@ -0,0 +1,34 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2009, 2011, 2013, 2014, 2015 Laboratoire de Recherche
## et Developpement 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/>.
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS)
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
kripkedir = $(pkgincludedir)/kripke
kripke_HEADERS = \
fairkripke.hh \
fwd.hh \
kripke.hh \
kripkegraph.hh
noinst_LTLIBRARIES = libkripke.la
libkripke_la_SOURCES = \
fairkripke.cc \
kripke.cc

57
spot/kripke/fairkripke.cc Normal file
View file

@ -0,0 +1,57 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2014 Laboratoire de Recherche et
// Developpement de l'Epita
//
// 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 <spot/kripke/fairkripke.hh>
namespace spot
{
fair_kripke_succ_iterator::fair_kripke_succ_iterator
(const bdd& cond, acc_cond::mark_t acc_cond)
: cond_(cond), acc_cond_(acc_cond)
{
}
fair_kripke_succ_iterator::~fair_kripke_succ_iterator()
{
}
bdd
fair_kripke_succ_iterator::cond() const
{
// Do not assert(!done()) here. It is OK to call
// this function on a state without successor.
return cond_;
}
acc_cond::mark_t
fair_kripke_succ_iterator::acc() const
{
// Do not assert(!done()) here. It is OK to call
// this function on a state without successor.
return acc_cond_;
}
bdd
fair_kripke::compute_support_conditions(const state* s) const
{
return state_condition(s);
}
}

108
spot/kripke/fairkripke.hh Normal file
View file

@ -0,0 +1,108 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2013, 2014, 2015 Laboratoire de Recherche et
// Developpement de l'Epita
//
// 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 <spot/twa/twa.hh>
#include <spot/kripke/fwd.hh>
/// \addtogroup kripke Kripke Structures
/// \ingroup twa
namespace spot
{
/// \ingroup kripke
/// \brief Iterator code for a Fair Kripke structure.
///
/// This iterator can be used to simplify the writing
/// of an iterator on a Fair Kripke structure (or lookalike).
///
/// If you inherit from this iterator, you should only
/// redefine
///
/// - fair_kripke_succ_iterator::first()
/// - fair_kripke_succ_iterator::next()
/// - fair_kripke_succ_iterator::done()
/// - fair_kripke_succ_iterator::dst()
///
/// This class implements fair_kripke_succ_iterator::cond(),
/// and fair_kripke_succ_iterator::acc().
class SPOT_API fair_kripke_succ_iterator : public twa_succ_iterator
{
public:
/// \brief Constructor
///
/// The \a cond and \a acc_cond arguments will be those returned
/// by fair_kripke_succ_iterator::cond(),
/// and fair_kripke_succ_iterator::acc().
fair_kripke_succ_iterator(const bdd& cond, acc_cond::mark_t acc_cond);
virtual ~fair_kripke_succ_iterator();
virtual bdd cond() const;
virtual acc_cond::mark_t acc() const;
protected:
bdd cond_;
acc_cond::mark_t acc_cond_;
};
/// \ingroup kripke
/// \brief Interface for a Fair Kripke structure.
///
/// A Kripke structure is a graph in which each node (=state) is
/// labeled by a conjunction of atomic proposition, and a set of
/// acceptance conditions.
///
/// Such a structure can be seen as spot::tgba by pushing all labels
/// to the outgoing transitions.
///
/// A programmer that develops an instance of Fair Kripke structure
/// needs just provide an implementation for the following methods:
///
/// - kripke::get_init_state()
/// - kripke::succ_iter()
/// - kripke::state_condition()
/// - kripke::state_acceptance_conditions()
/// - kripke::format_state()
/// - and optionally kripke::transition_annotation()
///
/// The other methods of the tgba interface are supplied by this
/// class and need not be defined.
///
/// See also spot::fair_kripke_succ_iterator.
class SPOT_API fair_kripke: public twa
{
public:
fair_kripke(const bdd_dict_ptr& d)
: twa(d)
{
}
/// \brief The condition that label the state \a s.
///
/// This should be a conjunction of atomic propositions.
virtual bdd state_condition(const state* s) const = 0;
/// \brief The set of acceptance conditions that label the state \a s.
virtual acc_cond::mark_t
state_acceptance_conditions(const state* s) const = 0;
protected:
virtual bdd compute_support_conditions(const state* s) const;
};
}

37
spot/kripke/fwd.hh Normal file
View file

@ -0,0 +1,37 @@
// -*- 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/>.
#pragma once
#include <memory>
namespace spot
{
class fair_kripke;
typedef std::shared_ptr<fair_kripke> fair_kripke_ptr;
typedef std::shared_ptr<const fair_kripke> const_fair_kripke_ptr;
class kripke;
typedef std::shared_ptr<const kripke> const_kripke_ptr;
typedef std::shared_ptr<kripke> tgba_kripke_ptr;
class kripke_explicit;
typedef std::shared_ptr<const kripke_explicit> const_kripke_explicit_ptr;
typedef std::shared_ptr<kripke_explicit> kripke_explicit_ptr;
}

54
spot/kripke/kripke.cc Normal file
View file

@ -0,0 +1,54 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2014 Laboratoire de Recherche et
// Developpement de l'Epita
//
// 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 <spot/kripke/kripke.hh>
namespace spot
{
kripke_succ_iterator::~kripke_succ_iterator()
{
}
bdd
kripke_succ_iterator::cond() const
{
// Do not assert(!done()) here. It is OK to call
// this function on a state without successor.
return cond_;
}
acc_cond::mark_t
kripke_succ_iterator::acc() const
{
// Do not assert(!done()) here. It is OK to call
// this function on a state without successor.
return 0U;
}
kripke::~kripke()
{
}
acc_cond::mark_t
kripke::state_acceptance_conditions(const state*) const
{
return 0U;
}
}

106
spot/kripke/kripke.hh Normal file
View file

@ -0,0 +1,106 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2013, 2014 Laboratoire de Recherche et
// Developpement de l'Epita
//
// 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 <spot/kripke/fairkripke.hh>
namespace spot
{
/// \ingroup kripke
/// \brief Iterator code for Kripke structure
///
/// This iterator can be used to simplify the writing
/// of an iterator on a Kripke structure (or lookalike).
///
/// If you inherit from this iterator, you should only
/// redefine
///
/// - kripke_succ_iterator::first()
/// - kripke_succ_iterator::next()
/// - kripke_succ_iterator::done()
/// - kripke_succ_iterator::dst()
///
/// This class implements kripke_succ_iterator::cond(),
/// and kripke_succ_iterator::acc().
class SPOT_API kripke_succ_iterator : public twa_succ_iterator
{
public:
/// \brief Constructor
///
/// The \a cond argument will be the one returned
/// by kripke_succ_iterator::cond().
kripke_succ_iterator(const bdd& cond)
: cond_(cond)
{
}
void recycle(const bdd& cond)
{
cond_ = cond;
}
virtual ~kripke_succ_iterator();
virtual bdd cond() const;
virtual acc_cond::mark_t acc() const;
protected:
bdd cond_;
};
/// \ingroup kripke
/// \brief Interface for a Kripke structure
///
/// A Kripke structure is a graph in which each node (=state) is
/// labeled by a conjunction of atomic proposition.
///
/// Such a structure can be seen as spot::tgba without
/// any acceptance condition.
///
/// A programmer that develops an instance of Kripke structure needs
/// just provide an implementation for the following methods:
///
/// - kripke::get_init_state()
/// - kripke::succ_iter()
/// - kripke::state_condition()
/// - kripke::format_state()
/// - and optionally kripke::transition_annotation()
///
/// The other methods of the tgba interface (like those dealing with
/// acceptance conditions) are supplied by this kripke class and
/// need not be defined.
///
/// See also spot::kripke_succ_iterator.
class SPOT_API kripke: public fair_kripke
{
public:
kripke(const bdd_dict_ptr& d)
: fair_kripke(d)
{
}
virtual ~kripke();
virtual acc_cond::mark_t state_acceptance_conditions(const state*) const;
};
typedef std::shared_ptr<kripke> kripke_ptr;
typedef std::shared_ptr<const kripke> const_kripke_ptr;
}

282
spot/kripke/kripkegraph.hh Normal file
View file

@ -0,0 +1,282 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 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 <iosfwd>
#include <spot/kripke/kripke.hh>
#include <spot/graph/graph.hh>
#include <spot/tl/formula.hh>
namespace spot
{
/// \brief Concrete class for kripke_graph states.
struct SPOT_API kripke_graph_state: public spot::state
{
public:
kripke_graph_state(bdd cond = bddfalse) noexcept
: cond_(cond)
{
}
virtual ~kripke_graph_state() noexcept
{
}
virtual int compare(const spot::state* other) const
{
auto o = down_cast<const kripke_graph_state*>(other);
assert(o);
// Do not simply return "other - this", it might not fit in an int.
if (o < this)
return -1;
if (o > this)
return 1;
return 0;
}
virtual size_t hash() const
{
return
reinterpret_cast<const char*>(this) - static_cast<const char*>(nullptr);
}
virtual kripke_graph_state*
clone() const
{
return const_cast<kripke_graph_state*>(this);
}
virtual void destroy() const
{
}
bdd cond() const
{
return cond_;
}
void cond(bdd c)
{
cond_ = c;
}
private:
bdd cond_;
};
template<class Graph>
class SPOT_API kripke_graph_succ_iterator final: public kripke_succ_iterator
{
private:
typedef typename Graph::edge edge;
typedef typename Graph::state_data_t state;
const Graph* g_;
edge t_;
edge p_;
public:
kripke_graph_succ_iterator(const Graph* g,
const typename Graph::state_storage_t* s):
kripke_succ_iterator(s->cond()),
g_(g),
t_(s->succ)
{
}
~kripke_graph_succ_iterator()
{
}
virtual void recycle(const typename Graph::state_storage_t* s)
{
cond_ = s->cond();
t_ = s->succ;
}
virtual bool first()
{
p_ = t_;
return p_;
}
virtual bool next()
{
p_ = g_->edge_storage(p_).next_succ;
return p_;
}
virtual bool done() const
{
return !p_;
}
virtual kripke_graph_state* dst() const
{
assert(!done());
return const_cast<kripke_graph_state*>
(&g_->state_data(g_->edge_storage(p_).dst));
}
};
/// \class kripke_graph
/// \brief Kripke Structure.
class SPOT_API kripke_graph : public kripke
{
public:
typedef digraph<kripke_graph_state, void> graph_t;
typedef graph_t::edge_storage_t edge_storage_t;
protected:
graph_t g_;
mutable unsigned init_number_;
public:
kripke_graph(const bdd_dict_ptr& d)
: kripke(d), init_number_(0)
{
}
virtual ~kripke_graph()
{
get_dict()->unregister_all_my_variables(this);
}
unsigned num_states() const
{
return g_.num_states();
}
unsigned num_edges() const
{
return g_.num_edges();
}
void set_init_state(graph_t::state s)
{
assert(s < num_states());
init_number_ = s;
}
graph_t::state get_init_state_number() const
{
if (num_states() == 0)
const_cast<graph_t&>(g_).new_state();
return init_number_;
}
virtual const kripke_graph_state* get_init_state() const
{
if (num_states() == 0)
const_cast<graph_t&>(g_).new_state();
return state_from_number(init_number_);
}
/// \brief Allow to get an iterator on the state we passed in
/// parameter.
virtual kripke_graph_succ_iterator<graph_t>*
succ_iter(const spot::state* st) const
{
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
assert(s);
assert(!s->succ || g_.valid_trans(s->succ));
if (this->iter_cache_)
{
auto it =
down_cast<kripke_graph_succ_iterator<graph_t>*>(this->iter_cache_);
it->recycle(s);
this->iter_cache_ = nullptr;
return it;
}
return new kripke_graph_succ_iterator<graph_t>(&g_, s);
}
graph_t::state
state_number(const state* st) const
{
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
assert(s);
return s - &g_.state_storage(0);
}
const kripke_graph_state*
state_from_number(graph_t::state n) const
{
return &g_.state_data(n);
}
kripke_graph_state*
state_from_number(graph_t::state n)
{
return &g_.state_data(n);
}
std::string format_state(unsigned n) const
{
std::stringstream ss;
ss << n;
return ss.str();
}
virtual std::string format_state(const state* st) const
{
return format_state(state_number(st));
}
/// \brief Get the condition on the state
virtual bdd state_condition(const state* s) const
{
return down_cast<const kripke_graph_state*>(s)->cond();
}
edge_storage_t& edge_storage(unsigned t)
{
return g_.edge_storage(t);
}
const edge_storage_t edge_storage(unsigned t) const
{
return g_.edge_storage(t);
}
unsigned new_state(bdd cond)
{
return g_.new_state(cond);
}
unsigned new_states(unsigned n, bdd cond)
{
return g_.new_states(n, cond);
}
unsigned new_edge(unsigned src, unsigned dst)
{
return g_.new_edge(src, dst);
}
};
typedef std::shared_ptr<kripke_graph> kripke_graph_ptr;
inline kripke_graph_ptr
make_kripke_graph(const bdd_dict_ptr& d)
{
return std::make_shared<kripke_graph>(d);
}
}

7
spot/misc/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
Makefile
Makefile.in
.deps
.libs
*.lo
*.la
_config.h

73
spot/misc/Makefile.am Normal file
View file

@ -0,0 +1,73 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2011, 2012, 2013, 2014 Laboratoire de Recherche et
## Développement de l'Epita (LRDE).
## Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
## Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
## Université Pierre et Marie Curie.
##
## 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/>.
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS) \
-I$(top_builddir)/lib -I$(top_srcdir)/lib
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
miscdir = $(pkgincludedir)/misc
nodist_misc_HEADERS = _config.h
DISTCLEANFILES = _config.h
misc_HEADERS = \
bareword.hh \
bddlt.hh \
bitvect.hh \
casts.hh \
common.hh \
escape.hh \
fixpool.hh \
formater.hh \
hash.hh \
hashfunc.hh \
intvcomp.hh \
intvcmp2.hh \
location.hh \
ltstr.hh \
minato.hh \
memusage.hh \
mspool.hh \
optionmap.hh \
position.hh \
random.hh \
satsolver.hh \
timer.hh \
tmpfile.hh \
version.hh
noinst_LTLIBRARIES = libmisc.la
libmisc_la_SOURCES = \
bareword.cc \
bitvect.cc \
escape.cc \
formater.cc \
intvcomp.cc \
intvcmp2.cc \
memusage.cc \
minato.cc \
optionmap.cc \
random.cc \
satsolver.cc \
timer.cc \
tmpfile.cc \
version.cc

65
spot/misc/bareword.cc Normal file
View file

@ -0,0 +1,65 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 "config.h"
#include <spot/misc/bareword.hh>
#include <ctype.h>
#include <spot/misc/escape.hh>
namespace spot
{
bool
is_bare_word(const char* str)
{
// Bare words cannot be empty and should start with a letter.
if (!*str
|| !(isalpha(*str) || *str == '_' || *str == '.'))
return false;
// The remaining of the word must be alphanumeric.
while (*++str)
if (!(isalnum(*str) || *str == '_' || *str == '.'))
return false;
return true;
}
std::string
quote_unless_bare_word(const std::string& str)
{
if (is_bare_word(str.c_str()))
return str;
else
return "\"" + escape_str(str) + "\"";
}
// This is for Spin 5. Spin 6 has a relaxed parser that can
// accept any parenthesized block as an atomic propoistion.
bool is_spin_ap(const char* str)
{
if (!str || !islower(*str))
return false;
while (*++str)
if (!(isalnum(*str) || *str == '_'))
return false;
return true;
}
}

53
spot/misc/bareword.hh Normal file
View file

@ -0,0 +1,53 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
#include <string>
namespace spot
{
/// \defgroup misc_tools Miscellaneous helper functions
/// \ingroup misc_tools
/// @{
/// \brief Whether a word is bare.
///
/// Bare words should start with a letter, an underscore, or a dot,
/// and consist solely of alphanumeric characters, underscores, and
/// dots.
SPOT_API bool is_bare_word(const char* str);
/// \brief Double-quote words that are not bare.
/// \see is_bare_word
SPOT_API std::string quote_unless_bare_word(const std::string& str);
/// \brief Whether a word can be used as an atomic proposition for Spin 5.
///
/// In Spin 5 (hence in ltl2ba and ltl3ba as well) atomic
/// propositions should start with a lowercase letter, and can then
/// consist solely of alphanumeric characters and underscores.
SPOT_API bool is_spin_ap(const char* str);
/// @}
}

53
spot/misc/bddlt.hh Normal file
View file

@ -0,0 +1,53 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2014 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <bddx.h>
#include <functional>
namespace spot
{
/// \ingroup misc_tools
/// \brief Comparison functor for BDDs.
struct bdd_less_than :
public std::binary_function<const bdd&, const bdd&, bool>
{
bool
operator()(const bdd& left, const bdd& right) const
{
return left.id() < right.id();
}
};
/// \ingroup misc_tools
/// \brief Hash functor for BDDs.
struct bdd_hash :
public std::unary_function<const bdd&, size_t>
{
size_t
operator()(const bdd& b) const
{
return b.id();
}
};
}

192
spot/misc/bitvect.cc Normal file
View file

@ -0,0 +1,192 @@
// -*- coding: utf-8 -*-
// Copyright (C) 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/>.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <spot/misc/bitvect.hh>
#include <new>
#include <iostream>
#include <cmath>
#include <hashfunc.hh>
#include <cstring>
#include <climits>
namespace spot
{
namespace
{
// How many block_t do we need to store BITCOUNT bits?
size_t block_needed(size_t bitcount)
{
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t n = (bitcount + bpb - 1) / bpb;
if (n < 1)
return 1;
return n;
}
// Fowler-Noll-Vo hash parameters.
// Add specializations as needed.
template<int numbytes>
struct fnv
{
};
// Do not define the following if ULONG_MAX cannot
// hold a 64-bit value, otherwise the parser will
// choke when parsing the constants.
#if ULONG_MAX >> 31 >> 31 >> 1 > 0
// Fowler-Noll-Vo hash parameters for 64bits
template<>
struct fnv<8>
{
static unsigned long init()
{
return 14695981039346656037UL;
}
static unsigned long prime()
{
return 1099511628211UL;
}
};
#endif
// Fowler-Noll-Vo hash parameters for 32bits
template<>
struct fnv<4>
{
static unsigned long init()
{
return 2166136261UL;
}
static unsigned long prime()
{
return 16777619UL;
}
};
}
bitvect::bitvect(size_t size, size_t block_count):
size_(size),
block_count_(block_count),
storage_(&local_storage_)
{
clear_all();
}
bitvect::bitvect(size_t size, size_t block_count, bool):
size_(size),
block_count_(block_count),
storage_(&local_storage_)
{
}
bitvect* bitvect::clone() const
{
size_t n = block_needed(size_);
// Allocate some memory for the bitvect. The instance
// already contains one int of local_storage_, but
// we allocate n-1 more so that we store the table.
void* mem = operator new(sizeof(bitvect)
+ (n - 1) * sizeof(bitvect::block_t));
bitvect* res = new(mem) bitvect(size_, n, true);
memcpy(res->storage_, storage_, res->block_count_ * sizeof(block_t));
return res;
}
size_t bitvect::hash() const
{
block_t res = fnv<sizeof(block_t)>::init();
size_t i;
size_t m = used_blocks();
if (m == 0)
return res;
for (i = 0; i < m - 1; ++i)
{
res ^= storage_[i];
res *= fnv<sizeof(block_t)>::prime();
}
// Deal with the last block, that might not be fully used.
// Compute the number n of bits used in the last block.
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t n = size() % bpb;
// Use only the least n bits from storage_[i].
res ^= storage_[i] & ((1UL << n) - 1);
return res;
}
bitvect* make_bitvect(size_t bitcount)
{
size_t n = block_needed(bitcount);
// Allocate some memory for the bitvect. The instance
// already contains one int of local_storage_, but
// we allocate n-1 more so that we store the table.
void* mem = operator new(sizeof(bitvect)
+ (n - 1) * sizeof(bitvect::block_t));
return new(mem) bitvect(bitcount, n);
}
bitvect_array* make_bitvect_array(size_t bitcount, size_t vectcount)
{
size_t n = block_needed(bitcount);
// Size of one bitvect.
size_t bvsize = sizeof(bitvect) + (n - 1) * sizeof(bitvect::block_t);
// Allocate the bitvect_array with enough space at the end
// to store all bitvect instances.
void* mem = operator new(sizeof(bitvect_array) + bvsize * vectcount);
bitvect_array* bva = new(mem) bitvect_array(vectcount, bvsize);
// Initialize all the bitvect instances.
for (size_t i = 0; i < vectcount; ++i)
new(bva->storage() + i * bvsize) bitvect(bitcount, n);
return bva;
}
std::ostream& operator<<(std::ostream& os , const bitvect& v)
{
for (size_t i = 0, end = v.size(); i != end; ++i)
os << (v.get(i) ? '1' : '0');
return os;
}
std::ostream& operator<<(std::ostream& os , const bitvect_array& a)
{
size_t end = a.size();
if (end == 0)
{
os << "empty\n";
return os;
}
int w = floor(log10(end - 1)) + 1;
for (size_t i = 0; i != end; ++i)
{
os.width(w);
os << i;
os.width(1);
os << ": " << a.at(i) << '\n';
}
return os;
}
}

534
spot/misc/bitvect.hh Normal file
View file

@ -0,0 +1,534 @@
// -*- 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/>.
#pragma once
#include <spot/misc/common.hh>
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include <iosfwd>
#include <iostream>
#include <algorithm>
namespace spot
{
/// \ingroup misc_tools
/// @{
class bitvect;
class bitvect_array;
///\brief Allocate a bit-vector of \a bitcount bits.
///
/// The resulting object should be released with <code>delete</code>.
SPOT_API bitvect* make_bitvect(size_t bitcount);
/// \brief Allocate \a vectcount bit-vectors of \a bitcount bits.
///
/// The resulting bitvect_array should be released with <code>delete</code>.
SPOT_API bitvect_array* make_bitvect_array(size_t bitcount,
size_t vectcount);
/// \brief A bit vector
class SPOT_API bitvect
{
private:
// Used by make_bitvect to construct a large bitvect in place.
bitvect(size_t size, size_t block_count);
bitvect(size_t size, size_t block_count, bool);
public:
typedef unsigned long block_t;
bitvect():
size_(0),
block_count_(1),
storage_(&local_storage_),
local_storage_(0)
{
}
bitvect(const bitvect& other):
size_(other.size_),
block_count_(1),
storage_(&local_storage_)
{
*this = other;
}
bitvect* clone() const;
void make_empty()
{
size_ = 0;
}
bitvect& operator=(const bitvect& other)
{
reserve_blocks(other.block_count_);
size_ = other.size();
for (size_t i = 0; i < block_count_; ++i)
storage_[i] = other.storage_[i];
return *this;
}
~bitvect()
{
if (storage_ != &local_storage_)
free(storage_);
}
/// Grow the bitvector to \a new_block_count blocks.
///
/// This only changes the capacity of the bitvector, not its size.
void reserve_blocks(size_t new_block_count)
{
if (new_block_count < block_count_)
return;
if (storage_ == &local_storage_)
{
block_t* new_storage_ = static_cast<block_t*>
(malloc(new_block_count * sizeof(block_t)));
for (size_t i = 0; i < block_count_; ++i)
new_storage_[i] = storage_[i];
storage_ = new_storage_;
}
else
{
storage_ = static_cast<block_t*>
(realloc(storage_, new_block_count * sizeof(block_t)));
}
block_count_ = new_block_count;
}
private:
void grow()
{
size_t new_block_count_ = (block_count_ + 1) * 7 / 5;
reserve_blocks(new_block_count_);
}
public:
size_t used_blocks() const
{
const size_t bpb = 8 * sizeof(block_t);
return (size_ + bpb - 1) / bpb;
}
/// Append one bit.
void push_back(bool val)
{
if (size() == capacity())
grow();
size_t pos = size_++;
if (val)
set(pos);
else
clear(pos);
}
/// \brief Append the lowest \a count bits of \a data.
void push_back(block_t data, unsigned count)
{
if (size() + count > capacity())
grow();
const size_t bpb = 8 * sizeof(block_t);
// Clear the higher bits.
if (count != bpb)
data &= (1UL << count) - 1;
size_t n = size() % bpb;
size_t i = size_ / bpb;
size_ += count;
if (n == 0) // Aligned on block_t boundary
{
storage_[i] = data;
}
else // Only (bpb-n) bits free in this block.
{
// Take the lower bpb-n bits of data...
block_t mask = (1UL << (bpb - n)) - 1;
block_t data1 = (data & mask) << n;
mask <<= n;
// ... write them on the higher bpb-n bits of last block.
storage_[i] = (storage_[i] & ~mask) | data1;
// Write the remaining bits in the next block.
if (bpb - n < count)
storage_[i + 1] = data >> (bpb - n);
}
}
size_t size() const
{
return size_;
}
size_t capacity() const
{
return 8 * block_count_ * sizeof(block_t);
}
size_t hash() const;
bool get(size_t pos) const
{
assert(pos < size_);
const size_t bpb = 8 * sizeof(block_t);
return storage_[pos / bpb] & (1UL << (pos % bpb));
}
void clear_all()
{
for (size_t i = 0; i < block_count_; ++i)
storage_[i] = 0;
}
bool is_fully_clear() const
{
size_t i;
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
for (i = 0; i < block_count_ - !!rest; ++i)
if (storage_[i] != 0)
return false;
// The last block might not be fully used, compare only the
// relevant portion.
if (!rest)
return true;
block_t mask = (1UL << rest) - 1;
return (storage_[i] & mask) == 0;
}
bool is_fully_set() const
{
size_t i;
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
for (i = 0; i < block_count_ - !!rest; ++i)
if (storage_[i] != -1UL)
return false;
if (!rest)
return true;
// The last block might not be fully used, compare only the
// relevant portion.
block_t mask = (1UL << rest) - 1;
return ((~storage_[i]) & mask) == 0;
}
void set_all()
{
for (size_t i = 0; i < block_count_; ++i)
storage_[i] = -1UL;
}
void flip_all()
{
for (size_t i = 0; i < block_count_; ++i)
storage_[i] = ~storage_[i];
}
void set(size_t pos)
{
assert(pos < size_);
const size_t bpb = 8 * sizeof(block_t);
storage_[pos / bpb] |= 1UL << (pos % bpb);
}
void clear(size_t pos)
{
assert(pos < size_);
const size_t bpb = 8 * sizeof(block_t);
storage_[pos / bpb] &= ~(1UL << (pos % bpb));
}
void flip(size_t pos)
{
assert(pos < size_);
const size_t bpb = 8 * sizeof(block_t);
storage_[pos / bpb] ^= (1UL << (pos % bpb));
}
bitvect& operator|=(const bitvect& other)
{
assert(other.size_ <= size_);
unsigned m = std::min(other.block_count_, block_count_);
for (size_t i = 0; i < m; ++i)
storage_[i] |= other.storage_[i];
return *this;
}
bitvect& operator&=(const bitvect& other)
{
assert(other.size_ <= size_);
unsigned m = std::min(other.block_count_, block_count_);
for (size_t i = 0; i < m; ++i)
storage_[i] &= other.storage_[i];
return *this;
}
bitvect& operator^=(const bitvect& other)
{
assert(other.size_ <= size_);
unsigned m = std::min(other.block_count_, block_count_);
for (size_t i = 0; i < m; ++i)
storage_[i] ^= other.storage_[i];
return *this;
}
bitvect& operator-=(const bitvect& other)
{
assert(other.block_count_ <= block_count_);
for (size_t i = 0; i < other.block_count_; ++i)
storage_[i] &= ~other.storage_[i];
return *this;
}
bool is_subset_of(const bitvect& other) const
{
assert(other.block_count_ >= block_count_);
size_t i;
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
for (i = 0; i < block_count_ - !!rest; ++i)
if ((storage_[i] & other.storage_[i]) != other.storage_[i])
return false;
if (!rest)
return true;
// The last block might not be fully used, compare only the
// relevant portion.
block_t mask = (1UL << rest) - 1;
return ((storage_[i] & mask & other.storage_[i])
== (other.storage_[i] & mask));
}
bool operator==(const bitvect& other) const
{
if (other.size_ != size_)
return false;
if (size_ == 0)
return true;
size_t i;
size_t m = other.used_blocks();
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
for (i = 0; i < m - !!rest; ++i)
if (storage_[i] != other.storage_[i])
return false;
if (!rest)
return true;
// The last block might not be fully used, compare only the
// relevant portion.
block_t mask = (1UL << rest) - 1;
return (storage_[i] & mask) == (other.storage_[i] & mask);
}
bool operator!=(const bitvect& other) const
{
return !(*this == other);
}
bool operator<(const bitvect& other) const
{
if (size_ != other.size_)
return size_ < other.size_;
if (size_ == 0)
return false;
size_t i;
size_t m = other.used_blocks();
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t rest = size() % bpb;
for (i = 0; i < m - !!rest; ++i)
if (storage_[i] > other.storage_[i])
return false;
if (!rest)
return true;
// The last block might not be fully used, compare only the
// relevant portion.
block_t mask = (1UL << rest) - 1;
return (storage_[i] & mask) < (other.storage_[i] & mask);
}
bool operator>=(const bitvect& other) const
{
return !(*this < other);
}
bool operator>(const bitvect& other) const
{
return other < *this;
}
bool operator<=(const bitvect& other) const
{
return !(other < *this);
}
// \brief Extract a range of bits.
//
// Build a new bit-vector using the bits from \a begin (included)
// to \a end (excluded).
bitvect* extract_range(size_t begin, size_t end)
{
assert(begin <= end);
assert(end <= size());
size_t count = end - begin;
bitvect* res = make_bitvect(count);
res->make_empty();
if (end == begin)
return res;
const size_t bpb = 8 * sizeof(bitvect::block_t);
size_t indexb = begin / bpb;
unsigned bitb = begin % bpb;
size_t indexe = (end - 1) / bpb;
if (indexb == indexe)
{
block_t data = storage_[indexb];
data >>= bitb;
res->push_back(data, count);
}
else
{
block_t data = storage_[indexb];
data >>= bitb;
res->push_back(data, bpb - bitb);
count -= bpb - bitb;
while (count >= bpb)
{
++indexb;
res->push_back(storage_[indexb], bpb);
count -= bpb;
assert(indexb != indexe || count == 0);
}
if (count > 0)
{
++indexb;
assert(indexb == indexe);
assert(count == end % bpb);
res->push_back(storage_[indexb], count);
}
}
return res;
}
friend SPOT_API bitvect*
::spot::make_bitvect(size_t bitcount);
/// Print a bitvect.
friend SPOT_API std::ostream& operator<<(std::ostream&,
const bitvect&);
private:
friend SPOT_API bitvect_array*
::spot::make_bitvect_array(size_t bitcount,
size_t vectcount);
size_t size_;
size_t block_count_;
// storage_ points to local_storage_ as long as size_ <= block_count_ * 8.
block_t* storage_;
// Keep this at the end of the structure: when make_bitvect is used,
// it may allocate more block_t at the end of this structure.
block_t local_storage_;
};
class SPOT_API bitvect_array
{
private:
/// Private constructor used by make_bitvect_array().
bitvect_array(size_t size, size_t bvsize):
size_(size),
bvsize_(bvsize)
{
}
SPOT_LOCAL bitvect_array(const bitvect_array&) = delete;
SPOT_LOCAL void operator=(const bitvect_array&) = delete;
// Extra storage has been allocated at the end of the struct.
char* storage()
{
return reinterpret_cast<char*>(this) + sizeof(*this);
}
const char* storage() const
{
return reinterpret_cast<const char*>(this) + sizeof(*this);
}
public:
~bitvect_array()
{
for (size_t i = 0; i < size_; ++i)
at(i).~bitvect();
}
/// The number of bitvect in the array.
size_t size() const
{
return size_;
}
/// Return the bit-vector at \a index.
bitvect& at(const size_t index)
{
assert(index < size_);
return *reinterpret_cast<bitvect*>(storage() + index * bvsize_);
}
void clear_all()
{
// FIXME: This could be changed into a large memset if the
// individual vectors where not allowed to be reallocated.
for (unsigned s = 0; s < size_; s++)
at(s).clear_all();
}
/// Return the bit-vector at \a index.
const bitvect& at(const size_t index) const
{
assert(index < size_);
return *reinterpret_cast<const bitvect*>(storage() + index * bvsize_);
}
friend SPOT_API bitvect_array*
::spot::make_bitvect_array(size_t bitcount,
size_t vectcount);
/// Print a bitvect_array.
friend SPOT_API std::ostream& operator<<(std::ostream&,
const bitvect_array&);
private:
size_t size_;
size_t bvsize_;
};
/// @}
}

35
spot/misc/casts.hh Normal file
View file

@ -0,0 +1,35 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 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
// We usually write code like
// subclass* i = down_cast<subclass*>(m);
// assert(i);
// ... use i ...
// When NDEBUG is set, the down_cast is a fast static_cast
// and the assert has no effect.
// Otherwise, the down_cast is a dynamic_cast and may return 0
// on error, which the assert catches.
#if NDEBUG
# define down_cast static_cast
#else
# define down_cast dynamic_cast
#endif

118
spot/misc/common.hh Normal file
View file

@ -0,0 +1,118 @@
// -*- 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 <cstdlib>
#include <stdexcept>
#pragma once
#ifdef __GNUC__
#define SPOT_LIKELY(expr) __builtin_expect(!!(expr), 1)
#define SPOT_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
#else
#define SPOT_LIKELY(expr) (expr)
#define SPOT_UNLIKELY(expr) (expr)
#endif
#ifdef __GNUC__
#define SPOT_DEPRECATED __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define SPOT_DEPRECATED __declspec(deprecated)
#else
#define SPOT_DEPRECATED func
#endif
#if defined _WIN32 || defined __CYGWIN__
#define SPOT_HELPER_DLL_IMPORT __declspec(dllimport)
#define SPOT_HELPER_DLL_EXPORT __declspec(dllexport)
#define SPOT_HELPER_DLL_LOCAL
#else
#if __GNUC__ >= 4
#define SPOT_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
#define SPOT_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))
#define SPOT_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define SPOT_HELPER_DLL_IMPORT
#define SPOT_HELPER_DLL_EXPORT
#define SPOT_HELPER_DLL_LOCAL
#endif
#endif
#ifdef SPOT_BUILD
#define SPOT_DLL
#endif
// SPOT_API is used for the public API symbols. It either DLL imports
// or DLL exports (or does nothing for static build) SPOT_LOCAL is
// used for non-api symbols that may occur in header files.
#ifdef SPOT_DLL
#ifdef SPOT_BUILD
#define SPOT_API SPOT_HELPER_DLL_EXPORT
#else
#define SPOT_API SPOT_HELPER_DLL_IMPORT
#endif
#define SPOT_LOCAL SPOT_HELPER_DLL_LOCAL
#else
#define SPOT_API
#define SPOT_LOCAL
#endif
#define SPOT_API_VAR extern SPOT_API
// Swig 3.0.2 does not understand 'final' when used
// at class definition.
#ifdef SWIG
#define final
#endif
// Do not use those in code, prefer SPOT_UNREACHABLE() instead.
#if defined __clang__ || defined __GNU__
# define SPOT_UNREACHABLE_BUILTIN() __builtin_unreachable()
# elif defined _MSC_VER
# define SPOT_UNREACHABLE_BUILTIN() __assume(0)
# else
# define SPOT_UNREACHABLE_BUILTIN() abort()
#endif
// The extra parentheses in assert() is so that this
// pattern is not caught by the style checker.
#define SPOT_UNREACHABLE() do { \
assert(!("unreachable code reached")); \
SPOT_UNREACHABLE_BUILTIN(); \
} while (0)
#define SPOT_UNIMPLEMENTED() throw std::runtime_error("unimplemented");
// Useful when forwarding methods such as:
// auto func(int param) SPOT_RETURN(implem_.func(param));
#define SPOT_RETURN(code) -> decltype(code) { return code; }
namespace spot
{
struct SPOT_API parse_error: public std::runtime_error
{
parse_error(const std::string& s)
: std::runtime_error(s)
{
}
};
}

162
spot/misc/escape.cc Normal file
View file

@ -0,0 +1,162 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE)
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 "config.h"
#include <sstream>
#include <ostream>
#include <cstring>
#include <spot/misc/escape.hh>
namespace spot
{
std::ostream&
escape_rfc4180(std::ostream& os, const std::string& str)
{
for (auto i: str)
switch (i)
{
case '"':
os << "\"\"";
break;
default:
os << i;
break;
}
return os;
}
std::ostream&
escape_latex(std::ostream& os, const std::string& str)
{
for (auto i: str)
switch (i)
{
case '~':
os << "\\text{\\textasciitilde}";
break;
case '^':
os << "\\text{\\textasciicircum}";
break;
case '\\':
os << "\\text{\\textbackslash}";
break;
case '&':
case '%':
case '$':
case '#':
case '_':
case '{':
case '}':
os << '\\';
default:
os << i;
break;
}
return os;
}
std::ostream&
escape_html(std::ostream& os, const std::string& str)
{
for (auto i: str)
switch (i)
{
case '&':
os << "&amp;";
break;
case '"':
os << "&quot;";
break;
case '<':
os << "&lt;";
break;
case '>':
os << "&gt;";
break;
default:
os << i;
break;
}
return os;
}
std::ostream&
escape_str(std::ostream& os, const std::string& str)
{
for (auto i: str)
switch (i)
{
case '\\':
os << "\\\\";
break;
case '"':
os << "\\\"";
break;
case '\n':
os << "\\n";
break;
default:
os << i;
break;
}
return os;
}
std::string
escape_str(const std::string& str)
{
std::ostringstream os;
escape_str(os, str);
return os.str();
}
std::ostream&
quote_shell_string(std::ostream& os, const char* str)
{
// Single quotes are best, unless the string to quote contains one.
if (!strchr(str, '\''))
{
os << '\'' << str << '\'';
}
else
{
// In double quotes we have to escape $ ` " or \.
os << '"';
while (*str)
switch (*str)
{
case '$':
case '`':
case '"':
case '\\':
os << '\\';
// fall through
default:
os << *str++;
break;
}
os << '"';
}
return os;
}
}

73
spot/misc/escape.hh Normal file
View file

@ -0,0 +1,73 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2015 Laboratoire de Recherche et
// Developpement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
#include <iosfwd>
#include <string>
namespace spot
{
/// \ingroup misc_tools
/// @{
/// \brief Double characters <code>"</code> in strings.
///
/// In CSV files, as defined by RFC4180, double-quoted string that
/// contain double-quotes should simply duplicate those quotes.
SPOT_API std::ostream&
escape_rfc4180(std::ostream& os, const std::string& str);
/// \brief Escape special LaTeX characters.
///
/// The following characters are rewritten:
/// <code>& % $ # _ { } ~ ^ \\</code>
SPOT_API std::ostream&
escape_latex(std::ostream& os, const std::string& str);
/// \brief Escape special HTML characters.
///
/// The following characters are rewritten:
/// <code>&gt; &lt; &quot; &amp;</code>
SPOT_API std::ostream&
escape_html(std::ostream& os, const std::string& str);
/// \brief Escape characters <code>"</code>, <code>\\</code>, and
/// <code>\\n</code> in \a str.
SPOT_API std::ostream&
escape_str(std::ostream& os, const std::string& str);
/// \brief Escape characters <code>"</code>, <code>\\</code>, and
/// <code>\\n</code> in \a str.
SPOT_API std::string
escape_str(const std::string& str);
/// \brief Output \a str between simple quote or double quotes
///
/// Simple quotes are preferred unless \a str contains some simple
/// quotes. In that case we use double quotes and escape anything
/// that needs to be escaped.
SPOT_API std::ostream&
quote_shell_string(std::ostream& os, const char* str);
/// @}
}

112
spot/misc/fixpool.hh Normal file
View file

@ -0,0 +1,112 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 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 <new>
#include <cstddef>
#include <cstdlib>
#include <cassert>
namespace spot
{
/// A fixed-size memory pool implementation.
class fixed_size_pool
{
public:
/// Create a pool allocating objects of \a size bytes.
fixed_size_pool(size_t size)
: freelist_(nullptr), free_start_(nullptr),
free_end_(nullptr), chunklist_(nullptr)
{
const size_t alignement = 2 * sizeof(size_t);
size_ = ((size >= sizeof(block_) ? size : sizeof(block_))
+ alignement - 1) & ~(alignement - 1);
}
/// Free any memory allocated by this pool.
~fixed_size_pool()
{
while (chunklist_)
{
chunk_* prev = chunklist_->prev;
free(chunklist_);
chunklist_ = prev;
}
}
/// Allocate \a size bytes of memory.
void*
allocate()
{
block_* f = freelist_;
// If we have free blocks available, return the first one.
if (f)
{
freelist_ = f->next;
return f;
}
// Else, create a block out of the last chunk of allocated
// memory.
// If all the last chunk has been used, allocate one more.
if (free_start_ + size_ > free_end_)
{
const size_t requested = (size_ > 128 ? size_ : 128) * 8192 - 64;
chunk_* c = reinterpret_cast<chunk_*>(malloc(requested));
if (!c)
throw std::bad_alloc();
c->prev = chunklist_;
chunklist_ = c;
free_start_ = c->data_ + size_;
free_end_ = c->data_ + requested;
}
void* res = free_start_;
free_start_ += size_;
return res;
}
/// \brief Recycle \a size bytes of memory.
///
/// Despite the name, the memory is not really deallocated in the
/// "delete" sense: it is still owned by the pool and will be
/// reused by allocate as soon as possible. The memory is only
/// freed when the pool is destroyed.
void
deallocate (const void* ptr)
{
assert(ptr);
block_* b = reinterpret_cast<block_*>(const_cast<void*>(ptr));
b->next = freelist_;
freelist_ = b;
}
private:
size_t size_;
struct block_ { block_* next; }* freelist_;
char* free_start_;
char* free_end_;
// chunk = several agglomerated blocks
union chunk_ { chunk_* prev; char data_[1]; }* chunklist_;
};
}

62
spot/misc/formater.cc Normal file
View file

@ -0,0 +1,62 @@
// -*- 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/>.
#include "config.h"
#include <spot/misc/formater.hh>
#include <iostream>
namespace spot
{
void
formater::scan(const char* fmt, std::vector<bool>& has) const
{
for (const char* pos = fmt; *pos; ++pos)
if (*pos == '%')
{
char c = *++pos;
has[c] = true;
if (!c)
break;
}
}
void
formater::prime(const char* fmt)
{
scan(fmt, has_);
}
std::ostream&
formater::format(const char* fmt)
{
for (const char* pos = fmt; *pos; ++pos)
if (*pos != '%')
{
*output_ << *pos;
}
else
{
char c = *++pos;
call_[c]->print(*output_, pos);
if (!c)
break;
}
return *output_;
}
}

200
spot/misc/formater.hh Normal file
View file

@ -0,0 +1,200 @@
// -*- 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 <spot/misc/common.hh>
#include <iostream>
#include <string>
#include <vector>
namespace spot
{
class printable
{
public:
virtual ~printable()
{
}
virtual void
print(std::ostream&, const char*) const = 0;
};
template <class T>
class printable_value: public printable
{
protected:
T val_;
public:
const T& val() const
{
return val_;
}
T& val()
{
return val_;
}
operator const T&() const
{
return val();
}
operator T&()
{
return val();
}
printable_value&
operator=(const T& new_val)
{
val_ = new_val;
return *this;
}
virtual void
print(std::ostream& os, const char*) const
{
os << val_;
}
};
/// The default callback simply writes "%c".
class printable_id: public printable
{
public:
virtual void
print(std::ostream& os, const char* x) const
{
os << '%' << *x;
}
};
/// Called by default for "%%" and "%\0".
class printable_percent: public printable
{
public:
virtual void
print(std::ostream& os, const char*) const
{
os << '%';
}
};
class SPOT_API formater
{
printable_id id;
printable_percent percent;
public:
formater()
: has_(256), call_(256, &id)
{
call_['%'] = call_[0] = &percent;
}
virtual ~formater()
{
}
/// \brief Scan the %-sequences occuring in \a fmt.
///
/// Set has['c'] for each %c in \a fmt. \a has must
/// be 256 wide.
/// @{
void
scan(const char* fmt, std::vector<bool>& has) const;
void
scan(const std::string& fmt, std::vector<bool>& has) const
{
scan(fmt.c_str(), has);
}
/// @}
/// Collect the %-sequences occurring in \a fmt.
/// @{
void
prime(const char* fmt);
void
prime(const std::string& fmt)
{
prime(fmt.c_str());
}
/// @}
/// Whether %c occurred in the primed formats.
bool
has(char c) const
{
return has_[c];
}
/// Declare a callback function for %c.
void
declare(char c, const printable* f)
{
call_[c] = f;
}
/// Remember where to output any string.
void
set_output(std::ostream& output)
{
output_ = &output;
}
/// Expand the %-sequences in \a fmt, write the result on \a output_.
std::ostream&
format(const char* fmt);
/// Expand the %-sequences in \a fmt, write the result on \a output.
std::ostream&
format(std::ostream& output, const char* fmt)
{
set_output(output);
return format(fmt);
}
/// Expand the %-sequences in \a fmt, write the result on \a output_.
std::ostream&
format(const std::string& fmt)
{
return format(fmt.c_str());
}
/// Expand the %-sequences in \a fmt, write the result on \a output.
std::ostream&
format(std::ostream& output, const std::string& fmt)
{
return format(output, fmt.c_str());
}
private:
std::vector<bool> has_;
std::vector<const printable*> call_;
protected:
std::ostream* output_;
};
}

91
spot/misc/hash.hh Normal file
View file

@ -0,0 +1,91 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2011, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// 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 <string>
#include <functional>
#include <spot/misc/hashfunc.hh>
#include <spot/misc/_config.h>
#include <unordered_map>
#include <unordered_set>
namespace spot
{
/// \ingroup hash_funcs
/// \brief A hash function for pointers.
template <class T>
struct ptr_hash :
public std::unary_function<const T*, size_t>
{
// A default constructor is needed if the ptr_hash object is
// stored in a const member. This occur with the clang version
// installed by OS X 10.9.
ptr_hash()
{
}
size_t operator()(const T* p) const
{
return knuth32_hash(reinterpret_cast<const char*>(p)
- static_cast<const char*>(nullptr));
}
};
/// \ingroup hash_funcs
/// \brief A hash function for strings.
typedef std::hash<std::string> string_hash;
/// \ingroup hash_funcs
/// \brief A hash function that returns identity
template<typename T>
struct identity_hash:
public std::unary_function<const T&, size_t>
{
// A default constructor is needed if the string_hash object is
// stored in a const member.
identity_hash()
{
}
size_t operator()(const T& s) const
{
return s;
}
};
struct pair_hash
{
template<typename T, typename U>
std::size_t operator()(const std::pair<T, U> &p) const
{
std::hash<T> th;
std::hash<U> uh;
return wang32_hash(static_cast<size_t>(th(p.first)) ^
static_cast<size_t>(uh(p.second)));
}
};
}

66
spot/misc/hashfunc.hh Normal file
View file

@ -0,0 +1,66 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE)
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <cstddef>
namespace spot
{
/// \defgroup hash_funcs Hashing functions
/// \ingroup misc_tools
/// \ingroup hash_funcs
/// @{
/// \brief Thomas Wang's 32 bit hash function.
///
/// Hash an integer amongst the integers.
/// http://web.archive.org/web/2011/concentric.net/~Ttwang/tech/inthash.htm
inline size_t
wang32_hash(size_t key)
{
// We assume that size_t has at least 32bits.
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
/// \brief Knuth's Multiplicative hash function.
///
/// This function is suitable for hashing values whose
/// high order bits do not vary much (ex. addresses of
/// memory objects). Prefer spot::wang32_hash() otherwise.
/// http://web.archive.org/web/2011/concentric.net/~Ttwang/tech/addrhash.htm
inline size_t
knuth32_hash(size_t key)
{
// 2654435761 is the golden ratio of 2^32. The right shift of 3
// bits assumes that all objects are aligned on a 8 byte boundary.
return (key >> 3) * 2654435761U;
}
/// @}
}

551
spot/misc/intvcmp2.cc Normal file
View file

@ -0,0 +1,551 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 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 "config.h"
#include <spot/misc/common.hh>
#include <cstddef>
#include <cassert>
#include <spot/misc/intvcmp2.hh>
namespace spot
{
namespace
{
// This implements integer compression inspired from "Simple-9".
//
// The first bits of an integer tell how the rest of the integer is coded:
// 00: 30 1-bit values id=0
// 01: 10 3-bit values id=1
// 10: 6 5-bit values id=2
// 1100: 4 7-bit values id=3
// 1101: 3 9-bit values (1 bit lost) id=4
// 1110: 2 14-bit values id=5
// 1111: 1 28-bit value id=6
template <class Self>
class stream_compression_base
{
public:
stream_compression_base(size_t size)
: size_(size)
{
}
void run()
{
static const unsigned bits_width[7] = { 1, 3, 5, 7, 9, 14, 28 };
static const unsigned max_count[8] = { 30, 10, 6, 4, 3, 2, 1, 0 };
static const unsigned max_allowed[8] = { 1,
(1 << 3) - 1,
(1 << 5) - 1,
(1 << 7) - 1,
(1 << 9) - 1,
(1 << 14) - 1,
(1 << 28) - 1,
-1U };
// If we have only X data to compress and they fit with the
// current bit width, the following table tells us we should
// use bits_width[count_to_level[X - 1]] to limit the number
// of trailing zeros we encode. E.g. count_to_level[5 - 1]
// is 2, which mean that 5 values should be encoded with
// bits_width[2] == 5 bits.
static const unsigned count_to_level[30] =
{
6, // 1
5, // 2
4, // 3
3, // 4
2, // 5
2, // 6
1, // 7
1, // 8
1, // 9
1, // 10
0, 0, 0, 0, 0, // 11-15
0, 0, 0, 0, 0, // 16-20
0, 0, 0, 0, 0, // 21-25
0, 0, 0, 0, 0, // 26-30
};
while (size_ > 0)
{
unsigned id = 0; // Current level in the above two tables.
unsigned curmax_allowed = max_allowed[id];
unsigned compressable = 0; // Number of integers ready to pack.
do
{
unsigned int val = self().data_at(compressable);
++compressable;
while (val > curmax_allowed)
{
curmax_allowed = max_allowed[++id];
if (compressable > max_count[id])
goto fast_encode;
}
if (compressable >= max_count[id])
goto fast_encode;
}
while (SPOT_LIKELY(compressable < size_));
assert(compressable < max_count[id]);
// Since we have less data than the current "id" allows,
// try to use more bits so we can encode faster.
id = count_to_level[compressable - 1];
if (compressable == max_count[id])
goto fast_encode;
// Slow compression for situations where we have
// compressable < max_count[id]. We can only be in
// one of the 3 first "id" (1, 3, or 5 bits);
{
assert(id <= 2);
unsigned bits = bits_width[id];
unsigned finalshifts = (max_count[id] - compressable) * bits;
size_t pos = 0;
unsigned output = self().data_at(pos);
while (--compressable)
{
output <<= bits;
output += self().data_at(++pos);
}
output <<= finalshifts;
output += id << 30;
self().push_data(output);
return;
}
fast_encode:
switch (id)
{
case 0: // 30 1-bit values
{
// This code has been tuned so that the compiler can
// efficiently encode it as a series of MOV+LEA
// instructions, without shifts. For instance
//
// output <<= 1;
// output += self().data_at(4);
//
// translates to (assuming %eax points to the input,
// and %edx holds the output) the following:
//
// mov ecx, [eax+16]
// lea edx, [ecx+edx*2]
//
// This optimization is the reason why we use 'output +='
// instead of the more intuitive 'output |=' everywhere in
// this file.
unsigned int output = 0x00 << 1; // 00
output += self().data_at(0);
output <<= 1;
output += self().data_at(1);
output <<= 1;
output += self().data_at(2);
output <<= 1;
output += self().data_at(3);
output <<= 1;
output += self().data_at(4);
output <<= 1;
output += self().data_at(5);
output <<= 1;
output += self().data_at(6);
output <<= 1;
output += self().data_at(7);
output <<= 1;
output += self().data_at(8);
output <<= 1;
output += self().data_at(9);
output <<= 1;
output += self().data_at(10);
output <<= 1;
output += self().data_at(11);
output <<= 1;
output += self().data_at(12);
output <<= 1;
output += self().data_at(13);
output <<= 1;
output += self().data_at(14);
output <<= 1;
output += self().data_at(15);
output <<= 1;
output += self().data_at(16);
output <<= 1;
output += self().data_at(17);
output <<= 1;
output += self().data_at(18);
output <<= 1;
output += self().data_at(19);
output <<= 1;
output += self().data_at(20);
output <<= 1;
output += self().data_at(21);
output <<= 1;
output += self().data_at(22);
output <<= 1;
output += self().data_at(23);
output <<= 1;
output += self().data_at(24);
output <<= 1;
output += self().data_at(25);
output <<= 1;
output += self().data_at(26);
output <<= 1;
output += self().data_at(27);
output <<= 1;
output += self().data_at(28);
output <<= 1;
output += self().data_at(29);
self().push_data(output);
}
break;
case 1: // 10 3-bit values
{
// This code has been tuned so that the compiler can
// efficiently encode it as a series of MOV+LEA
// instructions, without shifts. For instance
//
// output <<= 3;
// output += self().data_at(4);
//
// translates to (assuming %eax points to the input,
// and %edx holds the output) the following:
//
// mov ecx, [eax+16]
// lea edx, [ecx+edx*8]
unsigned int output = 0x01 << 3; // 01
output += self().data_at(0);
output <<= 3;
output += self().data_at(1);
output <<= 3;
output += self().data_at(2);
output <<= 3;
output += self().data_at(3);
output <<= 3;
output += self().data_at(4);
output <<= 3;
output += self().data_at(5);
output <<= 3;
output += self().data_at(6);
output <<= 3;
output += self().data_at(7);
output <<= 3;
output += self().data_at(8);
output <<= 3;
output += self().data_at(9);
self().push_data(output);
}
break;
case 2: // 6 5-bit values
{
unsigned int output = 0x02U << 30; // 10
output += self().data_at(0) << 25;
output += self().data_at(1) << 20;
output += self().data_at(2) << 15;
output += self().data_at(3) << 10;
output += self().data_at(4) << 5;
output += self().data_at(5);
self().push_data(output);
}
break;
case 3: // 4 7-bit values
{
unsigned int output = 0x0CU << 28; // 1100
output += self().data_at(0) << 21;
output += self().data_at(1) << 14;
output += self().data_at(2) << 7;
output += self().data_at(3);
self().push_data(output);
}
break;
case 4: // 3 9-bit values
{
unsigned int output = 0x0DU << 28; // 1101x (1 bit lost)
output += self().data_at(0) << 18;
output += self().data_at(1) << 9;
output += self().data_at(2);
self().push_data(output);
}
break;
case 5: // 2 14-bit values
{
unsigned int output = 0x0EU << 28; // 1110
output += self().data_at(0) << 14;
output += self().data_at(1);
self().push_data(output);
}
break;
case 6: // one 28-bit value
{
unsigned int output = 0x0FU << 28; // 1111
output += self().data_at(0);
self().push_data(output);
}
break;
}
self().forward(max_count[id]);
size_ -= max_count[id];
}
}
protected:
size_t size_;
Self& self()
{
return static_cast<Self&>(*this);
}
const Self& self() const
{
return static_cast<const Self&>(*this);
}
};
class int_array_array_compression:
public stream_compression_base<int_array_array_compression>
{
public:
int_array_array_compression(const int* array, size_t n,
int* dest, size_t& dest_n)
: stream_compression_base<int_array_array_compression>(n),
array_(array), result_size_(dest_n),
result_(dest), result_end_(dest + dest_n)
{
result_size_ = 0; // this resets dest_n.
}
void push_data(unsigned int i)
{
assert(result_ < result_end_);
++result_size_;
*result_++ = static_cast<int>(i);
}
unsigned int data_at(size_t offset)
{
return static_cast<unsigned int>(array_[offset]);
}
void forward(size_t offset)
{
array_ += offset;
}
protected:
const int* array_;
size_t& result_size_;
int* result_;
int* result_end_;
};
} // anonymous
void
int_array_array_compress2(const int* array, size_t n,
int* dest, size_t& dest_size)
{
int_array_array_compression c(array, n, dest, dest_size);
c.run();
}
namespace
{
template<class Self>
class stream_decompression_base
{
public:
void run()
{
while (SPOT_LIKELY(self().have_comp_data()))
{
unsigned val = self().next_comp_data();
unsigned id = val >> 28;
switch (id)
{
case 0x00: // 00xx - 30 1-bit values.
case 0x01:
case 0x02:
case 0x03:
self().write_data_at(0, !!(val & (1 << 29)));
self().write_data_at(1, !!(val & (1 << 28)));
self().write_data_at(2, !!(val & (1 << 27)));
self().write_data_at(3, !!(val & (1 << 26)));
self().write_data_at(4, !!(val & (1 << 25)));
self().write_data_at(5, !!(val & (1 << 24)));
self().write_data_at(6, !!(val & (1 << 23)));
self().write_data_at(7, !!(val & (1 << 22)));
self().write_data_at(8, !!(val & (1 << 21)));
self().write_data_at(9, !!(val & (1 << 20)));
self().write_data_at(10, !!(val & (1 << 19)));
self().write_data_at(11, !!(val & (1 << 18)));
self().write_data_at(12, !!(val & (1 << 17)));
self().write_data_at(13, !!(val & (1 << 16)));
self().write_data_at(14, !!(val & (1 << 15)));
self().write_data_at(15, !!(val & (1 << 14)));
self().write_data_at(16, !!(val & (1 << 13)));
self().write_data_at(17, !!(val & (1 << 12)));
self().write_data_at(18, !!(val & (1 << 11)));
self().write_data_at(19, !!(val & (1 << 10)));
self().write_data_at(20, !!(val & (1 << 9)));
self().write_data_at(21, !!(val & (1 << 8)));
self().write_data_at(22, !!(val & (1 << 7)));
self().write_data_at(23, !!(val & (1 << 6)));
self().write_data_at(24, !!(val & (1 << 5)));
self().write_data_at(25, !!(val & (1 << 4)));
self().write_data_at(26, !!(val & (1 << 3)));
self().write_data_at(27, !!(val & (1 << 2)));
self().write_data_at(28, !!(val & (1 << 1)));
self().write_data_at(29, !!(val & (1 << 0)));
self().forward(30);
break;
case 0x04: // 01xx - 10 3-bit values.
case 0x05:
case 0x06:
case 0x07:
self().write_data_at(0, (val >> 27) & 0x07);
self().write_data_at(1, (val >> 24) & 0x07);
self().write_data_at(2, (val >> 21) & 0x07);
self().write_data_at(3, (val >> 18) & 0x07);
self().write_data_at(4, (val >> 15) & 0x07);
self().write_data_at(5, (val >> 12) & 0x07);
self().write_data_at(6, (val >> 9) & 0x07);
self().write_data_at(7, (val >> 6) & 0x07);
self().write_data_at(8, (val >> 3) & 0x07);
self().write_data_at(9, (val >> 0) & 0x07);
self().forward(10);
break;
case 0x08: // 10xx - 6 5-bit values.
case 0x09:
case 0x0A:
case 0x0B:
self().write_data_at(0, (val >> 25) & 0x1F);
self().write_data_at(1, (val >> 20) & 0x1F);
self().write_data_at(2, (val >> 15) & 0x1F);
self().write_data_at(3, (val >> 10) & 0x1F);
self().write_data_at(4, (val >> 5) & 0x1F);
self().write_data_at(5, (val >> 0) & 0x1F);
self().forward(6);
break;
case 0x0C: // 1100 - 4 7-bit values
self().write_data_at(0, (val >> 21) & 0x7F);
self().write_data_at(1, (val >> 14) & 0x7F);
self().write_data_at(2, (val >> 7) & 0x7F);
self().write_data_at(3, (val >> 0) & 0x7F);
self().forward(4);
break;
case 0x0D: // 1101x - 3 9-bit values.
self().write_data_at(0, (val >> 18) & 0x1FF);
self().write_data_at(1, (val >> 9) & 0x1FF);
self().write_data_at(2, (val >> 0) & 0x1FF);
self().forward(3);
break;
case 0x0E: // 110x - 2 14-bit values.
self().write_data_at(0, (val >> 14) & 0x3FFF);
self().write_data_at(1, (val >> 0) & 0x3FFF);
self().forward(2);
break;
case 0x0F: // 1100 - 1 28-bit value.
self().write_data_at(0, val & 0xFFFFFFF);
self().forward(1);
break;
}
}
}
protected:
Self& self()
{
return static_cast<Self&>(*this);
}
const Self& self() const
{
return static_cast<const Self&>(*this);
}
};
class int_array_array_decompression:
public stream_decompression_base<int_array_array_decompression>
{
public:
int_array_array_decompression(const int* array,
size_t array_size,
int* res)
: array_(array), n_(array_size), pos_(0), result_(res)
{
}
void write_data_at(size_t pos, unsigned int i)
{
result_[pos] = i;
}
void forward(size_t i)
{
result_ += i;
}
bool have_comp_data() const
{
return pos_ < n_;
}
unsigned int next_comp_data()
{
return array_[pos_++];
}
protected:
const int* array_;
size_t n_;
size_t pos_;
int* result_;
};
}
void
int_array_array_decompress2(const int* array, size_t array_size, int* res,
size_t)
{
int_array_array_decompression c(array, array_size, res);
c.run();
}
} // spot

50
spot/misc/intvcmp2.hh Normal file
View file

@ -0,0 +1,50 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et
// Developpement 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 <spot/misc/common.hh>
#include <vector>
#include <stddef.h>
namespace spot
{
/// \ingroup misc_tools
/// @{
/// \brief Compress an int array of size \a n into a int array.
///
/// The destination array should be at least \a dest_size large An
/// assert will be triggered if \a dest_size is too small. On
/// return, \a dest_size will be set to the actually number of int
/// filled in \a dest
SPOT_API void
int_array_array_compress2(const int* array, size_t n,
int* dest, size_t& dest_size);
/// \brief Uncompress an int array of size \a array_size into a int
/// array of size \a size.
///
/// \a size must be the exact expected size of uncompressed array.
SPOT_API void
int_array_array_decompress2(const int* array, size_t array_size,
int* res, size_t size);
/// @}
}

703
spot/misc/intvcomp.cc Normal file
View file

@ -0,0 +1,703 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 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 "config.h"
#include <spot/misc/common.hh>
#include <cstddef>
#include <cassert>
#include <spot/misc/intvcomp.hh>
namespace spot
{
// Compression scheme
// ------------------
//
// Assumptions:
// - small and positive values are more frequent than negative
// and large values.
// - 0 is the most frequent value
// - repeated values (esp. repeated 0s occur often).
//
// 00 encodes "value 0"
// 010 encodes "value 1"
// 011 encodes "a value in [2..5]" followed by 2 bits
// 100 encodes "a value in [6..22]" followed by 4 bits
// 101 encodes "repeat prev. value [1..8] times" followed by 3 bits count
// 110 encodes "repeat prev. value [9..40] times" followed by 5 bits count
// 111 encodes "an int value" followed by 32 bits
//
// If 101 or 110 occur at the start, the value to repeat is 0.
namespace
{
template <class Self>
class stream_compression_base
{
static const unsigned int max_bits = sizeof(unsigned int) * 8;
public:
stream_compression_base()
: cur_(0), bits_left_(max_bits)
{
}
void emit(unsigned int val)
{
if (val == 0)
{
self().push_bits(0x0, 2, 0x3);
}
else if (val == 1)
{
self().push_bits(0x2, 3, 0x7);
}
else if (val >= 2 && val <= 5)
{
self().push_bits(0x3, 3, 0x7);
self().push_bits(val - 2, 2, 0x3);
}
else if (val >= 6 && val <= 22)
{
self().push_bits(0x4, 3, 0x7);
self().push_bits(val - 6, 4, 0xf);
}
else
{
assert(val > 22);
self().push_bits(0x7, 3, 0x7);
self().push_bits(val, 32, -1U);
}
}
void run()
{
unsigned int last_val = 0;
while (SPOT_LIKELY(self().have_data()))
{
unsigned int val = self().next_data();
// Repeated value? Try to find more.
if (val == last_val)
{
unsigned int count = 1;
while (count < 40 && self().skip_if(val))
++count;
if ((val == 0 && count < 3) || (val == 1 && count == 1))
{
// it is more efficient to emit 0 once or twice directly
// (e.g., 00 00 vs. 011 11)
// for value 1, repetition is worthwhile for count > 1
// (e.g., 010 010 vs. 011 00)
while (count--)
emit(val);
}
else if (count < 9)
{
self().push_bits(0x5, 3, 0x7);
self().push_bits(count - 1, 3, 0x7);
}
else
{
self().push_bits(0x6, 3, 0x7);
self().push_bits(count - 9, 5, 0x1f);
}
}
else
{
emit(val);
last_val = val;
}
}
flush();
}
// This version assumes there is at least n bits free in cur_.
void
push_bits_unchecked(unsigned int bits, unsigned int n, unsigned int mask)
{
cur_ <<= n;
cur_ |= (bits & mask);
if (SPOT_LIKELY(bits_left_ -= n))
return;
self().push_data(cur_);
cur_ = 0;
bits_left_ = max_bits;
}
void
push_bits(unsigned int bits, unsigned int n, unsigned int mask)
{
if (SPOT_LIKELY(n <= bits_left_))
{
push_bits_unchecked(bits, n, mask);
return;
}
// bits_left_ < n
unsigned int right_bit_count = n - bits_left_;
unsigned int left = bits >> right_bit_count;
push_bits_unchecked(left, bits_left_, (1 << bits_left_) - 1);
push_bits_unchecked(bits, right_bit_count, (1 << right_bit_count) - 1);
}
void flush()
{
if (bits_left_ == max_bits)
return;
cur_ <<= bits_left_;
self().push_data(cur_);
}
protected:
Self& self()
{
return static_cast<Self&>(*this);
}
const Self& self() const
{
return static_cast<const Self&>(*this);
}
unsigned int cur_;
unsigned int bits_left_;
};
class int_array_vector_compression:
public stream_compression_base<int_array_vector_compression>
{
public:
int_array_vector_compression(const int* array, size_t n)
: array_(array), n_(n), pos_(0), result_(new std::vector<unsigned int>)
{
}
void push_data(unsigned int i)
{
result_->push_back(i);
}
const std::vector<unsigned int>*
result() const
{
return result_;
}
bool have_data() const
{
return pos_ < n_;
}
unsigned int next_data()
{
return static_cast<unsigned int>(array_[pos_++]);
}
bool skip_if(unsigned int val)
{
if (SPOT_UNLIKELY(!have_data()))
return false;
if (static_cast<unsigned int>(array_[pos_]) != val)
return false;
++pos_;
return true;
}
protected:
const int* array_;
size_t n_;
size_t pos_;
std::vector<unsigned int>* result_;
};
class int_vector_vector_compression:
public stream_compression_base<int_vector_vector_compression>
{
public:
int_vector_vector_compression(const std::vector<int>& input,
std::vector<unsigned int>& output)
: input_(input), pos_(input.begin()), end_(input.end()), output_(output)
{
}
void push_data(unsigned int i)
{
output_.push_back(i);
}
bool have_data() const
{
return pos_ < end_;
}
unsigned int next_data()
{
return static_cast<unsigned int>(*pos_++);
}
bool skip_if(unsigned int val)
{
if (SPOT_UNLIKELY(!have_data()))
return false;
if (static_cast<unsigned int>(*pos_) != val)
return false;
++pos_;
return true;
}
protected:
const std::vector<int>& input_;
std::vector<int>::const_iterator pos_;
std::vector<int>::const_iterator end_;
std::vector<unsigned int>& output_;
};
class int_array_array_compression:
public stream_compression_base<int_array_array_compression>
{
public:
int_array_array_compression(const int* array, size_t n,
int* dest, size_t& dest_n)
: array_(array), n_(n), pos_(0),
result_size_(dest_n), result_(dest), result_end_(dest + dest_n)
{
result_size_ = 0; // this resets dest_n.
}
void push_data(unsigned int i)
{
assert(result_ < result_end_);
++result_size_;
*result_++ = static_cast<int>(i);
}
bool have_data() const
{
return pos_ < n_;
}
unsigned int next_data()
{
return static_cast<unsigned int>(array_[pos_++]);
}
bool skip_if(unsigned int val)
{
if (SPOT_UNLIKELY(!have_data()))
return false;
if (static_cast<unsigned int>(array_[pos_]) != val)
return false;
++pos_;
return true;
}
protected:
const int* array_;
size_t n_;
size_t pos_;
size_t& result_size_;
int* result_;
int* result_end_;
};
}
void
int_vector_vector_compress(const std::vector<int>& input,
std::vector<unsigned>& output)
{
int_vector_vector_compression c(input, output);
c.run();
}
const std::vector<unsigned int>*
int_array_vector_compress(const int* array, size_t n)
{
int_array_vector_compression c(array, n);
c.run();
return c.result();
}
void
int_array_array_compress(const int* array, size_t n,
int* dest, size_t& dest_size)
{
int_array_array_compression c(array, n, dest, dest_size);
c.run();
}
//////////////////////////////////////////////////////////////////////
namespace
{
template<class Self>
class stream_decompression_base
{
static const unsigned int max_bits = sizeof(unsigned int) * 8;
public:
void refill()
{
if (SPOT_UNLIKELY(look_bits_ == 0))
{
look_bits_ = max_bits;
look_ = buffer_;
if (SPOT_LIKELY(self().have_comp_data()))
buffer_ = self().next_comp_data();
if (SPOT_LIKELY(buffer_bits_ != max_bits))
{
unsigned int fill_size = max_bits - buffer_bits_;
look_ <<= fill_size;
look_ |= buffer_ >> buffer_bits_;
}
}
else
{
unsigned int fill_size = max_bits - look_bits_;
if (fill_size > buffer_bits_)
fill_size = buffer_bits_;
look_ <<= fill_size;
buffer_bits_ -= fill_size;
look_ |= (buffer_ >> buffer_bits_) & ((1 << fill_size) - 1);
look_bits_ += fill_size;
if (buffer_bits_ == 0)
{
if (SPOT_LIKELY(self().have_comp_data()))
buffer_ = self().next_comp_data();
unsigned int left = max_bits - look_bits_;
if (left != 0)
{
look_ <<= left;
look_ |= buffer_ >> look_bits_;
buffer_bits_ = look_bits_;
look_bits_ = max_bits;
}
else
{
buffer_bits_ = max_bits;
}
}
}
}
unsigned int look_n_bits(unsigned int n)
{
if (SPOT_UNLIKELY(look_bits_ < n))
refill();
assert(n <= look_bits_);
return (look_ >> (look_bits_ - n)) & ((1 << n) - 1);
}
void skip_n_bits(unsigned int n)
{
assert (n <= look_bits_);
look_bits_ -= n;
}
unsigned int get_n_bits(unsigned int n)
{
if (SPOT_UNLIKELY(look_bits_ < n))
refill();
look_bits_ -= n;
return (look_ >> look_bits_) & ((1 << n) - 1);
}
unsigned int get_32_bits()
{
// std::cerr << "get_32" << std::endl;
if (SPOT_LIKELY(look_bits_ < 32))
refill();
unsigned int val = look_;
look_bits_ = 0;
refill();
return val;
}
void run()
{
if (SPOT_UNLIKELY(!self().have_comp_data()))
return;
look_ = self().next_comp_data();
look_bits_ = max_bits;
if (SPOT_LIKELY(self().have_comp_data()))
{
buffer_ = self().next_comp_data();
buffer_bits_ = max_bits;
}
else
{
buffer_ = 0;
buffer_bits_ = 0;
}
while (SPOT_LIKELY(!self().complete()))
{
unsigned int token = look_n_bits(3);
switch (token)
{
case 0x0: // 00[0]
case 0x1: // 00[1]
skip_n_bits(2);
self().push_data(0);
break;
case 0x2: // 010
skip_n_bits(3);
self().push_data(1);
break;
case 0x3: // 011
skip_n_bits(3);
self().push_data(2 + get_n_bits(2));
break;
case 0x4: // 100
skip_n_bits(3);
self().push_data(6 + get_n_bits(4));
break;
case 0x5: // 101
skip_n_bits(3);
self().repeat(1 + get_n_bits(3));
break;
case 0x6: // 110
skip_n_bits(3);
self().repeat(9 + get_n_bits(5));
break;
case 0x7: // 111
skip_n_bits(3);
self().push_data(get_32_bits());
break;
default:
SPOT_UNREACHABLE();
}
}
}
protected:
Self& self()
{
return static_cast<Self&>(*this);
}
const Self& self() const
{
return static_cast<const Self&>(*this);
}
unsigned int look_;
unsigned int look_bits_;
unsigned int buffer_;
unsigned int buffer_bits_;
};
class int_vector_vector_decompression:
public stream_decompression_base<int_vector_vector_decompression>
{
public:
int_vector_vector_decompression(const std::vector<unsigned int>& array,
std::vector<int>& res, size_t size)
: prev_(0), array_(array),
pos_(array.begin()), end_(array.end()),
result_(res), size_(size)
{
result_.reserve(size);
}
bool complete() const
{
return size_ == 0;
}
void push_data(int i)
{
prev_ = i;
result_.push_back(i);
--size_;
}
void repeat(unsigned int i)
{
size_ -= i;
while (i--)
result_.push_back(prev_);
}
bool have_comp_data() const
{
return pos_ != end_;
}
unsigned int next_comp_data()
{
return *pos_++;
}
protected:
int prev_;
const std::vector<unsigned int>& array_;
std::vector<unsigned int>::const_iterator pos_;
std::vector<unsigned int>::const_iterator end_;
std::vector<int>& result_;
size_t size_;
};
class int_vector_array_decompression:
public stream_decompression_base<int_vector_array_decompression>
{
public:
int_vector_array_decompression(const std::vector<unsigned int>* array,
int* res,
size_t size)
: prev_(0), array_(array), n_(array->size()), pos_(0), result_(res),
size_(size)
{
}
bool complete() const
{
return size_ == 0;
}
void push_data(int i)
{
prev_ = i;
*result_++ = i;
--size_;
}
void repeat(unsigned int i)
{
size_ -= i;
while (i--)
*result_++ = prev_;
}
bool have_comp_data() const
{
return pos_ < n_;
}
unsigned int next_comp_data()
{
return (*array_)[pos_++];
}
protected:
int prev_;
const std::vector<unsigned int>* array_;
size_t n_;
size_t pos_;
int* result_;
size_t size_;
};
class int_array_array_decompression:
public stream_decompression_base<int_array_array_decompression>
{
public:
int_array_array_decompression(const int* array,
size_t array_size,
int* res,
size_t size)
: prev_(0), array_(array), n_(array_size), pos_(0), result_(res),
size_(size)
{
}
bool complete() const
{
return size_ == 0;
}
void push_data(int i)
{
prev_ = i;
*result_++ = i;
--size_;
}
void repeat(unsigned int i)
{
size_ -= i;
while (i--)
*result_++ = prev_;
}
bool have_comp_data() const
{
return pos_ < n_;
}
unsigned int next_comp_data()
{
return array_[pos_++];
}
protected:
int prev_;
const int* array_;
size_t n_;
size_t pos_;
int* result_;
size_t size_;
};
}
void
int_vector_vector_decompress(const std::vector<unsigned int>& input,
std::vector<int>& output, size_t size)
{
int_vector_vector_decompression c(input, output, size);
c.run();
}
void
int_vector_array_decompress(const std::vector<unsigned int>* array, int* res,
size_t size)
{
int_vector_array_decompression c(array, res, size);
c.run();
}
void
int_array_array_decompress(const int* array, size_t array_size,
int* res, size_t size)
{
int_array_array_decompression c(array, array_size, res, size);
c.run();
}
}

75
spot/misc/intvcomp.hh Normal file
View file

@ -0,0 +1,75 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et
// Developpement 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 <spot/misc/common.hh>
#include <vector>
#include <stddef.h>
namespace spot
{
/// \ingroup misc_tools
/// @{
/// Compress an int vector into a vector of unsigned int.
SPOT_API void
int_vector_vector_compress(const std::vector<int>& input,
std::vector<unsigned int>& output);
/// \brief Uncompress a vector of unsigned int into a vector of
/// size \a size.
///
/// \a size must be the exact expected size of uncompressed array.
SPOT_API void
int_vector_vector_decompress(const std::vector<unsigned int>& array,
std::vector<int>& output, size_t size);
/// Compress an int array if size \a n into a vector of unsigned int.
SPOT_API const std::vector<unsigned int>*
int_array_vector_compress(const int* array, size_t n);
/// \brief Uncompress a vector of unsigned int into an int array of
/// size \a size.
///
/// \a size must be the exact expected size of uncompressed array.
SPOT_API void
int_vector_array_decompress(const std::vector<unsigned int>* array,
int* res, size_t size);
/// \brief Compress an int array of size \a n into a int array.
///
/// The destination array should be at least \a dest_size large An
/// assert will be triggered if \a dest_size is too small. On
/// return, \a dest_size will be set to the actually number of int
/// filled in \a dest
SPOT_API void
int_array_array_compress(const int* array, size_t n,
int* dest, size_t& dest_size);
/// \brief Uncompress an int array of size \a array_size into a int
/// array of size \a size.
///
/// \a size must be the exact expected size of uncompressed array.
SPOT_API void
int_array_array_decompress(const int* array, size_t array_size,
int* res, size_t size);
/// @}
}

169
spot/misc/location.hh Normal file
View file

@ -0,0 +1,169 @@
// Note: this file was comped from A Bison parser, made by GNU Bison
// 2.7.12-4996. It is shared by all the parsers in Spot. Unfortunately,
// at the time of writing there is no Bison option to generate this
// file an update it.
/* Locations for Bison parsers in C++
Copyright (C) 2002-2007, 2009-2013 Free Software Foundation, Inc.
This program 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.
This program 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/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#pragma once
#include <spot/misc/position.hh>
namespace spot
{
/// Abstract a location.
class location
{
public:
/// Construct a location from \a b to \a e.
location (const position& b, const position& e)
: begin (b)
, end (e)
{
}
/// Construct a 0-width location in \a p.
explicit location (const position& p = position ())
: begin (p)
, end (p)
{
}
/// Construct a 0-width location in \a f, \a l, \a c.
explicit location (std::string* f,
unsigned int l = 1u,
unsigned int c = 1u)
: begin (f, l, c)
, end (f, l, c)
{
}
/// Initialization.
void initialize (std::string* f = YY_NULL,
unsigned int l = 1u,
unsigned int c = 1u)
{
begin.initialize (f, l, c);
end = begin;
}
/** \name Line and Column related manipulators
** \{ */
public:
/// Reset initial location to final location.
void step ()
{
begin = end;
}
/// Extend the current location to the COUNT next columns.
void columns (unsigned int count = 1)
{
end += count;
}
/// Extend the current location to the COUNT next lines.
void lines (unsigned int count = 1)
{
end.lines (count);
}
/** \} */
public:
/// Beginning of the located region.
position begin;
/// End of the located region.
position end;
};
/// Join two location objects to create a location.
inline const location operator+ (const location& begin, const location& end)
{
location res = begin;
res.end = end.end;
return res;
}
/// Add two location objects.
inline const location operator+ (const location& begin, unsigned int width)
{
location res = begin;
res.columns (width);
return res;
}
/// Add and assign a location.
inline location& operator+= (location& res, unsigned int width)
{
res.columns (width);
return res;
}
/// Compare two location objects.
inline bool
operator== (const location& loc1, const location& loc2)
{
return loc1.begin == loc2.begin && loc1.end == loc2.end;
}
/// Compare two location objects.
inline bool
operator!= (const location& loc1, const location& loc2)
{
return !(loc1 == loc2);
}
/** \brief Intercept output stream redirection.
** \param ostr the destination output stream
** \param loc a reference to the location to redirect
**
** Avoid duplicate information.
*/
template <typename YYChar>
inline std::basic_ostream<YYChar>&
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
{
position last = loc.end - 1;
ostr << loc.begin;
if (last.filename
&& (!loc.begin.filename
|| *loc.begin.filename != *last.filename))
ostr << '-' << last;
else if (loc.begin.line != last.line)
ostr << '-' << last.line << '.' << last.column;
else if (loc.begin.column != last.column)
ostr << '-' << last.column;
return ostr;
}
}

51
spot/misc/ltstr.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)
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <cstring>
#include <functional>
namespace spot
{
/// \ingroup misc_tools
/// \brief Strict Weak Ordering for \c char*.
///
/// This is meant to be used as a comparison functor for
/// STL \c map whose key are of type <code>const char*</code>.
///
/// For instance here is how one could declare
/// a map of <code>const state*</code>.
/// \code
/// std::map<const char*, int, spot::state_ptr_less_than> seen;
/// \endcode
struct char_ptr_less_than:
public std::binary_function<const char*, const char*, bool>
{
bool
operator()(const char* left, const char* right) const
{
return strcmp(left, right) < 0;
}
};
}

44
spot/misc/memusage.cc Normal file
View file

@ -0,0 +1,44 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE).
// Copyright (C) 2006 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 "config.h"
#include <spot/misc/memusage.hh>
#include <cstdio>
namespace spot
{
int
memusage()
{
int size;
FILE* file = fopen("/proc/self/statm", "r");
if (!file)
return -1;
int res = fscanf(file, "%d", &size);
(void) fclose(file);
if (res != 1)
return -1;
return size;
}
}

34
spot/misc/memusage.hh Normal file
View file

@ -0,0 +1,34 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE).
// Copyright (C) 2006 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
namespace spot
{
/// \brief Total number of pages in use by the program.
///
/// \return The total number of pages in use by the program if known.
/// -1 otherwise.
SPOT_API int memusage();
}

178
spot/misc/minato.cc Normal file
View file

@ -0,0 +1,178 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// 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 "config.h"
#include <spot/misc/minato.hh>
#include <cassert>
namespace spot
{
minato_isop::minato_isop(bdd input)
: minato_isop(input, bdd_support(input))
{
}
minato_isop::minato_isop(bdd input, bdd vars)
: ret_(bddfalse)
{
// If INPUT has the form a&b&c&(binary function) we want to
// compute the ISOP of the only binary and prepend a&b&c latter.
//
// Calling bdd_satprefix (it returns a&b&c and modify input to
// point to function) this way is an optimization to the
// original algorithm, because in many cases we are trying to
// build ISOPs out of formulae that are already cubes.
cube_.push(bdd_satprefix(input));
todo_.emplace(input, input, vars);
}
minato_isop::minato_isop(bdd input_min, bdd input_max, bool)
: ret_(bddfalse)
{
if (input_min == input_max)
{
cube_.push(bdd_satprefix(input_min));
input_max = input_min;
}
else
{
cube_.push(bddtrue);
}
bdd common = input_min & input_max;
todo_.emplace(input_min, input_max, bdd_support(common));
}
bdd
minato_isop::next()
{
while (!todo_.empty())
{
local_vars& l = todo_.top();
switch (l.step)
{
case local_vars::FirstStep:
next_var:
{
if (l.f_min == bddfalse)
{
ret_ = bddfalse;
todo_.pop();
continue;
}
if (l.vars == bddtrue || l.f_max == bddtrue)
{
ret_ = l.f_max;
todo_.pop();
return cube_.top() & ret_;
}
assert(l.vars != bddfalse);
// Pick the first variable in VARS that is used by F_MIN
// or F_MAX. We know that VARS, F_MIN or F_MAX are not
// constants (bddtrue or bddfalse) because one of the
// two above `if' would have matched; so it's ok to call
// bdd_var().
int v = bdd_var(l.vars);
l.vars = bdd_high(l.vars);
int v_min = bdd_var(l.f_min);
int v_max = bdd_var(l.f_max);
if (v < v_min && v < v_max)
// Do not use a while() for this goto, because we want
// `continue' to be relative to the outermost while().
goto next_var;
l.step = local_vars::SecondStep;
bdd v0 = bdd_nithvar(v);
l.v1 = bdd_ithvar(v);
// All the following should be equivalent to
// f0_min = bdd_restrict(f_min, v0);
// f0_max = bdd_restrict(f_max, v0);
// f1_min = bdd_restrict(f_min, v1);
// f1_max = bdd_restrict(f_max, v1);
// but we try to avoid bdd_restrict when possible.
if (v == v_min)
{
l.f0_min = bdd_low(l.f_min);
l.f1_min = bdd_high(l.f_min);
}
else if (v_min < v)
{
l.f0_min = bdd_restrict(l.f_min, v0);
l.f1_min = bdd_restrict(l.f_min, l.v1);
}
else
{
l.f1_min = l.f0_min = l.f_min;
}
if (v == v_max)
{
l.f0_max = bdd_low(l.f_max);
l.f1_max = bdd_high(l.f_max);
}
else if (v_max < v)
{
l.f0_max = bdd_restrict(l.f_max, v0);
l.f1_max = bdd_restrict(l.f_max, l.v1);
}
else
{
l.f1_max = l.f0_max = l.f_max;
}
cube_.push(cube_.top() & v0);
todo_.emplace(l.f0_min - l.f1_max, l.f0_max, l.vars);
}
continue;
case local_vars::SecondStep:
l.step = local_vars::ThirdStep;
l.g0 = ret_;
cube_.pop();
cube_.push(cube_.top() & l.v1);
todo_.emplace(l.f1_min - l.f0_max, l.f1_max, l.vars);
continue;
case local_vars::ThirdStep:
l.step = local_vars::FourthStep;
l.g1 = ret_;
cube_.pop();
{
bdd fs_max = l.f0_max & l.f1_max;
bdd fs_min = fs_max & ((l.f0_min - l.g0) | (l.f1_min - l.g1));
todo_.emplace(fs_min, fs_max, l.vars);
}
continue;
case local_vars::FourthStep:
ret_ |= (l.g0 - l.v1) | (l.g1 & l.v1);
todo_.pop();
continue;
}
SPOT_UNREACHABLE();
}
return bddfalse;
}
}

100
spot/misc/minato.hh Normal file
View file

@ -0,0 +1,100 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
#include <bddx.h>
#include <stack>
namespace spot
{
/// \ingroup misc_tools
/// \brief Generate an irredundant sum-of-products (ISOP) form of a
/// BDD function.
///
/// This algorithm implements a derecursived version the Minato-Morreale
/// algorithm presented in the following paper.
/** \verbatim
@InProceedings{ minato.92.sasimi,
author = {Shin-ichi Minato},
title = {Fast Generation of Irredundant Sum-of-Products Forms
from Binary Decision Diagrams},
booktitle = {Proceedings of the third Synthesis and Simulation
and Meeting International Interchange workshop
(SASIMI'92)},
pages = {64--73},
year = {1992},
address = {Kobe, Japan},
month = {April}
}
\endverbatim */
class SPOT_API minato_isop
{
public:
/// \brief Conctructor.
/// \arg input The BDD function to translate in ISOP.
minato_isop(bdd input);
/// \brief Conctructor.
/// \arg input The BDD function to translate in ISOP.
/// \arg vars The set of BDD variables to factorize in \a input.
minato_isop(bdd input, bdd vars);
/// \brief Conctructor.
///
/// This version allow some flexibility in computing the ISOP.
/// the result must be within \a input_min and \a input_max.
/// \arg input_min The minimum BDD function to translate in ISOP.
/// \arg input_max The maximum BDD function to translate in ISOP.
minato_isop(bdd input_min, bdd input_max, bool);
/// \brief Compute the next sum term of the ISOP form.
/// Return \c bddfalse when all terms have been output.
bdd next();
private:
/// Internal variables for minato_isop.
struct local_vars
{
// If you are following the paper, f_min and f_max correspond
// to the pair of BDD functions used to encode the ternary function f
// (see Section 3.4).
// Also note that f0, f0', and f0'' all share the same _max function.
// Likewise for f1, f1', and f1''.
bdd f_min, f_max;
// Because we need a non-recursive version of the algorithm,
// we had to split it in four steps (each step is separated
// from the other by a call to ISOP in the original algorithm).
enum { FirstStep, SecondStep, ThirdStep, FourthStep } step;
// The list of variables to factorize. This is an addition to
// the original algorithm.
bdd vars;
bdd v1;
bdd f0_min, f0_max;
bdd f1_min, f1_max;
bdd g0, g1;
local_vars(bdd f_min, bdd f_max, bdd vars)
: f_min(f_min), f_max(f_max), step(FirstStep), vars(vars) {}
};
std::stack<local_vars> todo_;
std::stack<bdd> cube_;
bdd ret_;
};
}

126
spot/misc/mspool.hh Normal file
View file

@ -0,0 +1,126 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2015 Laboratoire de Recherche et Developpement
// 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 <new>
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include <spot/misc/hash.hh>
namespace spot
{
/// A multiple-size memory pool implementation.
class multiple_size_pool
{
static const size_t alignment_ = 2 * sizeof(size_t) - 1;
public:
/// Create a pool.
multiple_size_pool()
: free_start_(nullptr), free_end_(nullptr), chunklist_(nullptr)
{
}
/// Free any memory allocated by this pool.
~multiple_size_pool()
{
while (chunklist_)
{
chunk_* prev = chunklist_->prev;
free(chunklist_);
chunklist_ = prev;
}
}
size_t fixsize(size_t size) const
{
if (size < sizeof(block_))
size = sizeof(block_);
return (size + alignment_ - 1) & ~(alignment_ - 1);
}
/// Allocate \a size bytes of memory.
void*
allocate(size_t size)
{
size = fixsize(size);
block_*& f = freelist_[size];
// If we have free blocks available, return the first one.
if (f)
{
block_* first = f;
f = f->next;
return first;
}
// Else, create a block out of the last chunk of allocated
// memory.
// If all the last chunk has been used, allocate one more.
if (free_start_ + size > free_end_)
{
const size_t requested = (size > 128 ? size : 128) * 8192 - 64;
chunk_* c = reinterpret_cast<chunk_*>(malloc(requested));
if (!c)
throw std::bad_alloc();
c->prev = chunklist_;
chunklist_ = c;
free_start_ = c->data_ + size;
free_end_ = c->data_ + requested;
}
void* res = free_start_;
free_start_ += size;
return res;
}
/// \brief Recycle \a size bytes of memory.
///
/// Despite the name, the memory is not really deallocated in the
/// "delete" sense: it is still owned by the pool and will be
/// reused by allocate as soon as possible. The memory is only
/// freed when the pool is destroyed.
///
/// The size argument should be the same as the one passed to
/// allocate().
void
deallocate (const void* ptr, size_t size)
{
assert(ptr);
size = fixsize(size);
block_* b = reinterpret_cast<block_*>(const_cast<void*>(ptr));
block_*& f = freelist_[size];
b->next = f;
f = b;
}
private:
struct block_ { block_* next; };
std::unordered_map<size_t, block_*> freelist_;
char* free_start_;
char* free_end_;
// chunk = several agglomerated blocks
union chunk_ { chunk_* prev; char data_[1]; }* chunklist_;
};
}

188
spot/misc/optionmap.cc Normal file
View file

@ -0,0 +1,188 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 "config.h"
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <spot/misc/optionmap.hh>
namespace spot
{
const char*
option_map::parse_options(const char* options)
{
while (*options)
{
// Skip leading separators.
while (*options && strchr(" \t\n,;", *options))
++options;
// `!foo' is a shorthand for `foo=0'.
const char* negated = nullptr;
if (*options == '!')
{
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
negated = options++;
}
if (!*options)
{
if (negated)
return negated;
else
break;
}
const char* name_start = options;
// Find the end of the name.
while (*options && !strchr(", \t\n;=", *options))
++options;
std::string name(name_start, options);
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
if (*options != '=')
{
options_[name] = (negated ? 0 : 1);
}
else if (negated)
{
return negated;
}
else
{
++options;
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
if (!*options)
return name_start;
if (*options == '\'' || *options == '"')
{
auto sep = *options;
auto start = options + 1;
do
++options;
while (*options && *options != sep);
if (*options != sep)
return start - 1;
std::string val(start, options);
options_str_[name] = val;
if (*options)
++options;
}
else
{
char* val_end;
int val = strtol(options, &val_end, 10);
if (val_end == options)
return name_start;
if (*val_end == 'K')
{
val *= 1024;
++val_end;
}
else if (*val_end == 'M')
{
val *= 1024 * 1024;
++val_end;
}
else if (*val_end && !strchr(" \t\n,;", *val_end))
{
return options;
}
options = val_end;
options_[name] = val;
}
}
}
return nullptr;
}
int
option_map::get(const char* option, int def) const
{
auto it = options_.find(option);
return (it == options_.end()) ? def : it->second;
}
std::string
option_map::get_str(const char* option, std::string def) const
{
auto it = options_str_.find(option);
return (it == options_str_.end()) ? def : it->second;
}
int
option_map::operator[](const char* option) const
{
return get(option);
}
int
option_map::set(const char* option, int val, int def)
{
int old = get(option, def);
options_[option] = val;
return old;
}
std::string
option_map::set_str(const char* option, std::string val, std::string def)
{
std::string old = get_str(option, def);
options_str_[option] = val;
return old;
}
void
option_map::set(const option_map& o)
{
options_ = o.options_;
options_str_ = o.options_str_;
}
int&
option_map::operator[](const char* option)
{
return options_[option];
}
std::ostream&
operator<<(std::ostream& os, const option_map& m)
{
for (auto p: m.options_)
os << '"' << p.first << "\" = " << p.second << '\n';
for (auto p: m.options_str_)
os << '"' << p.first << "\" = \"" << p.second << "\"\n";
return os;
}
}

103
spot/misc/optionmap.hh Normal file
View file

@ -0,0 +1,103 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2015 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE)
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
#include <string>
#include <map>
#include <iosfwd>
namespace spot
{
/// \ingroup misc_tools
/// \brief Manage a map of options.
///
/// Each option is defined by a string and is associated to an integer value.
class SPOT_API option_map
{
public:
/// \brief Add the parsed options to the map.
///
/// \a options are separated by a space, comma, semicolon or tabulation and
/// can be optionnaly followed by an integer value (preceded by an equal
/// sign). If not specified, the default value is 1.
///
/// The following three lines are equivalent.
/** \verbatim
optA !optB optC=4194304
optA=1, optB=0, optC=4096K
optC = 4M; optA !optB
\endverbatim */
///
/// \return A non-null pointer to the option for which an expected integer
/// value cannot be parsed.
const char* parse_options(const char* options);
/// \brief Get the value of \a option.
///
/// \return The value associated to \a option if it exists,
/// \a def otherwise.
/// \see operator[]()
int get(const char* option, int def = 0) const;
/// \brief Get the value of \a option.
///
/// \return The value associated to \a option if it exists,
/// \a def otherwise.
/// \see operator[]()
std::string get_str(const char* option, std::string def = {}) const;
/// \brief Get the value of \a option.
///
/// \return The value associated to \a option if it exists, 0 otherwise.
/// \see get()
int operator[](const char* option) const;
/// \brief Set the value of \a option to \a val.
///
/// \return The previous value associated to \a option if declared,
/// or \a def otherwise.
int set(const char* option, int val, int def = 0);
/// \brief Set the value of a string \a option to \a val.
///
/// \return The previous value associated to \a option if declared,
/// or \a def otherwise.
std::string set_str(const char* option,
std::string val, std::string def = {});
/// Acquire all the settings of \a o.
void set(const option_map& o);
/// \brief Get a reference to the current value of \a option.
int& operator[](const char* option);
/// \brief Print the option_map \a m.
friend SPOT_API std::ostream&
operator<<(std::ostream& os, const option_map& m);
private:
std::map<std::string, int> options_;
std::map<std::string, std::string> options_str_;
};
}

164
spot/misc/position.hh Normal file
View file

@ -0,0 +1,164 @@
// Note: this file was comped from A Bison parser, made by GNU Bison
// 2.7.12-4996. It is shared by all the parsers in Spot. Unfortunately,
// at the time of writing there is no Bison option to generate this
// file an update it.
/* Positions for Bison parsers in C++
Copyright (C) 2002-2007, 2009-2013 Free Software Foundation, Inc.
This program 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.
This program 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/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#pragma once
#include <algorithm> // std::max
#include <iostream>
#include <string>
#ifndef YY_NULL
# if defined __cplusplus && 201103L <= __cplusplus
# define YY_NULL nullptr
# else
# define YY_NULL 0
# endif
#endif
namespace spot
{
/// Abstract a position.
class position
{
public:
/// Construct a position.
explicit position (std::string* f = YY_NULL,
unsigned int l = 1u,
unsigned int c = 1u)
: filename (f)
, line (l)
, column (c)
{
}
/// Initialization.
void initialize (std::string* fn = YY_NULL,
unsigned int l = 1u,
unsigned int c = 1u)
{
filename = fn;
line = l;
column = c;
}
/** \name Line and Column related manipulators
** \{ */
/// (line related) Advance to the COUNT next lines.
void lines (int count = 1)
{
column = 1u;
line += count;
}
/// (column related) Advance to the COUNT next columns.
void columns (int count = 1)
{
column = std::max (1u, column + count);
}
/** \} */
/// File name to which this position refers.
std::string* filename;
/// Current line number.
unsigned int line;
/// Current column number.
unsigned int column;
};
/// Add and assign a position.
inline position&
operator+= (position& res, const int width)
{
res.columns (width);
return res;
}
/// Add two position objects.
inline const position
operator+ (const position& begin, const int width)
{
position res = begin;
return res += width;
}
/// Add and assign a position.
inline position&
operator-= (position& res, const int width)
{
return res += -width;
}
/// Add two position objects.
inline const position
operator- (const position& begin, const int width)
{
return begin + -width;
}
/// Compare two position objects.
inline bool
operator== (const position& pos1, const position& pos2)
{
return (pos1.line == pos2.line
&& pos1.column == pos2.column
&& (pos1.filename == pos2.filename
|| (pos1.filename && pos2.filename
&& *pos1.filename == *pos2.filename)));
}
/// Compare two position objects.
inline bool
operator!= (const position& pos1, const position& pos2)
{
return !(pos1 == pos2);
}
/** \brief Intercept output stream redirection.
** \param ostr the destination output stream
** \param pos a reference to the position to redirect
*/
template <typename YYChar>
inline std::basic_ostream<YYChar>&
operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
{
if (pos.filename)
ostr << *pos.filename << ':';
return ostr << pos.line << '.' << pos.column;
}
}

132
spot/misc/random.cc Normal file
View file

@ -0,0 +1,132 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 "config.h"
#include <spot/misc/random.hh>
#include <random>
namespace spot
{
static std::mt19937 gen;
void
srand(unsigned int seed)
{
gen.seed(seed);
}
double
drand()
{
return gen() / (1.0 + gen.max());
}
int
mrand(int max)
{
return static_cast<int>(max * drand());
}
int
rrand(int min, int max)
{
return min + static_cast<int>((max - min + 1) * drand());
}
double
nrand()
{
const double r = drand();
const double lim = 1.e-20;
if (r < lim)
return -1./lim;
if (r > 1.0 - lim)
return 1./lim;
double t;
if (r < 0.5)
t = sqrt(-2.0 * log(r));
else
t = sqrt(-2.0 * log(1.0 - r));
const double p0 = 0.322232431088;
const double p1 = 1.0;
const double p2 = 0.342242088547;
const double p3 = 0.204231210245e-1;
const double p4 = 0.453642210148e-4;
const double q0 = 0.099348462606;
const double q1 = 0.588581570495;
const double q2 = 0.531103462366;
const double q3 = 0.103537752850;
const double q4 = 0.385607006340e-2;
const double p = p0 + t * (p1 + t * (p2 + t * (p3 + t * p4)));
const double q = q0 + t * (q1 + t * (q2 + t * (q3 + t * q4)));
if (r < 0.5)
return (p / q) - t;
else
return t - (p / q);
}
double
bmrand()
{
static double next;
static bool has_next = false;
if (has_next)
{
has_next = false;
return next;
}
double x;
double y;
double r;
do
{
x = 2.0 * drand() - 1.0;
y = 2.0 * drand() - 1.0;
r = x * x + y * y;
}
while (r >= 1.0 || r == 0.0);
r = sqrt(-2 * log(r) / r);
next = y * r;
has_next = true;
return x * r;
}
int
prand(double p)
{
double s = 0.0;
long x = 0;
while (s < p)
{
s -= log(1.0 - drand());
++x;
}
return x - 1;
}
}

139
spot/misc/random.hh Normal file
View file

@ -0,0 +1,139 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// 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 <spot/misc/common.hh>
#include <cassert>
#include <cmath>
#include <vector>
namespace spot
{
/// \defgroup random Random functions
/// \ingroup misc_tools
/// \ingroup random
/// @{
/// \brief Reset the seed of the pseudo-random number generator.
///
/// \see drand, mrand, rrand
SPOT_API void srand(unsigned int seed);
/// \brief Compute a pseudo-random integer value between \a min and
/// \a max included.
///
/// \see drand, mrand, srand
SPOT_API int rrand(int min, int max);
/// \brief Compute a pseudo-random integer value between 0 and
/// \a max-1 included.
///
/// \see drand, rrand, srand
SPOT_API int mrand(int max);
/// \brief Compute a pseudo-random double value
/// between 0.0 and 1.0 (1.0 excluded).
///
/// \see mrand, rrand, srand
SPOT_API double drand();
/// \brief Compute a pseudo-random double value
/// following a standard normal distribution. (Odeh & Evans)
///
/// This uses a polynomial approximation of the inverse cumulated
/// density function from Odeh & Evans, Journal of Applied
/// Statistics, 1974, vol 23, pp 96-97.
SPOT_API double nrand();
/// \brief Compute a pseudo-random double value
/// following a standard normal distribution. (Box-Muller)
///
/// This uses the polar form of the Box-Muller transform
/// to generate random values.
SPOT_API double bmrand();
/// \brief Compute pseudo-random integer value between 0
/// and \a n included, following a binomial distribution
/// with probability \a p.
///
/// \a gen must be a random function computing a pseudo-random
/// double value following a standard normal distribution.
/// Use nrand() or bmrand().
///
/// Usually approximating a binomial distribution using a normal
/// distribution and is accurate only if <code>n*p</code> and
/// <code>n*(1-p)</code> are greater than 5.
template<double (*gen)()>
class barand
{
public:
barand(int n, double p)
: n_(n), m_(n * p), s_(sqrt(n * p * (1 - p)))
{
}
int
rand() const
{
for (;;)
{
int x = round(gen() * s_ + m_);
if (x < 0)
continue;
if (x <= n_)
return x;
}
SPOT_UNREACHABLE();
return 0;
}
protected:
const int n_;
const double m_;
const double s_;
};
/// \brief Return a pseudo-random positive integer value
/// following a Poisson distribution with parameter \a p.
///
/// \pre <code>p > 0</code>
SPOT_API int prand(double p);
/// \brief Shuffle the container using mrand function above.
/// This allows to get rid off shuffle or random_shuffle that use
/// uniform_distribution and RandomIterator that are not portables.
template<class iterator_type>
SPOT_API void mrandom_shuffle(iterator_type&& first, iterator_type&& last)
{
auto d = std::distance(first, last);
if (d > 1)
{
for (--last; first < last; ++first, --d)
{
auto i = mrand(d);
std::swap(*first, *(first + i));
}
}
}
/// @}
}

168
spot/misc/satsolver.cc Normal file
View file

@ -0,0 +1,168 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 "config.h"
#include <spot/misc/formater.hh>
#include <cstdlib>
#include <sstream>
#include <stdexcept>
#include <spot/misc/satsolver.hh>
#include <fstream>
#include <limits>
#include <sys/wait.h>
namespace spot
{
namespace
{
struct satsolver_command: formater
{
const char* satsolver;
satsolver_command()
{
satsolver = getenv("SPOT_SATSOLVER");
if (!satsolver)
{
satsolver = "glucose -verb=0 -model %I >%O";
return;
}
prime(satsolver);
if (!has('I'))
throw std::runtime_error("SPOT_SATSOLVER should contain %I to "
"indicate how to use the input filename.");
if (!has('O'))
throw std::runtime_error("SPOT_SATSOLVER should contain %O to "
"indicate how to use the output filename.");
}
int
run(printable* in, printable* out)
{
declare('I', in);
declare('O', out);
std::ostringstream s;
format(s, satsolver);
int res = system(s.str().c_str());
if (res < 0 || (WIFEXITED(res) && WEXITSTATUS(res) == 127))
{
s << ": failed to execute";
throw std::runtime_error(s.str());
}
// For POSIX shells, "The exit status of a command that
// terminated because it received a signal shall be reported
// as greater than 128."
if (WIFEXITED(res) && WEXITSTATUS(res) >= 128)
{
s << ": terminated by signal";
throw std::runtime_error(s.str());
}
if (WIFSIGNALED(res))
{
s << ": terminated by signal " << WTERMSIG(res);
throw std::runtime_error(s.str());
}
return res;
}
};
}
satsolver::solution
satsolver_get_solution(const char* filename)
{
satsolver::solution sol;
std::istream* in;
if (filename[0] == '-' && filename[1] == 0)
in = &std::cin;
else
in = new std::fstream(filename, std::ios_base::in);
int c;
while ((c = in->get()) != EOF)
{
// If a line does not start with 'v ', ignore it.
if (c != 'v' || in->get() != ' ')
{
in->ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
// Otherwise, read integers one by one.
int i;
while (*in >> i)
{
if (i == 0)
goto done;
sol.push_back(i);
}
if (!in->eof())
// If we haven't reached end-of-file, then we just attempted
// to extract something that wasn't an integer. Clear the
// fail bit so that will loop over.
in->clear();
}
done:
if (in != &std::cin)
delete in;
return sol;
}
satsolver::satsolver()
: cnf_tmp_(nullptr), cnf_stream_(nullptr)
{
start();
}
void satsolver::start()
{
cnf_tmp_ = create_tmpfile("sat-", ".cnf");
cnf_stream_ = new std::fstream(cnf_tmp_->name(),
std::ios_base::trunc | std::ios_base::out);
cnf_stream_->exceptions(std::ifstream::failbit | std::ifstream::badbit);
}
satsolver::~satsolver()
{
delete cnf_tmp_;
delete cnf_stream_;
}
std::ostream& satsolver::operator()()
{
return *cnf_stream_;
}
satsolver::solution_pair
satsolver::get_solution()
{
delete cnf_stream_; // Close the file.
cnf_stream_ = nullptr;
temporary_file* output = create_tmpfile("sat-", ".out");
solution_pair p;
// Make this static, so the SPOT_SATSOLVER lookup is done only on
// the first call to run_sat().
static satsolver_command cmd;
p.first = cmd.run(cnf_tmp_, output);
p.second = satsolver_get_solution(output->name());
delete output;
return p;
}
}

100
spot/misc/satsolver.hh Normal file
View file

@ -0,0 +1,100 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 <spot/misc/common.hh>
#include <spot/misc/tmpfile.hh>
#include <vector>
#include <stdexcept>
#include <iosfwd>
namespace spot
{
class printable;
class clause_counter
{
private:
int count_;
public:
clause_counter()
: count_(0)
{
}
void check() const
{
if (count_ < 0)
throw std::runtime_error("too many SAT clauses (more than INT_MAX)");
}
clause_counter& operator++()
{
++count_;
check();
return *this;
}
clause_counter& operator+=(int n)
{
count_ += n;
check();
return *this;
}
int nb_clauses() const
{
return count_;
}
};
/// \brief Interface with a SAT solver.
///
/// Call start() to create some temporary file, then send DIMACs
/// text to the stream returned by operator(), and finally call
/// get_solution().
///
/// The satsolver called can be configured via the
/// <code>SPOT_SATSOLVER</code> environment variable. It
/// defaults to
/// "satsolver -verb=0 %I >%O"
/// where %I and %O are replaced by input and output files.
class SPOT_API satsolver
{
public:
satsolver();
~satsolver();
void start();
std::ostream& operator()();
typedef std::vector<int> solution;
typedef std::pair<int, solution> solution_pair;
solution_pair get_solution();
private:
temporary_file* cnf_tmp_;
std::ostream* cnf_stream_;
};
/// \brief Extract the solution of a SAT solver output.
SPOT_API satsolver::solution
satsolver_get_solution(const char* filename);
}

Some files were not shown because too many files have changed in this diff Show more