+=item C<generate_grammar_variant ($base, $max, @directive)>
+
+Generate a Bison file F<$base.y> that uses, or not, the Boost.Variants
+depending on the C<@directive>.
+
+=cut
+
+sub generate_grammar_variant ($$@)
+{
+ my ($base, $max, @directive) = @_;
+ my $directives = directives ($base, @directive);
+ my $variant = grep { $_ eq '%define variant' } @directive;
+
+ my $out = new IO::File ">$base.y"
+ or die;
+ print $out <<EOF;
+%language "C++"
+%defines
+$directives
+
+%code requires // variant.h
+{
+#include <string>
+}
+
+%code // variant.c
+{
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+
+// Prototype of the yylex function providing subsequent tokens.
+static yy::parser::token_type yylex(yy::parser::semantic_type* yylval);
+
+#define STAGE_MAX ($max * 10) // max = $max
+#define USE_VARIANTS $variant
+#if USE_VARIANTS
+# define IF_VARIANTS(True, False) True
+#else
+# define IF_VARIANTS(True, False) False
+#endif
+}
+EOF
+
+ if ($variant)
+ {
+ print $out <<'EOF';
+%token <std::string> TEXT
+%token <int> NUMBER
+%printer { std::cerr << "Number: " << $$; } <int>
+%printer { std::cerr << "Text: " << $$; } <std::string>
+%token END_OF_FILE 0
+%type <std::string> text result
+
+%%
+result:
+ text { /* Throw away the result. */ }
+;
+
+text:
+ /* nothing */ { /* This will generate an empty string */ }
+| text TEXT { std::swap($$,$1); $$.append($2); }
+| text NUMBER {
+ std::swap($$,$1);
+ std::ostringstream ss;
+ ss << ' ' << $2;
+ $$.append(ss.str());
+ }
+;
+EOF
+ }
+ else
+ {
+ # Not using Bison variants.
+ print $out <<'EOF';
+%union {int ival; std::string* sval;}
+%token <sval> TEXT
+%token <ival> NUMBER
+%printer { std::cerr << "Number: " << $$; } <ival>
+%printer { std::cerr << "Text: " << *$$; } <sval>
+%token END_OF_FILE 0
+%type <sval> text result
+
+%%
+result:
+ text { delete $1; }
+;
+
+text:
+ /* nothing */ { $$ = new std::string; }
+| text TEXT { $$->append(*$2); delete $2; }
+| text NUMBER {
+ std::ostringstream ss;
+ ss << ' ' << $2;
+ $$->append(ss.str());
+ }
+;
+EOF
+ }
+
+ print $out <<'EOF';
+%%
+static
+yy::parser::token_type
+yylex(yy::parser::semantic_type* yylval)
+{
+ static int stage = -1;
+ ++stage;
+ if (stage == STAGE_MAX)
+ return yy::parser::token::END_OF_FILE;
+ else if (stage % 2)
+ {
+ IF_VARIANTS(yylval->build<int>(), yylval->ival) = stage;
+ return yy::parser::token::NUMBER;
+ }
+ else
+ {
+ IF_VARIANTS(yylval->build<std::string>() =, yylval->sval = new) std::string("A string.");
+ return yy::parser::token::TEXT;
+ }
+ abort();
+}
+
+// Mandatory error function
+void
+yy::parser::error(const yy::parser::location_type& yylloc,
+ const std::string& message)
+{
+ std::cerr << yylloc << ": " << message << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ yy::parser p;
+#if YYDEBUG
+ p.set_debug_level(!!getenv("YYDEBUG"));
+#endif
+ p.parse();
+ return 0;
+}
+EOF
+}
+
+##################################################################
+
+=item C<generate_grammar ($name, $base, @directive)>
+
+Generate F<$base.y> by calling C<&generate_grammar_$name>.
+
+=cut
+
+sub generate_grammar ($$@)
+{
+ my ($name, $base, @directive) = @_;
+ verbose 2, "Generating $base.y\n";
+ my %generator =
+ (
+ "calc" => \&generate_grammar_calc,
+ "triangular" => \&generate_grammar_triangular,
+ "variant" => \&generate_grammar_variant,
+ );
+ &{$generator{$name}}($base, 200, @directive);
+}
+
+##################################################################
+
+=item C<run ($command)>
+
+Run, possibly verbosely, the shell C<$command>.
+
+=cut
+
+sub run ($)
+{
+ my ($command) = @_;
+ verbose 2, "$command\n";
+ system ("$command") == 0
+ or die "$command failed";
+}
+
+##################################################################
+