bin: introduce autcross
Fixes #252. * NEWS: Mention it. * bin/autcross.cc, bin/man/autcross.x, doc/org/autcross.org: New files. * bin/Makefile.am, bin/man/Makefile.am, doc/org/tools.org, doc/Makefile.am: Add them. * bin/autfilt.cc: Use is_universal() instead of is_deterministic(). * bin/common_hoaread.hh, bin/common_trans.cc, bin/common_trans.hh, bin/ltlcross.cc, bin/ltldo.cc: Factor some bits common between ltlcross, ltldo and autcross. * tests/core/autcross.test, tests/core/autcross2.test: New files. * tests/Makefile.am: Add them. * tests/core/dra2dba.test, tests/core/sbacc.test, tests/core/streett.test: Use autcross.
This commit is contained in:
parent
b9fff6a4b1
commit
0cf250d839
21 changed files with 1726 additions and 53 deletions
1
bin/.gitignore
vendored
1
bin/.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
autcross
|
||||
autfilt
|
||||
dstar2tgba
|
||||
genaut
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ libcommon_a_SOURCES = \
|
|||
common_trans.hh
|
||||
|
||||
bin_PROGRAMS = \
|
||||
autcross \
|
||||
autfilt \
|
||||
dstar2tgba \
|
||||
genaut \
|
||||
|
|
@ -77,6 +78,7 @@ bin_PROGRAMS = \
|
|||
# version number that is automatically updated).
|
||||
noinst_PROGRAMS = spot-x spot
|
||||
|
||||
autcross_SOURCES = autcross.cc
|
||||
autfilt_SOURCES = autfilt.cc
|
||||
ltlfilt_SOURCES = ltlfilt.cc
|
||||
genaut_SOURCES = genaut.cc
|
||||
|
|
|
|||
898
bin/autcross.cc
Normal file
898
bin/autcross.cc
Normal file
|
|
@ -0,0 +1,898 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2017 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 <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <argp.h>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <sys/wait.h>
|
||||
#include <iomanip>
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_hoaread.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_color.hh"
|
||||
#include "common_trans.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/postproc.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include <spot/twaalgos/determinize.hh>
|
||||
#include <spot/twaalgos/dualize.hh>
|
||||
#include <spot/twaalgos/alternation.hh>
|
||||
#include <spot/twaalgos/cleanacc.hh>
|
||||
#include <spot/twaalgos/remfin.hh>
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Call several tools that process automata and cross-compare their output \
|
||||
to detect bugs, or to gather statistics. The list of automata to use \
|
||||
should be supplied on standard input, or using the -f or -F options.\v\
|
||||
Exit status:\n\
|
||||
0 everything went fine (timeouts are OK too)\n\
|
||||
1 some tools failed to output something we understand, or failed\n\
|
||||
sanity checks (statistics were output nonetheless)\n\
|
||||
2 autcross aborted on error\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_BOGUS = 256,
|
||||
OPT_CSV,
|
||||
OPT_HIGH,
|
||||
OPT_IGNORE_EXEC_FAIL,
|
||||
OPT_LANG,
|
||||
OPT_LOW,
|
||||
OPT_MEDIUM,
|
||||
OPT_NOCHECKS,
|
||||
OPT_OMIT,
|
||||
OPT_STOP_ERR,
|
||||
OPT_VERBOSE,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process automata from FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "autcross behavior:", 5 },
|
||||
{ "stop-on-error", OPT_STOP_ERR, nullptr, 0,
|
||||
"stop on first execution error or failure to pass"
|
||||
" sanity checks (timeouts are OK)", 0 },
|
||||
{ "ignore-execution-failures", OPT_IGNORE_EXEC_FAIL, nullptr, 0,
|
||||
"ignore automata from translators that return with a non-zero exit code,"
|
||||
" but do not flag this as an error", 0 },
|
||||
{ "language-preserved", OPT_LANG, nullptr, 0,
|
||||
"expect that each tool preserves the input language", 0 },
|
||||
{ "no-checks", OPT_NOCHECKS, nullptr, 0,
|
||||
"do not perform any sanity checks", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Statistics output:", 7 },
|
||||
{ "csv", OPT_CSV, "[>>]FILENAME", OPTION_ARG_OPTIONAL,
|
||||
"output statistics as CSV in FILENAME or on standard output "
|
||||
"(if '>>' is used to request append mode, the header line is "
|
||||
"not output)", 0 },
|
||||
{ "omit-missing", OPT_OMIT, nullptr, 0,
|
||||
"do not output statistics for timeouts or failed translations", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level (for complementation):",
|
||||
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, "Miscellaneous options:", -2 },
|
||||
{ "save-bogus", OPT_BOGUS, "[>>]FILENAME", 0,
|
||||
"save formulas for which problems were detected in FILENAME", 0 },
|
||||
{ "verbose", OPT_VERBOSE, nullptr, 0,
|
||||
"print what is being done, for debugging", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &autproc_argp, 0, nullptr, 0 },
|
||||
{ &hoaread_argp, 0, "Parsing of automata:", 4 },
|
||||
{ &misc_argp, 0, nullptr, -2 },
|
||||
{ &color_argp, 0, nullptr, 0 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static bool verbose = false;
|
||||
static bool ignore_exec_fail = false;
|
||||
static unsigned ignored_exec_fail = 0;
|
||||
static bool stop_on_error = false;
|
||||
static bool no_checks = false;
|
||||
static bool opt_language_preserved = false;
|
||||
static bool opt_omit = false;
|
||||
static const char* csv_output = nullptr;
|
||||
static unsigned round_num = 0;
|
||||
static const char* bogus_output_filename = nullptr;
|
||||
static output_file* bogus_output = nullptr;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_BOGUS:
|
||||
{
|
||||
bogus_output = new output_file(arg);
|
||||
bogus_output_filename = arg;
|
||||
break;
|
||||
}
|
||||
case OPT_CSV:
|
||||
csv_output = arg ? arg : "-";
|
||||
break;
|
||||
case OPT_HIGH:
|
||||
level = spot::postprocessor::High;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_IGNORE_EXEC_FAIL:
|
||||
ignore_exec_fail = true;
|
||||
break;
|
||||
case OPT_LANG:
|
||||
opt_language_preserved = true;
|
||||
break;
|
||||
case OPT_LOW:
|
||||
level = spot::postprocessor::Low;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_MEDIUM:
|
||||
level = spot::postprocessor::Medium;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_NOCHECKS:
|
||||
no_checks = true;
|
||||
break;
|
||||
case OPT_OMIT:
|
||||
opt_omit = true;
|
||||
break;
|
||||
case OPT_STOP_ERR:
|
||||
stop_on_error = true;
|
||||
break;
|
||||
case OPT_VERBOSE:
|
||||
verbose = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools_push_autproc(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool global_error_flag = false;
|
||||
// static unsigned oom_count = 0U;
|
||||
|
||||
static std::ostream&
|
||||
global_error()
|
||||
{
|
||||
global_error_flag = true;
|
||||
std::cerr << bright_red;
|
||||
return std::cerr;
|
||||
}
|
||||
|
||||
static void
|
||||
end_error()
|
||||
{
|
||||
std::cerr << reset_color;
|
||||
}
|
||||
|
||||
static std::ostream&
|
||||
example()
|
||||
{
|
||||
std::cerr << bold_std;
|
||||
return std::cerr;
|
||||
}
|
||||
|
||||
|
||||
struct aut_statistics
|
||||
{
|
||||
unsigned ap;
|
||||
unsigned states;
|
||||
unsigned edges;
|
||||
unsigned transitions;
|
||||
unsigned acc_sets;
|
||||
unsigned scc;
|
||||
unsigned nondetstates;
|
||||
bool nondeterministic;
|
||||
bool alternating;
|
||||
|
||||
aut_statistics()
|
||||
{
|
||||
}
|
||||
|
||||
void set(const spot::const_twa_graph_ptr& aut)
|
||||
{
|
||||
if (!csv_output)
|
||||
// Do not waste time.
|
||||
return;
|
||||
ap = aut->ap().size();
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states = s.states;
|
||||
edges = s.edges;
|
||||
transitions = s.transitions;
|
||||
spot::scc_info m(aut);
|
||||
acc_sets = aut->num_sets();
|
||||
unsigned c = m.scc_count();
|
||||
scc = c;
|
||||
nondetstates = spot::count_nondet_states(aut);
|
||||
nondeterministic = nondetstates != 0;
|
||||
alternating = !aut->is_existential();
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
os << ap << ','
|
||||
<< states << ','
|
||||
<< edges << ','
|
||||
<< transitions << ','
|
||||
<< acc_sets << ','
|
||||
<< scc << ','
|
||||
<< nondetstates << ','
|
||||
<< nondeterministic << ','
|
||||
<< alternating;
|
||||
}
|
||||
|
||||
void empty(std::ostream& os) const
|
||||
{
|
||||
os << ",,,,,,,,";
|
||||
}
|
||||
|
||||
static void fields(std::ostream& os, const char* prefix)
|
||||
{
|
||||
os << '"'
|
||||
<< prefix << "ap\",\""
|
||||
<< prefix << "states\",\""
|
||||
<< prefix << "edges\",\""
|
||||
<< prefix << "transitions\",\""
|
||||
<< prefix << "acc_sets\",\""
|
||||
<< prefix << "scc\",\""
|
||||
<< prefix << "nondetstates\",\""
|
||||
<< prefix << "nondeterministic\",\""
|
||||
<< prefix << "alternating\"";
|
||||
}
|
||||
};
|
||||
|
||||
struct in_statistics
|
||||
{
|
||||
std::string input_source;
|
||||
std::string input_name;
|
||||
aut_statistics input;
|
||||
|
||||
static void fields(std::ostream& os)
|
||||
{
|
||||
os << "\"input.source\",\"input.name\",";
|
||||
aut_statistics::fields(os, "input.");
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
spot::escape_rfc4180(os << '"', input_source) << "\",";
|
||||
if (!input_name.empty())
|
||||
spot::escape_rfc4180(os << '"', input_name) << "\",";
|
||||
else
|
||||
os << ',';
|
||||
input.to_csv(os);
|
||||
}
|
||||
};
|
||||
|
||||
struct out_statistics
|
||||
{
|
||||
|
||||
// If OK is false, output statistics are not available.
|
||||
bool ok;
|
||||
const char* status_str;
|
||||
int status_code;
|
||||
double time;
|
||||
aut_statistics output;
|
||||
|
||||
out_statistics()
|
||||
: ok(false),
|
||||
status_str(nullptr),
|
||||
status_code(0),
|
||||
time(0)
|
||||
{
|
||||
}
|
||||
|
||||
static void fields(std::ostream& os)
|
||||
{
|
||||
os << "\"exit_status\",\"exit_code\",\"time\",";
|
||||
aut_statistics::fields(os, "output.");
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
os << '"' << status_str << "\"," << status_code << ','
|
||||
<< time << ',';
|
||||
if (ok)
|
||||
output.to_csv(os);
|
||||
else
|
||||
output.empty(os);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<in_statistics> input_statistics;
|
||||
typedef std::vector<out_statistics> vector_tool_statistics;
|
||||
std::vector<vector_tool_statistics> output_statistics;
|
||||
|
||||
namespace
|
||||
{
|
||||
class autcross_runner final: public autproc_runner
|
||||
{
|
||||
spot::bdd_dict_ptr dict;
|
||||
public:
|
||||
autcross_runner(spot::bdd_dict_ptr dict)
|
||||
: dict(dict)
|
||||
{
|
||||
}
|
||||
|
||||
spot::twa_graph_ptr
|
||||
run_tool(unsigned int tool_num, char l, bool& problem,
|
||||
out_statistics& stats)
|
||||
{
|
||||
output.reset(tool_num);
|
||||
|
||||
std::ostringstream command;
|
||||
format(command, tools[tool_num].cmd);
|
||||
|
||||
std::string cmd = command.str();
|
||||
std::cerr << "Running [" << l << tool_num << "]: "
|
||||
<< cmd << std::endl;
|
||||
process_timer timer;
|
||||
timer.start();
|
||||
int es = exec_with_timeout(cmd.c_str());
|
||||
timer.stop();
|
||||
const char* status_str = nullptr;
|
||||
|
||||
spot::twa_graph_ptr res = nullptr;
|
||||
if (timed_out)
|
||||
{
|
||||
// This is not considered to be a global error.
|
||||
std::cerr << "warning: timeout during execution of command\n";
|
||||
++timeout_count;
|
||||
status_str = "timeout";
|
||||
problem = false; // A timeout is not a sign of a bug
|
||||
es = -1;
|
||||
}
|
||||
else if (WIFSIGNALED(es))
|
||||
{
|
||||
status_str = "signal";
|
||||
problem = true;
|
||||
es = WTERMSIG(es);
|
||||
global_error() << "error: execution terminated by signal "
|
||||
<< es << ".\n";
|
||||
end_error();
|
||||
}
|
||||
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
|
||||
{
|
||||
es = WEXITSTATUS(es);
|
||||
status_str = "exit code";
|
||||
if (!ignore_exec_fail)
|
||||
{
|
||||
problem = true;
|
||||
global_error() << "error: execution returned exit code "
|
||||
<< es << ".\n";
|
||||
end_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
problem = false;
|
||||
std::cerr << "warning: execution returned exit code "
|
||||
<< es << ".\n";
|
||||
++ignored_exec_fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status_str = "ok";
|
||||
problem = false;
|
||||
es = 0;
|
||||
|
||||
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (!aut->errors.empty())
|
||||
{
|
||||
status_str = "parse error";
|
||||
problem = true;
|
||||
es = -1;
|
||||
std::ostream& err = global_error();
|
||||
err << "error: failed to parse the produced automaton.\n";
|
||||
aut->format_errors(err);
|
||||
end_error();
|
||||
res = nullptr;
|
||||
}
|
||||
else if (aut->aborted)
|
||||
{
|
||||
status_str = "aborted";
|
||||
problem = true;
|
||||
es = -1;
|
||||
global_error() << "error: aborted HOA file.\n";
|
||||
end_error();
|
||||
res = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = aut->aut;
|
||||
}
|
||||
}
|
||||
output.cleanup();
|
||||
|
||||
stats.status_str = status_str;
|
||||
stats.status_code = es;
|
||||
stats.time = timer.get_lap_sw();
|
||||
if (res)
|
||||
{
|
||||
stats.ok = true;
|
||||
stats.output.set(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
static std::string
|
||||
autname(unsigned i, bool complemented = false)
|
||||
{
|
||||
std::ostringstream str;
|
||||
if (complemented)
|
||||
str << "Comp(";
|
||||
if (i < tools.size())
|
||||
str << 'A' << i;
|
||||
else
|
||||
str << "input";
|
||||
if (complemented)
|
||||
str << ')';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
static bool
|
||||
check_empty_prod(const spot::const_twa_graph_ptr& aut_i,
|
||||
const spot::const_twa_graph_ptr& aut_j,
|
||||
size_t i, size_t j)
|
||||
{
|
||||
if (aut_i->num_sets() + aut_j->num_sets()
|
||||
> 8 * sizeof(spot::acc_cond::mark_t::value_t))
|
||||
{
|
||||
std::cerr << "info: building " << autname(i)
|
||||
<< '*' << autname(j, true)
|
||||
<< " requires more acceptance sets than supported\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prod = spot::product(aut_i, aut_j);
|
||||
|
||||
if (verbose)
|
||||
std::cerr << "info: check_empty "
|
||||
<< autname(i) << '*' << autname(j, true) << '\n';
|
||||
|
||||
auto w = prod->accepting_word();
|
||||
if (w)
|
||||
{
|
||||
std::ostream& err = global_error();
|
||||
err << "error: " << autname(i) << '*' << autname(j, true)
|
||||
<< (" is nonempty; both automata accept the infinite word:\n"
|
||||
" ");
|
||||
example() << *w << '\n';
|
||||
end_error();
|
||||
}
|
||||
return !!w;
|
||||
}
|
||||
|
||||
class autcross_processor final: public hoa_processor
|
||||
{
|
||||
autcross_runner runner;
|
||||
public:
|
||||
autcross_processor()
|
||||
: hoa_processor(spot::make_bdd_dict()), runner(dict_)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut) override
|
||||
{
|
||||
auto printsize = [](const spot::const_twa_graph_ptr& aut,
|
||||
bool props)
|
||||
{
|
||||
std::cerr << '(' << aut->num_states() << " st.,"
|
||||
<< aut->num_edges() << " ed.,"
|
||||
<< aut->num_sets() << " sets)";
|
||||
if (props)
|
||||
{
|
||||
if (!aut->is_existential())
|
||||
std::cerr << " univ-edges";
|
||||
if (is_deterministic(aut))
|
||||
std::cerr << " deterministic";
|
||||
if (is_complete(aut))
|
||||
std::cerr << " complete";
|
||||
std::cerr << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
spot::twa_graph_ptr input = haut->aut;
|
||||
runner.round_automaton(input, round_num);
|
||||
|
||||
std::string source = [&]()
|
||||
{
|
||||
std::ostringstream src;
|
||||
src << haut->filename << ':' << haut->loc;
|
||||
return src.str();
|
||||
}();
|
||||
|
||||
input_statistics.push_back(in_statistics());
|
||||
|
||||
std::cerr << bold << source << reset_color;
|
||||
input_statistics[round_num].input_source = std::move(source);
|
||||
if (auto name = input->get_named_prop<std::string>("automaton-name"))
|
||||
{
|
||||
std::cerr << '\t' << *name;
|
||||
input_statistics[round_num].input_name = *name;
|
||||
}
|
||||
std::cerr << '\n';
|
||||
input_statistics[round_num].input.set(input);
|
||||
|
||||
int problems = 0;
|
||||
size_t m = tools.size();
|
||||
size_t mi = m + opt_language_preserved;
|
||||
std::vector<spot::twa_graph_ptr> pos(mi);
|
||||
std::vector<spot::twa_graph_ptr> neg(mi);
|
||||
vector_tool_statistics stats(m);
|
||||
|
||||
if (opt_language_preserved)
|
||||
pos[mi - 1] = input;
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: input\t";
|
||||
printsize(input, true);
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < m; ++n)
|
||||
{
|
||||
bool prob;
|
||||
pos[n] = runner.run_tool(n, 'A', prob, stats[n]);
|
||||
problems += prob;
|
||||
}
|
||||
spot::cleanup_tmpfiles();
|
||||
++round_num;
|
||||
output_statistics.push_back(std::move(stats));
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: collected automata:\n";
|
||||
for (unsigned i = 0; i < m; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
std::cerr << "info: A" << i << '\t';
|
||||
printsize(pos[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_checks)
|
||||
{
|
||||
std::cerr << "Performing sanity checks and gathering statistics..."
|
||||
<< std::endl;
|
||||
|
||||
{
|
||||
bool print_first = true;
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
if (!pos[i])
|
||||
continue;
|
||||
if (!pos[i]->is_existential())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr <<
|
||||
"info: getting rid of universal edges...\n";
|
||||
print_first = false;
|
||||
}
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i) << '\t';
|
||||
printsize(pos[i], false);
|
||||
std::cerr << " -> ";
|
||||
}
|
||||
pos[i] = remove_alternation(pos[i]);
|
||||
if (verbose)
|
||||
{
|
||||
printsize(pos[i], false);
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
if (is_universal(pos[i]))
|
||||
neg[i] = dualize(pos[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bool print_first = verbose;
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
if (pos[i] && !neg[i])
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr << "info: complementing non-deterministic "
|
||||
"automata via determinization...\n";
|
||||
print_first = false;
|
||||
}
|
||||
spot::postprocessor p;
|
||||
p.set_type(spot::postprocessor::Generic);
|
||||
p.set_pref(spot::postprocessor::Deterministic);
|
||||
p.set_level(level);
|
||||
neg[i] = dualize(p.run(pos[i]));
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i) << '\t';
|
||||
printsize(pos[i], false);
|
||||
std::cerr << " -> ";
|
||||
printsize(neg[i], false);
|
||||
std::cerr << '\t' << autname(i, true) << '\n';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
bool print_first = true;
|
||||
auto tmp = [&](std::vector<spot::twa_graph_ptr>& x, unsigned i,
|
||||
bool neg)
|
||||
{
|
||||
if (!x[i])
|
||||
return;
|
||||
if (x[i]->acc().uses_fin_acceptance())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr <<
|
||||
"info: getting rid of any Fin acceptance...\n";
|
||||
print_first = false;
|
||||
}
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i, neg) << '\t';
|
||||
printsize(x[i], false);
|
||||
std::cerr << " ->";
|
||||
}
|
||||
cleanup_acceptance_here(x[i]);
|
||||
x[i] = remove_fin(x[i]);
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << ' ';
|
||||
printsize(x[i], false);
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove useless sets nonetheless.
|
||||
cleanup_acceptance_here(x[i]);
|
||||
}
|
||||
};
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
tmp(pos, i, false);
|
||||
tmp(neg, i, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Just make a circular implication check
|
||||
// A0 <= A1, A1 <= A2, ..., AN <= A0
|
||||
unsigned ok = 0;
|
||||
for (size_t i = 0; i < mi; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
size_t j = ((i + 1) % mi);
|
||||
if (i != j && neg[j])
|
||||
{
|
||||
int res = check_empty_prod(pos[i], neg[j], i, j);
|
||||
problems += res;
|
||||
ok += !res;
|
||||
}
|
||||
}
|
||||
// If the circular check failed, do the rest of all
|
||||
// mi(mi-1)/2 checks, as this will help diagnose the issue.
|
||||
if (ok != mi)
|
||||
for (size_t i = 0; i < mi; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
size_t k = ((i + 1) % mi);
|
||||
for (size_t j = 0; j < mi; ++j)
|
||||
if (i != j && j != k && neg[j])
|
||||
problems += check_empty_prod(pos[i], neg[j], i, j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Gathering statistics..." << std::endl;
|
||||
}
|
||||
|
||||
|
||||
if (problems && bogus_output)
|
||||
print_hoa(bogus_output->ostream(), input) << std::endl;
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
// Shall we stop processing now?
|
||||
abort_run = global_error_flag && stop_on_error;
|
||||
return problems;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Output an RFC4180-compatible CSV file.
|
||||
static void
|
||||
print_stats_csv(const char* filename)
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr << "info: writing CSV to " << filename << '\n';
|
||||
|
||||
output_file outf(filename);
|
||||
std::ostream& out = outf.ostream();
|
||||
|
||||
unsigned ntools = tools.size();
|
||||
assert(round_num == output_statistics.size());
|
||||
assert(round_num == input_statistics.size());
|
||||
|
||||
if (!outf.append())
|
||||
{
|
||||
// Do not output the header line if we append to a file.
|
||||
// (Even if that file was empty initially.)
|
||||
in_statistics::fields(out);
|
||||
out << ",\"tool\",";
|
||||
out_statistics::fields(out);
|
||||
out << '\n';
|
||||
}
|
||||
for (unsigned r = 0; r < round_num; ++r)
|
||||
for (unsigned t = 0; t < ntools; ++t)
|
||||
if (!opt_omit || output_statistics[r][t].ok)
|
||||
{
|
||||
input_statistics[r].to_csv(out);
|
||||
out << ",\"";
|
||||
spot::escape_rfc4180(out, tools[t].name);
|
||||
out << "\",";
|
||||
output_statistics[r][t].to_csv(out);
|
||||
out << '\n';
|
||||
}
|
||||
outf.close(filename);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
check_no_automaton();
|
||||
|
||||
auto s = tools.size();
|
||||
if (s == 0)
|
||||
error(2, 0, "No tool to run? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
if (s == 1 && !opt_language_preserved && !no_checks)
|
||||
error(2, 0, "Since --language-preserved is not used, you need "
|
||||
"at least two tools to compare.");
|
||||
|
||||
|
||||
setup_color();
|
||||
setup_sig_handler();
|
||||
|
||||
autcross_processor p;
|
||||
if (p.run())
|
||||
return 2;
|
||||
|
||||
if (round_num == 0)
|
||||
{
|
||||
error(2, 0, "no automaton to translate");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (global_error_flag)
|
||||
{
|
||||
std::ostream& err = global_error();
|
||||
if (bogus_output)
|
||||
err << ("error: some error was detected during the above runs.\n"
|
||||
" Check file ")
|
||||
<< bogus_output_filename
|
||||
<< " for problematic automata.";
|
||||
else
|
||||
err << ("error: some error was detected during the above runs,\n"
|
||||
" please search for 'error:' messages in the above"
|
||||
" trace.");
|
||||
err << std::endl;
|
||||
end_error();
|
||||
}
|
||||
else if (timeout_count == 0 && ignored_exec_fail == 0)
|
||||
{
|
||||
std::cerr << "No problem detected." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "No major problem detected." << std::endl;
|
||||
}
|
||||
|
||||
unsigned additional_errors = 0U;
|
||||
additional_errors += timeout_count > 0;
|
||||
additional_errors += ignored_exec_fail > 0;
|
||||
if (additional_errors)
|
||||
{
|
||||
std::cerr << (global_error_flag ? "Additionally, " : "However, ");
|
||||
if (timeout_count)
|
||||
{
|
||||
if (additional_errors > 1)
|
||||
std::cerr << "\n - ";
|
||||
if (timeout_count == 1)
|
||||
std::cerr << "1 timeout occurred";
|
||||
else
|
||||
std::cerr << timeout_count << " timeouts occurred";
|
||||
}
|
||||
|
||||
if (ignored_exec_fail)
|
||||
{
|
||||
if (additional_errors > 1)
|
||||
std::cerr << "\n - ";
|
||||
if (ignored_exec_fail == 1)
|
||||
std::cerr << "1 non-zero exit status was ignored";
|
||||
else
|
||||
std::cerr << ignored_exec_fail
|
||||
<< " non-zero exit statuses were ignored";
|
||||
}
|
||||
if (additional_errors == 1)
|
||||
std::cerr << '.';
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (csv_output)
|
||||
print_stats_csv(csv_output);
|
||||
|
||||
return global_error_flag;
|
||||
}
|
||||
|
|
@ -515,7 +515,7 @@ static bool opt_highlight_languages = false;
|
|||
static spot::twa_graph_ptr
|
||||
ensure_deterministic(const spot::twa_graph_ptr& aut, bool nonalt = false)
|
||||
{
|
||||
if ((!nonalt || aut->is_existential()) && spot::is_deterministic(aut))
|
||||
if ((!nonalt || aut->is_existential()) && spot::is_universal(aut))
|
||||
return aut;
|
||||
spot::postprocessor p;
|
||||
p.set_type(spot::postprocessor::Generic);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ read_automaton(const char* filename, spot::bdd_dict_ptr& dict);
|
|||
|
||||
class hoa_processor: public job_processor
|
||||
{
|
||||
protected:
|
||||
spot::bdd_dict_ptr dict_;
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -33,14 +33,17 @@
|
|||
#include <spot/tl/unabbrev.hh>
|
||||
#include "common_conv.hh"
|
||||
#include <spot/misc/escape.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
|
||||
// A set of tools for which we know the correct output
|
||||
static struct shorthands_t
|
||||
struct shorthands_t
|
||||
{
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
}
|
||||
shorthands[] = {
|
||||
};
|
||||
static shorthands_t shorthands_ltl[] = {
|
||||
{ "lbt", " <%L>%O" },
|
||||
{ "ltl2ba", " -f %s>%O" },
|
||||
{ "ltl2da", " %f>%O" },
|
||||
|
|
@ -55,25 +58,30 @@ static struct shorthands_t
|
|||
{ "spin", " -f %s>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands()
|
||||
static shorthands_t shorthands_autproc[] = {
|
||||
{ "autfilt", " %H>%O" },
|
||||
{ "dstar2tgba", " %H>%O" },
|
||||
{ "ltl2dstar", " -B %H %O" },
|
||||
{ "nba2ldpa", " <%H>%O" },
|
||||
{ "seminator", " %H>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands(shorthands_t* begin, shorthands_t* end)
|
||||
{
|
||||
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 %[MR]L %O'\n");
|
||||
while (begin != end)
|
||||
{
|
||||
std::cout << " "
|
||||
<< std::left << std::setw(12) << begin->prefix
|
||||
<< begin->suffix << '\n';
|
||||
++begin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tool_spec::tool_spec(const char* spec)
|
||||
tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end)
|
||||
: spec(spec), cmd(spec), name(spec)
|
||||
{
|
||||
if (*cmd == '{')
|
||||
|
|
@ -114,8 +122,9 @@ tool_spec::tool_spec(const char* spec)
|
|||
++pos;
|
||||
}
|
||||
// Match a shorthand.
|
||||
for (auto& p: shorthands)
|
||||
while (begin != end)
|
||||
{
|
||||
auto& p = *begin++;
|
||||
int n = strlen(p.prefix);
|
||||
if (strncmp(basename, p.prefix, n) == 0)
|
||||
{
|
||||
|
|
@ -165,6 +174,20 @@ tool_spec::~tool_spec()
|
|||
|
||||
std::vector<tool_spec> tools;
|
||||
|
||||
void tools_push_trans(const char* trans)
|
||||
{
|
||||
tools.emplace_back(trans,
|
||||
std::begin(shorthands_ltl),
|
||||
std::end(shorthands_ltl));
|
||||
}
|
||||
|
||||
void tools_push_autproc(const char* proc)
|
||||
{
|
||||
tools.emplace_back(proc,
|
||||
std::begin(shorthands_autproc),
|
||||
std::end(shorthands_autproc));
|
||||
}
|
||||
|
||||
void
|
||||
quoted_string::print(std::ostream& os, const char*) const
|
||||
{
|
||||
|
|
@ -254,16 +277,8 @@ printable_result_filename::print(std::ostream& os, const char*) const
|
|||
spot::quote_shell_string(os, val()->name());
|
||||
}
|
||||
|
||||
void
|
||||
filed_formula::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
f_.print(ss, pos);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
std::string
|
||||
filed_formula::string_to_tmp(const std::string str, unsigned n) const
|
||||
static std::string
|
||||
string_to_tmp(const std::string str, unsigned n)
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
|
||||
|
|
@ -278,6 +293,43 @@ filed_formula::string_to_tmp(const std::string str, unsigned n) const
|
|||
return tmpname;
|
||||
}
|
||||
|
||||
void
|
||||
filed_formula::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
f_.print(ss, pos);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
void
|
||||
filed_automaton::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
char* opt = nullptr;
|
||||
if (*pos == '[')
|
||||
{
|
||||
++pos;
|
||||
auto end = strchr(pos, ']');
|
||||
opt = strndup(pos, end - pos);
|
||||
pos = end + 1;
|
||||
}
|
||||
switch (*pos)
|
||||
{
|
||||
case 'H':
|
||||
spot::print_hoa(ss, aut_, opt);
|
||||
break;
|
||||
case 'S':
|
||||
spot::print_never_claim(ss, aut_, opt);
|
||||
break;
|
||||
case 'L':
|
||||
spot::print_lbtt(ss, aut_, opt);
|
||||
break;
|
||||
}
|
||||
if (opt)
|
||||
free(opt);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
|
||||
bool no_output_allowed)
|
||||
: dict(dict)
|
||||
|
|
@ -345,6 +397,42 @@ translator_runner::round_formula(spot::formula f, unsigned serial)
|
|||
filename_formula.new_round(serial);
|
||||
}
|
||||
|
||||
|
||||
autproc_runner::autproc_runner(bool no_output_allowed)
|
||||
{
|
||||
declare('H', &filename_automaton);
|
||||
declare('S', &filename_automaton);
|
||||
declare('L', &filename_automaton);
|
||||
declare('O', &output);
|
||||
|
||||
size_t s = tools.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 tool_spec& t = tools[n];
|
||||
scan(t.cmd, has);
|
||||
if (!(has['H'] || has['S'] || has['L']))
|
||||
error(2, 0, "no input %%-sequence in '%s'.\n Use "
|
||||
"one of %%H,%%S,%%L to indicate the input automaton filename.",
|
||||
t.spec);
|
||||
if (!no_output_allowed && !has['O'])
|
||||
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 tools.
|
||||
prime(t.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
autproc_runner::round_automaton(spot::const_twa_graph_ptr aut, unsigned serial)
|
||||
{
|
||||
filename_automaton.new_round(aut, serial);
|
||||
}
|
||||
|
||||
volatile bool timed_out = false;
|
||||
unsigned timeout_count = 0;
|
||||
|
||||
|
|
@ -553,7 +641,7 @@ exec_command(const char* cmd)
|
|||
int fd0 = open(stdin, O_RDONLY, 0644);
|
||||
if (fd0 < 0)
|
||||
error(2, errno, "failed to open '%s'", stdin);
|
||||
if (dup2(fd0, 0) < 0)
|
||||
if (dup2(fd0, STDIN_FILENO) < 0)
|
||||
error(2, errno, "dup2() failed");
|
||||
if (close(fd0) < 0)
|
||||
error(2, errno, "close() failed");
|
||||
|
|
@ -563,7 +651,7 @@ exec_command(const char* cmd)
|
|||
int fd1 = creat(stdout, 0644);
|
||||
if (fd1 < 0)
|
||||
error(2, errno, "failed to open '%s'", stdout);
|
||||
if (dup2(fd1, 1) < 0)
|
||||
if (dup2(fd1, STDOUT_FILENO) < 0)
|
||||
error(2, errno, "dup2() failed");
|
||||
if (close(fd1) < 0)
|
||||
error(2, errno, "close() failed");
|
||||
|
|
@ -602,6 +690,11 @@ exec_with_timeout(const char* cmd)
|
|||
if (child_pid == 0)
|
||||
{
|
||||
setpgid(0, 0);
|
||||
// Close stdin so that children may not read our input. We had
|
||||
// this nice surprise with Seminator, who greedily consumes its
|
||||
// stdin (which was also ours) even if it does not use it
|
||||
// because it was given a file.
|
||||
close(STDIN_FILENO);
|
||||
exec_command(cmd);
|
||||
// never reached
|
||||
return -1;
|
||||
|
|
@ -675,7 +768,7 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
case 'T':
|
||||
timeout = to_pos_int(arg);
|
||||
|
|
@ -685,7 +778,14 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
#endif
|
||||
break;
|
||||
case OPT_LIST:
|
||||
show_shorthands();
|
||||
show_shorthands(std::begin(shorthands_ltl), std::end(shorthands_ltl));
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose "
|
||||
"of\nmatching those prefixes. So for instance\n "
|
||||
"'{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
|
||||
"will be changed into\n "
|
||||
"'{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %[MW]L %O'"
|
||||
"\n");
|
||||
exit(0);
|
||||
case OPT_RELABEL:
|
||||
opt_relabel = true;
|
||||
|
|
@ -698,3 +798,61 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
|
||||
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
|
||||
|
||||
static const argp_option options_aut[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Specifying tools to call:", 2 },
|
||||
{ "tool", 't', "COMMANDFMT", 0,
|
||||
"register one tool to call", 0 },
|
||||
{ "timeout", 'T', "NUMBER", 0, "kill tools 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 },
|
||||
{ "%H,%S,%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"filename for the input automaton in (H) HOA, (S) Spin's neverclaim, "
|
||||
"or (L) LBTT's format", 0 },
|
||||
{ "%O", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"filename for the automaton 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, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int parse_opt_autproc(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
tools_push_autproc(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(std::begin(shorthands_autproc),
|
||||
std::end(shorthands_autproc));
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose "
|
||||
"of\nmatching those prefixes. So for instance\n "
|
||||
"'{AF} ~/mytools/autfilt-2.4 --remove-fin'\n"
|
||||
"will be changed into\n "
|
||||
"'{AF} ~/mytools/autfilt-2.4 --remove-fin %H>%O'\n");
|
||||
exit(0);
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp autproc_argp = { options_aut, parse_opt_autproc, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr };
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@
|
|||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
|
||||
extern const struct argp trans_argp;
|
||||
extern const struct argp trans_argp; // ltlcross, ltldo
|
||||
extern const struct argp autproc_argp; // autcross
|
||||
|
||||
extern bool opt_relabel;
|
||||
|
||||
struct shorthands_t;
|
||||
|
||||
struct tool_spec
|
||||
{
|
||||
// The translator command, as specified on the command-line.
|
||||
|
|
@ -44,7 +48,7 @@ struct tool_spec
|
|||
// name of the translator (or spec)
|
||||
const char* name;
|
||||
|
||||
tool_spec(const char* spec);
|
||||
tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end);
|
||||
tool_spec(const tool_spec& other);
|
||||
tool_spec& operator=(const tool_spec& other);
|
||||
~tool_spec();
|
||||
|
|
@ -52,6 +56,9 @@ struct tool_spec
|
|||
|
||||
extern std::vector<tool_spec> tools;
|
||||
|
||||
void tools_push_trans(const char* trans);
|
||||
void tools_push_autproc(const char* proc);
|
||||
|
||||
struct quoted_string final: public spot::printable_value<std::string>
|
||||
{
|
||||
using spot::printable_value<std::string>::operator=;
|
||||
|
|
@ -80,7 +87,25 @@ struct filed_formula final: public spot::printable
|
|||
private:
|
||||
const quoted_formula& f_;
|
||||
unsigned serial_;
|
||||
std::string string_to_tmp(const std::string str, unsigned n) const;
|
||||
};
|
||||
|
||||
struct filed_automaton final: public spot::printable
|
||||
{
|
||||
filed_automaton()
|
||||
{
|
||||
}
|
||||
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
|
||||
void new_round(spot::const_twa_graph_ptr aut, unsigned serial)
|
||||
{
|
||||
aut_ = aut;
|
||||
serial_ = serial;
|
||||
}
|
||||
|
||||
private:
|
||||
spot::const_twa_graph_ptr aut_;
|
||||
unsigned serial_;
|
||||
};
|
||||
|
||||
struct printable_result_filename final:
|
||||
|
|
@ -118,6 +143,23 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class autproc_runner: protected spot::formater
|
||||
{
|
||||
protected:
|
||||
// Round-specific variables
|
||||
filed_automaton filename_automaton;
|
||||
// Run-specific variables
|
||||
printable_result_filename output;
|
||||
public:
|
||||
using spot::formater::has;
|
||||
|
||||
autproc_runner(// whether we accept the absence of output
|
||||
// specifier
|
||||
bool no_output_allowed = false);
|
||||
void round_automaton(spot::const_twa_graph_ptr aut, unsigned serial);
|
||||
};
|
||||
|
||||
|
||||
// Disable handling of timeout on systems that miss kill() or alarm().
|
||||
// For instance MinGW.
|
||||
#if HAVE_KILL && HAVE_ALARM
|
||||
|
|
|
|||
|
|
@ -219,7 +219,6 @@ example()
|
|||
return std::cerr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
end_error()
|
||||
{
|
||||
|
|
@ -423,7 +422,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
case OPT_AUTOMATA:
|
||||
opt_automata = true;
|
||||
|
|
@ -1111,7 +1110,7 @@ namespace
|
|||
std::vector<spot::twa_graph_ptr>& comp,
|
||||
unsigned i)
|
||||
{
|
||||
if (!no_complement && x[i] && is_deterministic(x[i]))
|
||||
if (!no_complement && x[i] && is_universal(x[i]))
|
||||
comp[i] = dualize(x[i]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ convman7 = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
|||
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N"
|
||||
|
||||
dist_man1_MANS = \
|
||||
autcross.1 \
|
||||
autfilt.1 \
|
||||
dstar2tgba.1 \
|
||||
genaut.1 \
|
||||
|
|
@ -44,6 +45,9 @@ dist_man7_MANS = \
|
|||
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
||||
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
|
||||
|
||||
autcross.1: $(common_dep) $(srcdir)/autcross.x $(srcdir)/../autcross.cc
|
||||
$(convman) ../autcross$(EXEEXT) $(srcdir)/autcross.x $@
|
||||
|
||||
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
|
||||
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
|
||||
|
||||
|
|
|
|||
97
bin/man/autcross.x
Normal file
97
bin/man/autcross.x
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
autcross \- cross-compare tools that process automata
|
||||
|
||||
[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 files.
|
||||
.TP
|
||||
\fBinput.source\fR
|
||||
Location of the input automaton fed to the tool.
|
||||
.TP
|
||||
\fBinput.name\fR
|
||||
Name of the input automaton, if any. This is supported
|
||||
by the HOA format.
|
||||
.TP
|
||||
\fBinput.ap\fR,\fBoutput.ap\fR,
|
||||
Number of atomic proposition in the input and output automata.
|
||||
.TP
|
||||
\fBinput.states\fR,\fBoutput.states\fR
|
||||
Number of states in the input and output automata.
|
||||
.TP
|
||||
\fBinput.edges\fR,\fBoutput.edges\fR
|
||||
Number of edges in the input and output automata.
|
||||
.TP
|
||||
\fBinput.transitions\fR,\fBoutput.transitions\fR
|
||||
Number of transitions in the input and output automata.
|
||||
.TP
|
||||
\fBinput.acc_sets\fR,\fBoutput.acc_sets\fR
|
||||
Number of acceptance sets in the input and output automata.
|
||||
.TP
|
||||
\fBinput.scc\fR,\fBoutput.scc\fR
|
||||
Number of strongly connected components in the input and output automata.
|
||||
.TP
|
||||
\fBinput.nondetstates\fR,\fBoutput.nondetstates\fR
|
||||
Number of nondeterministic states in the input and output automata.
|
||||
.TP
|
||||
\fBinput.nondeterministic\fR,\fBoutput.nondetstates\fR
|
||||
1 if the automaton is nondeterministic, 0 if it is deterministic.
|
||||
.TP
|
||||
\fBinput.alternating\fR,\fBoutput.alternating\fR
|
||||
1 if the automaton has some universal branching, 0 otherwise.
|
||||
|
||||
\fBexit_status\fR, \fBexit_code\fR
|
||||
Information about how the execution of the tool went.
|
||||
\fBexit_status\fR is a string that can take the following
|
||||
values:
|
||||
.RS
|
||||
.TP
|
||||
\f(CW"ok"\fR
|
||||
The tool ran succesfully (this does not imply that the produced
|
||||
automaton is correct) and autcross could parse the resulting
|
||||
automaton. In this case \fBexit_code\fR is always 0.
|
||||
.TP
|
||||
\f(CW"timeout"\fR
|
||||
The tool 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 tool terminated with a non-zero exit code.
|
||||
\fBexit_code\fR contains that value.
|
||||
.TP
|
||||
\f(CW"signal"\fR
|
||||
The tool terminated with a signal.
|
||||
\fBexit_code\fR contains that signal's number.
|
||||
.TP
|
||||
\f(CW"parse error"\fR
|
||||
The tool terminated normally, but autcross could not
|
||||
parse its output. In this case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"no output"\fR
|
||||
The tool 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 tool in seconds.
|
||||
This is reported for all executions, even failling ones.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR randaut (1),
|
||||
.BR genaut (1),
|
||||
.BR autfilt (1),
|
||||
.BR ltlcross (1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue