[Aptitude-svn-commit] r3704 - in branches/aptitude-0.3/aptitude: . src src/generic

Daniel Burrows dburrows at costa.debian.org
Fri Aug 5 05:04:30 UTC 2005


Author: dburrows
Date: Fri Aug  5 05:04:26 2005
New Revision: 3704

Modified:
   branches/aptitude-0.3/aptitude/ChangeLog
   branches/aptitude-0.3/aptitude/src/generic/matchers.h
   branches/aptitude-0.3/aptitude/src/load_grouppolicy.cc
   branches/aptitude-0.3/aptitude/src/pkg_grouppolicy.h
Log:
Rewrite the load_grouppolicy code to be a bit more general: it's now possible
to have actual syntactic entities inside a grouping policy -- say,
for instance, pattern matchers in the pattern match policy.

Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog	(original)
+++ branches/aptitude-0.3/aptitude/ChangeLog	Fri Aug  5 05:04:26 2005
@@ -1,5 +1,16 @@
 2005-08-04  Daniel Burrows  <dburrows at debian.org>
 
+	* src/generic/matchers.h, src/load_grouppolicy.cc, src/pkg_grouppolicy.h:
+
+	  Total rewrite of the load_grouppolicy code, and a few interface
+	  changes to accomodate it.  Instead of assuming a fully regular
+	  syntax and splitting grouping declarations up-front,
+	  load_grouppolicy now works by chaining parsers together, where
+	  each parser implicitly pulls its text off as a side effect.
+	  This will make nested policies feasible; it also means that the
+	  pattern grouping policy should now work correctly even in the
+	  presence of things like parentheses within the pattern.
+
 	* src/generic/matchers.cc, src/generic/matchers.h:
 
 	  Change the parse_pattern interface again (and make it less

Modified: branches/aptitude-0.3/aptitude/src/generic/matchers.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/matchers.h	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/matchers.h	Fri Aug  5 05:04:26 2005
@@ -90,7 +90,7 @@
 			   bool require_full_parse=true);
 
 inline pkg_matcher *parse_pattern(const std::string &s,
-				  const std::vector<const char *> &terminators = std::vector<const char *>(),
+				  const std::vector<const char *> &terminators,
 				  bool search_descriptions = false,
 				  bool flag_errors = true)
 {
@@ -100,6 +100,16 @@
 		       true);
 }
 
+inline pkg_matcher *parse_pattern(const std::string &s,
+				  bool search_descriptions = false,
+				  bool flag_errors = true)
+{
+  std::string::const_iterator start = s.begin();
+  return parse_pattern(start, s.end(),
+		       std::vector<const char *>(),
+		       search_descriptions, flag_errors, true);
+}
+
 inline bool pkg_matches(const string &s,
 			const pkgCache::PkgIterator &pkg,
 			const pkgCache::VerIterator &ver)

Modified: branches/aptitude-0.3/aptitude/src/load_grouppolicy.cc
==============================================================================
--- branches/aptitude-0.3/aptitude/src/load_grouppolicy.cc	(original)
+++ branches/aptitude-0.3/aptitude/src/load_grouppolicy.cc	Fri Aug  5 05:04:26 2005
@@ -9,6 +9,18 @@
 // The parsers take an arglist and a chain argument, and return either a
 // new pkg_grouppolicy_factory, or (if an error was encountered) NULL.
 // (note: the chain argument will be NULL if the policy is the tail of a list)
+//
+// The astute observer will wonder why I don't just use bison or
+// something similar.  The main reason is also the main reason I can't
+// just do a straightforward parse: the matcher grammar.
+// Unfortunately, it's hard to assemble grammars modularly in yacc,
+// and I don't want to duplicate the matchers' grammar in several
+// places; there's also the "minor" problem that the lexical analysis
+// of matchers is radically different from the rest of the constructs.
+//
+// TODO: a lot of classes just exist to complain about having
+// arguments and then creating a new instance of a policy0;
+// commonalize this.
 
 #include "load_grouppolicy.h"
 
@@ -30,296 +42,598 @@
 
 using namespace std;
 
