main.cc 10.5 KB
Newer Older
1
2
3
4
// main.cc: this file is part of the Vaucanson project.
//
// Vaucanson, a generic library for finite state machines.
//
5
// Copyright (C) 2006, 2007, 2008 The Vaucanson Group.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// The complete GNU General Public Licence Notice can be found as the
// `COPYING' file in the root directory.
//
// The Vaucanson Group consists of people listed in the `AUTHORS' file.
//

/**
 * @file main.cc
 * @author Michal Cadilhac <michael.cadilhac@lrde.org>
21
 *         Jerome Galtier <jerome.galtier@lrde.epita.fr>
22
23
24
25
 *
 * Main file, common to all TAF-Kit binaries.
 */

26
#include <stdlib.h>
27
28
#include <string>
#include <iostream>
29
#include <fstream>
30
31
#include <stdexcept>

Reuben Thomas's avatar
Reuben Thomas committed
32
#include "argp.h"
33
#include "progname.h"
Reuben Thomas's avatar
Reuben Thomas committed
34

35
36
37
#include "common.hh"
#include "predefined_alphabets.hh"
#include "commands.hh"
38
#include "interface.hh"
39
#include "parser_options.hh"
40

41

42
43
44
/**
 * Base info for the program.
 */
45
const char* argp_program_version =
46
47
"VCSN TAF-Kit (" PACKAGE_STRING ")\n"
"\n"
48
"Copyright (C) 2006, 2007, 2008 The Vaucanson Group.\n"
49
50
51
"This is free software; see the source for copying conditions.  There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n"
"to the extent permitted by law.";
52

Akim Demaille's avatar
Akim Demaille committed
53
const char* argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
54

55
/// A global timer.
56
vcsn::misc::Timer global_timer;
Akim Demaille's avatar
Akim Demaille committed
57

58
59
60
/// A global bencher.
vcsn::misc::Bencher bencher;

Florent D'Halluin's avatar
Florent D'Halluin committed
61
62
// A global command output;
command_output last_command_output;
63
#include "pipe_command_sequence.hh"
Florent D'Halluin's avatar
Florent D'Halluin committed
64
65

// Writer for final output
66
#include "pipe_writers.hh"
Florent D'Halluin's avatar
Florent D'Halluin committed
67

68
69
70
71
/**
 * Documentation of the program, of the arguments we accept and the
 * options we understand.
 */
Akim Demaille's avatar
Akim Demaille committed
72
73
74
75
76
77
78
79
80
namespace
{
  const char doc[] = "VCSN TAF-Kit -- a toolkit for working with automata";
  const char args_doc[] = "<command> <args...>";
  const argp_option options[] = {
    {"list-commands",	'l', 0, 0,
     "List the commands handled by the program", 0 },
    { "verbose",		'v', 0, 0,
      "Be more verbose (print boolean results)", 0 },
81
    { "report-time",	'T', "VERBOSE_DEGREE", OPTION_ARG_OPTIONAL,
Akim Demaille's avatar
Akim Demaille committed
82
      "Report time statistics", 0 },
83
84
85
86
    { "export-time-dot", 'D', "VERBOSE_DEGREE", OPTION_ARG_OPTIONAL,
      "Export time statistics in DOT format", 0 },
    { "export-time-xml", 'X', 0, 0,
      "Export time statistics in XML format", 0 },
87
88
89
90
    { "bench",	'B', "NB_ITERATIONS", 0,
      "Bench", 0 },
    { "bench-plot-output", 'O', "OUTPUT_FILENAME", 0,
      "Bench output filename", 0 },
91

92
93
94
95
96
    { "input-type", 'i', "INPUT_TYPE", 0,
      "Automaton input type (FSM or XML)", 0 },
    { "output-type", 'o', "OUTPUT_TYPE", 0,
      "Automaton input type (FSM, XML or DOT)", 0 },

97
#ifdef WITH_TWO_ALPHABETS
Akim Demaille's avatar
Akim Demaille committed
98
    { "alphabet1",	'a', "ALPHABET", 0,
99
      "Set the first alphabet for rational expressions or automata", 0 },
Akim Demaille's avatar
Akim Demaille committed
100
    { "alphabet2",	'A', "ALPHABET", 0,
101
      "Set the second alphabet for rational expressions or automata", 0 },
102
    { "parser1",	'p', "OPTIONS", 0,
103
      "Set the first parsing options for rational expressions", 0 },
104
    { "parser2",	'P', "OPTIONS", 0,
105
      "Set the second parsing options for rational expressions", 0 },
106
#else /* ! WITH_TWO_ALPHABETS */
Akim Demaille's avatar
Akim Demaille committed
107
    { "alphabet",		'a', "ALPHABET", 0,
108
      "Set the alphabet for rational expressions or automata", 0 },
109
    { "parser",			'p', "OPTIONS", 0,
110
      "Set the parsing options for rational expressions", 0 },
111
112
#endif /* ! WITH_TWO_ALPHABETS */

113
#ifndef NO_PREDEF_ALPHABETS
Akim Demaille's avatar
Akim Demaille committed
114
    { 0, 0, 0, 0, "The following alphabets are predefined:\n"
115
116
      "	 `letters': Use [a-z] as the alphabet, " DEFAULT_EPSILON " as epsilon\n"
      "	 `alpha': Use [a-zA-Z] as the alphabet, " DEFAULT_EPSILON " as epsilon\n"
117
      "	 `digits': Use [0-9] as the alphabet, " DEFAULT_EPSILON " as epsilon\n"
118
      "	 `ascii': Use ascii characters as the alphabet, " DEFAULT_EPSILON " as epsilon\n", 0},
119
#endif
120

Akim Demaille's avatar
Akim Demaille committed
121
122
    { 0, 0, 0, 0, 0, 0 }
  };
123

124
#ifndef NO_PREDEF_ALPHABETS
Akim Demaille's avatar
Akim Demaille committed
125
126
  const struct alphabet
  {
127
128
    const char*	name;
    const char*	alphabet;
129
130
131
132
133
    const char*	epsilon;
  } predefined_alphabets[] = { { "letters", ALPHABET_AZ, DEFAULT_EPSILON },
			       { "alpha", ALPHABET_AZAZ, DEFAULT_EPSILON },
			       { "digits", ALPHABET_DIGITS, DEFAULT_EPSILON },
			       { "ascii", ALPHABET_ASCII, DEFAULT_EPSILON },
Akim Demaille's avatar
Akim Demaille committed
134
			       { 0, 0, 0 } };
135
#endif
Akim Demaille's avatar
Akim Demaille committed
136
137

