move spot/bin/ and spot/tests/ up by one level
* spot/bin/: Move... * bin/: ... here. * spot/tests/: Move... * tests/: ... here. * Makefile.am, README, bench/stutter/Makefile.am, bench/stutter/stutter_invariance_formulas.cc, doc/Makefile.am, configure.ac, debian/rules, spot/Makefile.am, spot/ltsmin/Makefile.am, spot/ltsmin/kripke.test, spot/sanity/style.test, python/tests/run.in: Adjust.
This commit is contained in:
parent
ff4837f4f2
commit
134dfc73de
220 changed files with 35 additions and 30 deletions
16
bin/.gitignore
vendored
Normal file
16
bin/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
autfilt
|
||||
dstar2tgba
|
||||
genltl
|
||||
ltl2tgba
|
||||
ltl2tgta
|
||||
ltlcross
|
||||
ltldo
|
||||
ltlfilt
|
||||
ltlgrind
|
||||
randaut
|
||||
randltl
|
||||
spot-x
|
||||
*.a
|
||||
*.1
|
||||
*.7
|
||||
lck-*
|
||||
90
bin/Makefile.am
Normal file
90
bin/Makefile.am
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
## Développement de l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
SUBDIRS = . man
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(BUDDY_CPPFLAGS) \
|
||||
-I$(top_builddir)/lib -I$(top_srcdir)/lib
|
||||
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
|
||||
LDADD = \
|
||||
libcommon.a \
|
||||
$(top_builddir)/lib/libgnu.la \
|
||||
$(top_builddir)/spot/libspot.la
|
||||
|
||||
noinst_LIBRARIES = libcommon.a
|
||||
libcommon_a_SOURCES = \
|
||||
common_aoutput.cc \
|
||||
common_aoutput.hh \
|
||||
common_conv.hh \
|
||||
common_conv.cc \
|
||||
common_cout.hh \
|
||||
common_cout.cc \
|
||||
common_file.cc \
|
||||
common_file.hh \
|
||||
common_finput.cc \
|
||||
common_finput.hh \
|
||||
common_hoaread.cc \
|
||||
common_hoaread.hh \
|
||||
common_output.cc \
|
||||
common_output.hh \
|
||||
common_post.cc \
|
||||
common_post.hh \
|
||||
common_range.cc \
|
||||
common_range.hh \
|
||||
common_r.cc \
|
||||
common_r.hh \
|
||||
common_setup.cc \
|
||||
common_setup.hh \
|
||||
common_sys.hh \
|
||||
common_trans.cc \
|
||||
common_trans.hh
|
||||
|
||||
bin_PROGRAMS = \
|
||||
autfilt \
|
||||
dstar2tgba \
|
||||
genltl \
|
||||
ltl2tgba \
|
||||
ltl2tgta \
|
||||
ltlcross \
|
||||
ltldo \
|
||||
ltlfilt \
|
||||
ltlgrind \
|
||||
randaut \
|
||||
randltl
|
||||
|
||||
# Dummy program used just to generate man/spot-x.7 in a way that is
|
||||
# consistent with the other man pages (e.g., with a version number that
|
||||
# is automatically updated).
|
||||
noinst_PROGRAMS = spot-x
|
||||
|
||||
autfilt_SOURCES = autfilt.cc
|
||||
ltlfilt_SOURCES = ltlfilt.cc
|
||||
genltl_SOURCES = genltl.cc
|
||||
randaut_SOURCES = randaut.cc
|
||||
randltl_SOURCES = randltl.cc
|
||||
ltl2tgba_SOURCES = ltl2tgba.cc
|
||||
ltl2tgta_SOURCES = ltl2tgta.cc
|
||||
ltlcross_SOURCES = ltlcross.cc
|
||||
ltlgrind_SOURCES = ltlgrind.cc
|
||||
ltldo_SOURCES = ltldo.cc
|
||||
dstar2tgba_SOURCES = dstar2tgba.cc
|
||||
spot_x_SOURCES = spot-x.cc
|
||||
ltlcross_LDADD = $(LDADD) $(LIB_GETHRXTIME)
|
||||
|
||||
EXTRA_DIST = options.py
|
||||
52
bin/README
Normal file
52
bin/README
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
This directory contains the source of some command-line tools that
|
||||
expose some of Spot's algorithms to Unix users.
|
||||
|
||||
Man pages are generated from the --help output of each tool,
|
||||
supplemented by any text in the man/*.x files. Usually the extra text
|
||||
contains either some bibliographical references, some formal
|
||||
definitions or some examples that are too long for --help. Having a
|
||||
few short examples at the end of --help is good.
|
||||
|
||||
This directory also build some non-installed binaries, like spot-x,
|
||||
whose purpose is just to generate a man-page with the same format as
|
||||
the other man pages (this includes keeping the version number
|
||||
up-to-date).
|
||||
|
||||
There is also a script called 'options.py' that summerizes how the
|
||||
different short options are used among the tools.
|
||||
|
||||
Routines that are shared by multiple command-line tools are stored in
|
||||
files called common_*.{cc,hh}.
|
||||
|
||||
|
||||
Recommendations when adding new tools or features:
|
||||
--------------------------------------------------
|
||||
|
||||
- Tools should be designed to work on multiple inputs (e.g., read
|
||||
different outputs from multiple files, and accept many inputs from
|
||||
the same file, including stdin). They should also all be designed
|
||||
to produce several outputs, usually one per input. This way they
|
||||
can be piped one onto the other easily.
|
||||
|
||||
- When naming an option, seek inspiration from the POSIX standard, or
|
||||
from GNU extensions. For instance ltlfilt and autfilt both have a
|
||||
-v option to invert the filter; this is inspired from grep's -v
|
||||
option. The long version of this option (--invert-match) is also
|
||||
the same as in grep.
|
||||
|
||||
- When adding a new option, implement only the --long-option by
|
||||
default. Do not add a short version unless
|
||||
(1) you are sure it will be frequently used interactively
|
||||
(if it is only used in scripts, then a long option is enough)
|
||||
(2) this option can be shared by multiple tools.
|
||||
|
||||
- As much as possible, use the same option names across tools. Use
|
||||
the script options.py in this directory to check what short options
|
||||
are used. It's OK if the same short option correspond to different
|
||||
long names in the various tools, as long as the intent is similar.
|
||||
For instance -n has different long options depending on the tool:
|
||||
autfilt -n N means --max-count=N
|
||||
randltl -n N means --formulas=N
|
||||
randaut -n N means --automata=N
|
||||
but in all cases, the intent is to specify the number of items
|
||||
to output.
|
||||
782
bin/autfilt.cc
Normal file
782
bin/autfilt.cc
Normal file
|
|
@ -0,0 +1,782 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include <spot/twaalgos/stutter.hh>
|
||||
#include <spot/twaalgos/isunamb.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
#include <spot/tl/exclusive.hh>
|
||||
#include <spot/twaalgos/remprop.hh>
|
||||
#include <spot/twaalgos/randomize.hh>
|
||||
#include <spot/twaalgos/are_isomorphic.hh>
|
||||
#include <spot/twaalgos/canonicalize.hh>
|
||||
#include <spot/twaalgos/mask.hh>
|
||||
#include <spot/twaalgos/sepsets.hh>
|
||||
#include <spot/twaalgos/stripacc.hh>
|
||||
#include <spot/twaalgos/remfin.hh>
|
||||
#include <spot/twaalgos/cleanacc.hh>
|
||||
#include <spot/twaalgos/dtwasat.hh>
|
||||
#include <spot/twaalgos/complement.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Convert, transform, and filter omega-automata.\v\
|
||||
Exit status:\n\
|
||||
0 if some automata were output\n\
|
||||
1 if no automata were output (no match)\n\
|
||||
2 if any error has been reported";
|
||||
|
||||
// Keep this list sorted
|
||||
enum {
|
||||
OPT_ACC_SETS = 256,
|
||||
OPT_ARE_ISOMORPHIC,
|
||||
OPT_CLEAN_ACC,
|
||||
OPT_CNF_ACC,
|
||||
OPT_COMPLEMENT,
|
||||
OPT_COMPLEMENT_ACC,
|
||||
OPT_COUNT,
|
||||
OPT_DECOMPOSE_STRENGTH,
|
||||
OPT_DESTUT,
|
||||
OPT_DNF_ACC,
|
||||
OPT_EDGES,
|
||||
OPT_EXCLUSIVE_AP,
|
||||
OPT_GENERIC,
|
||||
OPT_INSTUT,
|
||||
OPT_INTERSECT,
|
||||
OPT_IS_COMPLETE,
|
||||
OPT_IS_DETERMINISTIC,
|
||||
OPT_IS_EMPTY,
|
||||
OPT_IS_UNAMBIGUOUS,
|
||||
OPT_IS_TERMINAL,
|
||||
OPT_IS_WEAK,
|
||||
OPT_IS_INHERENTLY_WEAK,
|
||||
OPT_KEEP_STATES,
|
||||
OPT_MASK_ACC,
|
||||
OPT_MERGE,
|
||||
OPT_PRODUCT_AND,
|
||||
OPT_PRODUCT_OR,
|
||||
OPT_RANDOMIZE,
|
||||
OPT_REM_AP,
|
||||
OPT_REM_DEAD,
|
||||
OPT_REM_UNREACH,
|
||||
OPT_REM_FIN,
|
||||
OPT_SAT_MINIMIZE,
|
||||
OPT_SEED,
|
||||
OPT_SEP_SETS,
|
||||
OPT_SIMPLIFY_EXCLUSIVE_AP,
|
||||
OPT_STATES,
|
||||
OPT_STRIPACC,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process the automaton in FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ "count", 'c', nullptr, 0, "print only a count of matched automata", 3 },
|
||||
{ "max-count", 'n', "NUM", 0, "output at most NUM automata", 3 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Transformations:", 5 },
|
||||
{ "merge-transitions", OPT_MERGE, nullptr, 0,
|
||||
"merge transitions with same destination and acceptance", 0 },
|
||||
{ "product", OPT_PRODUCT_AND, "FILENAME", 0,
|
||||
"build the product with the automaton in FILENAME "
|
||||
"to intersect languages", 0 },
|
||||
{ "product-and", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
{ "product-or", OPT_PRODUCT_OR, "FILENAME", 0,
|
||||
"build the product with the automaton in FILENAME "
|
||||
"to sum languages", 0 },
|
||||
{ "randomize", OPT_RANDOMIZE, "s|t", OPTION_ARG_OPTIONAL,
|
||||
"randomize states and transitions (specify 's' or 't' to "
|
||||
"randomize only states or transitions)", 0 },
|
||||
{ "instut", OPT_INSTUT, "1|2", OPTION_ARG_OPTIONAL,
|
||||
"allow more stuttering (two possible algorithms)", 0 },
|
||||
{ "destut", OPT_DESTUT, nullptr, 0, "allow less stuttering", 0 },
|
||||
{ "mask-acc", OPT_MASK_ACC, "NUM[,NUM...]", 0,
|
||||
"remove all transitions in specified acceptance sets", 0 },
|
||||
{ "strip-acceptance", OPT_STRIPACC, nullptr, 0,
|
||||
"remove the acceptance condition and all acceptance sets", 0 },
|
||||
{ "keep-states", OPT_KEEP_STATES, "NUM[,NUM...]", 0,
|
||||
"only keep specified states. The first state will be the new "\
|
||||
"initial state. Implies --remove-unreachable-states.", 0 },
|
||||
{ "dnf-acceptance", OPT_DNF_ACC, nullptr, 0,
|
||||
"put the acceptance condition in Disjunctive Normal Form", 0 },
|
||||
{ "cnf-acceptance", OPT_CNF_ACC, nullptr, 0,
|
||||
"put the acceptance condition in Conjunctive Normal Form", 0 },
|
||||
{ "remove-fin", OPT_REM_FIN, nullptr, 0,
|
||||
"rewrite the automaton without using Fin acceptance", 0 },
|
||||
{ "cleanup-acceptance", OPT_CLEAN_ACC, nullptr, 0,
|
||||
"remove unused acceptance sets from the automaton", 0 },
|
||||
{ "complement", OPT_COMPLEMENT, nullptr, 0,
|
||||
"complement each automaton (currently support only deterministic "
|
||||
"automata)", 0 },
|
||||
{ "complement-acceptance", OPT_COMPLEMENT_ACC, nullptr, 0,
|
||||
"complement the acceptance condition (without touching the automaton)",
|
||||
0 },
|
||||
{ "decompose-strength", OPT_DECOMPOSE_STRENGTH, "t|w|s", 0,
|
||||
"extract the (t) terminal, (w) weak, or (s) strong part of an automaton"
|
||||
" (letters may be combined to combine more strengths in the output)", 0 },
|
||||
{ "exclusive-ap", OPT_EXCLUSIVE_AP, "AP,AP,...", 0,
|
||||
"if any of those APs occur in the automaton, restrict all edges to "
|
||||
"ensure two of them may not be true at the same time. Use this option "
|
||||
"multiple times to declare independent groups of exclusive "
|
||||
"propositions.", 0 },
|
||||
{ "simplify-exclusive-ap", OPT_SIMPLIFY_EXCLUSIVE_AP, nullptr, 0,
|
||||
"if --exclusive-ap is used, assume those AP groups are actually exclusive"
|
||||
" in the system to simplify the expression of transition labels (implies "
|
||||
"--merge-transitions)", 0 },
|
||||
{ "remove-ap", OPT_REM_AP, "AP[=0|=1][,AP...]", 0,
|
||||
"remove atomic propositions either by existential quantification, or "
|
||||
"by assigning them 0 or 1", 0 },
|
||||
{ "remove-unreachable-states", OPT_REM_UNREACH, nullptr, 0,
|
||||
"remove states that are unreachable from the initial state", 0 },
|
||||
{ "remove-dead-states", OPT_REM_DEAD, nullptr, 0,
|
||||
"remove states that are unreachable, or that cannot belong to an "
|
||||
"infinite path", 0 },
|
||||
{ "separate-sets", OPT_SEP_SETS, nullptr, 0,
|
||||
"if both Inf(x) and Fin(x) appear in the acceptance condition, replace "
|
||||
"Fin(x) by a new Fin(y) and adjust the automaton", 0 },
|
||||
{ "sat-minimize", OPT_SAT_MINIMIZE, "options", OPTION_ARG_OPTIONAL,
|
||||
"minimize the automaton using a SAT solver (only work for deterministic"
|
||||
" automata)", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Filtering options:", 6 },
|
||||
{ "are-isomorphic", OPT_ARE_ISOMORPHIC, "FILENAME", 0,
|
||||
"keep automata that are isomorphic to the automaton in FILENAME", 0 },
|
||||
{ "isomorphic", 0, nullptr, OPTION_ALIAS | OPTION_HIDDEN, nullptr, 0 },
|
||||
{ "unique", 'u', nullptr, 0,
|
||||
"do not output the same automaton twice (same in the sense that they "\
|
||||
"are isomorphic)", 0 },
|
||||
{ "is-complete", OPT_IS_COMPLETE, nullptr, 0,
|
||||
"keep complete automata", 0 },
|
||||
{ "is-deterministic", OPT_IS_DETERMINISTIC, nullptr, 0,
|
||||
"keep deterministic automata", 0 },
|
||||
{ "is-empty", OPT_IS_EMPTY, nullptr, 0,
|
||||
"keep automata with an empty language", 0 },
|
||||
{ "is-unambiguous", OPT_IS_UNAMBIGUOUS, nullptr, 0,
|
||||
"keep only unambiguous automata", 0 },
|
||||
{ "is-terminal", OPT_IS_TERMINAL, nullptr, 0,
|
||||
"keep only terminal automata", 0 },
|
||||
{ "is-weak", OPT_IS_WEAK, nullptr, 0,
|
||||
"keep only weak automata", 0 },
|
||||
{ "is-inherently-weak", OPT_IS_INHERENTLY_WEAK, nullptr, 0,
|
||||
"keep only inherently weak automata", 0 },
|
||||
{ "intersect", OPT_INTERSECT, "FILENAME", 0,
|
||||
"keep automata whose languages have an non-empty intersection with"
|
||||
" the automaton from FILENAME", 0 },
|
||||
{ "invert-match", 'v', nullptr, 0, "select non-matching automata", 0 },
|
||||
{ "states", OPT_STATES, "RANGE", 0,
|
||||
"keep automata whose number of states are in RANGE", 0 },
|
||||
{ "edges", OPT_EDGES, "RANGE", 0,
|
||||
"keep automata whose number of edges are in RANGE", 0 },
|
||||
{ "acc-sets", OPT_ACC_SETS, "RANGE", 0,
|
||||
"keep automata whose number of acceptance sets are in RANGE", 0 },
|
||||
RANGE_DOC_FULL,
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"If any option among --small, --deterministic, or --any is given, "
|
||||
"then the simplification level defaults to --high unless specified "
|
||||
"otherwise. If any option among --low, --medium, or --high is given, "
|
||||
"then the simplification goal defaults to --small unless specified "
|
||||
"otherwise. If none of those options are specified, then autfilt "
|
||||
"acts as is --any --low were given: these actually disable the "
|
||||
"simplification routines.", 22 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_io_format_argp, 0, nullptr, 4 },
|
||||
{ &post_argp_disabled, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
|
||||
typedef std::set<std::vector<tr_t>> unique_aut_t;
|
||||
static long int match_count = 0;
|
||||
static spot::option_map extra_options;
|
||||
static bool randomize_st = false;
|
||||
static bool randomize_tr = false;
|
||||
static int opt_seed = 0;
|
||||
|
||||
// We want all these variable to be destroyed when we exit main, to
|
||||
// make sure it happens before all other global variables (like the
|
||||
// atomic propositions maps) are destroyed. Otherwise we risk
|
||||
// accessing deleted stuff.
|
||||
static struct opt_t
|
||||
{
|
||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
||||
spot::twa_graph_ptr product_and = nullptr;
|
||||
spot::twa_graph_ptr product_or = nullptr;
|
||||
spot::twa_graph_ptr intersect = nullptr;
|
||||
spot::twa_graph_ptr are_isomorphic = nullptr;
|
||||
std::unique_ptr<spot::isomorphism_checker>
|
||||
isomorphism_checker = nullptr;
|
||||
std::unique_ptr<unique_aut_t> uniq = nullptr;
|
||||
}* opt;
|
||||
|
||||
static bool opt_merge = false;
|
||||
static bool opt_is_complete = false;
|
||||
static bool opt_is_deterministic = false;
|
||||
static bool opt_is_unambiguous = false;
|
||||
static bool opt_is_terminal = false;
|
||||
static bool opt_is_weak = false;
|
||||
static bool opt_is_inherently_weak = false;
|
||||
static bool opt_invert = false;
|
||||
static range opt_states = { 0, std::numeric_limits<int>::max() };
|
||||
static range opt_edges = { 0, std::numeric_limits<int>::max() };
|
||||
static range opt_accsets = { 0, std::numeric_limits<int>::max() };
|
||||
static int opt_max_count = -1;
|
||||
static bool opt_destut = false;
|
||||
static char opt_instut = 0;
|
||||
static bool opt_is_empty = false;
|
||||
static bool opt_stripacc = false;
|
||||
static bool opt_dnf_acc = false;
|
||||
static bool opt_cnf_acc = false;
|
||||
static bool opt_rem_fin = false;
|
||||
static bool opt_clean_acc = false;
|
||||
static bool opt_complement = false;
|
||||
static bool opt_complement_acc = false;
|
||||
static char* opt_decompose_strength = nullptr;
|
||||
static spot::acc_cond::mark_t opt_mask_acc = 0U;
|
||||
static std::vector<bool> opt_keep_states = {};
|
||||
static unsigned int opt_keep_states_initial = 0;
|
||||
static spot::exclusive_ap excl_ap;
|
||||
static spot::remove_ap rem_ap;
|
||||
static bool opt_simplify_exclusive_ap = false;
|
||||
static bool opt_rem_dead = false;
|
||||
static bool opt_rem_unreach = false;
|
||||
static bool opt_sep_sets = false;
|
||||
static const char* opt_sat_minimize = nullptr;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'c':
|
||||
automaton_format = Count;
|
||||
break;
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case 'n':
|
||||
opt_max_count = to_pos_int(arg);
|
||||
break;
|
||||
case 'u':
|
||||
opt->uniq =
|
||||
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
|
||||
break;
|
||||
case 'v':
|
||||
opt_invert = true;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case OPT_ACC_SETS:
|
||||
opt_accsets = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_ARE_ISOMORPHIC:
|
||||
opt->are_isomorphic = read_automaton(arg, opt->dict);
|
||||
break;
|
||||
case OPT_CLEAN_ACC:
|
||||
opt_clean_acc = true;
|
||||
break;
|
||||
case OPT_CNF_ACC:
|
||||
opt_dnf_acc = false;
|
||||
opt_cnf_acc = true;
|
||||
break;
|
||||
case OPT_COMPLEMENT:
|
||||
opt_complement = true;
|
||||
break;
|
||||
case OPT_COMPLEMENT_ACC:
|
||||
opt_complement_acc = true;
|
||||
break;
|
||||
case OPT_DECOMPOSE_STRENGTH:
|
||||
opt_decompose_strength = arg;
|
||||
break;
|
||||
case OPT_DESTUT:
|
||||
opt_destut = true;
|
||||
break;
|
||||
case OPT_DNF_ACC:
|
||||
opt_dnf_acc = true;
|
||||
opt_cnf_acc = false;
|
||||
break;
|
||||
case OPT_EDGES:
|
||||
opt_edges = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_EXCLUSIVE_AP:
|
||||
excl_ap.add_group(arg);
|
||||
break;
|
||||
case OPT_INSTUT:
|
||||
if (!arg || (arg[0] == '1' && arg[1] == 0))
|
||||
opt_instut = 1;
|
||||
else if (arg[0] == '2' && arg[1] == 0)
|
||||
opt_instut = 2;
|
||||
else
|
||||
error(2, 0, "unknown argument for --instut: %s", arg);
|
||||
break;
|
||||
case OPT_INTERSECT:
|
||||
opt->intersect = read_automaton(arg, opt->dict);
|
||||
break;
|
||||
case OPT_IS_COMPLETE:
|
||||
opt_is_complete = true;
|
||||
break;
|
||||
case OPT_IS_DETERMINISTIC:
|
||||
opt_is_deterministic = true;
|
||||
break;
|
||||
case OPT_IS_EMPTY:
|
||||
opt_is_empty = true;
|
||||
break;
|
||||
case OPT_IS_INHERENTLY_WEAK:
|
||||
opt_is_inherently_weak = true;
|
||||
break;
|
||||
case OPT_IS_UNAMBIGUOUS:
|
||||
opt_is_unambiguous = true;
|
||||
break;
|
||||
case OPT_IS_TERMINAL:
|
||||
opt_is_terminal = true;
|
||||
break;
|
||||
case OPT_IS_WEAK:
|
||||
opt_is_weak = true;
|
||||
break;
|
||||
case OPT_MERGE:
|
||||
opt_merge = true;
|
||||
break;
|
||||
case OPT_MASK_ACC:
|
||||
{
|
||||
for (auto res : to_longs(arg))
|
||||
{
|
||||
if (res < 0)
|
||||
error(2, 0, "acceptance sets should be non-negative:"
|
||||
" --mask-acc=%ld", res);
|
||||
if (static_cast<unsigned long>(res)
|
||||
> sizeof(spot::acc_cond::mark_t::value_t))
|
||||
error(2, 0, "this implementation does not support that many"
|
||||
" acceptance sets: --mask-acc=%ld", res);
|
||||
opt_mask_acc.set(res);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPT_KEEP_STATES:
|
||||
{
|
||||
std::vector<long> values = to_longs(arg);
|
||||
if (!values.empty())
|
||||
opt_keep_states_initial = values[0];
|
||||
for (auto res : values)
|
||||
{
|
||||
if (res < 0)
|
||||
error(2, 0, "state ids should be non-negative:"
|
||||
" --mask-acc=%ld", res);
|
||||
// We don't know yet how many states the automata contain.
|
||||
if (opt_keep_states.size() <= static_cast<unsigned long>(res))
|
||||
opt_keep_states.resize(res + 1, false);
|
||||
opt_keep_states[res] = true;
|
||||
}
|
||||
opt_rem_unreach = true;
|
||||
break;
|
||||
}
|
||||
case OPT_PRODUCT_AND:
|
||||
{
|
||||
auto a = read_automaton(arg, opt->dict);
|
||||
if (!opt->product_and)
|
||||
opt->product_and = std::move(a);
|
||||
else
|
||||
opt->product_and = spot::product(std::move(opt->product_and),
|
||||
std::move(a));
|
||||
}
|
||||
break;
|
||||
case OPT_PRODUCT_OR:
|
||||
{
|
||||
auto a = read_automaton(arg, opt->dict);
|
||||
if (!opt->product_or)
|
||||
opt->product_or = std::move(a);
|
||||
else
|
||||
opt->product_or = spot::product_or(std::move(opt->product_or),
|
||||
std::move(a));
|
||||
}
|
||||
break;
|
||||
case OPT_RANDOMIZE:
|
||||
if (arg)
|
||||
{
|
||||
for (auto p = arg; *p; ++p)
|
||||
switch (*p)
|
||||
{
|
||||
case 's':
|
||||
randomize_st = true;
|
||||
break;
|
||||
case 't':
|
||||
randomize_tr = true;
|
||||
break;
|
||||
default:
|
||||
error(2, 0, "unknown argument for --randomize: '%c'", *p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
randomize_tr = true;
|
||||
randomize_st = true;
|
||||
}
|
||||
break;
|
||||
case OPT_REM_AP:
|
||||
rem_ap.add_ap(arg);
|
||||
break;
|
||||
case OPT_REM_DEAD:
|
||||
opt_rem_dead = true;
|
||||
break;
|
||||
case OPT_REM_FIN:
|
||||
opt_rem_fin = true;
|
||||
break;
|
||||
case OPT_REM_UNREACH:
|
||||
opt_rem_unreach = true;
|
||||
break;
|
||||
case OPT_SAT_MINIMIZE:
|
||||
opt_sat_minimize = arg ? arg : "";
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
break;
|
||||
case OPT_SEP_SETS:
|
||||
opt_sep_sets = true;
|
||||
break;
|
||||
case OPT_SIMPLIFY_EXCLUSIVE_AP:
|
||||
opt_simplify_exclusive_ap = true;
|
||||
opt_merge = true;
|
||||
break;
|
||||
case OPT_STATES:
|
||||
opt_states = parse_range(arg, 0, std::numeric_limits<int>::max());
|
||||
break;
|
||||
case OPT_STRIPACC:
|
||||
opt_stripacc = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class hoa_processor: public job_processor
|
||||
{
|
||||
private:
|
||||
spot::postprocessor& post;
|
||||
automaton_printer printer;
|
||||
public:
|
||||
|
||||
hoa_processor(spot::postprocessor& post)
|
||||
: post(post), printer(aut_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula, const char*, int)
|
||||
{
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut,
|
||||
const char* filename)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
|
||||
// If --stats or --name is used, duplicate the automaton so we
|
||||
// never modify the original automaton (e.g. with
|
||||
// merge_edges()) and the statistics about it make sense.
|
||||
auto aut = ((automaton_format == Stats) || opt_name)
|
||||
? spot::make_twa_graph(haut->aut, spot::twa::prop_set::all())
|
||||
: haut->aut;
|
||||
|
||||
// Preprocessing.
|
||||
|
||||
if (opt_stripacc)
|
||||
spot::strip_acceptance_here(aut);
|
||||
if (opt_merge)
|
||||
aut->merge_edges();
|
||||
if (opt_clean_acc || opt_rem_fin)
|
||||
cleanup_acceptance_here(aut);
|
||||
if (opt_sep_sets)
|
||||
separate_sets_here(aut);
|
||||
if (opt_complement_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().complement());
|
||||
if (opt_complement)
|
||||
{
|
||||
if (!spot::is_deterministic(aut))
|
||||
{
|
||||
std::cerr << filename << ':'
|
||||
<< haut->loc << (": --complement currently supports "
|
||||
"only deterministic automata\n");
|
||||
exit(2);
|
||||
}
|
||||
aut = spot::dtwa_complement(aut);
|
||||
}
|
||||
if (opt_rem_fin)
|
||||
aut = remove_fin(aut);
|
||||
if (opt_dnf_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().to_dnf());
|
||||
if (opt_cnf_acc)
|
||||
aut->set_acceptance(aut->acc().num_sets(),
|
||||
aut->get_acceptance().to_cnf());
|
||||
|
||||
// Filters.
|
||||
|
||||
bool matched = true;
|
||||
|
||||
matched &= opt_states.contains(aut->num_states());
|
||||
matched &= opt_edges.contains(aut->num_edges());
|
||||
matched &= opt_accsets.contains(aut->acc().num_sets());
|
||||
if (opt_is_complete)
|
||||
matched &= is_complete(aut);
|
||||
if (opt_is_deterministic)
|
||||
matched &= is_deterministic(aut);
|
||||
if (opt_is_deterministic)
|
||||
matched &= is_deterministic(aut);
|
||||
else if (opt_is_unambiguous)
|
||||
matched &= is_unambiguous(aut);
|
||||
if (opt_is_terminal)
|
||||
matched &= is_terminal_automaton(aut);
|
||||
else if (opt_is_weak)
|
||||
matched &= is_weak_automaton(aut);
|
||||
else if (opt_is_inherently_weak)
|
||||
matched &= is_inherently_weak_automaton(aut);
|
||||
if (opt->are_isomorphic)
|
||||
matched &= opt->isomorphism_checker->is_isomorphic(aut);
|
||||
if (opt_is_empty)
|
||||
matched &= aut->is_empty();
|
||||
if (opt->intersect)
|
||||
matched &= !spot::product(aut, opt->intersect)->is_empty();
|
||||
|
||||
// Drop or keep matched automata depending on the --invert option
|
||||
if (matched == opt_invert)
|
||||
return 0;
|
||||
|
||||
// Postprocessing.
|
||||
|
||||
if (opt_mask_acc)
|
||||
aut = mask_acc_sets(aut, opt_mask_acc & aut->acc().all_sets());
|
||||
|
||||
if (!excl_ap.empty())
|
||||
aut = excl_ap.constrain(aut, opt_simplify_exclusive_ap);
|
||||
|
||||
if (!rem_ap.empty())
|
||||
aut = rem_ap.strip(aut);
|
||||
|
||||
if (opt_destut)
|
||||
aut = spot::closure(std::move(aut));
|
||||
if (opt_instut == 1)
|
||||
aut = spot::sl(std::move(aut));
|
||||
else if (opt_instut == 2)
|
||||
aut = spot::sl2(std::move(aut));
|
||||
|
||||
if (!opt_keep_states.empty())
|
||||
aut = mask_keep_states(aut, opt_keep_states, opt_keep_states_initial);
|
||||
if (opt_rem_dead)
|
||||
aut->purge_dead_states();
|
||||
else if (opt_rem_unreach)
|
||||
aut->purge_unreachable_states();
|
||||
|
||||
if (opt->product_and)
|
||||
aut = spot::product(std::move(aut), opt->product_and);
|
||||
if (opt->product_or)
|
||||
aut = spot::product_or(std::move(aut), opt->product_or);
|
||||
|
||||
if (opt_decompose_strength)
|
||||
{
|
||||
aut = decompose_strength(aut, opt_decompose_strength);
|
||||
if (!aut)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opt_sat_minimize)
|
||||
{
|
||||
aut = spot::sat_minimize(aut, opt_sat_minimize, sbacc);
|
||||
if (!aut)
|
||||
return 0;
|
||||
}
|
||||
|
||||
aut = post.run(aut, nullptr);
|
||||
|
||||
if (randomize_st || randomize_tr)
|
||||
spot::randomize(aut, randomize_st, randomize_tr);
|
||||
|
||||
const double conversion_time = sw.stop();
|
||||
|
||||
if (opt->uniq)
|
||||
{
|
||||
auto tmp =
|
||||
spot::canonicalize(make_twa_graph(aut,
|
||||
spot::twa::prop_set::all()));
|
||||
if (!opt->uniq->emplace(tmp->edge_vector().begin() + 1,
|
||||
tmp->edge_vector().end()).second)
|
||||
return 0;
|
||||
}
|
||||
|
||||
++match_count;
|
||||
|
||||
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
|
||||
|
||||
if (opt_max_count >= 0 && match_count >= opt_max_count)
|
||||
abort_run = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
|
||||
{
|
||||
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
process_file(const char* filename)
|
||||
{
|
||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||
int err = 0;
|
||||
while (!abort_run)
|
||||
{
|
||||
auto haut = hp.parse(opt->dict);
|
||||
if (!haut->aut && haut->errors.empty())
|
||||
break;
|
||||
if (haut->format_errors(std::cerr))
|
||||
err = 2;
|
||||
if (!haut->aut)
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
else if (haut->aborted)
|
||||
err = std::max(err, aborted(haut, filename));
|
||||
else
|
||||
process_automaton(haut, filename);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAMES...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
try
|
||||
{
|
||||
// This will ensure that all objects stored in this struct are
|
||||
// destroyed before global variables.
|
||||
opt_t o;
|
||||
opt = &o;
|
||||
|
||||
// Disable post-processing as much as possible by default.
|
||||
level = spot::postprocessor::Low;
|
||||
pref = spot::postprocessor::Any;
|
||||
type = spot::postprocessor::Generic;
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (level_set && !pref_set)
|
||||
pref = spot::postprocessor::Small;
|
||||
if (pref_set && !level_set)
|
||||
level = spot::postprocessor::High;
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", true);
|
||||
|
||||
if (opt->are_isomorphic)
|
||||
{
|
||||
if (opt_merge)
|
||||
opt->are_isomorphic->merge_edges();
|
||||
opt->isomorphism_checker = std::unique_ptr<spot::isomorphism_checker>
|
||||
(new spot::isomorphism_checker(opt->are_isomorphic));
|
||||
}
|
||||
|
||||
|
||||
spot::srand(opt_seed);
|
||||
|
||||
spot::postprocessor post(&extra_options);
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
hoa_processor processor(post);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
if (automaton_format == Count)
|
||||
std::cout << match_count << std::endl;
|
||||
|
||||
return !match_count;
|
||||
}
|
||||
368
bin/common_aoutput.cc
Normal file
368
bin/common_aoutput.cc
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_cout.hh"
|
||||
|
||||
#include <spot/twa/bddprint.hh>
|
||||
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
#include <spot/twaalgos/stutter.hh>
|
||||
#include <spot/twaalgos/isunamb.hh>
|
||||
#include <spot/twaalgos/strength.hh>
|
||||
|
||||
automaton_format_t automaton_format = Dot;
|
||||
static const char* opt_dot = nullptr;
|
||||
static const char* opt_never = nullptr;
|
||||
static const char* hoa_opt = nullptr;
|
||||
static const char* opt_lbtt = nullptr;
|
||||
const char* opt_name = nullptr;
|
||||
static const char* opt_output = nullptr;
|
||||
static const char* stats = "";
|
||||
enum check_type
|
||||
{
|
||||
check_unambiguous = (1 << 0),
|
||||
check_stutter = (1 << 1),
|
||||
check_strength = (1 << 2),
|
||||
check_all = -1U,
|
||||
};
|
||||
static char const *const check_args[] =
|
||||
{
|
||||
"unambiguous",
|
||||
"stutter-invariant", "stuttering-invariant",
|
||||
"stutter-insensitive", "stuttering-insensitive",
|
||||
"stutter-sensitive", "stuttering-sensitive",
|
||||
"strength", "weak", "terminal",
|
||||
"all",
|
||||
nullptr
|
||||
};
|
||||
static check_type const check_types[] =
|
||||
{
|
||||
check_unambiguous,
|
||||
check_stutter, check_stutter,
|
||||
check_stutter, check_stutter,
|
||||
check_stutter, check_stutter,
|
||||
check_strength, check_strength, check_strength,
|
||||
check_all
|
||||
};
|
||||
ARGMATCH_VERIFY(check_args, check_types);
|
||||
unsigned opt_check = 0U;
|
||||
|
||||
enum {
|
||||
OPT_DOT = 1,
|
||||
OPT_LBTT,
|
||||
OPT_NAME,
|
||||
OPT_STATS,
|
||||
OPT_CHECK,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output format:", 3 },
|
||||
{ "dot", OPT_DOT, "1|a|b|B|c|e|f(FONT)|h|n|N|o|r|R|s|t|v|+INT",
|
||||
OPTION_ARG_OPTIONAL,
|
||||
"GraphViz's format (default). Add letters for "
|
||||
"(1) force numbered states, "
|
||||
"(a) acceptance display, (b) acceptance sets as bullets, "
|
||||
"(B) bullets except for Büchi/co-Büchi automata, "
|
||||
"(c) force circular nodes, (e) force elliptic nodes, "
|
||||
"(f(FONT)) use FONT, (h) horizontal layout, "
|
||||
"(v) vertical layout, (n) with name, (N) without name, "
|
||||
"(o) ordered transitions, "
|
||||
"(r) rainbow colors for acceptance sets, "
|
||||
"(R) color acceptance sets by Inf/Fin, (s) with SCCs, "
|
||||
"(t) force transition-based acceptance, "
|
||||
"(+INT) add INT to all set numbers", 0 },
|
||||
{ "hoaf", 'H', "i|l|m|s|t|v", OPTION_ARG_OPTIONAL,
|
||||
"Output the automaton in HOA format. Add letters to select "
|
||||
"(i) use implicit labels for complete deterministic automata, "
|
||||
"(s) prefer state-based acceptance when possible [default], "
|
||||
"(t) force transition-based acceptance, "
|
||||
"(m) mix state and transition-based acceptance, "
|
||||
"(k) use state labels when possible, "
|
||||
"(l) single-line output, "
|
||||
"(v) verbose properties", 0 },
|
||||
{ "lbtt", OPT_LBTT, "t", OPTION_ARG_OPTIONAL,
|
||||
"LBTT's format (add =t to force transition-based acceptance even"
|
||||
" on Büchi automata)", 0 },
|
||||
{ "name", OPT_NAME, "FORMAT", 0,
|
||||
"set the name of the output automaton", 0 },
|
||||
{ "output", 'o', "FORMAT", 0,
|
||||
"send output to a file named FORMAT instead of standard output. The"
|
||||
" first automaton sent to a file truncates it unless FORMAT starts"
|
||||
" with '>>'.", 0 },
|
||||
{ "quiet", 'q', nullptr, 0, "suppress all normal output", 0 },
|
||||
{ "spin", 's', "6|c", OPTION_ARG_OPTIONAL, "Spin neverclaim (implies --ba)."
|
||||
" Add letters to select (6) Spin's 6.2.4 style, (c) comments on states",
|
||||
0 },
|
||||
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output "
|
||||
"(ignored with --lbtt or --spin)", 0 },
|
||||
{ "stats", OPT_STATS, "FORMAT", 0,
|
||||
"output statistics about the automaton", 0 },
|
||||
{ "check", OPT_CHECK, "PROP", OPTION_ARG_OPTIONAL,
|
||||
"test for the additional property PROP and output the result "
|
||||
"in the HOA format (implies -H). PROP may be any prefix of "
|
||||
"'all' (default), 'unambiguous', 'stutter-invariant', or 'strength'.",
|
||||
0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_argp = { options, parse_opt_aoutput, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
// Those can be overridden by individual tools. E.g. randaut has no
|
||||
// notion of input file, so %F and %L represent something else.
|
||||
char F_doc[32] = "name of the input file";
|
||||
char L_doc[32] = "location in the input file";
|
||||
|
||||
static const argp_option io_options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
|
||||
"the following interpreted sequences (capitals for input,"
|
||||
" minuscules for output):", 4 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
|
||||
{ "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"name of the automaton", 0 },
|
||||
{ "%S, %s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of states", 0 },
|
||||
{ "%E, %e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of edges", 0 },
|
||||
{ "%T, %t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of transitions", 0 },
|
||||
{ "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of acceptance sets", 0 },
|
||||
{ "%G, %g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"acceptance condition (in HOA syntax)", 0 },
|
||||
{ "%C, %c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of SCCs", 0 },
|
||||
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of nondeterministic states in output", 0 },
|
||||
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is deterministic, 0 otherwise", 0 },
|
||||
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is complete, 0 otherwise", 0 },
|
||||
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"processing time (excluding parsing) in seconds", 0 },
|
||||
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"one word accepted by the output automaton", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_io_format_argp = { io_options, nullptr, nullptr,
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
static const argp_option o_options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Any FORMAT string may use "\
|
||||
"the following interpreted sequences:", 4 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, F_doc, 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, L_doc, 0 },
|
||||
{ "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"name of the automaton", 0 },
|
||||
{ "%s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of states", 0 },
|
||||
{ "%e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of edges", 0 },
|
||||
{ "%t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of transitions", 0 },
|
||||
{ "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of acceptance sets", 0 },
|
||||
{ "%g", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"acceptance condition (in HOA syntax)", 0 },
|
||||
{ "%c", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of SCCs", 0 },
|
||||
{ "%n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"number of nondeterministic states in output", 0 },
|
||||
{ "%d", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is deterministic, 0 otherwise", 0 },
|
||||
{ "%p", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"1 if the output is complete, 0 otherwise", 0 },
|
||||
{ "%r", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"processing time (excluding parsing) in seconds", 0 },
|
||||
{ "%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"one word accepted by the output automaton", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp aoutput_o_format_argp = { o_options,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
int parse_opt_aoutput(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'H':
|
||||
automaton_format = Hoa;
|
||||
hoa_opt = arg;
|
||||
break;
|
||||
case 'q':
|
||||
automaton_format = Quiet;
|
||||
break;
|
||||
case 's':
|
||||
automaton_format = Spin;
|
||||
if (type != spot::postprocessor::Monitor)
|
||||
type = spot::postprocessor::BA;
|
||||
if (arg)
|
||||
opt_never = arg;
|
||||
break;
|
||||
case 'o':
|
||||
opt_output = arg;
|
||||
break;
|
||||
case OPT_CHECK:
|
||||
automaton_format = Hoa;
|
||||
if (arg)
|
||||
opt_check |= XARGMATCH("--check", arg, check_args, check_types);
|
||||
else
|
||||
opt_check |= check_all;
|
||||
break;
|
||||
case OPT_DOT:
|
||||
automaton_format = Dot;
|
||||
opt_dot = arg;
|
||||
break;
|
||||
case OPT_LBTT:
|
||||
automaton_format = Lbtt;
|
||||
opt_lbtt = arg;
|
||||
// This test could be removed when more options are added,
|
||||
// because print_lbtt will raise an exception anyway. The
|
||||
// error message is slightly better in the current way.
|
||||
if (arg && (arg[0] != 't' || arg[1] != 0))
|
||||
error(2, 0, "unknown argument for --lbtt: '%s'", arg);
|
||||
break;
|
||||
case OPT_NAME:
|
||||
opt_name = arg;
|
||||
break;
|
||||
case OPT_STATS:
|
||||
if (!*arg)
|
||||
error(2, 0, "empty format string for --stats");
|
||||
stats = arg;
|
||||
automaton_format = Stats;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
automaton_printer::automaton_printer(stat_style input)
|
||||
: statistics(std::cout, stats, input),
|
||||
namer(name, opt_name, input),
|
||||
outputnamer(outputname, opt_output, input)
|
||||
{
|
||||
if (automaton_format == Count && opt_output)
|
||||
throw std::runtime_error
|
||||
("options --output and --count are incompatible");
|
||||
}
|
||||
|
||||
void
|
||||
automaton_printer::print(const spot::twa_graph_ptr& aut,
|
||||
spot::formula f,
|
||||
// Input location for errors and statistics.
|
||||
const char* filename,
|
||||
int loc,
|
||||
// Time and input automaton for statistics
|
||||
double time,
|
||||
const spot::const_parsed_aut_ptr& haut)
|
||||
{
|
||||
if (opt_check)
|
||||
{
|
||||
if (opt_check & check_stutter)
|
||||
spot::check_stutter_invariance(aut, f);
|
||||
if (opt_check & check_unambiguous)
|
||||
spot::check_unambiguous(aut);
|
||||
if (opt_check & check_strength)
|
||||
spot::check_strength(aut);
|
||||
}
|
||||
|
||||
// Name the output automaton.
|
||||
if (opt_name)
|
||||
{
|
||||
name.str("");
|
||||
namer.print(haut, aut, f, filename, loc, time);
|
||||
aut->set_named_prop("automaton-name", new std::string(name.str()));
|
||||
}
|
||||
|
||||
std::ostream* out = &std::cout;
|
||||
if (opt_output)
|
||||
{
|
||||
outputname.str("");
|
||||
outputnamer.print(haut, aut, f, filename, loc, time);
|
||||
std::string fname = outputname.str();
|
||||
auto p = outputfiles.emplace(fname, nullptr);
|
||||
if (p.second)
|
||||
p.first->second.reset(new output_file(fname.c_str()));
|
||||
out = &p.first->second->ostream();
|
||||
}
|
||||
|
||||
// Output it.
|
||||
switch (automaton_format)
|
||||
{
|
||||
case Count:
|
||||
case Quiet:
|
||||
// Do not output anything.
|
||||
break;
|
||||
case Dot:
|
||||
spot::print_dot(*out, aut, opt_dot);
|
||||
break;
|
||||
case Lbtt:
|
||||
spot::print_lbtt(*out, aut, opt_lbtt);
|
||||
break;
|
||||
case Hoa:
|
||||
spot::print_hoa(*out, aut, hoa_opt) << '\n';
|
||||
break;
|
||||
case Spin:
|
||||
spot::print_never_claim(*out, aut, opt_never);
|
||||
break;
|
||||
case Stats:
|
||||
statistics.set_output(*out);
|
||||
statistics.print(haut, aut, f, filename, loc, time) << '\n';
|
||||
break;
|
||||
}
|
||||
flush_cout();
|
||||
}
|
||||
|
||||
void automaton_printer::add_stat(char c, const spot::printable* p)
|
||||
{
|
||||
namer.declare(c, p);
|
||||
statistics.declare(c, p);
|
||||
outputnamer.declare(c, p);
|
||||
}
|
||||
235
bin/common_aoutput.hh
Normal file
235
bin/common_aoutput.hh
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <memory>
|
||||
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
#include <spot/twaalgos/gtec/gtec.hh>
|
||||
#include <spot/twaalgos/word.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include "common_file.hh"
|
||||
|
||||
|
||||
// Format for automaton output
|
||||
enum automaton_format_t {
|
||||
Dot,
|
||||
Lbtt,
|
||||
Spin,
|
||||
Stats,
|
||||
Hoa,
|
||||
Quiet,
|
||||
Count,
|
||||
};
|
||||
|
||||
// The format to use in output_automaton()
|
||||
extern automaton_format_t automaton_format;
|
||||
// Set to the argument of --name, else nullptr.
|
||||
extern const char* opt_name;
|
||||
// Output options
|
||||
extern const struct argp aoutput_argp;
|
||||
|
||||
// help text for %F and %L
|
||||
extern char F_doc[32];
|
||||
extern char L_doc[32];
|
||||
|
||||
// FORMAT help text
|
||||
extern const struct argp aoutput_io_format_argp;
|
||||
extern const struct argp aoutput_o_format_argp;
|
||||
|
||||
// Parse output options
|
||||
int parse_opt_aoutput(int key, char* arg, struct argp_state* state);
|
||||
|
||||
|
||||
enum stat_style { no_input, aut_input, ltl_input };
|
||||
|
||||
/// \brief prints various statistics about a TGBA
|
||||
///
|
||||
/// This object can be configured to display various statistics
|
||||
/// about a TGBA. Some %-sequence of characters are interpreted in
|
||||
/// the format string, and replaced by the corresponding statistics.
|
||||
class hoa_stat_printer: protected spot::stat_printer
|
||||
{
|
||||
public:
|
||||
hoa_stat_printer(std::ostream& os, const char* format,
|
||||
stat_style input = no_input)
|
||||
: spot::stat_printer(os, format)
|
||||
{
|
||||
if (input == aut_input)
|
||||
{
|
||||
declare('A', &haut_acc_);
|
||||
declare('C', &haut_scc_);
|
||||
declare('E', &haut_edges_);
|
||||
declare('G', &haut_gen_acc_);
|
||||
declare('M', &haut_name_);
|
||||
declare('S', &haut_states_);
|
||||
declare('T', &haut_trans_);
|
||||
}
|
||||
declare('F', &filename_);
|
||||
declare('L', &location_);
|
||||
if (input != ltl_input)
|
||||
declare('f', &filename_); // Override the formula printer.
|
||||
declare('m', &aut_name_);
|
||||
declare('w', &aut_word_);
|
||||
}
|
||||
|
||||
using spot::formater::declare;
|
||||
using spot::formater::set_output;
|
||||
|
||||
/// \brief print the configured statistics.
|
||||
///
|
||||
/// The \a f argument is not needed if the Formula does not need
|
||||
/// to be output.
|
||||
std::ostream&
|
||||
print(const spot::const_parsed_aut_ptr& haut,
|
||||
const spot::const_twa_graph_ptr& aut,
|
||||
spot::formula f,
|
||||
const char* filename, int loc, double run_time)
|
||||
{
|
||||
filename_ = filename ? filename : "";
|
||||
if (loc >= 0 && has('L'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << loc;
|
||||
location_ = os.str();
|
||||
}
|
||||
if (haut)
|
||||
{
|
||||
if (loc < 0 && has('L'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << haut->loc;
|
||||
location_ = os.str();
|
||||
}
|
||||
|
||||
if (has('T'))
|
||||
{
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
|
||||
haut_states_ = s.states;
|
||||
haut_edges_ = s.edges;
|
||||
haut_trans_ = s.transitions;
|
||||
}
|
||||
else if (has('E'))
|
||||
{
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(haut->aut);
|
||||
haut_states_ = s.states;
|
||||
haut_edges_ = s.edges;
|
||||
}
|
||||
if (has('M'))
|
||||
{
|
||||
auto n = haut->aut->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
haut_name_ = *n;
|
||||
else
|
||||
haut_name_.val().clear();
|
||||
}
|
||||
if (has('S'))
|
||||
haut_states_ = haut->aut->num_states();
|
||||
|
||||
if (has('A'))
|
||||
haut_acc_ = haut->aut->acc().num_sets();
|
||||
|
||||
if (has('C'))
|
||||
haut_scc_ = spot::scc_info(haut->aut).scc_count();
|
||||
|
||||
if (has('G'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << haut->aut->get_acceptance();
|
||||
haut_gen_acc_ = os.str();
|
||||
}
|
||||
}
|
||||
|
||||
if (has('m'))
|
||||
{
|
||||
auto n = aut->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
aut_name_ = *n;
|
||||
else
|
||||
aut_name_.val().clear();
|
||||
}
|
||||
if (has('w'))
|
||||
{
|
||||
auto res = spot::couvreur99(aut)->check();
|
||||
if (res)
|
||||
{
|
||||
auto run = res->accepting_run();
|
||||
assert(run);
|
||||
spot::twa_word w(run->reduce());
|
||||
w.simplify();
|
||||
std::ostringstream out;
|
||||
out << w;
|
||||
aut_word_ = out.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
aut_word_.val().clear();
|
||||
}
|
||||
}
|
||||
|
||||
return this->spot::stat_printer::print(aut, f, run_time);
|
||||
}
|
||||
|
||||
private:
|
||||
spot::printable_value<const char*> filename_;
|
||||
spot::printable_value<std::string> location_;
|
||||
spot::printable_value<std::string> haut_name_;
|
||||
spot::printable_value<std::string> aut_name_;
|
||||
spot::printable_value<std::string> aut_word_;
|
||||
spot::printable_value<std::string> haut_gen_acc_;
|
||||
spot::printable_value<unsigned> haut_states_;
|
||||
spot::printable_value<unsigned> haut_edges_;
|
||||
spot::printable_value<unsigned> haut_trans_;
|
||||
spot::printable_value<unsigned> haut_acc_;
|
||||
spot::printable_value<unsigned> haut_scc_;
|
||||
};
|
||||
|
||||
|
||||
class automaton_printer
|
||||
{
|
||||
hoa_stat_printer statistics;
|
||||
std::ostringstream name;
|
||||
hoa_stat_printer namer;
|
||||
std::ostringstream outputname;
|
||||
hoa_stat_printer outputnamer;
|
||||
std::map<std::string, std::unique_ptr<output_file>> outputfiles;
|
||||
|
||||
public:
|
||||
|
||||
automaton_printer(stat_style input = no_input);
|
||||
|
||||
void
|
||||
print(const spot::twa_graph_ptr& aut,
|
||||
spot::formula f = nullptr,
|
||||
// Input location for errors and statistics.
|
||||
const char* filename = nullptr,
|
||||
int loc = -1,
|
||||
// Time and input automaton for statistics
|
||||
double time = 0.0,
|
||||
const spot::const_parsed_aut_ptr& haut = nullptr);
|
||||
|
||||
void add_stat(char c, const spot::printable* p);
|
||||
};
|
||||
88
bin/common_conv.cc
Normal file
88
bin/common_conv.cc
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_conv.hh"
|
||||
#include <cstdlib>
|
||||
#include "error.h"
|
||||
|
||||
int
|
||||
to_int(const char* s)
|
||||
{
|
||||
char* endptr;
|
||||
int res = strtol(s, &endptr, 10);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as an integer.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
to_pos_int(const char* s)
|
||||
{
|
||||
int res = to_int(s);
|
||||
if (res < 0)
|
||||
error(2, 0, "%d is not positive", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned
|
||||
to_unsigned (const char *s)
|
||||
{
|
||||
char* endptr;
|
||||
unsigned res = strtoul(s, &endptr, 10);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as an unsigned integer.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
float
|
||||
to_float(const char* s)
|
||||
{
|
||||
char* endptr;
|
||||
float res = strtof(s, &endptr);
|
||||
if (*endptr)
|
||||
error(2, 0, "failed to parse '%s' as a float.", s);
|
||||
return res;
|
||||
}
|
||||
|
||||
float
|
||||
to_probability(const char* s)
|
||||
{
|
||||
float res = to_float(s);
|
||||
if (res < 0.0 || res > 1.0)
|
||||
error(2, 0, "%f is not between 0 and 1.", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<long>
|
||||
to_longs(const char* arg)
|
||||
{
|
||||
std::vector<long> res;
|
||||
while (*arg)
|
||||
{
|
||||
char* endptr;
|
||||
long value = strtol(arg, &endptr, 10);
|
||||
if (endptr == arg)
|
||||
error(2, 0, "failed to parse '%s' as an integer.", arg);
|
||||
res.push_back(value);
|
||||
while (*endptr == ' ' || *endptr == ',')
|
||||
++endptr;
|
||||
arg = endptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
32
bin/common_conv.hh
Normal file
32
bin/common_conv.hh
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
int to_int(const char* s);
|
||||
int to_pos_int(const char* s);
|
||||
unsigned to_unsigned (const char *s);
|
||||
float to_float(const char* s);
|
||||
float to_probability(const char* s);
|
||||
|
||||
// Parse the comma or space seperate string of numbers.
|
||||
std::vector<long> to_longs(const char* s);
|
||||
37
bin/common_cout.cc
Normal file
37
bin/common_cout.cc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "common_cout.hh"
|
||||
#include <iostream>
|
||||
#include "error.h"
|
||||
|
||||
void check_cout()
|
||||
{
|
||||
// Make sure we abort if we can't write to std::cout anymore
|
||||
// (like disk full or broken pipe with SIGPIPE ignored).
|
||||
if (!std::cout)
|
||||
error(2, 0, "error writing to standard output");
|
||||
}
|
||||
|
||||
void flush_cout()
|
||||
{
|
||||
std::cout.flush();
|
||||
check_cout();
|
||||
}
|
||||
23
bin/common_cout.hh
Normal file
23
bin/common_cout.hh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
void check_cout();
|
||||
void flush_cout();
|
||||
43
bin/common_file.cc
Normal file
43
bin/common_file.cc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_file.hh"
|
||||
#include <error.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
output_file::output_file(const char* name)
|
||||
{
|
||||
std::ios_base::openmode mode = std::ios_base::trunc;
|
||||
if (name[0] == '>' && name[1] == '>')
|
||||
{
|
||||
mode = std::ios_base::app;
|
||||
append_ = true;
|
||||
name += 2;
|
||||
}
|
||||
if (name[0] == '-' && name[1] == 0)
|
||||
{
|
||||
os_ = &std::cout;
|
||||
return;
|
||||
}
|
||||
of_ = new std::ofstream(name, mode);
|
||||
if (!*of_)
|
||||
error(2, errno, "cannot open '%s'", name);
|
||||
os_ = of_;
|
||||
}
|
||||
51
bin/common_file.hh
Normal file
51
bin/common_file.hh
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <iosfwd>
|
||||
#include <fstream>
|
||||
|
||||
class output_file
|
||||
{
|
||||
std::ostream* os_;
|
||||
std::ofstream* of_ = nullptr;
|
||||
bool append_ = false;
|
||||
public:
|
||||
// Open a file for output. "-" is interpreted as stdout.
|
||||
// Names that start with ">>" are opened for append.
|
||||
// The function calls error() on... error.
|
||||
output_file(const char* name);
|
||||
|
||||
~output_file()
|
||||
{
|
||||
delete of_;
|
||||
}
|
||||
|
||||
bool append() const
|
||||
{
|
||||
return append_;
|
||||
}
|
||||
|
||||
std::ostream& ostream()
|
||||
{
|
||||
return *os_;
|
||||
}
|
||||
};
|
||||
356
bin/common_finput.cc
Normal file
356
bin/common_finput.cc
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_finput.hh"
|
||||
#include "error.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
enum {
|
||||
OPT_LBT = 1,
|
||||
OPT_LENIENT,
|
||||
};
|
||||
|
||||
jobs_t jobs;
|
||||
bool lbt_input = false;
|
||||
static bool lenient = false;
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Input options:", 1 },
|
||||
{ "formula", 'f', "STRING", 0, "process the formula STRING", 0 },
|
||||
{ "file", 'F', "FILENAME[/COL]", 0,
|
||||
"process each line of FILENAME as a formula; if COL is a "
|
||||
"positive integer, assume a CSV file and read column COL; use "
|
||||
"a negative COL to drop the first line of the CSV file", 0 },
|
||||
{ "lbt-input", OPT_LBT, nullptr, 0,
|
||||
"read all formulas using LBT's prefix syntax", 0 },
|
||||
{ "lenient", OPT_LENIENT, nullptr, 0,
|
||||
"parenthesized blocks that cannot be parsed as subformulas "
|
||||
"are considered as atomic properties", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp finput_argp = { options, parse_opt_finput,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
int
|
||||
parse_opt_finput(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'f':
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_LBT:
|
||||
lbt_input = true;
|
||||
break;
|
||||
case OPT_LENIENT:
|
||||
lenient = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
spot::formula
|
||||
parse_formula(const std::string& s, spot::parse_error_list& pel)
|
||||
{
|
||||
if (lbt_input)
|
||||
return spot::parse_prefix_ltl(s, pel);
|
||||
else
|
||||
return spot::parse_infix_psl
|
||||
(s, pel, spot::default_environment::instance(), false, lenient);
|
||||
}
|
||||
|
||||
job_processor::job_processor()
|
||||
: abort_run(false), real_filename(nullptr),
|
||||
col_to_read(0), prefix(nullptr), suffix(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
job_processor::~job_processor()
|
||||
{
|
||||
if (real_filename)
|
||||
free(real_filename);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (suffix)
|
||||
free(suffix);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
job_processor::process_string(const std::string& input,
|
||||
const char* filename,
|
||||
int linenum)
|
||||
{
|
||||
spot::parse_error_list pel;
|
||||
auto f = parse_formula(input, pel);
|
||||
|
||||
if (!f || !pel.empty())
|
||||
{
|
||||
if (filename)
|
||||
error_at_line(0, 0, filename, linenum, "parse error:");
|
||||
spot::format_parse_errors(std::cerr, input, pel);
|
||||
return 1;
|
||||
}
|
||||
return process_formula(f, filename, linenum);
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::process_stream(std::istream& is,
|
||||
const char* filename)
|
||||
{
|
||||
int error = 0;
|
||||
int linenum = 1;
|
||||
std::string line;
|
||||
|
||||
// Discard the first line of a CSV file if requested.
|
||||
if (col_to_read < 0)
|
||||
{
|
||||
std::getline(is, line);
|
||||
col_to_read = -col_to_read;
|
||||
++linenum;
|
||||
}
|
||||
|
||||
// Each line of the file and send them to process_string,
|
||||
// optionally extracting a column of a CSV file.
|
||||
while (!abort_run && std::getline(is, line))
|
||||
if (!line.empty())
|
||||
{
|
||||
if (col_to_read == 0)
|
||||
{
|
||||
error |= process_string(line, filename, linenum++);
|
||||
}
|
||||
else // We are reading column COL_TO_READ in a CSV file.
|
||||
{
|
||||
// If the line we have read contains an odd number
|
||||
// of double-quotes, then it is an incomplete CSV line
|
||||
// that should be completed by the next lines.
|
||||
unsigned dquotes = 0;
|
||||
std::string fullline;
|
||||
unsigned csvlines = 0;
|
||||
do
|
||||
{
|
||||
++csvlines;
|
||||
size_t s = line.size();
|
||||
for (unsigned i = 0; i < s; ++i)
|
||||
dquotes += line[i] == '"';
|
||||
if (fullline.empty())
|
||||
fullline = line;
|
||||
else
|
||||
(fullline += '\n') += line;
|
||||
if (!(dquotes &= 1))
|
||||
break;
|
||||
}
|
||||
while (std::getline(is, line));
|
||||
if (dquotes)
|
||||
error_at_line(2, errno, filename, linenum,
|
||||
"mismatched double-quote, "
|
||||
"reached EOF while parsing this line");
|
||||
|
||||
// Now that we have a full CSV line, extract the right
|
||||
// column.
|
||||
|
||||
const char* str = fullline.c_str();
|
||||
const char* col1_start = str;
|
||||
// Delimiters for the extracted column.
|
||||
const char* coln_start = str;
|
||||
const char* coln_end = nullptr;
|
||||
// The current column. (1-based)
|
||||
int colnum = 1;
|
||||
// Whether we are parsing a double-quoted string.
|
||||
bool instring = false;
|
||||
// Note that RFC 4180 has strict rules about
|
||||
// double-quotes: ① if a field is double-quoted, the first
|
||||
// and last characters of the field should be
|
||||
// double-quotes; ② if a field contains a double-quote
|
||||
// then it should be double quoted, and the occurrences
|
||||
// of double-quotes should be doubled. Therefore a CSV file
|
||||
// may no contain a line such as:
|
||||
// foo,bar"ba""z",12
|
||||
// Tools have different interpretation of such a line.
|
||||
// For instance Python's pandas.read_csv() function will
|
||||
// load the second field verbatim as the string 'bar"ba""z"',
|
||||
// while R's read.csv() function will load it as the
|
||||
// string 'barba"z'. We use this second interpretation, because
|
||||
// it also makes it possible to parse CSVs fields formatted
|
||||
// with leading spaces (often for cosmetic purpose). When
|
||||
// extracting the second field of
|
||||
// foo, "ba""z", 12
|
||||
// we will return ' baz' and the leading space will be ignored
|
||||
// by our LTL formula parser.
|
||||
while (*str)
|
||||
{
|
||||
switch (*str)
|
||||
{
|
||||
case '"':
|
||||
// Doubled double-quotes are used to escape
|
||||
// double-quotes.
|
||||
if (instring && str[1] == '"')
|
||||
++str;
|
||||
else
|
||||
instring = !instring;
|
||||
break;
|
||||
case ',':
|
||||
if (!instring)
|
||||
{
|
||||
if (col_to_read == colnum)
|
||||
coln_end = str;
|
||||
++colnum;
|
||||
if (col_to_read == colnum)
|
||||
coln_start = str + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Once we have the end delimiter for our target
|
||||
// column, we have all we need.
|
||||
if (coln_end)
|
||||
break;
|
||||
++str;
|
||||
}
|
||||
if (!*str)
|
||||
{
|
||||
if (colnum != col_to_read)
|
||||
// Skip this line as it has no enough columns.
|
||||
continue;
|
||||
else
|
||||
// The target columns ends at the end of the line.
|
||||
coln_end = str;
|
||||
}
|
||||
|
||||
// Skip the line if it has an empty field.
|
||||
if (coln_start == coln_end)
|
||||
continue;
|
||||
|
||||
// save the contents before and after that columns for the
|
||||
// %< and %> escapes (ignoring the trailing and leading
|
||||
// commas).
|
||||
prefix = (col_to_read != 1) ?
|
||||
strndup(col1_start, coln_start - col1_start - 1) : nullptr;
|
||||
suffix = (*coln_end != 0) ? strdup(coln_end + 1) : nullptr;
|
||||
std::string field(coln_start, coln_end);
|
||||
// Remove double-quotes if any.
|
||||
if (field.find('"') != std::string::npos)
|
||||
{
|
||||
unsigned dst = 0;
|
||||
bool instring = false;
|
||||
for (; coln_start != coln_end; ++coln_start)
|
||||
if (*coln_start == '"')
|
||||
// A doubled double-quote instead a double-quoted
|
||||
// string is an escaped double-quote.
|
||||
if (instring && coln_start[1] == '"')
|
||||
field[dst++] = *++coln_start;
|
||||
else
|
||||
instring = !instring;
|
||||
else
|
||||
field[dst++] = *coln_start;
|
||||
field.resize(dst);
|
||||
}
|
||||
error |= process_string(field, filename, linenum);
|
||||
linenum += csvlines;
|
||||
if (prefix)
|
||||
{
|
||||
free(prefix);
|
||||
prefix = nullptr;
|
||||
}
|
||||
if (suffix)
|
||||
{
|
||||
free(suffix);
|
||||
suffix = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::process_file(const char* filename)
|
||||
{
|
||||
// Special case for stdin.
|
||||
if (filename[0] == '-' && filename[1] == 0)
|
||||
return process_stream(std::cin, filename);
|
||||
|
||||
errno = 0;
|
||||
std::ifstream input(filename);
|
||||
if (input)
|
||||
return process_stream(input, filename);
|
||||
int saved_errno = errno;
|
||||
|
||||
// If we have a filename like "foo/NN" such
|
||||
// that:
|
||||
// ① foo/NN is not a file (already the case),
|
||||
// ② NN is a number > 0,
|
||||
// ③ foo is a file,
|
||||
// then it means we want to open foo as
|
||||
// a CSV file and process column NN.
|
||||
|
||||
if (const char* slash = strrchr(filename, '/'))
|
||||
{
|
||||
char* end;
|
||||
errno = 0;
|
||||
long int col = strtol(slash + 1, &end, 10);
|
||||
// strtol ate all remaining characters and NN is positive
|
||||
if (errno == 0 && !*end && col != 0)
|
||||
{
|
||||
col_to_read = col;
|
||||
if (real_filename)
|
||||
free(real_filename);
|
||||
real_filename = strndup(filename, slash - filename);
|
||||
|
||||
// Special case for stdin.
|
||||
if (real_filename[0] == '-' && real_filename[1] == 0)
|
||||
return process_stream(std::cin, real_filename);
|
||||
|
||||
std::ifstream input(real_filename);
|
||||
if (input)
|
||||
return process_stream(input, real_filename);
|
||||
|
||||
error(2, errno, "cannot open '%s' nor '%s'",
|
||||
filename, real_filename);
|
||||
}
|
||||
}
|
||||
|
||||
error(2, saved_errno, "cannot open '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
job_processor::run()
|
||||
{
|
||||
int error = 0;
|
||||
for (auto& j: jobs)
|
||||
{
|
||||
if (!j.file_p)
|
||||
error |= process_string(j.str);
|
||||
else
|
||||
error |= process_file(j.str);
|
||||
if (abort_run)
|
||||
break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
80
bin/common_finput.hh
Normal file
80
bin/common_finput.hh
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <vector>
|
||||
#include <spot/tl/parse.hh>
|
||||
|
||||
struct job
|
||||
{
|
||||
const char* str;
|
||||
bool file_p; // true if str is a filename, false if it is a formula
|
||||
|
||||
job(const char* str, bool file_p)
|
||||
: str(str), file_p(file_p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<job> jobs_t;
|
||||
extern jobs_t jobs;
|
||||
extern bool lbt_input;
|
||||
|
||||
extern const struct argp finput_argp;
|
||||
|
||||
int parse_opt_finput(int key, char* arg, struct argp_state* state);
|
||||
|
||||
spot::formula
|
||||
parse_formula(const std::string& s, spot::parse_error_list& error_list);
|
||||
|
||||
|
||||
class job_processor
|
||||
{
|
||||
protected:
|
||||
bool abort_run; // Set to true in process_formula() to abort run().
|
||||
public:
|
||||
job_processor();
|
||||
|
||||
virtual ~job_processor();
|
||||
|
||||
virtual int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0) = 0;
|
||||
|
||||
virtual int
|
||||
process_string(const std::string& str,
|
||||
const char* filename = nullptr, int linenum = 0);
|
||||
virtual int
|
||||
process_stream(std::istream& is, const char* filename);
|
||||
|
||||
virtual int
|
||||
process_file(const char* filename);
|
||||
|
||||
virtual int
|
||||
run();
|
||||
|
||||
char* real_filename;
|
||||
long int col_to_read;
|
||||
char* prefix;
|
||||
char* suffix;
|
||||
};
|
||||
90
bin/common_hoaread.cc
Normal file
90
bin/common_hoaread.cc
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_hoaread.hh"
|
||||
#include "argmatch.h"
|
||||
#include "error.h"
|
||||
|
||||
enum
|
||||
{
|
||||
OPT_TRUST_HOA = 1,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "trust-hoa", OPT_TRUST_HOA, "BOOL", 0,
|
||||
"If False, properties listed in HOA files are ignored, "
|
||||
"unless they can be easily verified. If True (the default) "
|
||||
"any supported property is trusted.", 1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
spot::automaton_parser_options opt_parse;
|
||||
|
||||
spot::twa_graph_ptr
|
||||
read_automaton(const char* filename, spot::bdd_dict_ptr& dict)
|
||||
{
|
||||
auto p = spot::parse_aut(filename, dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (p->format_errors(std::cerr))
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
if (p->aborted)
|
||||
error(2, 0, "failed to read automaton from %s (--ABORT-- read)", filename);
|
||||
return std::move(p->aut);
|
||||
}
|
||||
|
||||
static bool parse_bool(const char* opt, const char* arg)
|
||||
{
|
||||
enum bool_type { bool_false, bool_true };
|
||||
static char const *const bool_args[] =
|
||||
{
|
||||
"false", "no", "0",
|
||||
"true", "yes", "1",
|
||||
nullptr
|
||||
};
|
||||
static bool_type const bool_types[] =
|
||||
{
|
||||
bool_false, bool_false, bool_false,
|
||||
bool_true, bool_true, bool_true,
|
||||
};
|
||||
ARGMATCH_VERIFY(bool_args, bool_types);
|
||||
bool_type bt = XARGMATCH(opt, arg, bool_args, bool_types);
|
||||
return bt == bool_true;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt_hoaread(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_TRUST_HOA:
|
||||
opt_parse.trust_hoa = parse_bool("--trust-hoa", arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct argp hoaread_argp = { options, parse_opt_hoaread,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
34
bin/common_hoaread.hh
Normal file
34
bin/common_hoaread.hh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
|
||||
extern const struct argp hoaread_argp;
|
||||
|
||||
extern spot::automaton_parser_options opt_parse;
|
||||
|
||||
spot::twa_graph_ptr
|
||||
read_automaton(const char* filename, spot::bdd_dict_ptr& dict);
|
||||
319
bin/common_output.cc
Normal file
319
bin/common_output.cc
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "common_output.hh"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <spot/misc/escape.hh>
|
||||
#include "common_cout.hh"
|
||||
#include "error.h"
|
||||
|
||||
enum {
|
||||
OPT_CSV = 1,
|
||||
OPT_FORMAT,
|
||||
OPT_LATEX,
|
||||
OPT_SPOT,
|
||||
OPT_WRING,
|
||||
};
|
||||
|
||||
output_format_t output_format = spot_output;
|
||||
bool full_parenth = false;
|
||||
bool escape_csv = false;
|
||||
char output_terminator = '\n';
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "full-parentheses", 'p', nullptr, 0,
|
||||
"output fully-parenthesized formulas", -20 },
|
||||
{ "spin", 's', nullptr, 0, "output in Spin's syntax", -20 },
|
||||
{ "spot", OPT_SPOT, nullptr, 0, "output in Spot's syntax (default)", -20 },
|
||||
{ "lbt", 'l', nullptr, 0, "output in LBT's syntax", -20 },
|
||||
{ "wring", OPT_WRING, nullptr, 0, "output in Wring's syntax", -20 },
|
||||
{ "utf8", '8', nullptr, 0, "output using UTF-8 characters", -20 },
|
||||
{ "latex", OPT_LATEX, nullptr, 0, "output using LaTeX macros", -20 },
|
||||
{ "csv-escape", OPT_CSV, nullptr, 0,
|
||||
"quote the formula for use in a CSV file", -20 },
|
||||
{ "format", OPT_FORMAT, "FORMAT", 0,
|
||||
"specify how each line should be output (default: \"%f\")", -20 },
|
||||
{ "output", 'o', "FORMAT", 0,
|
||||
"send output to a file named FORMAT instead of standard output. The"
|
||||
" first formula sent to a file truncates it unless FORMAT starts"
|
||||
" with '>>'.", 0 },
|
||||
{ "zero-terminated-output", '0', nullptr, 0,
|
||||
"separate output formulas with \\0 instead of \\n "
|
||||
"(for use with xargs -0)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp output_argp = { options, parse_opt_output,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
|
||||
static
|
||||
void
|
||||
report_not_ltl(spot::formula f,
|
||||
const char* filename, int linenum, const char* syn)
|
||||
{
|
||||
std::string s = spot::str_psl(f);
|
||||
static const char msg[] =
|
||||
"formula '%s' cannot be written %s's syntax because it is not LTL";
|
||||
if (filename)
|
||||
error_at_line(2, 0, filename, linenum, msg, s.c_str(), syn);
|
||||
else
|
||||
error(2, 0, msg, s.c_str(), syn);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
stream_formula(std::ostream& out,
|
||||
spot::formula f, const char* filename, int linenum)
|
||||
{
|
||||
switch (output_format)
|
||||
{
|
||||
case lbt_output:
|
||||
if (f.is_ltl_formula())
|
||||
spot::print_lbt_ltl(out, f);
|
||||
else
|
||||
report_not_ltl(f, filename, linenum, "LBT");
|
||||
break;
|
||||
case spot_output:
|
||||
spot::print_psl(out, f, full_parenth);
|
||||
break;
|
||||
case spin_output:
|
||||
if (f.is_ltl_formula())
|
||||
spot::print_spin_ltl(out, f, full_parenth);
|
||||
else
|
||||
report_not_ltl(f, filename, linenum, "Spin");
|
||||
break;
|
||||
case wring_output:
|
||||
if (f.is_ltl_formula())
|
||||
spot::print_wring_ltl(out, f);
|
||||
else
|
||||
report_not_ltl(f, filename, linenum, "Wring");
|
||||
break;
|
||||
case utf8_output:
|
||||
spot::print_utf8_psl(out, f, full_parenth);
|
||||
break;
|
||||
case latex_output:
|
||||
spot::print_latex_psl(out, f, full_parenth);
|
||||
break;
|
||||
case count_output:
|
||||
case quiet_output:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void
|
||||
stream_escapable_formula(std::ostream& os,
|
||||
spot::formula f,
|
||||
const char* filename, int linenum)
|
||||
{
|
||||
if (escape_csv)
|
||||
{
|
||||
std::ostringstream out;
|
||||
stream_formula(out, f, filename, linenum);
|
||||
os << '"';
|
||||
spot::escape_rfc4180(os, out.str());
|
||||
os << '"';
|
||||
}
|
||||
else
|
||||
{
|
||||
stream_formula(os, f, filename, linenum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct formula_with_location
|
||||
{
|
||||
spot::formula f;
|
||||
const char* filename;
|
||||
int line;
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
};
|
||||
|
||||
class printable_formula:
|
||||
public spot::printable_value<const formula_with_location*>
|
||||
{
|
||||
public:
|
||||
printable_formula&
|
||||
operator=(const formula_with_location* new_val)
|
||||
{
|
||||
val_ = new_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const
|
||||
{
|
||||
stream_escapable_formula(os, val_->f, val_->filename, val_->line);
|
||||
}
|
||||
};
|
||||
|
||||
class formula_printer: protected spot::formater
|
||||
{
|
||||
public:
|
||||
formula_printer(std::ostream& os, const char* format)
|
||||
: format_(format)
|
||||
{
|
||||
declare('f', &fl_);
|
||||
declare('F', &filename_);
|
||||
declare('L', &line_);
|
||||
declare('<', &prefix_);
|
||||
declare('>', &suffix_);
|
||||
set_output(os);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
print(const formula_with_location& fl)
|
||||
{
|
||||
fl_ = &fl;
|
||||
filename_ = fl.filename ? fl.filename : "";
|
||||
line_ = fl.line;
|
||||
prefix_ = fl.prefix ? fl.prefix : "";
|
||||
suffix_ = fl.suffix ? fl.suffix : "";
|
||||
return format(format_);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* format_;
|
||||
printable_formula fl_;
|
||||
spot::printable_value<const char*> filename_;
|
||||
spot::printable_value<int> line_;
|
||||
spot::printable_value<const char*> prefix_;
|
||||
spot::printable_value<const char*> suffix_;
|
||||
};
|
||||
}
|
||||
|
||||
static formula_printer* format = nullptr;
|
||||
static std::ostringstream outputname;
|
||||
static formula_printer* outputnamer = nullptr;
|
||||
static std::map<std::string, std::unique_ptr<output_file>> outputfiles;
|
||||
|
||||
int
|
||||
parse_opt_output(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '0':
|
||||
output_terminator = 0;
|
||||
break;
|
||||
case '8':
|
||||
output_format = utf8_output;
|
||||
break;
|
||||
case 'l':
|
||||
output_format = lbt_output;
|
||||
break;
|
||||
case 'o':
|
||||
outputnamer = new formula_printer(outputname, arg);
|
||||
break;
|
||||
case 'p':
|
||||
full_parenth = true;
|
||||
break;
|
||||
case 's':
|
||||
output_format = spin_output;
|
||||
break;
|
||||
case OPT_CSV:
|
||||
escape_csv = true;
|
||||
break;
|
||||
case OPT_LATEX:
|
||||
output_format = latex_output;
|
||||
break;
|
||||
case OPT_SPOT:
|
||||
output_format = spot_output;
|
||||
break;
|
||||
case OPT_WRING:
|
||||
output_format = wring_output;
|
||||
break;
|
||||
case OPT_FORMAT:
|
||||
delete format;
|
||||
format = new formula_printer(std::cout, arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
output_formula(std::ostream& out,
|
||||
spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0,
|
||||
const char* prefix = nullptr, const char* suffix = nullptr)
|
||||
{
|
||||
if (!format)
|
||||
{
|
||||
if (prefix)
|
||||
out << prefix << ',';
|
||||
stream_escapable_formula(out, f, filename, linenum);
|
||||
if (suffix)
|
||||
out << ',' << suffix;
|
||||
}
|
||||
else
|
||||
{
|
||||
formula_with_location fl = { f, filename, linenum, prefix, suffix };
|
||||
format->print(fl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
::printable_formula::print(std::ostream& os, const char*) const
|
||||
{
|
||||
output_formula(os, val_);
|
||||
}
|
||||
|
||||
void
|
||||
output_formula_checked(spot::formula f,
|
||||
const char* filename, int linenum,
|
||||
const char* prefix, const char* suffix)
|
||||
{
|
||||
if (output_format == count_output)
|
||||
{
|
||||
if (outputnamer)
|
||||
throw std::runtime_error
|
||||
("options --output and --count are incompatible");
|
||||
return;
|
||||
}
|
||||
if (output_format == quiet_output)
|
||||
return;
|
||||
std::ostream* out = &std::cout;
|
||||
if (outputnamer)
|
||||
{
|
||||
outputname.str("");
|
||||
formula_with_location fl = { f, filename, linenum, prefix, suffix };
|
||||
outputnamer->print(fl);
|
||||
std::string fname = outputname.str();
|
||||
auto p = outputfiles.emplace(fname, nullptr);
|
||||
if (p.second)
|
||||
p.first->second.reset(new output_file(fname.c_str()));
|
||||
out = &p.first->second->ostream();
|
||||
}
|
||||
output_formula(*out, f, filename, linenum, prefix, suffix);
|
||||
*out << output_terminator;
|
||||
// Make sure we abort if we can't write to std::cout anymore
|
||||
// (like disk full or broken pipe with SIGPIPE ignored).
|
||||
check_cout();
|
||||
}
|
||||
90
bin/common_output.hh
Normal file
90
bin/common_output.hh
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <argp.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include "common_output.hh"
|
||||
#include "common_file.hh"
|
||||
|
||||
enum output_format_t { spot_output, spin_output, utf8_output,
|
||||
lbt_output, wring_output, latex_output,
|
||||
quiet_output, count_output };
|
||||
extern output_format_t output_format;
|
||||
extern bool full_parenth;
|
||||
extern bool escape_csv;
|
||||
|
||||
extern const struct argp output_argp;
|
||||
|
||||
int parse_opt_output(int key, char* arg, struct argp_state* state);
|
||||
|
||||
// Low-level output
|
||||
std::ostream&
|
||||
stream_formula(std::ostream& out,
|
||||
spot::formula f, const char* filename, int linenum);
|
||||
|
||||
void output_formula_checked(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0,
|
||||
const char* prefix = nullptr,
|
||||
const char* suffix = nullptr);
|
||||
|
||||
|
||||
class printable_formula:
|
||||
public spot::printable_value<spot::formula>
|
||||
{
|
||||
public:
|
||||
printable_formula&
|
||||
operator=(spot::formula new_val)
|
||||
{
|
||||
val_ = new_val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual void
|
||||
print(std::ostream& os, const char*) const;
|
||||
};
|
||||
|
||||
class aut_stat_printer: protected spot::stat_printer
|
||||
{
|
||||
public:
|
||||
aut_stat_printer(std::ostream& os, const char* format)
|
||||
: spot::stat_printer(os, format)
|
||||
{
|
||||
declare('f', &formula_); // Override the formula printer.
|
||||
}
|
||||
|
||||
using spot::formater::set_output;
|
||||
|
||||
std::ostream&
|
||||
print(const spot::const_twa_graph_ptr& aut,
|
||||
spot::formula f = nullptr,
|
||||
double run_time = -1.)
|
||||
{
|
||||
formula_ = f;
|
||||
return this->spot::stat_printer::print(aut, f, run_time);
|
||||
}
|
||||
|
||||
printable_formula formula_;
|
||||
};
|
||||
166
bin/common_post.cc
Normal file
166
bin/common_post.cc
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_post.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "error.h"
|
||||
|
||||
spot::postprocessor::output_type type = spot::postprocessor::TGBA;
|
||||
spot::postprocessor::output_pref pref = spot::postprocessor::Small;
|
||||
spot::postprocessor::output_pref comp = spot::postprocessor::Any;
|
||||
spot::postprocessor::output_pref sbacc = spot::postprocessor::Any;
|
||||
spot::postprocessor::optimization_level level = spot::postprocessor::High;
|
||||
|
||||
bool level_set = false;
|
||||
bool pref_set = false;
|
||||
|
||||
enum {
|
||||
OPT_GENERIC = 1,
|
||||
OPT_HIGH,
|
||||
OPT_LOW,
|
||||
OPT_MEDIUM,
|
||||
OPT_SMALL,
|
||||
OPT_TGBA,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
|
||||
{ "tgba", OPT_TGBA, nullptr, 0,
|
||||
"Transition-based Generalized Büchi Automaton (default)", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"Büchi Automaton (implies -S)", 0 },
|
||||
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
|
||||
"of the given property)", 0 },
|
||||
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"define the acceptance using states", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
|
||||
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata (default)", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
|
||||
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
|
||||
"or deterministic", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
|
||||
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
|
||||
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
|
||||
{ "high", OPT_HIGH, nullptr, 0,
|
||||
"all available optimizations (slow, default)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const argp_option options_disabled[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output automaton type:", 2 },
|
||||
{ "generic", OPT_GENERIC, nullptr, 0,
|
||||
"any acceptance is allowed (default)", 0 },
|
||||
{ "tgba", OPT_TGBA, nullptr, 0,
|
||||
"Transition-based Generalized Büchi Automaton", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"Büchi Automaton (with state-based acceptance)", 0 },
|
||||
{ "monitor", 'M', nullptr, 0, "Monitor (accepts all finite prefixes "
|
||||
"of the given property)", 0 },
|
||||
{ "complete", 'C', nullptr, 0, "output a complete automaton", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"define the acceptance using states", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification goal:", 20 },
|
||||
{ "small", OPT_SMALL, nullptr, 0, "prefer small automata", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0, "prefer deterministic automata", 0 },
|
||||
{ "any", 'a', nullptr, 0, "no preference, do not bother making it small "
|
||||
"or deterministic", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level:", 21 },
|
||||
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
|
||||
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
|
||||
{ "high", OPT_HIGH, nullptr, 0,
|
||||
"all available optimizations (slow)", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt_post(int key, char*, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'a':
|
||||
pref = spot::postprocessor::Any;
|
||||
pref_set = true;
|
||||
break;
|
||||
case 'B':
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case 'C':
|
||||
comp = spot::postprocessor::Complete;
|
||||
break;
|
||||
case 'D':
|
||||
pref = spot::postprocessor::Deterministic;
|
||||
pref_set = true;
|
||||
break;
|
||||
case 'M':
|
||||
type = spot::postprocessor::Monitor;
|
||||
break;
|
||||
case 'S':
|
||||
sbacc = spot::postprocessor::SBAcc;
|
||||
break;
|
||||
case OPT_GENERIC:
|
||||
type = spot::postprocessor::Generic;
|
||||
break;
|
||||
case OPT_HIGH:
|
||||
level = spot::postprocessor::High;
|
||||
simplification_level = 3;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_LOW:
|
||||
level = spot::postprocessor::Low;
|
||||
simplification_level = 1;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_MEDIUM:
|
||||
level = spot::postprocessor::Medium;
|
||||
simplification_level = 2;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_SMALL:
|
||||
pref = spot::postprocessor::Small;
|
||||
pref_set = true;
|
||||
break;
|
||||
case OPT_TGBA:
|
||||
if (automaton_format == Spin)
|
||||
error(2, 0, "--spin and --tgba are incompatible");
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp post_argp = { options, parse_opt_post,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
const struct argp post_argp_disabled = { options_disabled, parse_opt_post,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
37
bin/common_post.hh
Normal file
37
bin/common_post.hh
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/twaalgos/postproc.hh>
|
||||
#include <argp.h>
|
||||
|
||||
extern const struct argp post_argp; // postprocessing enabled
|
||||
extern const struct argp post_argp_disabled; // postprocessing disabled
|
||||
|
||||
extern spot::postprocessor::output_type type;
|
||||
extern spot::postprocessor::output_pref pref;
|
||||
extern spot::postprocessor::output_pref comp;
|
||||
extern spot::postprocessor::output_pref sbacc;
|
||||
extern spot::postprocessor::optimization_level level;
|
||||
// True if --low, --medium, or --high has been given
|
||||
extern bool level_set;
|
||||
// True if --any, --small, or --deterministic has been given
|
||||
extern bool pref_set;
|
||||
40
bin/common_r.cc
Normal file
40
bin/common_r.cc
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
#include "common_r.hh"
|
||||
|
||||
int simplification_level = 0;
|
||||
|
||||
void
|
||||
parse_r(const char* arg)
|
||||
{
|
||||
if (!arg)
|
||||
{
|
||||
simplification_level = 3;
|
||||
return;
|
||||
}
|
||||
if (arg[1] == 0 && arg[0] >= '0' && arg[0] <= '3')
|
||||
{
|
||||
simplification_level = arg[0] - '0';
|
||||
return;
|
||||
}
|
||||
error(2, 0, "invalid simplification level '%s'", arg);
|
||||
}
|
||||
47
bin/common_r.hh
Normal file
47
bin/common_r.hh
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <spot/tl/simplify.hh>
|
||||
|
||||
#define OPT_R 'r'
|
||||
|
||||
#define DECLARE_OPT_R \
|
||||
{ "simplify", OPT_R, "LEVEL", OPTION_ARG_OPTIONAL, \
|
||||
"simplify formulas according to LEVEL (see below); LEVEL is " \
|
||||
"set to 3 if omitted", 0 }
|
||||
|
||||
#define LEVEL_DOC(g) \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"The simplification LEVEL may be set as follows.", g }, \
|
||||
{ " 0", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"No rewriting", 0 }, \
|
||||
{ " 1", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"basic rewritings and eventual/universal rules", 0 }, \
|
||||
{ " 2", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"additional syntactic implication rules", 0 }, \
|
||||
{ " 3", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, \
|
||||
"better implications using containment", 0 }
|
||||
|
||||
extern int simplification_level;
|
||||
|
||||
void parse_r(const char* arg);
|
||||
spot::tl_simplifier_options simplifier_options();
|
||||
81
bin/common_range.cc
Normal file
81
bin/common_range.cc
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2014 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "error.h"
|
||||
|
||||
#include "common_range.hh"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
// The range should have the form INT..INT or INT:INT, with
|
||||
// "42" standing for "42..42",
|
||||
// "..42" meaning "missing_left..42".
|
||||
// and "42.." meaning "42..missing_right".
|
||||
//
|
||||
// As an exception, if missing_right is 0, then missing right bounds
|
||||
// are disallowed.
|
||||
range
|
||||
parse_range(const char* str, int missing_left, int missing_right)
|
||||
{
|
||||
range res;
|
||||
char* end;
|
||||
res.min = strtol(str, &end, 10);
|
||||
if (end == str)
|
||||
{
|
||||
// No leading number. It's OK as long as the string is not
|
||||
// empty.
|
||||
if (!*end)
|
||||
error(1, 0, "invalid empty range");
|
||||
res.min = missing_left;
|
||||
}
|
||||
if (!*end)
|
||||
{
|
||||
// Only one number.
|
||||
res.max = res.min;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip : or ..
|
||||
if (end[0] == ':')
|
||||
++end;
|
||||
else if (end[0] == '.' && end[1] == '.')
|
||||
end += 2;
|
||||
|
||||
if (!*end && missing_right != 0)
|
||||
{
|
||||
res.max = missing_right;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse the next integer.
|
||||
char* end2;
|
||||
res.max = strtol(end, &end2, 10);
|
||||
if (end == end2)
|
||||
error(1, 0, "invalid range '%s' (missing end?)", str);
|
||||
if (*end2)
|
||||
error(1, 0, "invalid range '%s' (trailing garbage?)", str);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.min < 0 || res.max < 0)
|
||||
error(1, 0, "invalid range '%s': values must be positive", str);
|
||||
|
||||
return res;
|
||||
}
|
||||
50
bin/common_range.hh
Normal file
50
bin/common_range.hh
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define RANGE_DOC \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"RANGE may have one of the following forms: 'INT', " \
|
||||
"'INT..INT', or '..INT'.\nIn the latter case, the missing number " \
|
||||
"is assumed to be 1.", 0 }
|
||||
|
||||
#define RANGE_DOC_FULL \
|
||||
{ nullptr, 0, nullptr, 0, \
|
||||
"RANGE may have one of the following forms: 'INT', " \
|
||||
"'INT..INT', '..INT', or 'INT..'", 0 }
|
||||
|
||||
struct range
|
||||
{
|
||||
int min;
|
||||
int max;
|
||||
|
||||
bool contains(int val)
|
||||
{
|
||||
return val >= min && val <= max;
|
||||
}
|
||||
};
|
||||
|
||||
// INT, INT..INT, ..INT, or INT..
|
||||
//
|
||||
// The missing_left and missing_right argument gives the default bound
|
||||
// values. Additionally, if missing_right == 0, then the INT.. form
|
||||
// is disallowed.
|
||||
range parse_range(const char* str,
|
||||
int missing_left = 1, int missing_right = 0);
|
||||
146
bin/common_setup.cc
Normal file
146
bin/common_setup.cc
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "argp.h"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <spot/misc/tmpfile.hh>
|
||||
|
||||
const char* argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
|
||||
|
||||
static void
|
||||
display_version(FILE *stream, struct argp_state*)
|
||||
{
|
||||
fputs(program_name, stream);
|
||||
fputs(" (" PACKAGE_STRING ")\n\
|
||||
\n\
|
||||
Copyright (C) 2015 Laboratoire de Recherche et Développement de l'Epita.\n\
|
||||
License GPLv3+: \
|
||||
GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
|
||||
This is free software: you are free to change and redistribute it.\n\
|
||||
There is NO WARRANTY, to the extent permitted by law.\n", stream);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
spot::cleanup_tmpfiles();
|
||||
// Send the signal again, this time to the default handler, so that
|
||||
// we return a meaningful error code.
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
static void setup_sig_handler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sig_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESETHAND;
|
||||
// Catch termination signals, so we can cleanup temporary files.
|
||||
sigaction(SIGALRM, &sa, nullptr);
|
||||
sigaction(SIGHUP, &sa, nullptr);
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
sigaction(SIGPIPE, &sa, nullptr);
|
||||
sigaction(SIGQUIT, &sa, nullptr);
|
||||
sigaction(SIGTERM, &sa, nullptr);
|
||||
}
|
||||
#else
|
||||
# define setup_sig_handler() while (0);
|
||||
#endif
|
||||
|
||||
void
|
||||
setup(char** argv)
|
||||
{
|
||||
// Simplify the program name, because argp() uses it to report
|
||||
// errors and display help text.
|
||||
set_program_name(argv[0]);
|
||||
argv[0] = const_cast<char*>(program_name);
|
||||
|
||||
argp_program_version_hook = display_version;
|
||||
|
||||
argp_err_exit_status = 2;
|
||||
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
|
||||
setup_sig_handler();
|
||||
}
|
||||
|
||||
|
||||
// argp's default behavior of offering -? for --help is just too silly.
|
||||
// I mean, come on, why not also add -* to Darwinise more shell users?
|
||||
// We disable this option as well as -V (because --version don't need
|
||||
// a short version).
|
||||
enum {
|
||||
OPT_HELP = 1,
|
||||
OPT_USAGE,
|
||||
OPT_VERSION,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ "version", OPT_VERSION, nullptr, 0, "print program version", -1 },
|
||||
{ "help", OPT_HELP, nullptr, 0, "print this help", -1 },
|
||||
// We support this option just in case, but we don't advertise it.
|
||||
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const argp_option options_hidden[] =
|
||||
{
|
||||
{ "version", OPT_VERSION, nullptr, OPTION_HIDDEN,
|
||||
"print program version", -1 },
|
||||
{ "help", OPT_HELP, nullptr, OPTION_HIDDEN, "print this help", -1 },
|
||||
// We support this option just in case, but we don't advertise it.
|
||||
{ "usage", OPT_USAGE, nullptr, OPTION_HIDDEN, "show short usage", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt_misc(int key, char*, struct argp_state* state)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_HELP:
|
||||
argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
|
||||
break;
|
||||
case OPT_USAGE:
|
||||
argp_state_help(state, state->out_stream,
|
||||
ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
|
||||
break;
|
||||
case OPT_VERSION:
|
||||
display_version(state->out_stream, state);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct argp misc_argp = { options, parse_opt_misc,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
|
||||
const struct argp misc_argp_hidden = { options_hidden, parse_opt_misc,
|
||||
nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr };
|
||||
28
bin/common_setup.hh
Normal file
28
bin/common_setup.hh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include "progname.h"
|
||||
|
||||
void setup(char** progname);
|
||||
|
||||
extern const struct argp misc_argp;
|
||||
extern const struct argp misc_argp_hidden;
|
||||
24
bin/common_sys.hh
Normal file
24
bin/common_sys.hh
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
440
bin/common_trans.cc
Normal file
440
bin/common_trans.cc
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_trans.hh"
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include <spot/tl/print.hh>
|
||||
#include "common_conv.hh"
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
// A set of tools for which we know the correct output
|
||||
static struct shorthands_t
|
||||
{
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
}
|
||||
shorthands[] = {
|
||||
{ "lbt", " <%L>%O" },
|
||||
{ "ltl2ba", " -f %s>%O" },
|
||||
{ "ltl2dstar", " --output-format=hoa %L %O"},
|
||||
{ "ltl2tgba", " -H %f>%O" },
|
||||
{ "ltl3ba", " -f %s>%O" },
|
||||
{ "ltl3dra", " -f %s>%O" },
|
||||
{ "modella", " %L %O" },
|
||||
{ "spin", " -f %s>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands()
|
||||
{
|
||||
std::cout
|
||||
<< ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n"
|
||||
"the following words, then the string on the right is appended.\n\n");
|
||||
for (auto& s: shorthands)
|
||||
std::cout << " "
|
||||
<< std::left << std::setw(12) << s.prefix
|
||||
<< s.suffix << '\n';
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose of\n"
|
||||
"matching those prefixes. So for instance\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
|
||||
"will changed into\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %L %O'\n");
|
||||
}
|
||||
|
||||
|
||||
translator_spec::translator_spec(const char* spec)
|
||||
: spec(spec), cmd(spec), name(spec)
|
||||
{
|
||||
if (*cmd == '{')
|
||||
{
|
||||
// Match the closing '}'
|
||||
const char* pos = cmd;
|
||||
unsigned count = 1;
|
||||
while (*++pos)
|
||||
{
|
||||
if (*pos == '{')
|
||||
++count;
|
||||
else if (*pos == '}')
|
||||
if (!--count)
|
||||
{
|
||||
name = strndup(cmd + 1, pos - cmd - 1);
|
||||
cmd = pos + 1;
|
||||
while (*cmd == ' ' || *cmd == '\t')
|
||||
++cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there is no % in the string, look for a known
|
||||
// command from our shorthand list. If we find it,
|
||||
// add the suffix.
|
||||
bool allocated = false;
|
||||
if (!strchr(cmd, '%'))
|
||||
{
|
||||
// Skip any leading directory name.
|
||||
auto basename = cmd;
|
||||
auto pos = cmd;
|
||||
while (*pos)
|
||||
{
|
||||
if (*pos == '/')
|
||||
basename = pos + 1;
|
||||
else if (*pos == ' ')
|
||||
break;
|
||||
++pos;
|
||||
}
|
||||
// Match a shorthand.
|
||||
for (auto& p: shorthands)
|
||||
{
|
||||
int n = strlen(p.prefix);
|
||||
if (strncmp(basename, p.prefix, n) == 0)
|
||||
{
|
||||
int m = strlen(p.suffix);
|
||||
int q = strlen(cmd);
|
||||
char* tmp = static_cast<char*>(malloc(q + m + 1));
|
||||
strcpy(tmp, cmd);
|
||||
strcpy(tmp + q, p.suffix);
|
||||
cmd = tmp;
|
||||
allocated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allocated)
|
||||
cmd = strdup(cmd);
|
||||
}
|
||||
|
||||
translator_spec::translator_spec(const translator_spec& other)
|
||||
: spec(other.spec), cmd(other.cmd), name(other.name)
|
||||
{
|
||||
if (name != spec)
|
||||
name = strdup(name);
|
||||
if (cmd != spec)
|
||||
cmd = strdup(cmd);
|
||||
}
|
||||
|
||||
translator_spec::~translator_spec()
|
||||
{
|
||||
if (name != spec)
|
||||
free(const_cast<char*>(name));
|
||||
if (cmd != spec)
|
||||
free(const_cast<char*>(cmd));
|
||||
}
|
||||
|
||||
std::vector<translator_spec> translators;
|
||||
|
||||
void
|
||||
quoted_string::print(std::ostream& os, const char*) const
|
||||
{
|
||||
spot::quote_shell_string(os, val().c_str());
|
||||
}
|
||||
|
||||
printable_result_filename::printable_result_filename()
|
||||
{
|
||||
val_ = nullptr;
|
||||
}
|
||||
|
||||
printable_result_filename::~printable_result_filename()
|
||||
{
|
||||
delete val_;
|
||||
}
|
||||
|
||||
void printable_result_filename::reset(unsigned n)
|
||||
{
|
||||
translator_num = n;
|
||||
}
|
||||
|
||||
void printable_result_filename::cleanup()
|
||||
{
|
||||
delete val_;
|
||||
val_ = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
printable_result_filename::print(std::ostream& os, const char*) const
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-o%u-", translator_num);
|
||||
const_cast<printable_result_filename*>(this)->val_ =
|
||||
spot::create_tmpfile(prefix);
|
||||
spot::quote_shell_string(os, val()->name());
|
||||
}
|
||||
|
||||
|
||||
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
|
||||
bool no_output_allowed)
|
||||
: dict(dict)
|
||||
{
|
||||
declare('f', &string_ltl_spot);
|
||||
declare('s', &string_ltl_spin);
|
||||
declare('l', &string_ltl_lbt);
|
||||
declare('w', &string_ltl_wring);
|
||||
declare('F', &filename_ltl_spot);
|
||||
declare('S', &filename_ltl_spin);
|
||||
declare('L', &filename_ltl_lbt);
|
||||
declare('W', &filename_ltl_wring);
|
||||
declare('D', &output);
|
||||
declare('H', &output);
|
||||
declare('N', &output);
|
||||
declare('T', &output);
|
||||
declare('O', &output);
|
||||
|
||||
size_t s = translators.size();
|
||||
assert(s);
|
||||
for (size_t n = 0; n < s; ++n)
|
||||
{
|
||||
// Check that each translator uses at least one input and
|
||||
// one output.
|
||||
std::vector<bool> has(256);
|
||||
const translator_spec& t = translators[n];
|
||||
scan(t.cmd, has);
|
||||
if (!(has['f'] || has['s'] || has['l'] || has['w']
|
||||
|| has['F'] || has['S'] || has['L'] || has['W']))
|
||||
error(2, 0, "no input %%-sequence in '%s'.\n Use "
|
||||
"one of %%f,%%s,%%l,%%w,%%F,%%S,%%L,%%W to indicate how "
|
||||
"to pass the formula.", t.spec);
|
||||
if (!no_output_allowed
|
||||
&& !(has['O'] ||
|
||||
// backward-compatibility
|
||||
has['D'] || has['N'] || has['T'] || has['H']))
|
||||
error(2, 0, "no output %%-sequence in '%s'.\n Use "
|
||||
"%%O to indicate where the automaton is output.",
|
||||
t.spec);
|
||||
// Remember the %-sequences used by all translators.
|
||||
prime(t.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
translator_runner::string_to_tmp(std::string& str, unsigned n,
|
||||
std::string& tmpname)
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
|
||||
spot::open_temporary_file* tmpfile = spot::create_open_tmpfile(prefix);
|
||||
tmpname = tmpfile->name();
|
||||
int fd = tmpfile->fd();
|
||||
ssize_t s = str.size();
|
||||
if (write(fd, str.c_str(), s) != s
|
||||
|| write(fd, "\n", 1) != 1)
|
||||
error(2, errno, "failed to write into %s", tmpname.c_str());
|
||||
tmpfile->close();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
translator_runner::formula() const
|
||||
{
|
||||
// Pick the most readable format we have...
|
||||
if (!string_ltl_spot.val().empty())
|
||||
return string_ltl_spot;
|
||||
if (!string_ltl_spin.val().empty())
|
||||
return string_ltl_spin;
|
||||
if (!string_ltl_wring.val().empty())
|
||||
return string_ltl_wring;
|
||||
if (!string_ltl_lbt.val().empty())
|
||||
return string_ltl_lbt;
|
||||
SPOT_UNREACHABLE();
|
||||
return string_ltl_spot;
|
||||
}
|
||||
|
||||
void
|
||||
translator_runner::round_formula(spot::formula f, unsigned serial)
|
||||
{
|
||||
if (has('f') || has('F'))
|
||||
string_ltl_spot = spot::str_psl(f, true);
|
||||
if (has('s') || has('S'))
|
||||
string_ltl_spin = spot::str_spin_ltl(f, true);
|
||||
if (has('l') || has('L'))
|
||||
string_ltl_lbt = spot::str_lbt_ltl(f);
|
||||
if (has('w') || has('W'))
|
||||
string_ltl_wring = spot::str_wring_ltl(f);
|
||||
if (has('F'))
|
||||
string_to_tmp(string_ltl_spot, serial, filename_ltl_spot);
|
||||
if (has('S'))
|
||||
string_to_tmp(string_ltl_spin, serial, filename_ltl_spin);
|
||||
if (has('L'))
|
||||
string_to_tmp(string_ltl_lbt, serial, filename_ltl_lbt);
|
||||
if (has('W'))
|
||||
string_to_tmp(string_ltl_wring, serial, filename_ltl_wring);
|
||||
}
|
||||
|
||||
|
||||
|
||||
volatile bool timed_out = false;
|
||||
unsigned timeout_count = 0;
|
||||
|
||||
static unsigned timeout = 0;
|
||||
#if ENABLE_TIMEOUT
|
||||
static volatile int alarm_on = 0;
|
||||
static int child_pid = -1;
|
||||
|
||||
static void
|
||||
sig_handler(int sig)
|
||||
{
|
||||
if (child_pid == 0)
|
||||
error(2, 0, "received signal %d before starting child", sig);
|
||||
|
||||
if (sig == SIGALRM && alarm_on)
|
||||
{
|
||||
timed_out = true;
|
||||
if (--alarm_on)
|
||||
{
|
||||
// Send SIGTERM to children.
|
||||
kill(-child_pid, SIGTERM);
|
||||
// Try again later if it didn't work. (alarm() will be reset
|
||||
// if it did work and the call to wait() returns)
|
||||
alarm(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// After a few gentle tries, really kill that child.
|
||||
kill(-child_pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// forward signal
|
||||
kill(-child_pid, sig);
|
||||
// cleanup files
|
||||
spot::cleanup_tmpfiles();
|
||||
// and die verbosely
|
||||
error(2, 0, "received signal %d", sig);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setup_sig_handler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sig_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; // So that wait() doesn't get aborted by SIGALRM.
|
||||
sigaction(SIGALRM, &sa, nullptr);
|
||||
// Catch termination signals, so we can kill the subprocess.
|
||||
sigaction(SIGHUP, &sa, nullptr);
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
sigaction(SIGQUIT, &sa, nullptr);
|
||||
sigaction(SIGTERM, &sa, nullptr);
|
||||
}
|
||||
|
||||
int
|
||||
exec_with_timeout(const char* cmd)
|
||||
{
|
||||
int status;
|
||||
|
||||
timed_out = false;
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == -1)
|
||||
error(2, errno, "failed to fork()");
|
||||
|
||||
if (child_pid == 0)
|
||||
{
|
||||
setpgid(0, 0);
|
||||
execlp("sh", "sh", "-c", cmd, nullptr);
|
||||
error(2, errno, "failed to run 'sh'");
|
||||
// never reached
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
alarm(timeout);
|
||||
// Upon SIGALRM, the child will receive up to 3
|
||||
// signals: SIGTERM, SIGTERM, SIGKILL.
|
||||
alarm_on = 3;
|
||||
int w = waitpid(child_pid, &status, 0);
|
||||
alarm_on = 0;
|
||||
|
||||
if (w == -1)
|
||||
error(2, errno, "error during wait()");
|
||||
|
||||
alarm(0);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
#endif // ENABLE_TIMEOUT
|
||||
|
||||
enum {
|
||||
OPT_LIST = 1,
|
||||
};
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Specifying translators to call:", 2 },
|
||||
{ "translator", 't', "COMMANDFMT", 0,
|
||||
"register one translator to call", 0 },
|
||||
{ "timeout", 'T', "NUMBER", 0, "kill translators after NUMBER seconds", 0 },
|
||||
{ "list-shorthands", OPT_LIST, nullptr, 0,
|
||||
"list availabled shorthands to use in COMMANDFMT", 0},
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"COMMANDFMT should specify input and output arguments using the "
|
||||
"following character sequences:", 3 },
|
||||
{ "%f,%s,%l,%w", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula as a (quoted) string in Spot, Spin, LBT, or Wring's syntax",
|
||||
0 },
|
||||
{ "%F,%S,%L,%W", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula as a file in Spot, Spin, LBT, or Wring's syntax", 0 },
|
||||
{ "%O", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the automaton is output in HOA, never claim, LBTT, or ltl2dstar's "
|
||||
"format", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"If either %l, %L, or %T are used, any input formula that does "
|
||||
"not use LBT-style atomic propositions (i.e. p0, p1, ...) will be "
|
||||
"relabeled automatically.\n"
|
||||
"Furthermore, if COMMANDFMT has the form \"{NAME}CMD\", then only CMD "
|
||||
"will be passed to the shell, and NAME will be used to name the tool "
|
||||
"in the output.", 4 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
translators.push_back(arg);
|
||||
break;
|
||||
case 'T':
|
||||
timeout = to_pos_int(arg);
|
||||
#if !ENABLE_TIMEOUT
|
||||
std::cerr << "warning: setting a timeout is not supported "
|
||||
<< "on your platform" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case OPT_LIST:
|
||||
show_shorthands();
|
||||
exit(0);
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
117
bin/common_trans.hh
Normal file
117
bin/common_trans.hh
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <vector>
|
||||
#include <argp.h>
|
||||
|
||||
#include <spot/misc/formater.hh>
|
||||
#include <spot/misc/tmpfile.hh>
|
||||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
|
||||
extern const struct argp trans_argp;
|
||||
|
||||
struct translator_spec
|
||||
{
|
||||
// The translator command, as specified on the command-line.
|
||||
// If this has the form of
|
||||
// {name}cmd
|
||||
// then it is split in two components.
|
||||
// Otherwise, spec=cmd=name.
|
||||
const char* spec;
|
||||
// actual shell command (or spec)
|
||||
const char* cmd;
|
||||
// name of the translator (or spec)
|
||||
const char* name;
|
||||
|
||||
translator_spec(const char* spec);
|
||||
translator_spec(const translator_spec& other);
|
||||
~translator_spec();
|
||||
};
|
||||
|
||||
extern std::vector<translator_spec> translators;
|
||||
|
||||
struct quoted_string final: public spot::printable_value<std::string>
|
||||
{
|
||||
using spot::printable_value<std::string>::operator=;
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
};
|
||||
|
||||
struct printable_result_filename final:
|
||||
public spot::printable_value<spot::temporary_file*>
|
||||
{
|
||||
unsigned translator_num;
|
||||
|
||||
printable_result_filename();
|
||||
~printable_result_filename();
|
||||
void reset(unsigned n);
|
||||
void cleanup();
|
||||
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
};
|
||||
|
||||
|
||||
class translator_runner: protected spot::formater
|
||||
{
|
||||
protected:
|
||||
spot::bdd_dict_ptr dict;
|
||||
// Round-specific variables
|
||||
quoted_string string_ltl_spot;
|
||||
quoted_string string_ltl_spin;
|
||||
quoted_string string_ltl_lbt;
|
||||
quoted_string string_ltl_wring;
|
||||
quoted_string filename_ltl_spot;
|
||||
quoted_string filename_ltl_spin;
|
||||
quoted_string filename_ltl_lbt;
|
||||
quoted_string filename_ltl_wring;
|
||||
// Run-specific variables
|
||||
printable_result_filename output;
|
||||
public:
|
||||
using spot::formater::has;
|
||||
|
||||
translator_runner(spot::bdd_dict_ptr dict,
|
||||
// whether we accept the absence of output
|
||||
// specifier
|
||||
bool no_output_allowed = false);
|
||||
void string_to_tmp(std::string& str, unsigned n, std::string& tmpname);
|
||||
const std::string& formula() const;
|
||||
void round_formula(spot::formula f, unsigned serial);
|
||||
};
|
||||
|
||||
|
||||
// Disable handling of timeout on systems that miss kill() or alarm().
|
||||
// For instance MinGW.
|
||||
#if HAVE_KILL && HAVE_ALARM
|
||||
# define ENABLE_TIMEOUT 1
|
||||
#else
|
||||
# define ENABLE_TIMEOUT 0
|
||||
#endif
|
||||
|
||||
extern volatile bool timed_out;
|
||||
extern unsigned timeout_count;
|
||||
#if ENABLE_TIMEOUT
|
||||
void setup_sig_handler();
|
||||
int exec_with_timeout(const char* cmd);
|
||||
#else // !ENABLE_TIMEOUT
|
||||
#define exec_with_timeout(cmd) system(cmd)
|
||||
#define setup_sig_handler() while (0);
|
||||
#endif // !ENABLE_TIMEOUT
|
||||
202
bin/dstar2tgba.cc
Normal file
202
bin/dstar2tgba.cc
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_file.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
#include <spot/twaalgos/stats.hh>
|
||||
#include <spot/twaalgos/totgba.hh>
|
||||
#include <spot/twa/bddprint.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
#include <spot/twaalgos/sccinfo.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Convert automata with any acceptance condition into variants of \
|
||||
Büchi automata.\n\nThis reads automata into any supported format \
|
||||
(HOA, LBTT, ltl2dstar, never claim) and outputs a \
|
||||
Transition-based Generalized Büchi Automata in GraphViz's format by default. \
|
||||
Each supplied file may contain multiple automata.";
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process the automaton in FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_io_format_argp, 0, nullptr, 4 },
|
||||
{ &post_argp, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static spot::option_map extra_options;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class dstar_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::postprocessor& post;
|
||||
automaton_printer printer;
|
||||
|
||||
dstar_processor(spot::postprocessor& post)
|
||||
: post(post), printer(aut_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula, const char*, int)
|
||||
{
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut,
|
||||
const char* filename)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
auto nba = spot::to_generalized_buchi(haut->aut);
|
||||
auto aut = post.run(nba, nullptr);
|
||||
const double conversion_time = sw.stop();
|
||||
|
||||
printer.print(aut, nullptr, filename, -1, conversion_time, haut);
|
||||
flush_cout();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
aborted(const spot::const_parsed_aut_ptr& h, const char* filename)
|
||||
{
|
||||
std::cerr << filename << ':' << h->loc << ": aborted input automaton\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
process_file(const char* filename)
|
||||
{
|
||||
auto hp = spot::automaton_stream_parser(filename, opt_parse);
|
||||
int err = 0;
|
||||
while (!abort_run)
|
||||
{
|
||||
auto haut = hp.parse(spot::make_bdd_dict());
|
||||
if (!haut->aut && haut->errors.empty())
|
||||
break;
|
||||
if (haut->format_errors(std::cerr))
|
||||
err = 2;
|
||||
if (!haut->aut)
|
||||
error(2, 0, "failed to read automaton from %s", filename);
|
||||
else if (haut->aborted)
|
||||
err = std::max(err, aborted(haut, filename));
|
||||
else
|
||||
process_automaton(haut, filename);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAMES...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", true);
|
||||
|
||||
spot::postprocessor post(&extra_options);
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
dstar_processor processor(post);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
834
bin/genltl.cc
Normal file
834
bin/genltl.cc
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2015 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Families defined here come from the following papers:
|
||||
//
|
||||
// @InProceedings{cichon.09.depcos,
|
||||
// author = {Jacek Cicho{\'n} and Adam Czubak and Andrzej Jasi{\'n}ski},
|
||||
// title = {Minimal {B\"uchi} Automata for Certain Classes of {LTL} Formulas},
|
||||
// booktitle = {Proceedings of the Fourth International Conference on
|
||||
// Dependability of Computer Systems},
|
||||
// pages = {17--24},
|
||||
// year = 2009,
|
||||
// publisher = {IEEE Computer Society},
|
||||
// }
|
||||
//
|
||||
// @InProceedings{geldenhuys.06.spin,
|
||||
// author = {Jaco Geldenhuys and Henri Hansen},
|
||||
// title = {Larger Automata and Less Work for LTL Model Checking},
|
||||
// booktitle = {Proceedings of the 13th International SPIN Workshop},
|
||||
// year = {2006},
|
||||
// pages = {53--70},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// volume = {3925},
|
||||
// publisher = {Springer}
|
||||
// }
|
||||
//
|
||||
// @InProceedings{gastin.01.cav,
|
||||
// author = {Paul Gastin and Denis Oddoux},
|
||||
// title = {Fast {LTL} to {B\"u}chi Automata Translation},
|
||||
// booktitle = {Proceedings of the 13th International Conference on
|
||||
// Computer Aided Verification (CAV'01)},
|
||||
// pages = {53--65},
|
||||
// year = 2001,
|
||||
// editor = {G. Berry and H. Comon and A. Finkel},
|
||||
// volume = {2102},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// address = {Paris, France},
|
||||
// publisher = {Springer-Verlag}
|
||||
// }
|
||||
//
|
||||
// @InProceedings{rozier.07.spin,
|
||||
// author = {Kristin Y. Rozier and Moshe Y. Vardi},
|
||||
// title = {LTL Satisfiability Checking},
|
||||
// booktitle = {Proceedings of the 12th International SPIN Workshop on
|
||||
// Model Checking of Software (SPIN'07)},
|
||||
// pages = {149--167},
|
||||
// year = {2007},
|
||||
// volume = {4595},
|
||||
// series = {Lecture Notes in Computer Science},
|
||||
// publisher = {Springer-Verlag}
|
||||
// }
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include "error.h"
|
||||
#include <vector>
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_range.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/tl/relabel.hh>
|
||||
|
||||
using namespace spot;
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Generate temporal logic formulas from predefined scalable patterns.";
|
||||
|
||||
enum {
|
||||
OPT_AND_F = 1,
|
||||
OPT_AND_FG,
|
||||
OPT_AND_GF,
|
||||
OPT_CCJ_ALPHA,
|
||||
OPT_CCJ_BETA,
|
||||
OPT_CCJ_BETA_PRIME,
|
||||
OPT_GH_Q,
|
||||
OPT_GH_R,
|
||||
OPT_GO_THETA,
|
||||
OPT_OR_FG,
|
||||
OPT_OR_G,
|
||||
OPT_OR_GF,
|
||||
OPT_R_LEFT,
|
||||
OPT_R_RIGHT,
|
||||
OPT_RV_COUNTER,
|
||||
OPT_RV_COUNTER_CARRY,
|
||||
OPT_RV_COUNTER_CARRY_LINEAR,
|
||||
OPT_RV_COUNTER_LINEAR,
|
||||
OPT_U_LEFT,
|
||||
OPT_U_RIGHT,
|
||||
LAST_CLASS,
|
||||
};
|
||||
|
||||
const char* const class_name[LAST_CLASS] =
|
||||
{
|
||||
"and-f",
|
||||
"and-fg",
|
||||
"and-gf",
|
||||
"ccj-alpha",
|
||||
"ccj-beta",
|
||||
"ccj-beta-prime",
|
||||
"gh-q",
|
||||
"gh-r",
|
||||
"go-theta",
|
||||
"or-fg",
|
||||
"or-g",
|
||||
"or-gf",
|
||||
"or-r-left",
|
||||
"or-r-right",
|
||||
"rv-counter",
|
||||
"rv-counter-carry",
|
||||
"rv-counter-carry-linear",
|
||||
"rv-counter-linear",
|
||||
"u-left",
|
||||
"u-right",
|
||||
};
|
||||
|
||||
|
||||
#define OPT_ALIAS(o) { #o, 0, nullptr, OPTION_ALIAS, nullptr, 0 }
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
// Keep this alphabetically sorted (expect for aliases).
|
||||
{ nullptr, 0, nullptr, 0, "Pattern selection:", 1},
|
||||
// J. Geldenhuys and H. Hansen (Spin'06): Larger automata and less
|
||||
// work for LTL model checking.
|
||||
{ "and-f", OPT_AND_F, "RANGE", 0, "F(p1)&F(p2)&...&F(pn)", 0 },
|
||||
OPT_ALIAS(gh-e),
|
||||
{ "and-fg", OPT_AND_FG, "RANGE", 0, "FG(p1)&FG(p2)&...&FG(pn)", 0 },
|
||||
{ "and-gf", OPT_AND_GF, "RANGE", 0, "GF(p1)&GF(p2)&...&GF(pn)", 0 },
|
||||
OPT_ALIAS(ccj-phi),
|
||||
OPT_ALIAS(gh-c2),
|
||||
{ "ccj-alpha", OPT_CCJ_ALPHA, "RANGE", 0,
|
||||
"F(p1&F(p2&F(p3&...F(pn)))) & F(q1&F(q2&F(q3&...F(qn))))", 0 },
|
||||
{ "ccj-beta", OPT_CCJ_BETA, "RANGE", 0,
|
||||
"F(p&X(p&X(p&...X(p)))) & F(q&X(q&X(q&...X(q))))", 0 },
|
||||
{ "ccj-beta-prime", OPT_CCJ_BETA_PRIME, "RANGE", 0,
|
||||
"F(p&(Xp)&(XXp)&...(X...X(p))) & F(q&(Xq)&(XXq)&...(X...X(q)))", 0 },
|
||||
{ "gh-q", OPT_GH_Q, "RANGE", 0,
|
||||
"(F(p1)|G(p2))&(F(p2)|G(p3))&... &(F(pn)|G(p{n+1}))", 0 },
|
||||
{ "gh-r", OPT_GH_R, "RANGE", 0,
|
||||
"(GF(p1)|FG(p2))&(GF(p2)|FG(p3))&... &(GF(pn)|FG(p{n+1}))", 0},
|
||||
{ "go-theta", OPT_GO_THETA, "RANGE", 0,
|
||||
"!((GF(p1)&GF(p2)&...&GF(pn)) -> G(q->F(r)))", 0 },
|
||||
{ "or-fg", OPT_OR_FG, "RANGE", 0, "FG(p1)|FG(p2)|...|FG(pn)", 0 },
|
||||
OPT_ALIAS(ccj-xi),
|
||||
{ "or-g", OPT_OR_G, "RANGE", 0, "G(p1)|G(p2)|...|G(pn)", 0 },
|
||||
OPT_ALIAS(gh-s),
|
||||
{ "or-gf", OPT_OR_GF, "RANGE", 0, "GF(p1)|GF(p2)|...|GF(pn)", 0 },
|
||||
OPT_ALIAS(gh-c1),
|
||||
{ "r-left", OPT_R_LEFT, "RANGE", 0, "(((p1 R p2) R p3) ... R pn)", 0 },
|
||||
{ "r-right", OPT_R_RIGHT, "RANGE", 0, "(p1 R (p2 R (... R pn)))", 0 },
|
||||
{ "rv-counter", OPT_RV_COUNTER, "RANGE", 0,
|
||||
"n-bit counter", 0 },
|
||||
{ "rv-counter-carry", OPT_RV_COUNTER_CARRY, "RANGE", 0,
|
||||
"n-bit counter w/ carry", 0 },
|
||||
{ "rv-counter-carry-linear", OPT_RV_COUNTER_CARRY_LINEAR, "RANGE", 0,
|
||||
"n-bit counter w/ carry (linear size)", 0 },
|
||||
{ "rv-counter-linear", OPT_RV_COUNTER_LINEAR, "RANGE", 0,
|
||||
"n-bit counter (linear size)", 0 },
|
||||
{ "u-left", OPT_U_LEFT, "RANGE", 0, "(((p1 U p2) U p3) ... U pn)", 0 },
|
||||
OPT_ALIAS(gh-u),
|
||||
{ "u-right", OPT_U_RIGHT, "RANGE", 0, "(p1 U (p2 U (... U pn)))", 0 },
|
||||
OPT_ALIAS(gh-u2),
|
||||
OPT_ALIAS(go-phi),
|
||||
RANGE_DOC,
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the name of the pattern", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the argument of the pattern", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
struct job
|
||||
{
|
||||
int pattern;
|
||||
struct range range;
|
||||
};
|
||||
|
||||
typedef std::vector<job> jobs_t;
|
||||
static jobs_t jobs;
|
||||
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &output_argp, 0, nullptr, -20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
enqueue_job(int pattern, const char* range_str)
|
||||
{
|
||||
job j;
|
||||
j.pattern = pattern;
|
||||
j.range = parse_range(range_str);
|
||||
jobs.push_back(j);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case OPT_AND_F:
|
||||
case OPT_AND_FG:
|
||||
case OPT_AND_GF:
|
||||
case OPT_CCJ_ALPHA:
|
||||
case OPT_CCJ_BETA:
|
||||
case OPT_CCJ_BETA_PRIME:
|
||||
case OPT_GH_Q:
|
||||
case OPT_GH_R:
|
||||
case OPT_GO_THETA:
|
||||
case OPT_OR_FG:
|
||||
case OPT_OR_G:
|
||||
case OPT_OR_GF:
|
||||
case OPT_R_LEFT:
|
||||
case OPT_R_RIGHT:
|
||||
case OPT_RV_COUNTER:
|
||||
case OPT_RV_COUNTER_CARRY:
|
||||
case OPT_RV_COUNTER_CARRY_LINEAR:
|
||||
case OPT_RV_COUNTER_LINEAR:
|
||||
case OPT_U_LEFT:
|
||||
case OPT_U_RIGHT:
|
||||
enqueue_job(key, arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define G_(x) formula::G(x)
|
||||
#define F_(x) formula::F(x)
|
||||
#define X_(x) formula::X(x)
|
||||
#define Not_(x) formula::Not(x)
|
||||
|
||||
#define Implies_(x, y) formula::Implies((x), (y))
|
||||
#define Equiv_(x, y) formula::Equiv((x), (y))
|
||||
#define And_(x, y) formula::And({(x), (y)})
|
||||
#define Or_(x, y) formula::Or({(x), (y)})
|
||||
#define U_(x, y) formula::U((x), (y))
|
||||
|
||||
// F(p_1 & F(p_2 & F(p_3 & ... F(p_n))))
|
||||
static formula
|
||||
E_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << n;
|
||||
formula f = formula::ap(p.str());
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
result = F_(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// p & X(p & X(p & ... X(p)))
|
||||
static formula
|
||||
phi_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
formula p = formula::ap(name);
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
if (result)
|
||||
result = And_(p, X_(result));
|
||||
else
|
||||
result = p;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static formula
|
||||
N_n(std::string name, int n)
|
||||
{
|
||||
return formula::F(phi_n(name, n));
|
||||
}
|
||||
|
||||
// p & X(p) & XX(p) & XXX(p) & ... X^n(p)
|
||||
static formula
|
||||
phi_prime_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula result = nullptr;
|
||||
formula p = formula::ap(name);
|
||||
for (; n > 0; --n)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
p = X_(p);
|
||||
result = And_(result, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static formula
|
||||
N_prime_n(std::string name, int n)
|
||||
{
|
||||
return F_(phi_prime_n(name, n));
|
||||
}
|
||||
|
||||
|
||||
// GF(p_1) & GF(p_2) & ... & GF(p_n) if conj == true
|
||||
// GF(p_1) | GF(p_2) | ... | GF(p_n) if conj == false
|
||||
static formula
|
||||
GF_n(std::string name, int n, bool conj = true)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op o = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = G_(F_(formula::ap(p.str())));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(o, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FG(p_1) | FG(p_2) | ... | FG(p_n) if conj == false
|
||||
// FG(p_1) & FG(p_2) & ... & FG(p_n) if conj == true
|
||||
static formula
|
||||
FG_n(std::string name, int n, bool conj = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op o = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = F_(G_(formula::ap(p.str())));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(o, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (((p1 OP p2) OP p3)...OP pn) if right_assoc == false
|
||||
// (p1 OP (p2 OP (p3 OP (... pn) if right_assoc == true
|
||||
static formula
|
||||
bin_n(std::string name, int n, op o, bool right_assoc = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
n = 1;
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << (right_assoc ? (n + 1 - i) : i);
|
||||
formula f = formula::ap(p.str());
|
||||
if (!result)
|
||||
result = f;
|
||||
else if (right_assoc)
|
||||
result = formula::binop(o, f, result);
|
||||
else
|
||||
result = formula::binop(o, result, f);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (GF(p1)|FG(p2))&(GF(p2)|FG(p3))&...&(GF(pn)|FG(p{n+1}))"
|
||||
static formula
|
||||
R_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula pi;
|
||||
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << 1;
|
||||
pi = formula::ap(p.str());
|
||||
}
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
formula gf = G_(F_(pi));
|
||||
std::ostringstream p;
|
||||
p << name << i + 1;
|
||||
pi = formula::ap(p.str());
|
||||
|
||||
formula fg = F_(G_(pi));
|
||||
|
||||
formula f = Or_(gf, fg);
|
||||
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// (F(p1)|G(p2))&(F(p2)|G(p3))&...&(F(pn)|G(p{n+1}))"
|
||||
static formula
|
||||
Q_n(std::string name, int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return formula::tt();
|
||||
|
||||
formula pi;
|
||||
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << 1;
|
||||
pi = formula::ap(p.str());
|
||||
}
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
formula f = F_(pi);
|
||||
|
||||
std::ostringstream p;
|
||||
p << name << i + 1;
|
||||
pi = formula::ap(p.str());
|
||||
|
||||
formula g = G_(pi);
|
||||
|
||||
f = Or_(f, g);
|
||||
|
||||
if (result)
|
||||
result = And_(f, result);
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// OP(p1) | OP(p2) | ... | OP(Pn) if conj == false
|
||||
// OP(p1) & OP(p2) & ... & OP(Pn) if conj == true
|
||||
static formula
|
||||
combunop_n(std::string name, int n, op o, bool conj = false)
|
||||
{
|
||||
if (n <= 0)
|
||||
return conj ? formula::tt() : formula::ff();
|
||||
|
||||
formula result = nullptr;
|
||||
|
||||
op cop = conj ? op::And : op::Or;
|
||||
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
std::ostringstream p;
|
||||
p << name << i;
|
||||
formula f = formula::unop(o, formula::ap(p.str()));
|
||||
|
||||
if (result)
|
||||
result = formula::multop(cop, {f, result});
|
||||
else
|
||||
result = f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// !((GF(p1)&GF(p2)&...&GF(pn))->G(q -> F(r)))
|
||||
// From "Fast LTL to Büchi Automata Translation" [gastin.01.cav]
|
||||
static formula
|
||||
fair_response(std::string p, std::string q, std::string r, int n)
|
||||
{
|
||||
formula fair = GF_n(p, n);
|
||||
formula resp = G_(Implies_(formula::ap(q), F_(formula::ap(r))));
|
||||
return Not_(Implies_(fair, resp));
|
||||
}
|
||||
|
||||
|
||||
// Builds X(X(...X(p))) with n occurrences of X.
|
||||
static formula
|
||||
X_n(formula p, int n)
|
||||
{
|
||||
assert(n >= 0);
|
||||
formula res = p;
|
||||
while (n--)
|
||||
res = X_(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Based on LTLcounter.pl from Kristin Rozier.
|
||||
// http://shemesh.larc.nasa.gov/people/kyr/benchmarking_scripts/
|
||||
static formula
|
||||
ltl_counter(std::string bit, std::string marker, int n, bool linear)
|
||||
{
|
||||
formula b = formula::ap(bit);
|
||||
formula neg_b = Not_(b);
|
||||
formula m = formula::ap(marker);
|
||||
formula neg_m = Not_(m);
|
||||
|
||||
std::vector<formula> res(4);
|
||||
|
||||
// The marker starts with "1", followed by n-1 "0", then "1" again,
|
||||
// n-1 "0", etc.
|
||||
if (!linear)
|
||||
{
|
||||
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
|
||||
std::vector<formula> v(n);
|
||||
for (int i = 0; i + 1 < n; ++i)
|
||||
v[i] = X_n(neg_m, i + 1);
|
||||
v[n - 1] = X_n(m, n);
|
||||
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
|
||||
formula p = m;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_m, X_(p));
|
||||
res[0] = And_(m, G_(Implies_(m, X_(p))));
|
||||
}
|
||||
|
||||
// All bits are initially zero.
|
||||
if (!linear)
|
||||
{
|
||||
// !b & X(!b) & XX(!b) [if n = 3]
|
||||
std::vector<formula> v2(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
v2[i] = X_n(neg_b, i);
|
||||
res[1] = formula::And(std::move(v2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// !b & X(!b & X(!b)) [if n = 3]
|
||||
formula p = neg_b;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_b, X_(p));
|
||||
res[1] = p;
|
||||
}
|
||||
|
||||
#define AndX_(x, y) (linear ? X_(And_((x), (y))) : And_(X_(x), X_(y)))
|
||||
|
||||
// If the least significant bit is 0, it will be 1 at the next time,
|
||||
// and other bits stay the same.
|
||||
formula Xnm1_b = X_n(b, n - 1);
|
||||
formula Xn_b = X_(Xnm1_b);
|
||||
res[2] = G_(Implies_(And_(m, neg_b),
|
||||
AndX_(Xnm1_b, U_(And_(Not_(m), Equiv_(b, Xn_b)), m))));
|
||||
|
||||
// From the least significant bit to the first 0, all the bits
|
||||
// are flipped on the next value. Remaining bits are identical.
|
||||
formula Xnm1_negb = X_n(neg_b, n - 1);
|
||||
formula Xn_negb = X_(Xnm1_negb);
|
||||
res[3] = G_(Implies_(And_(m, b),
|
||||
AndX_(Xnm1_negb,
|
||||
U_(And_(And_(b, neg_m), Xn_negb),
|
||||
Or_(m, And_(And_(neg_m, neg_b),
|
||||
AndX_(Xnm1_b,
|
||||
U_(And_(neg_m,
|
||||
Equiv_(b, Xn_b)),
|
||||
m))))))));
|
||||
return formula::And(std::move(res));
|
||||
}
|
||||
|
||||
static formula
|
||||
ltl_counter_carry(std::string bit, std::string marker,
|
||||
std::string carry, int n, bool linear)
|
||||
{
|
||||
formula b = formula::ap(bit);
|
||||
formula neg_b = Not_(b);
|
||||
formula m = formula::ap(marker);
|
||||
formula neg_m = Not_(m);
|
||||
formula c = formula::ap(carry);
|
||||
formula neg_c = Not_(c);
|
||||
|
||||
std::vector<formula> res(6);
|
||||
|
||||
// The marker starts with "1", followed by n-1 "0", then "1" again,
|
||||
// n-1 "0", etc.
|
||||
if (!linear)
|
||||
{
|
||||
// G(m -> X(!m)&XX(!m)&XXX(m)) [if n = 3]
|
||||
std::vector<formula> v(n);
|
||||
for (int i = 0; i + 1 < n; ++i)
|
||||
v[i] = X_n(neg_m, i + 1);
|
||||
v[n - 1] = X_n(m, n);
|
||||
res[0] = And_(m, G_(Implies_(m, formula::And(std::move(v)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// G(m -> X(!m & X(!m X(m)))) [if n = 3]
|
||||
formula p = m;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_m, X_(p));
|
||||
res[0] = And_(m, G_(Implies_(m, X_(p))));
|
||||
}
|
||||
|
||||
// All bits are initially zero.
|
||||
if (!linear)
|
||||
{
|
||||
// !b & X(!b) & XX(!b) [if n = 3]
|
||||
std::vector<formula> v2(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
v2[i] = X_n(neg_b, i);
|
||||
res[1] = formula::And(std::move(v2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// !b & X(!b & X(!b)) [if n = 3]
|
||||
formula p = neg_b;
|
||||
for (int i = n - 1; i > 0; --i)
|
||||
p = And_(neg_b, X_(p));
|
||||
res[1] = p;
|
||||
}
|
||||
|
||||
formula Xn_b = X_n(b, n);
|
||||
formula Xn_negb = X_n(neg_b, n);
|
||||
|
||||
// If m is 1 and b is 0 then c is 0 and n steps later b is 1.
|
||||
res[2] = G_(Implies_(And_(m, neg_b), And_(neg_c, Xn_b)));
|
||||
|
||||
// If m is 1 and b is 1 then c is 1 and n steps later b is 0.
|
||||
res[3] = G_(Implies_(And_(m, b), And_(c, Xn_negb)));
|
||||
|
||||
if (!linear)
|
||||
{
|
||||
// If there's no carry, then all of the bits stay the same n steps later.
|
||||
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
|
||||
And_(X_(Not_(c)), Equiv_(X_(b), X_(Xn_b)))));
|
||||
|
||||
// If there's a carry, then add one: flip the bits of b and
|
||||
// adjust the carry.
|
||||
res[5] = G_(Implies_(c, And_(Implies_(X_(neg_b),
|
||||
And_(X_(neg_c), X_(Xn_b))),
|
||||
Implies_(X_(b),
|
||||
And_(X_(c), X_(Xn_negb))))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's no carry, then all of the bits stay the same n steps later.
|
||||
res[4] = G_(Implies_(And_(neg_c, X_(neg_m)),
|
||||
X_(And_(Not_(c), Equiv_(b, Xn_b)))));
|
||||
// If there's a carry, then add one: flip the bits of b and
|
||||
// adjust the carry.
|
||||
res[5] = G_(Implies_(c, X_(And_(Implies_(neg_b, And_(neg_c, Xn_b)),
|
||||
Implies_(b, And_(c, Xn_negb))))));
|
||||
}
|
||||
return formula::And(std::move(res));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
output_pattern(int pattern, int n)
|
||||
{
|
||||
formula f = nullptr;
|
||||
switch (pattern)
|
||||
{
|
||||
// Keep this alphabetically-ordered!
|
||||
case OPT_AND_F:
|
||||
f = combunop_n("p", n, op::F, true);
|
||||
break;
|
||||
case OPT_AND_FG:
|
||||
f = FG_n("p", n, true);
|
||||
break;
|
||||
case OPT_AND_GF:
|
||||
f = GF_n("p", n, true);
|
||||
break;
|
||||
case OPT_CCJ_ALPHA:
|
||||
f = formula::And({E_n("p", n), E_n("q", n)});
|
||||
break;
|
||||
case OPT_CCJ_BETA:
|
||||
f = formula::And({N_n("p", n), N_n("q", n)});
|
||||
break;
|
||||
case OPT_CCJ_BETA_PRIME:
|
||||
f = formula::And({N_prime_n("p", n), N_prime_n("q", n)});
|
||||
break;
|
||||
case OPT_GH_Q:
|
||||
f = Q_n("p", n);
|
||||
break;
|
||||
case OPT_GH_R:
|
||||
f = R_n("p", n);
|
||||
break;
|
||||
case OPT_GO_THETA:
|
||||
f = fair_response("p", "q", "r", n);
|
||||
break;
|
||||
case OPT_OR_FG:
|
||||
f = FG_n("p", n, false);
|
||||
break;
|
||||
case OPT_OR_G:
|
||||
f = combunop_n("p", n, op::G, false);
|
||||
break;
|
||||
case OPT_OR_GF:
|
||||
f = GF_n("p", n, false);
|
||||
break;
|
||||
case OPT_R_LEFT:
|
||||
f = bin_n("p", n, op::R, false);
|
||||
break;
|
||||
case OPT_R_RIGHT:
|
||||
f = bin_n("p", n, op::R, true);
|
||||
break;
|
||||
case OPT_RV_COUNTER_CARRY:
|
||||
f = ltl_counter_carry("b", "m", "c", n, false);
|
||||
break;
|
||||
case OPT_RV_COUNTER_CARRY_LINEAR:
|
||||
f = ltl_counter_carry("b", "m", "c", n, true);
|
||||
break;
|
||||
case OPT_RV_COUNTER:
|
||||
f = ltl_counter("b", "m", n, false);
|
||||
break;
|
||||
case OPT_RV_COUNTER_LINEAR:
|
||||
f = ltl_counter("b", "m", n, true);
|
||||
break;
|
||||
case OPT_U_LEFT:
|
||||
f = bin_n("p", n, op::U, false);
|
||||
break;
|
||||
case OPT_U_RIGHT:
|
||||
f = bin_n("p", n, op::U, true);
|
||||
break;
|
||||
default:
|
||||
error(100, 0, "internal error: pattern not implemented");
|
||||
}
|
||||
|
||||
// Make sure we use only "p42"-style of atomic propositions
|
||||
// in lbt's output.
|
||||
if (output_format == lbt_output && !f.has_lbt_atomic_props())
|
||||
f = relabel(f, Pnn);
|
||||
|
||||
output_formula_checked(f, class_name[pattern - 1], n);
|
||||
}
|
||||
|
||||
static void
|
||||
run_jobs()
|
||||
{
|
||||
for (auto& j: jobs)
|
||||
{
|
||||
int inc = (j.range.max < j.range.min) ? -1 : 1;
|
||||
int n = j.range.min;
|
||||
for (;;)
|
||||
{
|
||||
output_pattern(j.pattern, n);
|
||||
if (n == j.range.max)
|
||||
break;
|
||||
n += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, nullptr, argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(1, 0, "Nothing to do. Try '%s --help' for more information.",
|
||||
program_name);
|
||||
|
||||
run_jobs();
|
||||
return 0;
|
||||
}
|
||||
180
bin/ltl2tgba.cc
Normal file
180
bin/ltl2tgba.cc
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/twaalgos/translate.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
|
||||
static const char argp_program_doc[] ="\
|
||||
Translate linear-time formulas (LTL/PSL) into Büchi automata.\n\n\
|
||||
By default it will apply all available optimizations to output \
|
||||
the smallest Transition-based Generalized Büchi Automata, \
|
||||
in GraphViz's format.\n\
|
||||
If multiple formulas are supplied, several automata will be output.";
|
||||
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula, in Spot's syntax", 4 },
|
||||
/**************************************************/
|
||||
{ "unambiguous", 'U', nullptr, 0, "output unambiguous automata", 2 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &aoutput_argp, 0, nullptr, 0 },
|
||||
{ &aoutput_o_format_argp, 0, nullptr, 0 },
|
||||
{ &post_argp, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static spot::option_map extra_options;
|
||||
static spot::postprocessor::output_pref unambig = 0;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'U':
|
||||
unambig = spot::postprocessor::Unambiguous;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class trans_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::translator& trans;
|
||||
automaton_printer printer;
|
||||
|
||||
trans_processor(spot::translator& trans)
|
||||
: trans(trans), printer(ltl_input)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
// This should not happen, because the parser we use can only
|
||||
// read PSL/LTL formula, but since our formula type can
|
||||
// represent more than PSL formula, let's make this
|
||||
// future-proof.
|
||||
if (!f.is_psl_formula())
|
||||
{
|
||||
std::string s = spot::str_psl(f);
|
||||
error_at_line(2, 0, filename, linenum,
|
||||
"formula '%s' is not an LTL or PSL formula",
|
||||
s.c_str());
|
||||
}
|
||||
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
auto aut = trans.run(&f);
|
||||
const double translation_time = sw.stop();
|
||||
|
||||
printer.print(aut, f, filename, linenum, translation_time, nullptr);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
// By default we name automata using the formula.
|
||||
opt_name = "%f";
|
||||
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FORMULA...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
simplification_level = 3;
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::translator trans(&extra_options);
|
||||
trans.set_pref(pref | comp | sbacc | unambig);
|
||||
trans.set_type(type);
|
||||
trans.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
trans_processor processor(trans);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
242
bin/ltl2tgta.cc
Normal file
242
bin/ltl2tgta.cc
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/tl/parse.hh>
|
||||
#include <spot/tl/print.hh>
|
||||
#include <spot/tl/simplify.hh>
|
||||
#include <spot/twaalgos/dot.hh>
|
||||
#include <spot/twaalgos/ltl2tgba_fm.hh>
|
||||
#include <spot/twaalgos/translate.hh>
|
||||
#include <spot/twa/bddprint.hh>
|
||||
|
||||
#include <spot/taalgos/tgba2ta.hh>
|
||||
#include <spot/taalgos/dot.hh>
|
||||
#include <spot/taalgos/minimize.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Translate linear-time formulas (LTL/PSL) into Testing Automata.\n\n\
|
||||
By default it outputs a transition-based generalized Testing Automaton \
|
||||
the smallest Transition-based Generalized Büchi Automata, \
|
||||
in GraphViz's format. The input formula is assumed to be \
|
||||
stuttering-insensitive.";
|
||||
|
||||
enum {
|
||||
OPT_GTA = 1,
|
||||
OPT_INIT,
|
||||
OPT_SPLV,
|
||||
OPT_SPNO,
|
||||
OPT_TA,
|
||||
OPT_TGTA,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Automaton type:", 1 },
|
||||
{ "tgta", OPT_TGTA, nullptr, 0,
|
||||
"Transition-based Generalized Testing Automaton (default)", 0 },
|
||||
{ "ta", OPT_TA, nullptr, 0, "Testing Automaton", 0 },
|
||||
{ "gta", OPT_GTA, nullptr, 0, "Generalized Testing Automaton", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Options for TA and GTA creation:", 3 },
|
||||
{ "single-pass-lv", OPT_SPLV, nullptr, 0,
|
||||
"add an artificial livelock state to obtain a single-pass (G)TA", 0 },
|
||||
{ "single-pass", OPT_SPNO, nullptr, 0,
|
||||
"create a single-pass (G)TA without artificial livelock state", 0 },
|
||||
{ "multiple-init", OPT_INIT, nullptr, 0,
|
||||
"do not create the fake initial state", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", 4 },
|
||||
{ "utf8", '8', nullptr, 0, "enable UTF-8 characters in output", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ "extra-options", 'x', "OPTS", 0,
|
||||
"fine-tuning options (see spot-x (7))", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &post_argp, 0, nullptr, 20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
enum ta_types { TGTA, GTA, TA };
|
||||
ta_types ta_type = TGTA;
|
||||
|
||||
bool utf8 = false;
|
||||
const char* stats = "";
|
||||
spot::option_map extra_options;
|
||||
bool opt_with_artificial_initial_state = true;
|
||||
bool opt_single_pass_emptiness_check = false;
|
||||
bool opt_with_artificial_livelock = false;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'B':
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char* opt = extra_options.parse_options(arg);
|
||||
if (opt)
|
||||
error(2, 0, "failed to parse --options near '%s'", opt);
|
||||
}
|
||||
break;
|
||||
case OPT_TGTA:
|
||||
ta_type = TGTA;
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
case OPT_GTA:
|
||||
ta_type = GTA;
|
||||
type = spot::postprocessor::TGBA;
|
||||
break;
|
||||
case OPT_TA:
|
||||
ta_type = TA;
|
||||
type = spot::postprocessor::BA;
|
||||
break;
|
||||
case OPT_INIT:
|
||||
opt_with_artificial_initial_state = false;
|
||||
break;
|
||||
case OPT_SPLV:
|
||||
opt_with_artificial_livelock = true;
|
||||
break;
|
||||
case OPT_SPNO:
|
||||
opt_single_pass_emptiness_check = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class trans_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::translator& trans;
|
||||
|
||||
trans_processor(spot::translator& trans)
|
||||
: trans(trans)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
auto aut = trans.run(&f);
|
||||
|
||||
// This should not happen, because the parser we use can only
|
||||
// read PSL/LTL formula, but since our formula type can
|
||||
// represent more than PSL formula, let's make this
|
||||
// future-proof.
|
||||
if (!f.is_psl_formula())
|
||||
{
|
||||
std::string s = spot::str_psl(f);
|
||||
error_at_line(2, 0, filename, linenum,
|
||||
"formula '%s' is not an LTL or PSL formula",
|
||||
s.c_str());
|
||||
}
|
||||
|
||||
bdd ap_set = atomic_prop_collect_as_bdd(f, aut);
|
||||
|
||||
if (ta_type != TGTA)
|
||||
{
|
||||
auto testing_automaton =
|
||||
tgba_to_ta(aut, ap_set, type == spot::postprocessor::BA,
|
||||
opt_with_artificial_initial_state,
|
||||
opt_single_pass_emptiness_check,
|
||||
opt_with_artificial_livelock);
|
||||
if (level != spot::postprocessor::Low)
|
||||
testing_automaton = spot::minimize_ta(testing_automaton);
|
||||
spot::print_dot(std::cout, testing_automaton);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tgta = tgba_to_tgta(aut, ap_set);
|
||||
if (level != spot::postprocessor::Low)
|
||||
tgta = spot::minimize_tgta(tgta);
|
||||
spot::print_dot(std::cout, tgta->get_ta());
|
||||
}
|
||||
flush_cout();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FORMULA...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
simplification_level = 3;
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
error(2, 0, "No formula to translate? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::translator trans(&extra_options);
|
||||
trans.set_pref(pref | comp | sbacc);
|
||||
trans.set_type(type);
|
||||
trans.set_level(level);
|
||||
|
||||
trans_processor processor(trans);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
1479
bin/ltlcross.cc
Normal file
1479
bin/ltlcross.cc
Normal file
File diff suppressed because it is too large
Load diff
337
bin/ltldo.cc
Normal file
337
bin/ltldo.cc
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
#include "common_trans.hh"
|
||||
#include "common_hoaread.hh"
|
||||
|
||||
#include <spot/tl/relabel.hh>
|
||||
#include <spot/misc/bareword.hh>
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/relabel.hh>
|
||||
#include <spot/twaalgos/totgba.hh>
|
||||
#include <spot/parseaut/public.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Run LTL/PSL formulas through another program, performing conversion\n\
|
||||
of input and output as required.";
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
static const argp_option more_o_format[] =
|
||||
{
|
||||
{ "%R", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"serial number of the formula translated", 0 },
|
||||
{ "%T", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"tool used for translation", 0 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"formula translated", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
// This is not very elegant, but we need to add the above %-escape
|
||||
// sequences to those of aoutput_o_fromat_argp for the --help output.
|
||||
// So far I've failed to instruct argp to merge those two lists into a
|
||||
// single block.
|
||||
static const struct argp*
|
||||
build_percent_list()
|
||||
{
|
||||
const argp_option* iter = aoutput_o_format_argp.options;
|
||||
unsigned count = 0;
|
||||
while (iter->name || iter->doc)
|
||||
{
|
||||
++count;
|
||||
++iter;
|
||||
}
|
||||
|
||||
unsigned s = count * sizeof(argp_option);
|
||||
argp_option* d =
|
||||
static_cast<argp_option*>(malloc(sizeof(more_o_format) + s));
|
||||
memcpy(d, aoutput_o_format_argp.options, s);
|
||||
memcpy(d + count, more_o_format, sizeof(more_o_format));
|
||||
|
||||
static const struct argp more_o_format_argp =
|
||||
{ d, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
return &more_o_format_argp;
|
||||
}
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &hoaread_argp, 0, "Parsing of automata:", 3 },
|
||||
{ &finput_argp, 0, nullptr, 1 },
|
||||
{ &trans_argp, 0, nullptr, 3 },
|
||||
{ &aoutput_argp, 0, nullptr, 4 },
|
||||
{ build_percent_list(), 0, nullptr, 5 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case ARGP_KEY_ARG:
|
||||
translators.push_back(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class xtranslator_runner: public translator_runner
|
||||
{
|
||||
public:
|
||||
xtranslator_runner(spot::bdd_dict_ptr dict)
|
||||
: translator_runner(dict, true)
|
||||
{
|
||||
}
|
||||
|
||||
spot::twa_graph_ptr
|
||||
translate(unsigned int translator_num, bool& problem, double& duration)
|
||||
{
|
||||
output.reset(translator_num);
|
||||
|
||||
std::ostringstream command;
|
||||
format(command, translators[translator_num].cmd);
|
||||
|
||||
std::string cmd = command.str();
|
||||
//std::cerr << "Running [" << l << translator_num << "]: "
|
||||
// << cmd << std::endl;
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
int es = exec_with_timeout(cmd.c_str());
|
||||
duration = sw.stop();
|
||||
|
||||
spot::twa_graph_ptr res = nullptr;
|
||||
if (timed_out)
|
||||
{
|
||||
problem = false; // A timeout is considered benign
|
||||
std::cerr << "warning: timeout during execution of command \""
|
||||
<< cmd << "\"\n";
|
||||
++timeout_count;
|
||||
}
|
||||
else if (WIFSIGNALED(es))
|
||||
{
|
||||
problem = true;
|
||||
es = WTERMSIG(es);
|
||||
std::cerr << "error: execution of command \"" << cmd
|
||||
<< "\" terminated by signal " << es << ".\n";
|
||||
}
|
||||
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
|
||||
{
|
||||
problem = true;
|
||||
es = WEXITSTATUS(es);
|
||||
std::cerr << "error: execution of command \"" << cmd
|
||||
<< "\" returned exit code " << es << ".\n";
|
||||
}
|
||||
else if (output.val())
|
||||
{
|
||||
problem = false;
|
||||
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (!aut->errors.empty())
|
||||
{
|
||||
problem = true;
|
||||
std::cerr << "error: failed to parse the automaton "
|
||||
"produced by \"" << cmd << "\".\n";
|
||||
aut->format_errors(std::cerr);
|
||||
res = nullptr;
|
||||
}
|
||||
else if (aut->aborted)
|
||||
{
|
||||
problem = true;
|
||||
std::cerr << "error: command \"" << cmd
|
||||
<< "\" aborted its output.\n";
|
||||
res = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = aut->aut;
|
||||
}
|
||||
}
|
||||
else // No automaton output
|
||||
{
|
||||
problem = false;
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
output.cleanup();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class processor: public job_processor
|
||||
{
|
||||
spot::bdd_dict_ptr dict = spot::make_bdd_dict();
|
||||
xtranslator_runner runner;
|
||||
automaton_printer printer;
|
||||
spot::postprocessor& post;
|
||||
spot::printable_value<std::string> cmdname;
|
||||
spot::printable_value<unsigned> roundval;
|
||||
spot::printable_value<std::string> inputf;
|
||||
|
||||
public:
|
||||
processor(spot::postprocessor& post)
|
||||
: runner(dict), post(post)
|
||||
{
|
||||
printer.add_stat('T', &cmdname);
|
||||
printer.add_stat('R', &roundval);
|
||||
printer.add_stat('f', &inputf);
|
||||
}
|
||||
|
||||
~processor()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_string(const std::string& input,
|
||||
const char* filename,
|
||||
int linenum)
|
||||
{
|
||||
spot::parse_error_list pel;
|
||||
spot::formula f = parse_formula(input, pel);
|
||||
|
||||
if (!f || !pel.empty())
|
||||
{
|
||||
if (filename)
|
||||
error_at_line(0, 0, filename, linenum, "parse error:");
|
||||
spot::format_parse_errors(std::cerr, input, pel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
inputf = input;
|
||||
process_formula(f, filename, linenum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
std::unique_ptr<spot::relabeling_map> relmap;
|
||||
|
||||
// If atomic propositions are incompatible with one of the
|
||||
// output, relabel the formula.
|
||||
if ((!f.has_lbt_atomic_props() &&
|
||||
(runner.has('l') || runner.has('L') || runner.has('T')))
|
||||
|| (!f.has_spin_atomic_props() &&
|
||||
(runner.has('s') || runner.has('S'))))
|
||||
{
|
||||
relmap.reset(new spot::relabeling_map);
|
||||
f = spot::relabel(f, spot::Pnn, relmap.get());
|
||||
}
|
||||
|
||||
static unsigned round = 1;
|
||||
runner.round_formula(f, round);
|
||||
|
||||
unsigned ts = translators.size();
|
||||
for (unsigned t = 0; t < ts; ++t)
|
||||
{
|
||||
bool problem;
|
||||
double translation_time;
|
||||
auto aut = runner.translate(t, problem, translation_time);
|
||||
if (problem)
|
||||
error_at_line(2, 0, filename, linenum, "aborting here");
|
||||
if (aut)
|
||||
{
|
||||
if (relmap)
|
||||
relabel_here(aut, relmap.get());
|
||||
aut = post.run(aut, f);
|
||||
cmdname = translators[t].name;
|
||||
roundval = round;
|
||||
printer.print(aut, f, filename, linenum, translation_time,
|
||||
nullptr);
|
||||
};
|
||||
}
|
||||
spot::cleanup_tmpfiles();
|
||||
++round;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
// Disable post-processing as much as possible by default.
|
||||
level = spot::postprocessor::Low;
|
||||
pref = spot::postprocessor::Any;
|
||||
type = spot::postprocessor::Generic;
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", 1);
|
||||
|
||||
if (translators.empty())
|
||||
error(2, 0, "No translator to run? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
setup_sig_handler();
|
||||
|
||||
spot::postprocessor post;
|
||||
post.set_pref(pref | comp | sbacc);
|
||||
post.set_type(type);
|
||||
post.set_level(level);
|
||||
|
||||
try
|
||||
{
|
||||
processor p(post);
|
||||
if (p.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
665
bin/ltlfilt.cc
Normal file
665
bin/ltlfilt.cc
Normal 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 ≡ (a∨b)→c
|
||||
implied_by = spot::formula::Or({implied_by, i});
|
||||
break;
|
||||
}
|
||||
case OPT_IMPLY:
|
||||
{
|
||||
// a→b∧a→c ≡ a→(b∧c)
|
||||
spot::formula i = parse_formula_arg(arg);
|
||||
imply = spot::formula::And({imply, i});
|
||||
break;
|
||||
}
|
||||
case OPT_LTL:
|
||||
ltl = true;
|
||||
break;
|
||||
case OPT_NEGATE:
|
||||
negate = true;
|
||||
break;
|
||||
case OPT_NNF:
|
||||
nnf = true;
|
||||
break;
|
||||
case OPT_OBLIGATION:
|
||||
obligation = true;
|
||||
break;
|
||||
case OPT_RELABEL:
|
||||
case OPT_RELABEL_BOOL:
|
||||
relabeling = (key == OPT_RELABEL_BOOL ? BseRelabeling : ApRelabeling);
|
||||
if (!arg || !strncasecmp(arg, "abc", 6))
|
||||
style = spot::Abc;
|
||||
else if (!strncasecmp(arg, "pnn", 4))
|
||||
style = spot::Pnn;
|
||||
else
|
||||
error(2, 0, "invalid argument for --relabel%s: '%s'",
|
||||
(key == OPT_RELABEL_BOOL ? "-bool" : ""),
|
||||
arg);
|
||||
break;
|
||||
case OPT_REMOVE_WM:
|
||||
unabbreviate += "MW";
|
||||
break;
|
||||
case OPT_REMOVE_X:
|
||||
remove_x = true;
|
||||
break;
|
||||
case OPT_SAFETY:
|
||||
safety = obligation = true;
|
||||
break;
|
||||
case OPT_SIZE_MIN:
|
||||
size_min = to_int(arg);
|
||||
break;
|
||||
case OPT_SIZE_MAX:
|
||||
size_max = to_int(arg);
|
||||
break;
|
||||
case OPT_SKIP_ERRORS:
|
||||
error_style = skip_errors;
|
||||
break;
|
||||
case OPT_STUTTER_INSENSITIVE:
|
||||
stutter_insensitive = true;
|
||||
break;
|
||||
case OPT_UNABBREVIATE:
|
||||
if (arg)
|
||||
unabbreviate += arg;
|
||||
else
|
||||
unabbreviate += spot::default_unabbrev_string;
|
||||
break;
|
||||
case OPT_AP_N:
|
||||
ap = true;
|
||||
ap_n = to_int(arg);
|
||||
break;
|
||||
case OPT_SYNTACTIC_SAFETY:
|
||||
syntactic_safety = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_GUARANTEE:
|
||||
syntactic_guarantee = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_OBLIGATION:
|
||||
syntactic_obligation = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_RECURRENCE:
|
||||
syntactic_recurrence = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_PERSISTENCE:
|
||||
syntactic_persistence = true;
|
||||
break;
|
||||
case OPT_SYNTACTIC_SI:
|
||||
syntactic_si = true;
|
||||
break;
|
||||
case OPT_UNIVERSAL:
|
||||
universal = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef
|
||||
std::unordered_set<spot::formula> fset_t;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ltl_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
spot::tl_simplifier& simpl;
|
||||
fset_t unique_set;
|
||||
spot::relabeling_map relmap;
|
||||
|
||||
ltl_processor(spot::tl_simplifier& simpl)
|
||||
: simpl(simpl)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_string(const std::string& input,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
spot::parse_error_list pel;
|
||||
spot::formula f = parse_formula(input, pel);
|
||||
|
||||
if (!f || pel.size() > 0)
|
||||
{
|
||||
if (!ignore_errors)
|
||||
{
|
||||
if (filename)
|
||||
error_at_line(0, 0, filename, linenum, "parse error:");
|
||||
spot::format_parse_errors(std::cerr, input, pel);
|
||||
}
|
||||
|
||||
if (error_style == skip_errors)
|
||||
std::cout << input << std::endl;
|
||||
else
|
||||
assert(error_style == drop_errors);
|
||||
check_cout();
|
||||
return !ignore_errors;
|
||||
}
|
||||
try
|
||||
{
|
||||
return process_formula(f, filename, linenum);
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error_at_line(2, 0, filename, linenum, "%s", e.what());
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
process_formula(spot::formula f,
|
||||
const char* filename = nullptr, int linenum = 0)
|
||||
{
|
||||
if (opt_max_count >= 0 && match_count >= opt_max_count)
|
||||
{
|
||||
abort_run = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (negate)
|
||||
f = spot::formula::Not(f);
|
||||
|
||||
if (remove_x)
|
||||
{
|
||||
// If simplification are enabled, we do them before and after.
|
||||
if (simplification_level)
|
||||
f = simpl.simplify(f);
|
||||
f = spot::remove_x(f);
|
||||
}
|
||||
|
||||
if (simplification_level || boolean_to_isop)
|
||||
f = simpl.simplify(f);
|
||||
|
||||
if (nnf)
|
||||
f = simpl.negative_normal_form(f);
|
||||
|
||||
switch (relabeling)
|
||||
{
|
||||
case ApRelabeling:
|
||||
{
|
||||
relmap.clear();
|
||||
f = spot::relabel(f, style, &relmap);
|
||||
break;
|
||||
}
|
||||
case BseRelabeling:
|
||||
{
|
||||
relmap.clear();
|
||||
f = spot::relabel_bse(f, style, &relmap);
|
||||
break;
|
||||
}
|
||||
case NoRelabeling:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!unabbreviate.empty())
|
||||
f = spot::unabbreviate(f, unabbreviate.c_str());
|
||||
|
||||
if (!excl_ap.empty())
|
||||
f = excl_ap.constrain(f);
|
||||
|
||||
bool matched = true;
|
||||
|
||||
matched &= !ltl || f.is_ltl_formula();
|
||||
matched &= !psl || f.is_psl_formula();
|
||||
matched &= !boolean || f.is_boolean();
|
||||
matched &= !universal || f.is_universal();
|
||||
matched &= !eventual || f.is_eventual();
|
||||
matched &= !syntactic_safety || f.is_syntactic_safety();
|
||||
matched &= !syntactic_guarantee || f.is_syntactic_guarantee();
|
||||
matched &= !syntactic_obligation || f.is_syntactic_obligation();
|
||||
matched &= !syntactic_recurrence || f.is_syntactic_recurrence();
|
||||
matched &= !syntactic_persistence || f.is_syntactic_persistence();
|
||||
matched &= !syntactic_si || f.is_syntactic_stutter_invariant();
|
||||
matched &= !ap || atomic_prop_collect(f)->size() == ap_n;
|
||||
|
||||
if (matched && (size_min > 0 || size_max >= 0))
|
||||
{
|
||||
int l = spot::length(f);
|
||||
matched &= (size_min <= 0) || (l >= size_min);
|
||||
matched &= (size_max < 0) || (l <= size_max);
|
||||
}
|
||||
|
||||
if (matched && (bsize_min > 0 || bsize_max >= 0))
|
||||
{
|
||||
int l = spot::length_boolone(f);
|
||||
matched &= (bsize_min <= 0) || (l >= bsize_min);
|
||||
matched &= (bsize_max < 0) || (l <= bsize_max);
|
||||
}
|
||||
|
||||
matched &= !implied_by || simpl.implication(implied_by, f);
|
||||
matched &= !imply || simpl.implication(f, imply);
|
||||
matched &= !equivalent_to || simpl.are_equivalent(f, equivalent_to);
|
||||
matched &= !stutter_insensitive || spot::is_stutter_invariant(f);
|
||||
|
||||
// Match obligations and subclasses using WDBA minimization.
|
||||
// Because this is costly, we compute it later, so that we don't
|
||||
// have to compute it on formulas that have been discarded for
|
||||
// other reasons.
|
||||
if (matched && obligation)
|
||||
{
|
||||
auto aut = ltl_to_tgba_fm(f, simpl.get_dict());
|
||||
auto min = minimize_obligation(aut, f);
|
||||
assert(min);
|
||||
if (aut == min)
|
||||
{
|
||||
// Not an obligation
|
||||
matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched &= !guarantee || is_terminal_automaton(min);
|
||||
matched &= !safety || is_safety_mwdba(min);
|
||||
}
|
||||
}
|
||||
|
||||
matched ^= invert;
|
||||
|
||||
if (unique && !unique_set.insert(f).second)
|
||||
matched = false;
|
||||
|
||||
if (matched)
|
||||
{
|
||||
if (output_define
|
||||
&& output_format != count_output
|
||||
&& output_format != quiet_output)
|
||||
{
|
||||
// Sort the formulas alphabetically.
|
||||
std::map<std::string, spot::formula> m;
|
||||
for (auto& p: relmap)
|
||||
m.emplace(str_psl(p.first), p.second);
|
||||
for (auto& p: m)
|
||||
stream_formula(output_define->ostream()
|
||||
<< "#define " << p.first << " (",
|
||||
p.second, filename, linenum) << ")\n";
|
||||
}
|
||||
one_match = true;
|
||||
output_formula_checked(f, filename, linenum, prefix, suffix);
|
||||
++match_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
try
|
||||
{
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.emplace_back("-", 1);
|
||||
|
||||
if (boolean_to_isop && simplification_level == 0)
|
||||
simplification_level = 1;
|
||||
spot::tl_simplifier_options opt(simplification_level);
|
||||
opt.boolean_to_isop = boolean_to_isop;
|
||||
spot::tl_simplifier simpl(opt);
|
||||
|
||||
ltl_processor processor(simpl);
|
||||
if (processor.run())
|
||||
return 2;
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
if (output_format == count_output)
|
||||
std::cout << match_count << std::endl;
|
||||
|
||||
return one_match ? 0 : 1;
|
||||
}
|
||||
199
bin/ltlgrind.cc
Normal file
199
bin/ltlgrind.cc
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <spot/tl/mutation.hh>
|
||||
|
||||
enum {
|
||||
OPT_AP2CONST = 1,
|
||||
OPT_SIMPLIFY_BOUNDS,
|
||||
OPT_REMOVE_MULTOP_OPERANDS,
|
||||
OPT_REMOVE_OPS,
|
||||
OPT_SPLIT_OPS,
|
||||
OPT_REWRITE_OPS,
|
||||
OPT_REMOVE_ONE_AP,
|
||||
OPT_SORT,
|
||||
};
|
||||
|
||||
static unsigned mutation_nb = 1;
|
||||
static unsigned max_output = -1U;
|
||||
|
||||
static unsigned opt_all = spot::Mut_All;
|
||||
static unsigned mut_opts = 0;
|
||||
static bool opt_sort = false;
|
||||
|
||||
static const char * argp_program_doc =
|
||||
"List formulas that are similar to but simpler than a given formula.";
|
||||
|
||||
static const argp_option options[] = {
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"Mutation rules (all enabled unless those options are used):", 15},
|
||||
{ "ap-to-const", OPT_AP2CONST, nullptr, 0,
|
||||
"atomic propositions are replaced with true/false", 15 },
|
||||
{ "remove-one-ap", OPT_REMOVE_ONE_AP, nullptr, 0,
|
||||
"all occurrences of an atomic proposition are replaced with another " \
|
||||
"atomic proposition used in the formula", 15 },
|
||||
{ "remove-multop-operands", OPT_REMOVE_MULTOP_OPERANDS, nullptr, 0,
|
||||
"remove one operand from multops", 15 },
|
||||
{ "remove-ops", OPT_REMOVE_OPS, nullptr, 0,
|
||||
"replace unary/binary operators with one of their operands", 15 },
|
||||
{ "split-ops", OPT_SPLIT_OPS, nullptr, 0,
|
||||
"when an operator can be expressed as a conjunction/disjunction using "
|
||||
"simpler operators, each term of the conjunction/disjunction is a "
|
||||
"mutation. e.g. a <-> b can be written as ((a & b) | (!a & !b)) or as "
|
||||
"((a -> b) & (b -> a)) so those four terms can be a mutation of a <-> b",
|
||||
0 },
|
||||
{ "rewrite-ops", OPT_REWRITE_OPS, nullptr, 0,
|
||||
"rewrite operators that have a semantically simpler form: a U b becomes "
|
||||
"a W b, etc.", 0 },
|
||||
{ "simplify-bounds", OPT_SIMPLIFY_BOUNDS, nullptr, 0,
|
||||
"on a bounded unary operator, decrement one of the bounds, or set min to "
|
||||
"0 or max to unbounded", 15 },
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", 20 },
|
||||
{ "max-count", 'n', "NUM", 0, "maximum number of mutations to output", 20 },
|
||||
{ "mutations", 'm', "NUM", 0, "number of mutations to apply to the "
|
||||
"formulae (default: 1)", 0 },
|
||||
{ "sort", OPT_SORT, nullptr, 0, "sort the result by formula size", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", 21 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%F", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the name of the input file", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the original line number in the input file", 0 },
|
||||
{ "%<", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line before the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%>", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the part of the line after the formula if it "
|
||||
"comes from a column extracted from a CSV file", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{nullptr, 0, nullptr, 0, "Miscellaneous options:", -1},
|
||||
{nullptr, 0, nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
static const argp_child children[] = {
|
||||
{ &finput_argp, 0, nullptr, 10 },
|
||||
{ &output_argp, 0, nullptr, 20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class mutate_processor: public job_processor
|
||||
{
|
||||
public:
|
||||
int
|
||||
process_formula(spot::formula f, const char* filename = nullptr,
|
||||
int linenum = 0)
|
||||
{
|
||||
auto mutations =
|
||||
spot::mutate(f, mut_opts, max_output, mutation_nb, opt_sort);
|
||||
for (auto g: mutations)
|
||||
output_formula_checked(g, filename, linenum, prefix, suffix);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'm':
|
||||
mutation_nb = to_unsigned(arg);
|
||||
break;
|
||||
case 'n':
|
||||
max_output = to_int(arg);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// FIXME: use stat() to distinguish filename from string?
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_AP2CONST:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Ap2Const;
|
||||
break;
|
||||
case OPT_REMOVE_ONE_AP:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_One_Ap;
|
||||
break;
|
||||
case OPT_REMOVE_MULTOP_OPERANDS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_Multop_Operands;
|
||||
break;
|
||||
case OPT_REMOVE_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Remove_Ops;
|
||||
break;
|
||||
case OPT_SPLIT_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Split_Ops;
|
||||
break;
|
||||
case OPT_REWRITE_OPS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Rewrite_Ops;
|
||||
break;
|
||||
case OPT_SIMPLIFY_BOUNDS:
|
||||
opt_all = 0;
|
||||
mut_opts |= spot::Mut_Simplify_Bounds;
|
||||
break;
|
||||
case OPT_SORT:
|
||||
opt_sort = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
mut_opts |= opt_all;
|
||||
|
||||
if (jobs.empty())
|
||||
jobs.push_back(job("-", true));
|
||||
|
||||
mutate_processor processor;
|
||||
if (processor.run())
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
79
bin/man/Makefile.am
Normal file
79
bin/man/Makefile.am
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
## Développement de l'Epita (LRDE).
|
||||
##
|
||||
## This file is part of Spot, a model checking library.
|
||||
##
|
||||
## Spot is free software; you can redistribute it and/or modify it
|
||||
## under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
## License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
common_dep = $(top_srcdir)/configure.ac
|
||||
x_to_1 = $(top_builddir)/tools/x-to-1
|
||||
convman = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
||||
"$(PERL)" "$(top_srcdir)/tools/help2man -N -L 'en_US.UTF-8'"
|
||||
convman7 = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
||||
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N -L 'en_US.UTF-8'"
|
||||
|
||||
dist_man1_MANS = \
|
||||
autfilt.1 \
|
||||
dstar2tgba.1 \
|
||||
genltl.1 \
|
||||
ltl2tgba.1 \
|
||||
ltl2tgta.1 \
|
||||
ltlcross.1 \
|
||||
ltldo.1 \
|
||||
ltlfilt.1 \
|
||||
ltlgrind.1 \
|
||||
randaut.1 \
|
||||
randltl.1
|
||||
dist_man7_MANS = \
|
||||
spot-x.7
|
||||
|
||||
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
||||
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
|
||||
|
||||
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
|
||||
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
|
||||
|
||||
dstar2tgba.1: $(common_dep) $(srcdir)/dstar2tgba.x $(srcdir)/../dstar2tgba.cc
|
||||
$(convman) ../dstar2tgba$(EXEEXT) $(srcdir)/dstar2tgba.x $@
|
||||
|
||||
ltl2tgba.1: $(common_dep) $(srcdir)/ltl2tgba.x $(srcdir)/../ltl2tgba.cc
|
||||
$(convman) ../ltl2tgba$(EXEEXT) $(srcdir)/ltl2tgba.x $@
|
||||
|
||||
ltl2tgta.1: $(common_dep) $(srcdir)/ltl2tgta.x $(srcdir)/../ltl2tgta.cc
|
||||
$(convman) ../ltl2tgta$(EXEEXT) $(srcdir)/ltl2tgta.x $@
|
||||
|
||||
ltlcross.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltlcross.cc
|
||||
$(convman) ../ltlcross$(EXEEXT) $(srcdir)/ltlcross.x $@
|
||||
|
||||
ltldo.1: $(common_dep) $(srcdir)/ltlcross.x $(srcdir)/../ltldo.cc
|
||||
$(convman) ../ltldo$(EXEEXT) $(srcdir)/ltldo.x $@
|
||||
|
||||
ltlfilt.1: $(common_dep) $(srcdir)/ltlfilt.x $(srcdir)/../ltlfilt.cc
|
||||
$(convman) ../ltlfilt$(EXEEXT) $(srcdir)/ltlfilt.x $@
|
||||
|
||||
genltl.1: $(common_dep) $(srcdir)/genltl.x $(srcdir)/../genltl.cc
|
||||
$(convman) ../genltl$(EXEEXT) $(srcdir)/genltl.x $@
|
||||
|
||||
randltl.1: $(common_dep) $(srcdir)/randltl.x $(srcdir)/../randltl.cc
|
||||
$(convman) ../randltl$(EXEEXT) $(srcdir)/randltl.x $@
|
||||
|
||||
randaut.1: $(common_dep) $(srcdir)/randaut.x $(srcdir)/../randaut.cc
|
||||
$(convman) ../randaut$(EXEEXT) $(srcdir)/randaut.x $@
|
||||
|
||||
spot-x.7: $(common_dep) $(srcdir)/spot-x.x $(srcdir)/../spot-x.cc
|
||||
$(convman7) ../spot-x$(EXEEXT) $(srcdir)/spot-x.x $@
|
||||
|
||||
ltlgrind.1: $(common_dep) $(srcdir)/ltlgrind.x $(srcdir)/../ltlgrind.cc
|
||||
$(convman) ../ltlgrind$(EXEEXT) $(srcdir)/ltlgrind.x $@
|
||||
44
bin/man/autfilt.x
Normal file
44
bin/man/autfilt.x
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
autfilt \- filter, convert, and transform omega-automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
The following papers are related to some of the transformations implemented
|
||||
in autfilt.
|
||||
|
||||
.TP
|
||||
\(bu
|
||||
Etienne Renault, Alexandre Duret-Lutz, Fabrice Kordon, and Denis Poitrenaud:
|
||||
Strength-based decomposition of the property Büchi automaton for faster
|
||||
model checking. Proceedings of TACAS'13. LNCS 7795.
|
||||
|
||||
The \fB\-\-strength\-decompose\fR option implements the definitions
|
||||
given in the above paper.
|
||||
.TP
|
||||
\(bu
|
||||
František Blahoudek, Alexandre Duret-Lutz, Vojtčech Rujbr, and Jan Strejček:
|
||||
On refinement of Büchi automata for explicit model checking.
|
||||
Proceedings of SPIN'15. LNCS 9232.
|
||||
|
||||
That paper gives the motivation for options \fB\-\-exclusive\-ap\fR
|
||||
and \fB\-\-simplify\-exclusive\-ap\fR.
|
||||
.TP
|
||||
\(bu
|
||||
Thibaud Michaud and Alexandre Duret-Lutz:
|
||||
Practical stutter-invariance checks for ω-regular languages.
|
||||
Proceedings of SPIN'15. LNCS 9232.
|
||||
|
||||
Describes the algorithms used by the \fB\-\-destut\fR and
|
||||
\fB\-\-instut\fR options. These options correpond respectively to
|
||||
cl() and sl() in the paper.
|
||||
.TP
|
||||
\(bu
|
||||
Souheib Baarir and Alexandre Duret-Lutz: SAT-based minimization of
|
||||
deterministic ω-automata. Proceedings of LPAR'15 (a.k.a LPAR-20).
|
||||
LNCS 9450.
|
||||
|
||||
Describes the \fB\-\-sat\-minimize\fR option.
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
.BR dstar2tgba (1)
|
||||
107
bin/man/dstar2tgba.x
Normal file
107
bin/man/dstar2tgba.x
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
[NAME]
|
||||
dstar2tgba \- convert automata into Büchi automata
|
||||
[HISTORY]
|
||||
.B dstar2tgba
|
||||
was introduced in Spot 1.2 as a command that reads automata
|
||||
in
|
||||
.BR ltl2dstar 's
|
||||
format, and converts them into TGBA. At this time it was
|
||||
the only command-line tool being able to read automata.
|
||||
.PP
|
||||
In Spot 1.99.1 the
|
||||
.B autfilt
|
||||
command was introduced, but could only read automata
|
||||
in the HOA format, or in
|
||||
.BR lbtt 's
|
||||
format, or as never claims. So
|
||||
.B dstar2tgba
|
||||
was still the only way to process automata
|
||||
in
|
||||
.BR ltl2dstar 's
|
||||
format.
|
||||
.PP
|
||||
In Spot 1.99.4 the parser for
|
||||
.BR ltl2dstar 's
|
||||
format was finally merged with the parser
|
||||
used by
|
||||
.B autfilt
|
||||
for reading the other format. This implies not only
|
||||
that
|
||||
.B autfilt
|
||||
can now read
|
||||
.BR ltl2dstar's
|
||||
format, but also that
|
||||
.B dstar2tgba
|
||||
can read the other formats as well.
|
||||
|
||||
Nowadays, the command
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% dstar2tgba some files
|
||||
.fi
|
||||
.PP
|
||||
can be used as a shorthand for
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% autfilt \-\-tgba \-\-high \-\-small some files
|
||||
.fi
|
||||
.PP
|
||||
The name
|
||||
.B dstar2tgba
|
||||
is kept for backward compatibility and because it is used
|
||||
in at least one published paper, but naming this tool
|
||||
.B aut2tgba
|
||||
would make more sense.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
.TP
|
||||
1.
|
||||
<http://www.ltl2dstar.de/docs/ltl2dstar.html>
|
||||
|
||||
Documents the output format of ltl2dstar.
|
||||
|
||||
.TP
|
||||
2.
|
||||
Chistof Löding: Mehods for the Transformation of ω-Automata:
|
||||
Complexity and Connection to Second Order Logic. Diploma Thesis.
|
||||
University of Kiel. 1998.
|
||||
|
||||
Describes various tranformations from non-deterministic Rabin and
|
||||
Streett automata to Büchi automata. Slightly optimized variants of
|
||||
these transformations are used by dstar2tgba for the general cases.
|
||||
|
||||
.TP
|
||||
3.
|
||||
Sriram C. Krishnan, Anuj Puri, and Robert K. Brayton: Deterministic
|
||||
ω-automata vis-a-vis Deterministic Büchi Automata. ISAAC'94.
|
||||
|
||||
Explains how to preserve the determinism of Rabin and Streett automata
|
||||
when the property can be repreted by a Deterministic automaton.
|
||||
dstar2tgba implements this for the Rabin case only. In other words,
|
||||
translating a deterministic Rabin automaton with dstar2tgba will
|
||||
produce a deterministic TGBA or BA if such a automaton exists.
|
||||
|
||||
.TP
|
||||
4.
|
||||
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
|
||||
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
|
||||
LNCS 8461.
|
||||
|
||||
Explains the SAT-based minimization techniques that can be used (on
|
||||
request only) by dstar2tgba to minimize deterministic Büchi automata.
|
||||
|
||||
.TP
|
||||
5.
|
||||
Souheib Baarir and Alexandre Duret-Lutz: SAT-based minimization of
|
||||
deterministic ω-automata. Proceedings of LPAR'15 (a.k.a LPAR-20).
|
||||
LNCS 9450.
|
||||
|
||||
Extends the previous paper by allowing arbitrary acceptance
|
||||
conditions.
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7),
|
||||
.BR autfilt (1)
|
||||
31
bin/man/genltl.x
Normal file
31
bin/man/genltl.x
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
[NAME]
|
||||
genltl \- generate LTL formulas from scalable patterns
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
Prefixes used in pattern names refer to the following papers:
|
||||
.TP
|
||||
gh
|
||||
J. Geldenhuys and H. Hansen: Larger automata and less
|
||||
work for LTL model checking. Proceedings of Spin'06. LNCS 3925.
|
||||
.TP
|
||||
ccj
|
||||
J. Cichoń, A. Czubak, and A. Jasiński (DepCoS'09): Minimal Büchi
|
||||
Automata for Certain Classes of LTL Formulas. Proceedings of DepCoS'09.
|
||||
.TP
|
||||
go
|
||||
P. Gastin and D. Oddoux: Fast LTL to Büchi Automata Translation.
|
||||
Proceedings of CAV'01. LNCS 2102.
|
||||
.TP
|
||||
rv
|
||||
K. Rozier and M. Vardi: LTL Satisfiability Checking.
|
||||
Proceedings of Spin'07. LNCS 4595.
|
||||
[SEE ALSO]
|
||||
.BR randltl (1)
|
||||
202
bin/man/ltl2tgba.x
Normal file
202
bin/man/ltl2tgba.x
Normal 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 d’Amorim and Grigoire Roşu: Efficient monitoring of
|
||||
ω-languages. Proceedings of CAV'05. LNCS 3576.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite one of the following papers:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: LTL translation improvements in Spot 1.0.
|
||||
Int. J. on Critical Computer-Based Systems, 5(1/2):31--54, March 2014.
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.TP
|
||||
\(bu
|
||||
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
|
||||
and Jan Strejček: Compositional approach to suspension and other
|
||||
improvements to LTL translation. Proceedings of SPIN'13. LNCS 7976.
|
||||
.TP
|
||||
\(bu
|
||||
Souheib Baarir and Alexandre Duret-Lutz: Mechanizing the minimization
|
||||
of deterministic generalized Büchi automata. Proceedings of FORTE'14.
|
||||
LNCS 8461.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
14
bin/man/ltl2tgta.x
Normal file
14
bin/man/ltl2tgta.x
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[NAME]
|
||||
ltl2tgta \- translate LTL/PSL formulas into Testing Automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Ala Eddine Ben Salem, Alexandre Duret-Lutz, and Fabrice Kordon: Model
|
||||
checking using generalized testing automata. Transactions on Petri
|
||||
Nets and Other Models of Concurrency (ToPNoC VI), 7400:94-112, 2012.
|
||||
[SEE ALSO]
|
||||
.BR spot-x (7)
|
||||
270
bin/man/ltlcross.x
Normal file
270
bin/man/ltlcross.x
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
ltlcross \- cross-compare LTL/PSL translators to Büchi automata
|
||||
[EXAMPLES]
|
||||
The following commands compare never claims produced by
|
||||
.BR ltl2tgba (1),
|
||||
.BR spin (1),
|
||||
and
|
||||
.B lbt
|
||||
for 100 random formulas, using a timeout of 2 minutes. Because
|
||||
.B ltlcross
|
||||
knows those tools, there is no need to specify their input and
|
||||
output. A trace of the execution of the two tools, including any
|
||||
potential issue detected, is reported on standard error, while
|
||||
statistics are written to \f(CWresults.json\fR.
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% randltl \-n100 \-\-tree\-size=20..30 a b c | \e
|
||||
ltlcross \-T120 ltl2tgba spin lbt \-\-json=results.json
|
||||
.fi
|
||||
.PP
|
||||
The next command compares
|
||||
.BR lbt ,
|
||||
.BR ltl3ba ,
|
||||
and
|
||||
.BR ltl2tgba (1)
|
||||
on a set of formulas saved in file \f(CWinput.ltl\fR.
|
||||
Statistics are again writen
|
||||
as CSV into \f(CWresults.csv\fR. This examples specify the
|
||||
input and output for each tool, to show how this can be done.
|
||||
Note the use of \f(CW%L\fR to indicate that the formula passed t
|
||||
for the formula in
|
||||
.BR spin (1)'s
|
||||
format, and \f(CW%f\fR for the
|
||||
formula in Spot's format. Each of these tool produces an
|
||||
automaton in a different format (respectively, LBTT's format,
|
||||
Spin's never claims, and HOA format), but Spot's parser can
|
||||
distinguish and understand these three formats.
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \-\-csv=results.csv \e
|
||||
'lbt <%L >%O' \e
|
||||
'ltl3ba \-f %s >%O' \e
|
||||
'ltl2tgba \-H %f >%O'
|
||||
.fi
|
||||
.PP
|
||||
Rabin or Streett automata output by
|
||||
.B ltl2dstar
|
||||
in its historical format can be read from a
|
||||
file specified with \f(CW%D\fR instead of \f(CW%O\fR. For instance:
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \e
|
||||
'ltl2dstar \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
|
||||
'ltl2dstar \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %D' \e
|
||||
.fi
|
||||
.PP
|
||||
However, we now recommand to use the HOA output of
|
||||
.BR ltl2dstar ,
|
||||
as supported since version 0.5.2:
|
||||
.PP
|
||||
.in +4n
|
||||
.nf
|
||||
.ft C
|
||||
% ltlcross \-F input.ltl \e
|
||||
'ltl2dstar \-\-output\-format=hoa \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
|
||||
'ltl2dstar \-\-output\-format=hoa \-\-automata=streett \-\-ltl2nba=spin:ltl2tgba@\-Ds %L %O' \e
|
||||
.fi
|
||||
.PP
|
||||
In more recent versions of ltl2dstar, \fB\-\-output\-format=hoa\fR can
|
||||
be abbreviated \fB-H\fR.
|
||||
|
||||
[ENVIRONMENT VARIABLES]
|
||||
.TP
|
||||
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
|
||||
These variables control in which directory temporary files (e.g.,
|
||||
those who contain the input and output when interfacing with
|
||||
translators) are created. \fBTMPDIR\fR is only read if
|
||||
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
|
||||
variables exist, or if their value is empty, files are created in the
|
||||
current directory.
|
||||
.TP
|
||||
\fBSPOT_TMPKEEP\fR
|
||||
When this variable is defined, temporary files are not removed.
|
||||
This is mostly useful for debugging.
|
||||
|
||||
[OUTPUT DATA]
|
||||
The following columns are output in the CSV or JSON files.
|
||||
.TP
|
||||
\fBformula\fR
|
||||
The formula translated.
|
||||
.TP
|
||||
\fBtool\fR
|
||||
The tool used to translate this formula. This is either the value of the
|
||||
full \fICOMMANDFMT\fR string specified on the command-line, or,
|
||||
if \fICOMMANDFMT\fR has the form \f(CW{\fISHORTNAME\fR\f(CW}\fR\fICMD\fR,
|
||||
the value of \fISHORTNAME\fR.
|
||||
.TP
|
||||
\fBexit_status\fR, \fBexit_code\fR
|
||||
Information about how the execution of the translator went. If the
|
||||
option \fB\-\-omit\-missing\fR is given, these two columns are omitted
|
||||
and only the lines corresponding to successful translation are output.
|
||||
Otherwise, \fBexit_status\fR is a string that can take the following
|
||||
values:
|
||||
.RS
|
||||
.TP
|
||||
\f(CW"ok"\fR
|
||||
The translator ran succesfully (this does not imply that the produced
|
||||
automaton is correct) and ltlcross could parse the resulting
|
||||
automaton. In this case \fBexit_code\fR is always 0.
|
||||
.TP
|
||||
\f(CW"timeout"\fR
|
||||
The translator ran for more than the number of seconds
|
||||
specified with the \fB\-\-timeout\fR option. In this
|
||||
case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"exit code"\fR
|
||||
The translator terminated with a non-zero exit code.
|
||||
\fBexit_code\fR contains that value.
|
||||
.TP
|
||||
\f(CW"signal"\fR
|
||||
The translator terminated with a signal.
|
||||
\fBexit_code\fR contains that signal's number.
|
||||
.TP
|
||||
\f(CW"parse error"\fR
|
||||
The translator terminated normally, but ltlcross could not
|
||||
parse its output. In this case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"no output"\fR
|
||||
The translator terminated normally, but without creating the specified
|
||||
output file. In this case \fBexit_code\fR is always -1.
|
||||
.RE
|
||||
.TP
|
||||
\fBtime\fR
|
||||
A floating point number giving the run time of the translator in seconds.
|
||||
This is reported for all executions, even failling ones.
|
||||
.PP
|
||||
Unless the \fB\-\-omit\-missing\fR option is used, data for all the
|
||||
following columns might be missing.
|
||||
.TP
|
||||
\fBstates\fR, \fBedges\fR, \fBtransitions\fR, \fBacc\fR
|
||||
The number of states, edges, transitions, and acceptance sets in the
|
||||
translated automaton. Column \fBedges\fR counts the number of edges
|
||||
(labeled by Boolean formulas) in the automaton seen as a graph, while
|
||||
\fBtransitions\fR counts the number of assignment-labeled transitions
|
||||
that might have been merged into a formula-labeled edge. For instance
|
||||
an edge labeled by \f(CWtrue\fR will be counted as 2^3=8 transitions if
|
||||
the automaton mention 3 atomic propositions.
|
||||
.PP
|
||||
If the translator produced a Streett or Rabin automaton, these columns
|
||||
contains the size of a TGBA (or BA) produced by ltlcross from that
|
||||
Streett or Rabin automaton. Check \fBin_states\fR, \fBin_edges\fR,
|
||||
\fBin_transitions\fR, and \fBin_acc\fR for statistics about the actual
|
||||
input automaton.
|
||||
.TP
|
||||
\fBscc\fR, \fBnonacc_scc\fR, \fBterminal_scc\fR, \fBweak_scc\fR, \fBstrong_scc\fR
|
||||
The number of strongly connected components in the automaton. The
|
||||
\fBscc\fR column gives the total number, while the other columns only
|
||||
count the SCCs that are non-accepting (a.k.a. transiant), terminal
|
||||
(recognizes and accepts all words), weak (do not recognize all words,
|
||||
but accepts all recognized words), or strong (accept some words, but
|
||||
reject some recognized words).
|
||||
.TP
|
||||
\fBnondet_states\fR, \fBnondet_aut\fR
|
||||
The number of nondeterministic states, and a Boolean indicating whether the
|
||||
automaton is nondeterministic.
|
||||
.TP
|
||||
\fBterminal_aut\fR, \fBweak_aut\fR, \fBstrong_aut\fR
|
||||
Three Boolean used to indicate whether the automaton is terminal (no
|
||||
weak nor strong SCCs), weak (some weak SCCs but no strong SCCs), or strong
|
||||
(some strong SCCs).
|
||||
.TP
|
||||
\fBproduct_states\fR, \fBproduct_transitions\fR, \fBproduct_scc\fR
|
||||
Size of the product between the translated automaton and a randomly
|
||||
generated state-space. For a given formula, the same state-space is
|
||||
of course used the result of each translator. When the
|
||||
\fB\-\-products\fR=\fIN\fR option is used, these values are averaged
|
||||
over the \fIN\fR products performed.
|
||||
|
||||
[DEPRECATED OUTPUT SPECIFIERS]
|
||||
Until version 1.2.6, the output of translators was specifed using the
|
||||
following escape sequences.
|
||||
.PP
|
||||
.TP
|
||||
%N
|
||||
An output file containing a never claim.
|
||||
.TP
|
||||
%T
|
||||
An output file in \fBlbtt\fR's format.
|
||||
.TP
|
||||
%D
|
||||
An output file in \fBltl2dstar\fR's format.
|
||||
.PP
|
||||
Some development versions leading to 1.99.1 also featured
|
||||
.PP
|
||||
.TP
|
||||
%H
|
||||
An output file in the HOA format.
|
||||
.PP
|
||||
Different specifiers were needed because Spot implemented
|
||||
different parsers for these formats. Nowadays, these parsers
|
||||
have been merged into a single parser that is able to
|
||||
distinguish these four formats automatically.
|
||||
.B ltlcross
|
||||
officially supports only one output specifier:
|
||||
.TP
|
||||
%O
|
||||
An output file in either \fBlbtt\fR's format, \fBltl2dstar\fR's format,
|
||||
as a never claim, or in the HOA format
|
||||
.P
|
||||
For backward compatibility, the sequences %D, %H, %N, and %T, are
|
||||
still supported (as aliases for %O), but are deprecated.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR ltl2tgba (1),
|
||||
.BR ltldo (1)
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.PP
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
.B ltlcross
|
||||
is a Spot-based reimplementation of a tool called LBTT. LBTT
|
||||
was developped by Heikki Tauriainen at the Helsinki University of
|
||||
Technology. The main motivation for the reimplementation was to
|
||||
support PSL, and output more statistics about the translations.
|
||||
.PP
|
||||
The sanity checks performed on the result of each translator (by
|
||||
either LBTT or ltlcross) are described in the following paper:
|
||||
.PP
|
||||
.TP
|
||||
\(bu
|
||||
H. Tauriainen and K. Heljanko: Testing LTL formula translation into
|
||||
Büchi automata. Int. J. on Software Tools for Technology Transfer.
|
||||
Volume 4, number 1, October 2002.
|
||||
.PP
|
||||
LBTT did not implement Test 2 described in this paper. ltlcross
|
||||
implements a slight variation: when an automaton produced by some
|
||||
translator is deterministic, its complement is built and used for
|
||||
additional cross-comparisons with other tools. If the translation P1
|
||||
of the positive formula and the translation N1 of the negative formula
|
||||
both yield deterministic automata (this may only happen for obligation
|
||||
properties) then the emptiness check of Comp(P1)*Comp(N1) is
|
||||
equivalent to Test 2 of Tauriainen and Heljanko. If only one
|
||||
automaton is deterministic, say P1, it can still be used to check we
|
||||
can be used to check the result of another translators, for instance
|
||||
checking the emptiness of Comp(P1)*P2.
|
||||
.PP
|
||||
Our implementation will detect and reports problems (like
|
||||
inconsistencies between two translations) but unlike LBTT it does not
|
||||
offer an interactive mode to investigate such problems.
|
||||
.PP
|
||||
Another major difference with LBTT is that ltlcross is
|
||||
not restricted to generalized Büchi acceptance. It supports
|
||||
Rabin and Streett automata via ltl2dstar's format, and automata
|
||||
with arbitrary acceptance conditions via the HOA format.
|
||||
8
bin/man/ltldo.x
Normal file
8
bin/man/ltldo.x
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[NAME]
|
||||
ltldo \- run LTL/PSL formulas through other tools
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR ltl2tgba (1),
|
||||
.BR ltldo (1)
|
||||
55
bin/man/ltlfilt.x
Normal file
55
bin/man/ltlfilt.x
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
[NAME]
|
||||
ltlfilt \- filter files or lists of LTL/PSL formulas
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
.PP
|
||||
The following papers describe algorithms used by ltlfilt:
|
||||
.TP
|
||||
\(bu
|
||||
Kousha Etessami: A note on a question of Peled and Wilke regarding
|
||||
stutter-invariant LTL. Information Processing Letters 75(6): 261-263
|
||||
(2000).
|
||||
|
||||
Describes the transformation behind the \fB\-\-remove\-x\fR option.
|
||||
.TP
|
||||
\(bu
|
||||
Thibaud Michaud and Alexandre Duret-Lutz:
|
||||
Practical stutter-invariance checks for ω-regular languages.
|
||||
Proceedings of SPIN'15. LNCS 9232.
|
||||
|
||||
Describes the algorithm used by \fB\-\-stutter\-insensitive\fR option.
|
||||
.TP
|
||||
\(bu
|
||||
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
|
||||
Powerset Construction for Restricted Classes of
|
||||
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
|
||||
|
||||
Describes the checks implemented by the \fB\-\-safety\fR,
|
||||
\fB\-\-guarantee\fR, and \fB\-\-obligation\fR options.
|
||||
.TP
|
||||
\(bu
|
||||
Ivana Černá, Radek Pelánek: Relating Hierarchy of Temporal Properties
|
||||
to Model Checking. Proceedings of MFCS'03. LNCS 2747.
|
||||
|
||||
Describes the syntactic LTL classes matched by the
|
||||
\fB\-\-syntactic\-safety\fR, \fB\-\-syntactic\-guarantee\fR,
|
||||
\fB\-\-syntactic\-obligation\fR options,
|
||||
\fB\-\-syntactic\-persistence\fR, and \fB\-\-syntactic\-recurrence\fR
|
||||
options.
|
||||
.TP
|
||||
\(bu
|
||||
Kousha Etessami, Gerard J. Holzmann: Optimizing Büchi
|
||||
Automata. Proceedings of CONCUR'00. LNCS 1877.
|
||||
|
||||
Describe the syntactic LTL classes matched by \fB\-\-eventual\fR, and
|
||||
\fB\-\-universal\fR.
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR ltldo (1)
|
||||
6
bin/man/ltlgrind.x
Normal file
6
bin/man/ltlgrind.x
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[NAME]
|
||||
ltlgrind \- list mutations of a formula.
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[SEE ALSO]
|
||||
.BR ltlcross (1)
|
||||
7
bin/man/randaut.x
Normal file
7
bin/man/randaut.x
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[NAME]
|
||||
randaut \- generate random automata
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR autfilt (1)
|
||||
15
bin/man/randltl.x
Normal file
15
bin/man/randltl.x
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[NAME]
|
||||
randltl \- generate random LTL/PSL formulas
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[BIBLIOGRAPHY]
|
||||
If you would like to give a reference to this tool in an article,
|
||||
we suggest you cite the following paper:
|
||||
.TP
|
||||
\(bu
|
||||
Alexandre Duret-Lutz: Manipulating LTL formulas using Spot 1.0.
|
||||
Proceedings of ATVA'13. LNCS 8172.
|
||||
[SEE ALSO]
|
||||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR randaut (1)
|
||||
123
bin/man/spot-x.x
Normal file
123
bin/man/spot-x.x
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
[NAME]
|
||||
spot-x \- Common fine-tuning options.
|
||||
|
||||
[SYNOPSIS]
|
||||
.B \-\-extra-options STRING
|
||||
.br
|
||||
.B \-x STRING
|
||||
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
|
||||
[ENVIRONMENT VARIABLES]
|
||||
|
||||
.TP
|
||||
\fBSPOT_DOTDEFAULT\fR
|
||||
Whenever the \f(CW--dot\fR option is used without argument (even
|
||||
implicitely), the contents of this variable is used as default
|
||||
argument. If you have some default setting in \fBSPOT_DOTDEFAULT\fR
|
||||
but want to alter them temporarily for one call, use
|
||||
\f(CW--dot=.yyy\fR: the dot character will be replaced by the contents
|
||||
of the \f(CWSPOT_DOTDEFAULT\fR environment variable.
|
||||
|
||||
.TP
|
||||
\fBSPOT_DOTEXTRA\fR
|
||||
The contents of this variable is added to any dot output, immediately
|
||||
before the first state is output. This makes it easy to override
|
||||
global attributes of the graph.
|
||||
|
||||
.TP
|
||||
\fBSPOT_SATLOG\fR
|
||||
If set to a filename, the SAT-based minimization routines will append
|
||||
statistics about each iteration to the named file. Each line lists
|
||||
the following comma-separated values: requested number of states,
|
||||
number of reachable states in the output, number of edges in the
|
||||
output, number of transitions in the output, number of variables in
|
||||
the SAT problem, number of clauses in the SAT problem, user time for
|
||||
encoding the SAT problem, system time for encoding the SAT problem,
|
||||
user time for solving the SAT problem, system time for solving the SAT
|
||||
problem.
|
||||
|
||||
.TP
|
||||
\fBSPOT_SATSOLVER\fR
|
||||
If set, this variable should indicate how to call a SAT\-solver. This
|
||||
is used by the sat\-minimize option described above. The default
|
||||
value is \f(CW"glucose -verb=0 -model %I >%O"\fR, it is correct for
|
||||
glucose version 3.0 (for older versions, remove the \fCW(-model\fR
|
||||
option). The escape sequences \f(CW%I\fR and \f(CW%O\fR respectively
|
||||
denote the names of the input and output files. These temporary files
|
||||
are created in the directory specified by \fBSPOT_TMPDIR\fR or
|
||||
\fBTMPDIR\fR (see below). The SAT-solver should follow the convention
|
||||
of the SAT Competition for its input and output format.
|
||||
|
||||
.TP
|
||||
\fBSPOT_STREETT_CONV_MIN\fR
|
||||
The number of Streett pairs above which conversion from Streett
|
||||
acceptance to generalized-Büchi acceptance should be made with a
|
||||
dedicated algorithm. By default this is 3, i.e., if a Streett
|
||||
automaton with 3 acceptance pairs or more has to be converted into
|
||||
generalized-Büchi, the dedicated algorithm is used. This algorithm is
|
||||
close to the classical conversion from Streett to Büchi, but with
|
||||
several tweaks. When this algorithm is not used, the standard
|
||||
"Fin-removal" approach is used instead: first the acceptance condition
|
||||
is converted into disjunctive normal form (DNF), then Fin acceptance
|
||||
is removed like for Rabin automata, yielding a disjuction of
|
||||
generalized Büchi acceptance, and the result is finally converted into
|
||||
conjunctive normal form (CNF) to obtain a generalized Büchi
|
||||
acceptance. Both algorithms have a worst-case size that is
|
||||
exponential in the number of Streett pairs, but in practice the
|
||||
dedicated algorithm works better for most Streett automata with 3 or
|
||||
more pairs (and many 2-pair Streett automata as well, but the
|
||||
difference here is less clear). Setting this variable to 0 will
|
||||
disable the dedicated algorithm. Setting it to 1 will enable it for
|
||||
all Streett automata, however we do not recommand setting it to less
|
||||
than 2, because the "Fin-removal" approach is better for single-pair
|
||||
Streett automata.
|
||||
|
||||
.TP
|
||||
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
|
||||
These variables control in which directory temporary files (e.g.,
|
||||
those who contain the input and output when interfacing with
|
||||
translators) are created. \fBTMPDIR\fR is only read if
|
||||
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
|
||||
variables exist, or if their value is empty, files are created in the
|
||||
current directory.
|
||||
|
||||
.TP
|
||||
\fBSPOT_TMPKEEP\fR
|
||||
When this variable is defined, temporary files are not removed.
|
||||
This is mostly useful for debugging.
|
||||
|
||||
[BIBLIOGRAPHY]
|
||||
.TP
|
||||
1.
|
||||
Christian Dax, Jochen Eisinger, Felix Klaedtke: Mechanizing the
|
||||
Powerset Construction for Restricted Classes of
|
||||
ω-Automata. Proceedings of ATVA'07. LNCS 4762.
|
||||
|
||||
Describes the WDBA-minimization algorithm implemented in Spot. The
|
||||
algorithm used for the tba-det options is also a generalization (to
|
||||
TBA instead of BA) of what they describe in sections 3.2 and 3.3.
|
||||
|
||||
.TP
|
||||
2.
|
||||
Tomáš Babiak, Thomas Badie, Alexandre Duret-Lutz, Mojmír Křetínský,
|
||||
Jan Strejček: Compositional Approach to Suspension and Other
|
||||
Improvements to LTL Translation. Proceedings of SPIN'13. LNCS 7976.
|
||||
|
||||
Describes the compositional suspension, the simulation-based
|
||||
reductions, and the SCC-based simplifications.
|
||||
|
||||
.TP
|
||||
3.
|
||||
Rüdiger Ehlers: Minimising Deterministic Büchi Automata Precisely using
|
||||
SAT Solving. Proceedings of SAT'10. LNCS 6175.
|
||||
|
||||
Our SAT-based minimization procedures are generalizations of this
|
||||
paper to deal with TBA or TGBA.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR ltl2tgba (1)
|
||||
.BR ltl2tgta (1)
|
||||
.BR dstar2tgba (1)
|
||||
.BR autfilt (1)
|
||||
71
bin/options.py
Executable file
71
bin/options.py
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014 Laboratoire de Recherche et
|
||||
# Développement de l'Epita (LRDE).
|
||||
#
|
||||
# This file is part of Spot, a model checking library.
|
||||
#
|
||||
# Spot is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
# License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# Run all binaries, and collect the long option associated to each
|
||||
# short option for easy comparison.
|
||||
# This script should work with both Python 2 and 3.
|
||||
|
||||
from sys import stdout as out
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
with open('Makefile.am', 'r') as mf:
|
||||
lines = mf.read()
|
||||
|
||||
lines = re.sub('\s*\\\\\s*', ' ', lines)
|
||||
bin_programs = re.search('bin_PROGRAMS\s*=([\w \t]*)', lines).group(1).split()
|
||||
|
||||
optre = re.compile('(-\w), (--[\w=-]+)')
|
||||
|
||||
d = {}
|
||||
|
||||
for tool in bin_programs:
|
||||
args = ('./' + tool, '--help')
|
||||
try:
|
||||
popen = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
except OSError:
|
||||
print("Cannot execute " + tool + ", is it compiled?")
|
||||
exit(1)
|
||||
popen.wait()
|
||||
output = popen.communicate()[0].decode('utf-8')
|
||||
|
||||
for match in optre.finditer(output):
|
||||
shortname, longname = match.group(1), match.group(2)
|
||||
if not shortname in d:
|
||||
d[shortname] = { longname: tool }
|
||||
elif not longname in d[shortname]:
|
||||
d[shortname][longname] = tool
|
||||
else:
|
||||
w = ('%29s' % '') + d[shortname][longname]
|
||||
w = w[w.rfind('\n') + 1 : -1]
|
||||
if len(w + ' ' + tool) < 80:
|
||||
d[shortname][longname] += ' ' + tool
|
||||
else:
|
||||
d[shortname][longname] += '\n%29s%s' % ('', tool)
|
||||
|
||||
# The lambda function works around the fact that x might be an str or
|
||||
# a unicode object depending on the Python implementation.
|
||||
for shortname in sorted(d, key=lambda x: x.lower()):
|
||||
out.write(shortname)
|
||||
first=''
|
||||
for longname in sorted(d[shortname]):
|
||||
out.write('%s %-24s %s\n' % (first, longname, d[shortname][longname]))
|
||||
first=' '
|
||||
397
bin/randaut.cc
Normal file
397
bin/randaut.cc
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <spot/misc/timer.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
|
||||
#include <spot/twa/bddprint.hh>
|
||||
#include <spot/twaalgos/randomgraph.hh>
|
||||
#include <spot/twaalgos/canonicalize.hh>
|
||||
|
||||
|
||||
const char argp_program_doc[] = "\
|
||||
Generate random connected automata.\n\n\
|
||||
The automata are built over the atomic propositions named by PROPS...\n\
|
||||
or, if N is a nonnegative number, using N arbitrary names.\n\
|
||||
If the density is set to D, and the number of states to Q, the degree\n\
|
||||
of each state follows a normal distribution with mean 1+(Q-1)D and\n\
|
||||
variance (Q-1)D(1-D). In particular, for D=0 all states have a single\n\
|
||||
successor, while for D=1 all states are interconnected.\v\
|
||||
Examples:\n\
|
||||
\n\
|
||||
This builds a random neverclaim with 4 states and labeled using the two\n\
|
||||
atomic propositions \"a\" and \"b\":\n\
|
||||
% randaut --spin -Q4 a b\n\
|
||||
\n\
|
||||
This builds three random, complete, and deterministic TGBA with 5 to 10\n\
|
||||
states, 1 to 3 acceptance sets, and three atomic propositions:\n\
|
||||
% randaut -n3 -D -H -Q5..10 -A1..3 3\n\
|
||||
\n\
|
||||
Build 3 random, complete, and deterministic Rabin automata\n\
|
||||
with 2 to 3 acceptance pairs, state-based acceptance, 8 states, \n\
|
||||
a high density of edges, and 3 to 4 atomic propositions:\n\
|
||||
% randaut -n3 -D -H -Q8 -d.8 -S -A 'Rabin 2..3' 3..4\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_SEED = 1,
|
||||
OPT_COLORED,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Generation:", 1 },
|
||||
{ "acceptance", 'A', "ACCEPTANCE", 0,
|
||||
"specify the acceptance type of the automaton", 0 },
|
||||
{ "acc-probability", 'a', "FLOAT", 0,
|
||||
"probability that an edge belongs to one acceptance set (0.2)", 0 },
|
||||
{ "automata", 'n', "INT", 0, "number of automata to output (1)\n"\
|
||||
"use a negative value for unbounded generation", 0 },
|
||||
{ "ba", 'B', nullptr, 0,
|
||||
"build a Buchi automaton (implies --acceptance=Buchi --state-acc)", 0 },
|
||||
{ "colored", OPT_COLORED, nullptr, 0,
|
||||
"build an automaton in which each edge (or state if combined with "
|
||||
"-S) belong to a single acceptance set", 0 },
|
||||
{ "density", 'd', "FLOAT", 0, "density of the edges (0.2)", 0 },
|
||||
{ "deterministic", 'D', nullptr, 0,
|
||||
"build a complete, deterministic automaton ", 0 },
|
||||
{ "unique", 'u', nullptr, 0,
|
||||
"do not output the same automaton twice (same in the sense that they "\
|
||||
"are isomorphic)", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ "states", 'Q', "RANGE", 0, "number of states to output (10)", 0 },
|
||||
{ "state-based-acceptance", 'S', nullptr, 0,
|
||||
"used state-based acceptance", 0 },
|
||||
{ "sbacc", 0, nullptr, OPTION_ALIAS, nullptr, 0 },
|
||||
RANGE_DOC,
|
||||
{ nullptr, 0, nullptr, 0, "ACCEPTANCE may be either a RANGE (in which case "
|
||||
"generalized Büchi is assumed), or an arbitrary acceptance formula "
|
||||
"such as 'Fin(0)|Inf(1)&Fin(2)' in the same syntax as in the HOA "
|
||||
"format, or one of the following patterns:\n"
|
||||
" none\n"
|
||||
" all\n"
|
||||
" Buchi\n"
|
||||
" co-Buchi\n"
|
||||
" generalized-Buchi RANGE\n"
|
||||
" generalized-co-Buchi RANGE\n"
|
||||
" Rabin RANGE\n"
|
||||
" Streett RANGE\n"
|
||||
" generalized-Rabin INT RANGE RANGE ... RANGE\n"
|
||||
" parity (min|max|rand) (odd|even|rand) RANGE\n"
|
||||
" random RANGE\n"
|
||||
" random RANGE PROBABILITY\n"
|
||||
"The random acceptance condition uses each set only once, "
|
||||
"unless a probability (to reuse the set again every time it is used) "
|
||||
"is given.", 2 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
static const struct argp_child children[] =
|
||||
{
|
||||
{ &aoutput_argp, 0, nullptr, 3 },
|
||||
{ &aoutput_o_format_argp, 0, nullptr, 4 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const char* opt_acceptance = nullptr;
|
||||
typedef spot::twa_graph::graph_t::edge_storage_t tr_t;
|
||||
typedef std::set<std::vector<tr_t>> unique_aut_t;
|
||||
static spot::atomic_prop_set aprops;
|
||||
static range ap_count_given = {-1, -2}; // Must be two different negative val
|
||||
static int opt_seed = 0;
|
||||
static const char* opt_seed_str = "0";
|
||||
static int opt_automata = 1;
|
||||
static range opt_states = { 10, 10 };
|
||||
static float opt_density = 0.2;
|
||||
static range opt_acc_sets = { -1, 0 };
|
||||
static float opt_acc_prob = 0.2;
|
||||
static bool opt_deterministic = false;
|
||||
static bool opt_state_acc = false;
|
||||
static bool opt_colored = false;
|
||||
static bool ba_wanted = false;
|
||||
static bool generic_wanted = false;
|
||||
static bool gba_wanted = false;
|
||||
static std::unique_ptr<unique_aut_t> opt_uniq = nullptr;
|
||||
|
||||
static void
|
||||
ba_options()
|
||||
{
|
||||
opt_acc_sets = { 1, 1 };
|
||||
opt_state_acc = true;
|
||||
}
|
||||
|
||||
// Range should have the form 12..34 or 12:34, maybe with spaces. The
|
||||
// characters between '.' and ':' include all digits plus '/', but the
|
||||
// parser will later choke on '/' if it is used, so let's not worry
|
||||
// here.
|
||||
static bool
|
||||
looks_like_a_range(const char* str)
|
||||
{
|
||||
while (*str == ' ' || (*str >= '.' && *str <= ':'))
|
||||
++str;
|
||||
return !*str;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state* as)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case '8':
|
||||
spot::enable_utf8();
|
||||
break;
|
||||
case 'a':
|
||||
opt_acc_prob = to_float(arg);
|
||||
if (opt_acc_prob < 0.0 || opt_acc_prob > 1.0)
|
||||
error(2, 0, "probability of acceptance set membership "
|
||||
"should be between 0.0 and 1.0");
|
||||
break;
|
||||
case 'A':
|
||||
if (looks_like_a_range(arg))
|
||||
{
|
||||
opt_acc_sets = parse_range(arg);
|
||||
if (opt_acc_sets.min > opt_acc_sets.max)
|
||||
std::swap(opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (opt_acc_sets.min < 0)
|
||||
error(2, 0, "number of acceptance sets should be positive");
|
||||
gba_wanted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
opt_acceptance = arg;
|
||||
generic_wanted = true;
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
ba_options();
|
||||
ba_wanted = true;
|
||||
break;
|
||||
case 'd':
|
||||
opt_density = to_float(arg);
|
||||
if (opt_density < 0.0 || opt_density > 1.0)
|
||||
error(2, 0, "density should be between 0.0 and 1.0");
|
||||
break;
|
||||
case 'D':
|
||||
opt_deterministic = true;
|
||||
break;
|
||||
case 'n':
|
||||
opt_automata = to_int(arg);
|
||||
break;
|
||||
case 'Q':
|
||||
opt_states = parse_range(arg);
|
||||
if (opt_states.min > opt_states.max)
|
||||
std::swap(opt_states.min, opt_states.max);
|
||||
if (opt_states.min == 0)
|
||||
error(1, 0, "cannot build an automaton with 0 states");
|
||||
break;
|
||||
case 'S':
|
||||
opt_state_acc = true;
|
||||
break;
|
||||
case 'u':
|
||||
opt_uniq =
|
||||
std::unique_ptr<unique_aut_t>(new std::set<std::vector<tr_t>>());
|
||||
break;
|
||||
case OPT_COLORED:
|
||||
opt_colored = true;
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
opt_seed_str = arg;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// If this is the unique non-option argument, it can
|
||||
// be a number of atomic propositions to build.
|
||||
//
|
||||
// argp reorganizes argv[] so that options always come before
|
||||
// non-options. So if as->argc == as->next we know this is the
|
||||
// last non-option argument, and if aprops.empty() we know this
|
||||
// is the also the first one.
|
||||
if (aprops.empty() && as->argc == as->next && looks_like_a_range(arg))
|
||||
{
|
||||
ap_count_given = parse_range(arg);
|
||||
// Create the set once if the count is fixed.
|
||||
if (ap_count_given.min == ap_count_given.max)
|
||||
aprops = spot::create_atomic_prop_set(ap_count_given.min);
|
||||
break;
|
||||
}
|
||||
aprops.insert(spot::formula::ap(arg));
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
strcpy(F_doc, "seed number");
|
||||
strcpy(L_doc, "automaton number");
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
// running 'randaut 0' is one way to generate automata using no
|
||||
// atomic propositions so do not complain in that case.
|
||||
if (aprops.empty() && ap_count_given.max < 0)
|
||||
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
if (generic_wanted && automaton_format == Spin)
|
||||
error(2, 0, "--spin implies --ba so should not be used with --acceptance");
|
||||
if (generic_wanted && ba_wanted)
|
||||
error(2, 0, "--acceptance and --ba may not be used together");
|
||||
|
||||
if (automaton_format == Spin && opt_acc_sets.max > 1)
|
||||
error(2, 0, "--spin is incompatible with --acceptance=%d..%d",
|
||||
opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (ba_wanted && opt_acc_sets.min != 1 && opt_acc_sets.max != 1)
|
||||
error(2, 0, "--ba is incompatible with --acceptance=%d..%d",
|
||||
opt_acc_sets.min, opt_acc_sets.max);
|
||||
if (ba_wanted && generic_wanted)
|
||||
error(2, 0, "--ba is incompatible with --acceptance=%s", opt_acceptance);
|
||||
|
||||
if (automaton_format == Spin)
|
||||
ba_options();
|
||||
|
||||
if (opt_colored && opt_acc_sets.min == -1 && !generic_wanted)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"use --acceptance");
|
||||
if (opt_colored && opt_acc_sets.min == 0)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"fix the range of --acceptance");
|
||||
|
||||
if (opt_acc_sets.min == -1)
|
||||
opt_acc_sets.min = 0;
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
spot::srand(opt_seed);
|
||||
auto d = spot::make_bdd_dict();
|
||||
|
||||
automaton_printer printer;
|
||||
|
||||
constexpr unsigned max_trials = 10000;
|
||||
unsigned trials = max_trials;
|
||||
|
||||
int automaton_num = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
spot::stopwatch sw;
|
||||
sw.start();
|
||||
|
||||
if (ap_count_given.max > 0
|
||||
&& ap_count_given.min != ap_count_given.max)
|
||||
{
|
||||
int c = spot::rrand(ap_count_given.min, ap_count_given.max);
|
||||
aprops = spot::create_atomic_prop_set(c);
|
||||
}
|
||||
|
||||
int size = opt_states.min;
|
||||
if (size != opt_states.max)
|
||||
size = spot::rrand(size, opt_states.max);
|
||||
|
||||
int accs = opt_acc_sets.min;
|
||||
if (accs != opt_acc_sets.max)
|
||||
accs = spot::rrand(accs, opt_acc_sets.max);
|
||||
|
||||
spot::acc_cond::acc_code code;
|
||||
if (opt_acceptance)
|
||||
{
|
||||
code = spot::acc_cond::acc_code(opt_acceptance);
|
||||
accs = code.used_sets().max_set();
|
||||
if (opt_colored && accs == 0)
|
||||
error(2, 0, "--colored requires at least one acceptance set; "
|
||||
"fix the range of --acceptance");
|
||||
}
|
||||
|
||||
auto aut =
|
||||
spot::random_graph(size, opt_density, &aprops, d,
|
||||
accs, opt_acc_prob, 0.5,
|
||||
opt_deterministic, opt_state_acc,
|
||||
opt_colored);
|
||||
|
||||
if (opt_acceptance)
|
||||
aut->set_acceptance(accs, code);
|
||||
|
||||
if (opt_uniq)
|
||||
{
|
||||
auto tmp = spot::canonicalize
|
||||
(make_twa_graph(aut, spot::twa::prop_set::all()));
|
||||
std::vector<tr_t> trans(tmp->edge_vector().begin() + 1,
|
||||
tmp->edge_vector().end());
|
||||
if (!opt_uniq->emplace(trans).second)
|
||||
{
|
||||
--trials;
|
||||
if (trials == 0)
|
||||
error(2, 0, "failed to generate a new unique automaton"
|
||||
" after %d trials", max_trials);
|
||||
continue;
|
||||
}
|
||||
trials = max_trials;
|
||||
}
|
||||
|
||||
auto runtime = sw.stop();
|
||||
|
||||
printer.print(aut, nullptr,
|
||||
opt_seed_str, automaton_num, runtime, nullptr);
|
||||
|
||||
++automaton_num;
|
||||
if (opt_automata > 0 && automaton_num >= opt_automata)
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
}
|
||||
317
bin/randltl.cc
Normal file
317
bin/randltl.cc
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common_sys.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <argp.h>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_range.hh"
|
||||
#include "common_r.hh"
|
||||
#include "common_conv.hh"
|
||||
|
||||
#include <sstream>
|
||||
#include <spot/tl/randomltl.hh>
|
||||
#include <spot/tl/simplify.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
#include <spot/misc/optionmap.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Generate random temporal logic formulas.\n\n\
|
||||
The formulas are built over the atomic propositions named by PROPS...\n\
|
||||
or, if N is a nonnegative number, using N arbitrary names.\v\
|
||||
Examples:\n\
|
||||
\n\
|
||||
The following generates 10 random LTL formulas over the propositions a, b,\n\
|
||||
and c, with the default tree-size, and all available operators.\n\
|
||||
% randltl -n10 a b c\n\
|
||||
\n\
|
||||
If you do not mind about the name of the atomic propositions, just give\n\
|
||||
a number instead:\n\
|
||||
% randltl -n10 3\n\
|
||||
\n\
|
||||
You can disable or favor certain operators by changing their priority.\n\
|
||||
The following disables xor, implies, and equiv, and multiply the probability\n\
|
||||
of X to occur by 10.\n\
|
||||
% randltl --ltl-priorities='xor=0, implies=0, equiv=0, X=10' -n10 a b c\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_BOOLEAN_PRIORITIES = 1,
|
||||
OPT_DUMP_PRIORITIES,
|
||||
OPT_DUPS,
|
||||
OPT_LTL_PRIORITIES,
|
||||
OPT_PSL_PRIORITIES,
|
||||
OPT_SEED,
|
||||
OPT_SERE_PRIORITIES,
|
||||
OPT_TREE_SIZE,
|
||||
OPT_WF,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
// Keep this alphabetically sorted (expect for aliases).
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Type of formula to generate:", 1 },
|
||||
{ "boolean", 'B', nullptr, 0, "generate Boolean formulas", 0 },
|
||||
{ "ltl", 'L', nullptr, 0, "generate LTL formulas (default)", 0 },
|
||||
{ "sere", 'S', nullptr, 0, "generate SERE", 0 },
|
||||
{ "psl", 'P', nullptr, 0, "generate PSL formulas", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Generation:", 2 },
|
||||
{ "weak-fairness", OPT_WF, nullptr, 0,
|
||||
"append some weak-fairness conditions", 0 },
|
||||
{ "formulas", 'n', "INT", 0, "number of formulas to output (1)\n"\
|
||||
"use a negative value for unbounded generation", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (0)", 0 },
|
||||
{ "tree-size", OPT_TREE_SIZE, "RANGE", 0,
|
||||
"tree size of the formulas generated, before mandatory "\
|
||||
"trivial simplifications (15)", 0 },
|
||||
{ "allow-dups", OPT_DUPS, nullptr, 0,
|
||||
"allow duplicate formulas to be output", 0 },
|
||||
DECLARE_OPT_R,
|
||||
RANGE_DOC,
|
||||
LEVEL_DOC(3),
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
|
||||
{ "dump-priorities", OPT_DUMP_PRIORITIES, nullptr, 0,
|
||||
"show current priorities, do not generate any formula", 0 },
|
||||
{ "ltl-priorities", OPT_LTL_PRIORITIES, "STRING", 0,
|
||||
"set priorities for LTL formulas", 0 },
|
||||
{ "sere-priorities", OPT_SERE_PRIORITIES, "STRING", 0,
|
||||
"set priorities for SERE formulas", 0 },
|
||||
{ "boolean-priorities", OPT_BOOLEAN_PRIORITIES, "STRING", 0,
|
||||
"set priorities for Boolean formulas", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "STRING should be a comma-separated list of "
|
||||
"assignments, assigning integer priorities to the tokens "
|
||||
"listed by --dump-priorities.", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the (serial) number of the formula", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &output_argp, 0, nullptr, -20 },
|
||||
{ &misc_argp, 0, nullptr, -1 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
spot::atomic_prop_set aprops;
|
||||
static int output = OUTPUTLTL;
|
||||
static char* opt_pL = nullptr;
|
||||
static char* opt_pS = nullptr;
|
||||
static char* opt_pB = nullptr;
|
||||
static bool opt_dump_priorities = false;
|
||||
static int opt_formulas = 1;
|
||||
static int opt_seed = 0;
|
||||
static range opt_tree_size = { 15, 15 };
|
||||
static bool opt_unique = true;
|
||||
static bool opt_wf = false;
|
||||
static bool ap_count_given = false;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state* as)
|
||||
{
|
||||
// This switch is alphabetically-ordered.
|
||||
switch (key)
|
||||
{
|
||||
case 'B':
|
||||
output = OUTPUTBOOL;
|
||||
break;
|
||||
case 'L':
|
||||
output = OUTPUTLTL;
|
||||
break;
|
||||
case 'n':
|
||||
opt_formulas = to_int(arg);
|
||||
break;
|
||||
case 'P':
|
||||
output = OUTPUTPSL;
|
||||
break;
|
||||
case OPT_R:
|
||||
parse_r(arg);
|
||||
break;
|
||||
case 'S':
|
||||
output = OUTPUTSERE;
|
||||
break;
|
||||
case OPT_BOOLEAN_PRIORITIES:
|
||||
opt_pB = arg;
|
||||
break;
|
||||
case OPT_DUPS:
|
||||
opt_unique = false;
|
||||
break;
|
||||
case OPT_LTL_PRIORITIES:
|
||||
opt_pL = arg;
|
||||
break;
|
||||
case OPT_DUMP_PRIORITIES:
|
||||
opt_dump_priorities = true;
|
||||
break;
|
||||
// case OPT_PSL_PRIORITIES: break;
|
||||
case OPT_SERE_PRIORITIES:
|
||||
opt_pS = arg;
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg);
|
||||
break;
|
||||
case OPT_TREE_SIZE:
|
||||
opt_tree_size = parse_range(arg);
|
||||
if (opt_tree_size.min > opt_tree_size.max)
|
||||
std::swap(opt_tree_size.min, opt_tree_size.max);
|
||||
break;
|
||||
case OPT_WF:
|
||||
opt_wf = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
// If this is the unique non-option argument, it can
|
||||
// be a number of atomic propositions to build.
|
||||
//
|
||||
// argp reorganizes argv[] so that options always come before
|
||||
// non-options. So if as->argc == as->next we know this is the
|
||||
// last non-option argument, and if aprops.empty() we know this
|
||||
// is the also the first one.
|
||||
if (aprops.empty() && as->argc == as->next)
|
||||
{
|
||||
char* endptr;
|
||||
int res = strtol(arg, &endptr, 10);
|
||||
if (!*endptr && res >= 0) // arg is a number
|
||||
{
|
||||
ap_count_given = true;
|
||||
aprops = spot::create_atomic_prop_set(res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
aprops.insert(spot::default_environment::instance().require(arg));
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "N|PROP...", argp_program_doc,
|
||||
children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
// running 'randltl 0' is one way to generate formulas using no
|
||||
// atomic propositions so do not complain in that case.
|
||||
if (aprops.empty() && !ap_count_given)
|
||||
error(2, 0, "No atomic proposition supplied? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
spot::srand(opt_seed);
|
||||
try
|
||||
{
|
||||
spot::randltlgenerator rg
|
||||
(aprops,
|
||||
[&] (){
|
||||
spot::option_map opts;
|
||||
opts.set("output", output);
|
||||
opts.set("tree_size_min", opt_tree_size.min);
|
||||
opts.set("tree_size_max", opt_tree_size.max);
|
||||
opts.set("wf", opt_wf);
|
||||
opts.set("seed", opt_seed);
|
||||
opts.set("simplification_level", simplification_level);
|
||||
return opts;
|
||||
}(), opt_pL, opt_pS, opt_pB);
|
||||
|
||||
if (opt_dump_priorities)
|
||||
{
|
||||
switch (output)
|
||||
{
|
||||
case OUTPUTLTL:
|
||||
std::cout <<
|
||||
"Use --ltl-priorities to set the following LTL priorities:\n";
|
||||
rg.dump_ltl_priorities(std::cout);
|
||||
break;
|
||||
case OUTPUTBOOL:
|
||||
std::cout <<
|
||||
"Use --boolean-priorities to set the following Boolean "
|
||||
"formula priorities:\n";
|
||||
rg.dump_bool_priorities(std::cout);
|
||||
break;
|
||||
case OUTPUTPSL:
|
||||
std::cout <<
|
||||
"Use --ltl-priorities to set the following LTL priorities:\n";
|
||||
rg.dump_psl_priorities(std::cout);
|
||||
// Fall through.
|
||||
case OUTPUTSERE:
|
||||
std::cout <<
|
||||
"Use --sere-priorities to set the following SERE priorities:\n";
|
||||
rg.dump_sere_priorities(std::cout);
|
||||
std::cout <<
|
||||
"Use --boolean-priorities to set the following Boolean "
|
||||
"formula priorities:\n";
|
||||
rg.dump_sere_bool_priorities(std::cout);
|
||||
break;
|
||||
default:
|
||||
error(2, 0, "internal error: unknown type of output");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
while (opt_formulas < 0 || opt_formulas--)
|
||||
{
|
||||
static int count = 0;
|
||||
spot::formula f = rg.next();
|
||||
if (!f)
|
||||
{
|
||||
error(2, 0, "failed to generate a new unique formula after %d " \
|
||||
"trials", MAX_TRIALS);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_formula_checked(f, nullptr, ++count);
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
error(2, 0, "%s", e.what());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
161
bin/spot-x.cc
Normal file
161
bin/spot-x.cc
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue