+######################################################################
+
+# 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;
+}
+
+######################################################################
+