  error_t parse_opt (int key, char* arg, argp_state* state)
138
  {
139
    bool found = false;
Akim Demaille's avatar
Akim Demaille committed
140
141
142
143
144
145
    arguments_t& args = *(static_cast <arguments_t*> (state->input));

    switch (key)
    {
      case 'a':
      case 'A':
146
#ifndef NO_PREDEF_ALPHABETS
Akim Demaille's avatar
Akim Demaille committed
147
	for (const alphabet* alpha = predefined_alphabets; alpha->name; ++alpha)
148
	{
Akim Demaille's avatar
Akim Demaille committed
149
	  if (std::string (alpha->name) == arg)
150
	  {
Akim Demaille's avatar
Akim Demaille committed
151
152
	    if (key == 'a')
	    {
153
154
	      args.add_parser_option("ALPHABET", alpha->alphabet);
	      args.add_parser_option("ONE", alpha->epsilon);
Akim Demaille's avatar
Akim Demaille committed
155
	    }
156
#ifdef WITH_TWO_ALPHABETS
Akim Demaille's avatar
Akim Demaille committed
157
158
	    else
	    {
159
160
	      args.add_parser2_option("ALPHABET", alpha->alphabet);
	      args.add_parser2_option("ONE", alpha->epsilon);
Akim Demaille's avatar
Akim Demaille committed
161
	    }
162
#endif /* ! WITH_TWO_ALPHABETS */
163
	    found = true;
Akim Demaille's avatar
Akim Demaille committed
164
165
	    break;
	  }
166
	}
167
#endif
168
169
170
171
172
173
174
175
176
177
178
179
	if (!found)
	{
	  if (key == 'a')
	    args.add_parser_option("ALPHABET", arg);
#ifdef WITH_TWO_ALPHABETS
	  else
	    args.add_parser2_option("ALPHABET", arg);
#endif /* ! WITH_TWO_ALPHABETS */
	}
	break;
      case 'p':
	args.add_parser_option(NULL, arg);
Akim Demaille's avatar
Akim Demaille committed
180
	break;
181
182
183
#ifdef WITH_TWO_ALPHABETS
      case 'P':
	args.add_parser2_option(NULL, arg);
Akim Demaille's avatar
Akim Demaille committed
184
	break;
185
#endif /* ! WITH_TWO_ALPHABETS */
Akim Demaille's avatar
Akim Demaille committed
186
187
188
189
190
191
      case 'l':
	list_commands ();
	exit (0);
	break;
      case 'T':
	args.report_time = true;
192
193
194
195
196
197
198
199
	args.report_degree = arg ? atoi(arg) : 2;
	break;
      case 'D':
	args.export_time_dot = true;
	args.export_dot_degree = arg ? atoi(arg) : 2;
	break;
      case 'X':
	args.export_time_xml = true;
Akim Demaille's avatar
Akim Demaille committed
200
	break;
201
202
203

      case 'B':
	args.bench = true;
204
	args.nb_iterations = atoi(arg);
205
206
207
208
209
210
	break;

      case 'O':
	args.plot_output_filename = arg;
	break;

211
212
213
214
215
216
217
218
219
220
221
222
223
224
      case 'i':
	if (std::string (arg).compare ("XML") == 0)
	  args.input_type = INPUT_TYPE_XML;
	else if (std::string (arg).compare ("FSM") == 0)
	  args.input_type = INPUT_TYPE_FSM;
	else
	{
	  std::cerr << "Unknown input type: " << arg << std::endl;
	  return ARGP_ERR_UNKNOWN;
	}
	break;

      case 'o':
	if (std::string (arg).compare ("XML") == 0)
225
226
227
228
	{
	  args.output_aut_type = OUTPUT_TYPE_XML;
	  args.output_exp_type = OUTPUT_TYPE_XML;
	}
229
	else if (std::string (arg).compare ("FSM") == 0)
230
231
232
233
	{
	  args.output_aut_type = OUTPUT_TYPE_FSM;
	  args.output_exp_type = OUTPUT_TYPE_FSM;
	}
234
	else if (std::string (arg).compare ("DOT") == 0)
235
236
237
238
	{
	  args.output_aut_type = OUTPUT_TYPE_DOT;
	  args.output_exp_type = OUTPUT_TYPE_DOT;
	}
239
240
241
242
243
244
245
246
	else
	{
	  std::cerr << "Unknown output type: " << arg << std::endl;
	  return ARGP_ERR_UNKNOWN;
	}
	break;


Akim Demaille's avatar
Akim Demaille committed
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
      case 'v':
	args.verbose = true;
	break;
      case ARGP_KEY_ARG:
	if (state->arg_num < 3)
	  args.args[state->arg_num] = arg;
	++args.n_args;
	break;
      case ARGP_KEY_END:
	if (state->arg_num < 1)
	  argp_usage (state);
	break;
      default:
	return ARGP_ERR_UNKNOWN;
    }
    return 0;
263
264
  }

Akim Demaille's avatar
Akim Demaille committed
265
266
267
268
  const argp argp_setup = { options, parse_opt, args_doc, doc, 0, 0, 0 };

} // anonymous namespace

269
270
271
# include <vaucanson/misc/bencher.hh>

using namespace vcsn::misc;
272
273
274