-typedef vector<string> arglist;
-typedef pkg_grouppolicy_factory * (*pkg_grouppolicy_parser)(const arglist &,
-							    pkg_grouppolicy_factory *);
-typedef map<string, pkg_grouppolicy_parser> parser_map;
-
-static pkg_grouppolicy_factory *parse_section_policy(const arglist &args,
-						     pkg_grouppolicy_factory *chain)
-{
-  int split_mode=pkg_grouppolicy_section_factory::split_none;
-  bool passthrough=false;
-
-  if(args.size()>=1)
-    {
-      if(!strcasecmp(args[0].c_str(), "none"))
-	split_mode=pkg_grouppolicy_section_factory::split_none;
-      else if(!strcasecmp(args[0].c_str(), "topdir"))
-	split_mode=pkg_grouppolicy_section_factory::split_topdir;
-      else if(!strcasecmp(args[0].c_str(), "subdir"))
-	split_mode=pkg_grouppolicy_section_factory::split_subdir;
-      else
-	{
-	  _error->Error(_("Bad section name '%s' (use 'none', 'topdir', or 'subdir')"), args[0].c_str());
-	  return NULL;
-	}
-    }
-
-  if(args.size()>=2)
-    {
-      if(!strcasecmp(args[1].c_str(), "passthrough"))
-	passthrough=true;
-      else if(!strcasecmp(args[1].c_str(), "nopassthrough"))
-	passthrough=false;
-      else
-	{
-	  _error->Error(_("Bad passthrough setting '%s' (use 'passthrough' or 'nopassthrough')"),
-			args[1].c_str());
-	  return NULL;
-	}
-    }
-
-  if(args.size()>2)
-    {
-      _error->Error(_("Too many arguments to by-section grouping policy"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_section_factory(split_mode, passthrough, chain);
-}
-
-static pkg_grouppolicy_factory *parse_status_policy(const arglist &args,
-						    pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("By-status grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_status_factory(chain);
-}
-
-static pkg_grouppolicy_factory *parse_filter_policy(const arglist &args,
-						    pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=1)
-    {
-      _error->Error(_("Exactly one filter name must be provided as an argument to a filter policy"));
-      return NULL;
-    }
-
-  string s=args[0];
-  stripws(s);
-  // Backwards compatibility cruft:
-  if(!strcasecmp(s.c_str(), "missing"))
-    s = "~T";
-
-  pkg_matcher *m = parse_pattern(s);
-
-  if(m == NULL)
-    {
-      _error->Error(_("Bad pattern in filter policy: %s"), s.c_str());
-      return NULL;
-    }
-  else
-    return new pkg_grouppolicy_filter_factory(m, chain);
-}
-
-static pkg_grouppolicy_factory *parse_mode_policy(const arglist &args,
-						  pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("By-mode grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_mode_factory(chain);
-}
-
-static pkg_grouppolicy_factory *parse_firstchar_policy(const arglist &args,
-						       pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("First-character grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_firstchar_factory(chain);
-}
-
-static pkg_grouppolicy_factory *parse_ver_policy(const arglist &args,
-						 pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("Version-generating grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(chain)
-    {
-      _error->Error(_("Version-generating grouping policies must be the tail of a chain"));
-      return NULL;
-    }
-
-  return new pkg_grouppolicy_ver_factory;
-}
-
-static pkg_grouppolicy_factory *parse_dep_policy(const arglist &args,
-						 pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("Dep-generating grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(chain)
-    {
-      _error->Error(_("Dep-generating grouping policies must be the tail of a chain"));
-      return NULL;
-    }
-
-  return new pkg_grouppolicy_dep_factory;
-}
-
-static pkg_grouppolicy_factory *parse_priority_policy(const arglist &args,
-						      pkg_grouppolicy_factory *chain)
+class GroupParseException
 {
-  if(args.size()!=0)
-    {
-      _error->Error(_("By-priority grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
+  string msg;
+public:
+  GroupParseException(const char *format,
+		      ...)
+#ifdef __GNUG__
+    __attribute__ ((format (printf, 2, 3)))
+#endif
+  {
+    va_list args;
+
+    va_start(args, format);
+
+    msg = vssprintf(format, args);
+
+    va_end(args);
+  }
+
+  const string &get_msg() const {return msg;}
+};
+
+// Since I fully parse grouping policies before instantiating them,
+// it's necessary to represent the parse "tree" here:
+
+/** Generic representation of a node in the grouping policy parse tree. */
+class group_policy_parse_node
+{
+public:
+  virtual ~group_policy_parse_node()
+  {
+  }
+
+  /** If \b true, the policy must be at the end of a chain and is
+   *  expected to properly handle a \b NULL chain value.  If \b false,
+   *  it must not be at the end of a chain (chains will be
+   *  automatically capped as necessary).
+   *
+   *  By default, this returns \b false.
+   */
+  virtual bool terminal() {return false;}
+
+
+  /** Instantiates the corresponding grouping policy; chain will be
+   *  \b NULL if no chain was created.
+   */
+  virtual pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)=0;
+};
+
+/** Represents a parser for a particular type of argument. */
+class group_policy_parser
+{
+public:
+  virtual ~group_policy_parser()
+  {
+  }
+
+  /** Parse the given string range into a grouping policy. */
+  virtual group_policy_parse_node *parse(string::const_iterator &begin,
+					 const string::const_iterator &end)=0;
+};
+
+
+
+template<class F>
+class policy_terminal_node0 : public group_policy_parse_node
+{
+public:
+  bool terminal()
+  {
+    return true;
+  }
+
+  pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)
+  {
+    if(chain != NULL)
+      throw GroupParseException("Internal error: non-NULL chain passed to terminal policy");
+
+    return new F;
+  }
+};
+
+// stupid; apparently you can't use string literals as template args.
+typedef policy_terminal_node0<pkg_grouppolicy_end_factory> group_policy_end_node;
+
+/** A grouping policy node that pairs two others. */
+class group_policy_pair_node : public group_policy_parse_node
+{
+  class group_policy_parse_node *left, *right;
+public:
+  group_policy_pair_node(group_policy_parse_node *_left,
+			 group_policy_parse_node *_right)
+    :left(_left), right(_right)
+  {
+  }
+
+  bool terminal()
+  {
+    return right->terminal();
+  }
+
+  ~group_policy_pair_node()
+  {
+    delete left;
+    delete right;
+  }
+
+  pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)
+  {
+    return left->instantiate(right->instantiate(chain));
+  }
+};
+
+/** Convenience classes to generate grouping policy nodes that
+ *  generate a particular type of factory.
+ */
+template<class F>
+class policy_node0 : public group_policy_parse_node
+{
+public:
+  pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)
+  {
+    return new F(chain);
+  }
+};
+
+template<class F, typename A1>
+class policy_node1 : public group_policy_parse_node
+{
+  A1 arg1;
+public:
+  policy_node1(const A1 &_arg1) : arg1(_arg1)
+  {
+  }
+
+  pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)
+  {
+    return new F(arg1, chain);
+  }
+};
+
+template<class F, typename A1, typename A2>
+class policy_node2 : public group_policy_parse_node
+{
+  A1 arg1;
+  A2 arg2;
+public:
+  policy_node2(const A1 &_arg1, const A2 &_arg2)
+    : arg1(_arg1), arg2(_arg2)
+  {
+  }
+
+  pkg_grouppolicy_factory *instantiate(pkg_grouppolicy_factory *chain)
+  {
+    return new F(arg1, arg2, chain);
+  }
+};
+
+
+
+
+// Generic parsers:
+
+/** Parse a grouping policy with no arguments. */
+template<class PN>
+class null_policy_parser : public group_policy_parser
+{
+public:
+  group_policy_parse_node *parse(string::const_iterator &,
+				 const string::const_iterator &)
+  {
+    return new PN;
+  }
+};
+
+/** Parse a grouping policy based on a list.  Looks for policy names,
+ *  then starts parsing their parameters as necessary according to a
+ *  table of parsers.  Names must be alphanumeric; the occurance
+ *  of a nonalphanumeric at the top level will terminate the parse.
+ */
+class list_policy_parser : public group_policy_parser
+{
+public:
+  typedef map<string, group_policy_parser *> parsemap;
+
+private:
+  const parsemap &parsers;
+public:
+  list_policy_parser(const parsemap &_parsers)
+    :parsers(_parsers)
+  {
+  }
+
+  group_policy_parse_node *parse(string::const_iterator &begin,
+				 const string::const_iterator &end)
+  {
+    string name;
+    auto_ptr<group_policy_parse_node> rval(NULL);
+
+    while(begin != end)
+      {
+	string last = name;
+	name.clear();
+
+	// This might be a bit overly eager to eat commas, but it
+	// shouldn't do any harm.
+	while(begin != end && (isspace(*begin) || *begin == ','))
+	  ++begin;
+
+	if(begin != end && !isalnum(*begin))
+	  throw GroupParseException(_("Expected policy identifier, got '%c'"), *begin);
+
+	while(begin != end && isalnum(*begin))
+	  {
+	    name += *begin;
+	    ++begin;
+	  }
+
+	while(begin != end && isspace(*begin))
+	  ++begin;
+
+	if(begin != end && *begin != ',' && *begin != '(')
+	  throw GroupParseException(_("Expected ',' or '(', got '%c'"), *begin);
+
+	if(!name.empty())
+	  {
+	    parsemap::const_iterator found = parsers.find(name);
+
+	    if(found == parsers.end())
+	      throw GroupParseException(_("Unknown grouping policy \"%s\""),
+					name.c_str());
+	    else
+	      {
+		auto_ptr<group_policy_parse_node> curr(found->second->parse(begin, end));
+
+		if(rval.get() != NULL && rval.get()->terminal())
+		  throw GroupParseException(_("Terminal policy \"%s\" should be the last policy in the list"), last.c_str());
+
+
+		if(rval.get() == NULL)
+		  rval = curr;
+		else
+		  rval = auto_ptr<group_policy_parse_node>(new group_policy_pair_node(rval.release(), curr.release()));
+	      }
+	  }
+      }
+
+    if(!rval->terminal())
+      rval = auto_ptr<group_policy_parse_node>(new group_policy_pair_node(rval.release(), new group_policy_end_node));
+
+    assert(rval->terminal());
+    return rval.release();
+  }
+};
+
+/** Parse a grouping policy based on a list of bare strings. */
+class string_policy_parser : public group_policy_parser
+{
+public:
+  virtual group_policy_parse_node *create_node(const vector<string> &args)=0;
+
+  group_policy_parse_node *parse(string::const_iterator &begin,
+				 const string::const_iterator &end)
+  {
+    vector<string> args;
+
+    while(begin != end && isspace(*begin))
+      ++begin;
+
+    if(begin != end && *begin == '(')
+      {
+	++begin;
+
+	while(begin != end && *begin != ')')
+	  {
+	    string::const_iterator oldbegin=begin;
+
+	    while(begin != end && *begin != ')' && *begin != ',')
+	      ++begin;
+
+	    string val(oldbegin, begin);
+	    stripws(val);
+	    args.push_back(val);
+
+	    if(begin != end && *begin == ',')
+	      {
+		++begin;
+		if(begin != end && *begin == ')')
+		  args.push_back(string());
+	      }
+	  }
+      }
+
+    if(begin != end)
+      ++begin;
+
+    return create_node(args);
+  }
+};
+
+class section_policy_parser : public string_policy_parser
+{
+public:
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    int split_mode=pkg_grouppolicy_section_factory::split_none;
+    bool passthrough=false;
+
+    if(args.size() >= 1)
+      {
+	if(!strcasecmp(args[0].c_str(), "none"))
+	  split_mode=pkg_grouppolicy_section_factory::split_none;
+	else if(!strcasecmp(args[0].c_str(), "topdir"))
+	  split_mode=pkg_grouppolicy_section_factory::split_topdir;
+	else if(!strcasecmp(args[0].c_str(), "subdir"))
+	  split_mode=pkg_grouppolicy_section_factory::split_subdir;
+	else
+	  throw GroupParseException(_("Bad section name '%s' (use 'none', 'topdir', or 'subdir')"), args[0].c_str());
+      }
+
+    if(args.size() >= 2)
+      {
+	if(!strcasecmp(args[1].c_str(), "passthrough"))
+	  passthrough=true;
+	else if(!strcasecmp(args[1].c_str(), "nopassthrough"))
+	  passthrough=false;
+	else
+	  throw GroupParseException(_("Bad passthrough setting '%s' (use 'passthrough' or 'nopassthrough')"),
+				    args[1].c_str());
+      }
+
+    if(args.size()>2)
+      throw GroupParseException(_("Too many arguments to by-section grouping policy"));
+
+    return new policy_node2<pkg_grouppolicy_section_factory,
+      int, bool>(split_mode, passthrough);
+  }
+};
+
+class status_policy_parser : public string_policy_parser
+{
+public:
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("By-status grouping policies take no arguments"));
+
+    return new policy_node0<pkg_grouppolicy_status_factory>;
+  }
+};
+
+class filter_policy_parser : public group_policy_parser
+{
+public:
+  group_policy_parse_node *parse(string::const_iterator &begin,
+				 const string::const_iterator &end)
+  {
+    // Backwards compatibility cruft:
+    static const string missing = "missing";
+    bool is_missing = true;
+
+    string::const_iterator begin2 = begin;
+    while(isspace(*begin2))
+      ++begin2;
+
+    for(string::const_iterator begin3 = missing.begin();
+	is_missing && begin2 != end && begin3 != missing.end(); ++begin2, ++begin3)
+      if(*begin2 != *begin3)
+	is_missing = false;
+
+    while(begin2 != end && isspace(*begin2))
+      ++begin2;
+
+    if(begin2 == end || *begin2 != ')')
+      is_missing = false;
+
+    if(is_missing)
+      {
+	pkg_matcher *m = parse_pattern("~T");
+	begin = begin2;
+	++begin;
+
+	return new policy_node1<pkg_grouppolicy_filter_factory, pkg_matcher *>(m);
+      }
+    else
+      {
+	vector<const char *> terminators;
+	terminators.push_back(",");
+	terminators.push_back(")");
+
+	auto_ptr<pkg_matcher> m(parse_pattern(begin, end, terminators,
+					      false, true, false));
+
+	if(m.get() == NULL)
+	  throw GroupParseException(_("Unable to parse pattern at '%s'"),
+				    string(begin, end).c_str());
+	else if(begin != end && *begin != ')')
+	  {
+	    assert(*begin == ',');
+	    throw GroupParseException(_("Exactly one filter must be provided as an argument to a filter policy"));
+	  }
+	else
+	  {
+	    if(begin != end)
+	      ++begin;
+
+	    return new policy_node1<pkg_grouppolicy_filter_factory, pkg_matcher *>(m.release());
+	  }
+      }
+  }
+};
+
+class mode_policy_parser : public string_policy_parser
+{
+public:
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("By-mode grouping policies take no arguments"));
+
+    return new policy_node0<pkg_grouppolicy_mode_factory>;
+  }
+};
+
+class firstchar_policy_parser : public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("First-character grouping policies take no arguments"));
+
+
+    return new policy_node0<pkg_grouppolicy_firstchar_factory>;
+  }
+};
+
+class ver_policy_parser : public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("Version-generating grouping policies take no arguments"));
+
+    return new policy_terminal_node0<pkg_grouppolicy_ver_factory>;
+  }
+};
+
+class dep_policy_parser: public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("Dep-generating grouping policies take no arguments"));
+
+    return new policy_terminal_node0<pkg_grouppolicy_dep_factory>;
+  }
+};
+
+class priority_policy_parser : public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("By-priority grouping policies take no arguments"));
+
+    return new policy_node0<pkg_grouppolicy_priority_factory>;
+  }
+};
+
+class hier_policy_parser : public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(!args.empty())
+      {
+	// FIXME: who deletes this??
+	pkg_hier *hier=new pkg_hier;
+
+	for(vector<string>::const_iterator i = args.begin();
+	    i != args.end(); ++i)
+	  hier->input_file(*i);
+
+	return new policy_node2<pkg_grouppolicy_hier_factory, pkg_hier *, bool>(hier, true);
+      }
+    else
+      return new policy_node2<pkg_grouppolicy_hier_factory, pkg_hier *, bool>(get_user_pkg_hier(), false);
+  }
+};
+
+class task_policy_parser : public string_policy_parser
+{
+  group_policy_parse_node *create_node(const vector<string> &args)
+  {
+    if(args.size()!=0)
+      throw GroupParseException(_("Task grouping policies take no arguments"));
+
+    return new policy_node0<pkg_grouppolicy_task_factory>;
+  }
+};
+
+class pattern_policy_parser : public group_policy_parser
+{
+  group_policy_parse_node *parse(string::const_iterator &begin,
+				 const string::const_iterator &end)
+  {
+    while(begin != end && isspace(*begin))
+      ++begin;
+
+    if(begin == end || *begin != '(')
+      throw GroupParseException(_("Expected '(' after 'pattern'"));
+
+    ++begin;
+
+    while(begin != end && isspace(*begin))
+      ++begin;
+
+    if(begin == end || *begin == ')')
+      throw GroupParseException(_("Missing arguments to 'pattern'"));
+
+    vector<pkg_grouppolicy_matchers_factory::match_pair> subgroups;
+
+    vector<const char *> terminators;
+    terminators.push_back(",");
+    terminators.push_back("=>");
+
+    try
+      {
+	while(begin != end && *begin != ')')
+	  {
+	    string format = "\\1";
+
+	    const string::const_iterator begin0 = begin;
+
+	    auto_ptr<pkg_matcher> matcher(parse_pattern(begin, end,
+							terminators,
+							false, true, false));
+
+	    if(matcher.get() == NULL)
+	      throw GroupParseException(_("Unable to parse pattern after \"%s\""),
+					string(begin0, end).c_str());
+
+	    if(begin != end && *begin == '=')
+	      {
+		++begin;
+
+		assert(begin != end && *begin == '>');
+
+		++begin;
+
+		format.clear();
+
+		while(begin != end && *begin != ',' && *begin != ')')
+		  {
+		    format += *begin;
+		    ++begin;
+		  }
+
+		stripws(format);
+
+		if(format.empty())
+		  throw GroupParseException(_("Unexpectedly empty tree title after \"%s\""),
+					    string(begin0, end).c_str());
+	      }
+
+	    subgroups.push_back(pkg_grouppolicy_matchers_factory::match_pair(matcher.release(), transcode(format)));
+
+	    if(begin != end && *begin == ',')
+	      ++begin;
+	  }
+
+	if(begin == end)
+	  throw GroupParseException(_("Unmatched '(' in pattern grouping policy"));
+	else
+	  {
+	    assert(*begin == ')');
+	    ++begin;
+	  }
+
+	return new policy_node1<pkg_grouppolicy_matchers_factory, vector<pkg_grouppolicy_matchers_factory::match_pair> >(subgroups);
+      }
+    catch(...)
+      {
+	for(vector<pkg_grouppolicy_matchers_factory::match_pair>::const_iterator i = subgroups.begin();
+	    i != subgroups.end(); ++i)
+	  delete i->matcher;
+
+	throw;
+      }
+  }
+};
 
