+sub help ($)
+{
+ my ($verbose) = @_;
+ use Pod::Usage;
+ # See <URL:http://perldoc.perl.org/pod2man.html#NOTES>.
+ pod2usage( { -message => "Bench Bison parsers",
+ -exitval => 0,
+ -verbose => $verbose,
+ -output => \*STDOUT });
+}
+
+######################################################################
+
+# The list of tokens parsed by the following functions.
+my @token;
+
+# Parse directive specifications:
+# expr: term (| term)*
+# term: fact (& fact)*
+# fact: ( expr ) | [ expr ] | dirs
+sub parse (@)
+{
+ @token = @_;
+ verbose 2, "Parsing: @token\n";
+ return parse_expr ();
+}
+
+sub parse_expr ()
+{
+ my @res = parse_term ();
+ while (defined $token[0] && $token[0] eq '|')
+ {
+ shift @token;
+ # Alternation.
+ push @res, parse_term ();
+ }
+ return @res;
+}
+
+sub parse_term ()
+{
+ my @res = parse_fact ();
+ while (defined $token[0] && $token[0] eq '&')
+ {
+ shift @token;
+ # Cartesian product.
+ my @lhs = @res;
+ @res = ();
+ for my $rhs (parse_fact ())
+ {
+ for my $lhs (@lhs)
+ {
+ push @res, "$lhs\n$rhs";
+ }
+ }
+ }
+ return @res;
+}
+
+sub parse_fact ()
+{
+ my @res;
+ die "unexpected end of expression"
+ unless defined $token[0];
+
+ if ($token[0] eq '(')
+ {
+ shift @token;
+ @res = parse_expr ();
+ die "unexpected $token[0], expected )"
+ unless $token[0] eq ')';
+ shift @token;
+ }
+ elsif ($token[0] eq '[')
+ {
+ shift @token;
+ @res = (parse_expr (), '');
+ die "unexpected $token[0], expected ]"
+ unless $token[0] eq ']';
+ shift @token;
+ }
+ else
+ {
+ @res = $token[0];
+ shift @token;
+ }
+ return @res;
+}
+
+######################################################################
+
+sub getopt ()
+{
+ use Getopt::Long;
+ my %option = (
+ "b|bench=s" => \$bench,
+ "c|cflags=s" => \$cflags,
+ "d|directive=s" => \@directive,
+ "g|grammar=s" => \$grammar,
+ "h|help" => sub { help ($verbose) },
+ "i|iterations=i" => \$iterations,
+ "q|quiet" => sub { --$verbose },
+ "v|verbose" => sub { ++$verbose },
+ );
+ Getopt::Long::Configure ("bundling", "pass_through");
+ GetOptions (%option)
+ or exit 1;
+
+ # Support -b: predefined benches.
+ my %bench =
+ (
+ "fusion" => \&bench_fusion_parser,
+ "push" => \&bench_push_parser,
+ "variant" => \&bench_variant_parser,
+ );
+
+ if (defined $bench)
+ {
+ die "invalid argument for --bench: $bench"
+ unless defined $bench{$bench};
+ &{$bench{$bench}}();
+ exit 0;
+ }
+}
+
+######################################################################
+
+getopt;
+verbose 1, "Using bison=$bison.\n";
+verbose 1, "Using cc=$cc.\n";
+verbose 1, "Using cxx=$cxx.\n";
+verbose 1, "Using cflags=$cflags.\n";
+verbose 2, "Grammar: $grammar\n";
+
+# Launch the bench marking.
+bench ($grammar, @ARGV);
+