Options & argumentsΒΆ

This example shows how to parse command-line options with possible arguments, where each individual option may need its own argument format. Our parser handles errors and typos: for known options it reports ill-formed arguments; for unrecognized options it collects them and reports at the end of parsing. Our imaginary options are summarized below:

  • -v, --verbose
  • -l LIMIT, --limit=LIMIT, where LIMIT is a decimal number followed by one of the suffixes B, K, M or G
  • -d DATE, --date=DATE, where DATE has the form DD/MM/YYYY
  • -p PATH, --path=PATH, where PATH is a /-separated file path
  • -f FORMAT, --format=FORMAT, where FORMAT is a double-quoted format string

[options.re]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <string>
#include <vector>

typedef std::vector<std::pair<std::string, std::string> > unknown_t;

struct options_t
{
    std::string date;
    std::string path;
    std::string format;
    std::string limit;
    bool verbose;
};

static void show(const options_t &o, const unknown_t &u)
{
    printf("\noptions:\n");
    printf("  date:    %s\n", o.date.c_str());
    printf("  path:    %s\n", o.path.c_str());
    printf("  format:  %s\n", o.format.c_str());
    printf("  limit:   %s\n", o.limit.c_str());
    printf("  verbose: %s\n", o.verbose ? "yes" : "no");

    printf("\nunknown:\n");
    unknown_t::const_iterator i = u.begin(), e = u.end();
    for (; i != e; ++i) {
        printf("  %s: '%s'\n", i->first.c_str(), i->second.c_str());
    }
}

static void bad_arg(const char *k, const char *v, const char *e)
{
    printf("bad argument '%.*s' to option %.*s\n",
        (int) (e - v), v, (int) (v - k), k);
}

static int lex(const char *s)
{
    options_t o;
    unknown_t u;
    const char *m, *k, *v;
    /*!stags:re2c format = 'const char *@@;'; */
loop:
    /*!re2c
        re2c:define:YYCTYPE = char;
        re2c:define:YYCURSOR = s;
        re2c:define:YYMARKER = m;
        re2c:yyfill:enable = 0;

        end    = "\x00";
        sp     = [ \t\n\r];
        eq     = "=";
        wsp    = sp*;
        char   = [^=] \ end;
        ochar  = char \ sp;
        pchar  = ochar \ [/];
        str    = ["] (char \ ["] | [\]["])* ["];
        opt    = ochar+;
        arg    = ochar* | str;
        date   = [0-9]{2} "/" [0-9]{2} "/" [0-9]{4};
        path   = pchar* ("/" pchar*)*;
        format = str;
        limit  = [0-9]+ [BKMG]?;

        *   { printf("error: %s\n", s); return 1; }
        end { show(o, u); return 0; }
        wsp { goto loop; }

        "-v" | "--verbose"              { o.verbose = true; goto loop; }
        ("-l" | "--limit"  eq) @v limit { o.limit  = std::string(v, s); goto loop; }
        ("-f" | "--format" eq) @v str   { o.format = std::string(v, s); goto loop; }
        ("-d" | "--date"   eq) @v date  { o.date   = std::string(v, s); goto loop; }
        ("-p" | "--path"   eq) @v path  { o.path   = std::string(v, s); goto loop; }

        @k ("--" ("limit" | "format" | "date" | "path") | "-" [lfdp]) @v eq? arg {
            bad_arg(k, v, s);
            goto loop;
        }
        [-]{1,2} @k opt @v eq? arg {
            u.push_back(std::make_pair(std::string(k, v), std::string(v, s)));
            goto loop;
        }
    */
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; ++i) lex(argv[i]);
    return 0;
}

Compile:

$ re2c --tags -o options.cc options.re
$ g++ -o options options.cc

Run:

$ ./options '-v --limit=8K -d08/08/1985 -p/usr/src/linux --format="%s" --limit -f=3 --verbos --d"19th May"'
bad argument '' to option --limit
bad argument '=3' to option -f

options:
  date:    08/08/1985
  path:    /usr/src/linux
  format:  "%s"
  limit:   8K
  verbose: yes

unknown:
  verbos: ''
  d: '"19th May"'