-  return new pkg_grouppolicy_priority_factory(chain);
-}
-
-static pkg_grouppolicy_factory *parse_hier_policy(const arglist &args,
-						  pkg_grouppolicy_factory *chain)
-{
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  if(!args.empty())
-    {
-      // FIXME: who deletes this??
-      pkg_hier *hier=new pkg_hier;
-
-      for(arglist::size_type i=0; i<args.size(); ++i)
-	hier->input_file(args[i]);
-
-      return new pkg_grouppolicy_hier_factory(hier, chain, true);
-    }
-  else
-    return new pkg_grouppolicy_hier_factory(get_user_pkg_hier(), chain, false);
-}
-
-static pkg_grouppolicy_factory *parse_task_policy(const arglist &args,
-						  pkg_grouppolicy_factory *chain)
-{
-  if(args.size()!=0)
-    {
-      _error->Error(_("Task grouping policies take no arguments"));
-      return NULL;
-    }
-
-  if(!chain)
-    chain=new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_task_factory(chain);
-}
-
-static pkg_grouppolicy_factory *parse_matchers_policy(const arglist &args,
-						      pkg_grouppolicy_factory *chain)
-{
-  if(args.size() == 0)
-    {
-      _error->Error(_("Match-based grouping policies require at least one parameter"));
-      return NULL;
-    }
-
-  vector<pkg_grouppolicy_matchers_factory :: match_pair> subgroups;
-
-  for(arglist::const_iterator i = args.begin();
-      i != args.end(); ++i)
-    {
-      // Split each argument at the first occurance of "=>".
-      string matcher, format="\\1";
-      bool in_matcher=true;
-
-      string::const_iterator j = i->begin();
-
-      while(j != i->end() && in_matcher)
-	{
-	  while(j != i->end() && *j != '=')
-	    {
-	      matcher+=*j;
-	      ++j;
-	    }
-
-	  if(j != i->end())
-	    {
-	      assert((*j) == '=');
-
-	      ++j;
-
-	      if(j == i->end() || *j != '>')
-		matcher += '=';
-	      else
-		{
-		  ++j;
-		  in_matcher = false;
-		}
-	    }
-	}
-
-      if(!in_matcher)
-	{
-	  format.clear();
-
-	  while(j != i->end())
-	    {
-	      format += *j;
-	      ++j;
-	    }
-	}
-
-      assert(j == i->end());
-
-      stripws(format);
-      stripws(matcher);
-
-      pkg_matcher *m = parse_pattern(matcher);
-
-      if(m == NULL)
-	{
-	  for(vector<pkg_grouppolicy_matchers_factory :: match_pair> :: const_iterator i = subgroups.begin();
-	      i != subgroups.end(); ++i)
-	    delete i->matcher;
-
-	  return NULL;
-	}
-
-      subgroups.push_back(pkg_grouppolicy_matchers_factory :: match_pair(m, transcode(format)));
-    }
-
-  if(!chain)
-    chain = new pkg_grouppolicy_end_factory;
-
-  return new pkg_grouppolicy_matchers_factory(chain, subgroups);
-}
-
-static parser_map parse_types;
+static map<string, group_policy_parser *> parse_types;
 
 static void init_parse_types()
 {
@@ -327,19 +641,19 @@
 
   if(!initted_parse_types)
     {
-      parse_types["section"]=parse_section_policy;
-      parse_types["status"]=parse_status_policy;
-      parse_types["action"]=parse_mode_policy;
-      parse_types["filter"]=parse_filter_policy;
-      parse_types["firstchar"]=parse_firstchar_policy;
-
-      parse_types["versions"]=parse_ver_policy;
-      parse_types["deps"]=parse_dep_policy;
-      parse_types["priority"]=parse_priority_policy;
-      parse_types["hier"]=parse_hier_policy;
-      parse_types["task"]=parse_task_policy;
+      parse_types["section"]=new section_policy_parser;
+      parse_types["status"]=new status_policy_parser;
+      parse_types["action"]=new mode_policy_parser;
+      parse_types["filter"]=new filter_policy_parser;
+      parse_types["firstchar"]=new firstchar_policy_parser;
+
+      parse_types["versions"]=new ver_policy_parser;
+      parse_types["deps"]=new dep_policy_parser;
+      parse_types["priority"]=new priority_policy_parser;
+      parse_types["hier"]=new hier_policy_parser;
+      parse_types["task"]=new task_policy_parser;
 
-      parse_types["pattern"]=parse_matchers_policy;
+      parse_types["pattern"]=new pattern_policy_parser;
 
       initted_parse_types=true;
     }
@@ -350,87 +664,22 @@
 {
   init_parse_types();
 
-  typedef vector<pair<string, arglist> > temp_parse_list;
-  temp_parse_list parsed;
-
-  string::size_type i=0;
+  string::const_iterator begin = s.begin();
 
-  while(i<s.size())
+  try
     {
-      pair<string, arglist> current;
+      auto_ptr<group_policy_parse_node> node(list_policy_parser(parse_types).parse(begin, s.end()));
 
-      // Find the first name.  Use tolower for nice case-insensitivity.
-      while(i<s.size() && s[i]!=',' && s[i]!='(')
-	current.first+=tolower(s[i++]);
-
-      if(current.first.size()==0)
-	{
-	  _error->Error(_("Invalid zero-length grouping policy name"));
-	  return NULL;
-	}
-
-      if(i<s.size() && s[i]=='(') // Parse argument list
-	{
-	  while(i<s.size() && s[i]!=')')
-	    {
-	      ++i; // Clear out the leading '(' or ','
-	      string curarg;
-	      while(i<s.size() && s[i]!=',' && s[i]!=')')
-		curarg+=s[i++];
-
-	      current.second.push_back(curarg);
-	    }
-
-	  if(!(i<s.size() && s[i]==')')) // Unexpected EOT, bail
-	    {
-	      _error->Error(_("Unmatched '(' in grouping policy description"));
-	      return NULL;
-	    }
+      assert(begin == s.end());
 
-	  ++i; // Clear out the ')'
-	}
+      pkg_grouppolicy_factory *rval = node->instantiate(NULL);
 
-      parsed.push_back(current);
-
-      if(i<s.size() && s[i]==',')
-	i++; // Clear out trailing commas.
+      return rval;
     }
-
-  // Now run through the parsed stuff from back-to-front and instantiate it.
-  pkg_grouppolicy_factory *rval=NULL;
-  temp_parse_list::reverse_iterator j=parsed.rbegin();
-
-  while(j!=parsed.rend())
+  catch(GroupParseException e)
     {
-      // Look up the parse function
-      parser_map::iterator found=parse_types.find(j->first);
-
-      // Die gracefully if it's bad.
-      if(found==parse_types.end())
-	{
-	  _error->Error(_("Invalid grouping policy type '%s'"),
-			  j->first.c_str());
-	  delete rval;
-	  return NULL;
-	}
-
-      // Apply it to the argument list
-      pkg_grouppolicy_factory *new_rval=found->second(j->second,
-						      rval);
-
-      // Check for failure
-      if(!new_rval)
-	{
-	  delete rval;
-	  return NULL;
-	}
+      _error->Error("%s", e.get_msg().c_str());
 
-      rval=new_rval;
-      ++j;
+      return NULL;
     }
-
-  if(!rval)
-    rval=new pkg_grouppolicy_end_factory;
-
-  return rval;
 }

Modified: branches/aptitude-0.3/aptitude/src/pkg_grouppolicy.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/pkg_grouppolicy.h	(original)
+++ branches/aptitude-0.3/aptitude/src/pkg_grouppolicy.h	Fri Aug  5 05:04:26 2005
@@ -230,8 +230,8 @@
   bool del_hier;
 public:
   pkg_grouppolicy_hier_factory(pkg_hier *_hier,
-			       pkg_grouppolicy_factory *_chain,
-			       bool _del_hier)
+			       bool _del_hier,
+			       pkg_grouppolicy_factory *_chain)
     :hier(_hier), chain(_chain), del_hier(_del_hier)
   {
   }
@@ -281,8 +281,8 @@
   std::vector<match_pair> subgroups;
 public:
 
-  pkg_grouppolicy_matchers_factory(pkg_grouppolicy_factory *_chain,
-				   const std::vector<match_pair> &_subgroups)
+  pkg_grouppolicy_matchers_factory(const std::vector<match_pair> &_subgroups,
+				   pkg_grouppolicy_factory *_chain)
     :chain(_chain), subgroups(_subgroups)
   {
   }



More information about the Aptitude-svn-commit mailing list