int main (int argc, char* argv[])
{
275
276
  set_program_name(argv[0]);

Florent D'Halluin's avatar
Florent D'Halluin committed
277
  std::list<pipe_command> command_list;
Akim Demaille's avatar
Akim Demaille committed
278

Florent D'Halluin's avatar
Florent D'Halluin committed
279
280
281
282
283
284
285
286
287
288
289
290
  // Cut the command line
  int i = 0;
  int j = 0;
  for (; i < argc; ++i)
    {
      if (std::string ("|").compare(argv[i]) == 0)
	{
	  command_list.push_back (pipe_command (argv, j, i));
	  j = i;
	}
    }
  command_list.push_back (pipe_command (argv, j, i));
Robert Bigaignon's avatar
Robert Bigaignon committed
291

Florent D'Halluin's avatar
Florent D'Halluin committed
292
293
294
295
296
297
298
299
300
  GLOBAL_RESULT.set_state (PIPE_GET_FROM_STDIN);

  arguments_t args ("");

  // Parse each command
  for (std::list<pipe_command>::iterator li = command_list.begin ();
       li != command_list.end (); ++li)
    {
      argp_parse (&argp_setup, li->length, li->arg, 0, 0, &(li->args));
301

302
303
304
305
      try
      {
	parser_options p_opts(li->args.parser);

306
	p_opts.check_collision();
307
	li->args.alphabet = p_opts.get_letters();
308
	li->args.srep = p_opts.get_srep();
309
	li->args.mrep = p_opts.get_mrep();
Florent D'Halluin's avatar
Florent D'Halluin committed
310

311
#ifdef WITH_TWO_ALPHABETS
312
	parser_options p_opts2(li->args.parser2);
313

314
	li->args.alphabet2 = p_opts2.get_letters();
315
#endif
316
317
318
319
320
      }
      catch (const std::logic_error& err)
      {
	warn (argv[0] << ": " << err.what ());
      }
321

Florent D'Halluin's avatar
Florent D'Halluin committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
      if (li->args.bench)
	{
	  args.bench = li->args.bench;
	  args.nb_iterations = li->args.nb_iterations;
	}
      if (li->args.report_time)
	{
	  args.report_time = li->args.report_time;
	  args.report_degree = li->args.report_degree;
	}
      if (li->args.export_time_dot)
	{
	  args.export_time_dot = li->args.export_time_dot;
	  args.export_dot_degree = li->args.export_dot_degree;
	}
      args.export_time_xml = args.export_time_xml || li->args.export_time_xml;
      if (!li->args.plot_output_filename.empty ())
	args.plot_output_filename = li->args.plot_output_filename;
340
    }
Florent D'Halluin's avatar
Florent D'Halluin committed
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357

  BENCH_DO(args.nb_iterations)
    {
      int task_number = 0;

      TIMER_START ();

      // Execute commands
      for (std::list<pipe_command>::iterator li = command_list.begin ();
	   li != command_list.end (); ++li, ++task_number)
	{
	  arguments_t& args = li->args;

	  int& status = li->status;

	  try
	    {
358
	      GLOBAL_RESULT.clear ();
359
	      GLOBAL_RESULT.set_name (args.args[0]);
360
361
	      GLOBAL_RESULT.output_aut_type = args.output_aut_type;
	      GLOBAL_RESULT.output_exp_type = args.output_exp_type;
362
	      GLOBAL_RESULT.input_type = args.input_type;
Florent D'Halluin's avatar
Florent D'Halluin committed
363
364
365
366
367
368
369
370
371
372
	      std::ostringstream os;
	      os << "CMD[" << task_number << "]: ";
	      TIMER_SCOPED(os.str () + std::string (args.args[0]));
	      status = execute_command (args);
	    }
	  catch (const std::logic_error& err) {
	    warn (argv[0] << ": " << err.what ());
	    status = -1;
	  }

373
	  GLOBAL_RESULT.status = status;
Florent D'Halluin's avatar
Florent D'Halluin committed
374
375
376
377
378
	  // Break upon error
	  if (status != 0)
	    break;
	}

379
      if (!GLOBAL_RESULT.empty)
380
	boost::apply_visitor (pipe_stream_writer (std::cout,
381
382
						  GLOBAL_RESULT.output_aut_type,
						  GLOBAL_RESULT.output_exp_type),
383
			      GLOBAL_RESULT.output);
Florent D'Halluin's avatar
Florent D'Halluin committed
384
385
386
387
388
389
390
391
392
393
394
395
396
397

      TIMER_STOP ();

      if (args.report_time)
	TIMER_PRINT_VD(std::cerr,
		       timer::get_verbose_degree (args.report_degree));
      if (args.export_time_dot)
	TIMER_EXPORT_DOT_VD(std::cerr,
			    timer::get_verbose_degree
			(args.export_dot_degree));
      if (args.export_time_xml)
	TIMER_DUMP(std::cerr);

      GLOBAL_RESULT.set_state (PIPE_BENCH);
398
    }
399
400

  if (args.bench)
401
    BENCH_PRINT(std::cerr);
402
403
  if (!args.plot_output_filename.empty())
    BENCH_SAVE_PLOT(args.plot_output_filename.c_str());
Florent D'Halluin's avatar
Florent D'Halluin committed
404
405

  return GLOBAL_RESULT.status;
406
}