diff --git a/AUTHORS b/AUTHORS
index bf1ef5c8e9555c6a3f8417f4f73f154aa0bf90a8..1e1c11f9998fc48c03b7c9a461a5b2630fa69b0f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,7 +1,7 @@
 Benjie Chen
 benjie@lcs.mit.edu
 polling extensions, Linux kernel patches, stride scheduling, Linux kernel
-thread, device driver updates, smp click
+thread, device driver updates, SMP patches
 
 Douglas S. J. De Couto
 decouto@lcs.mit.edu
@@ -27,7 +27,7 @@ universal improvements, new elements
 
 Robert Morris
 rtm@lcs.mit.edu
-design, Ethernet, IP, Linux kernel module, and radio elements, Linux kernel 
+design, Ethernet, IP, Linux kernel module, radio elements, Linux kernel 
 patches, IP router configuration, element documentation, other elements
 
 Massimiliano Poletto
diff --git a/DISTFILES b/DISTFILES
index 052d0d1f029b1feddef134eac6957fbb4cb899a1..1b09be8481929d5b3d1dd34bca98c0cc54d6a631 100644
--- a/DISTFILES
+++ b/DISTFILES
@@ -113,6 +113,7 @@ include/click/string.hh
 include/click/subvector.hh
 include/click/timer.hh
 include/click/userutils.hh
+include/click/variableenv.hh
 include/click/vector.cc
 include/click/vector.hh
 
@@ -149,6 +150,7 @@ lib/string.cc
 lib/templatei.cc
 lib/timer.cc
 lib/userutils.cc
+lib/variableenv.cc
 lib/vectorv.cc
 
 userlevel
diff --git a/doc/click.5 b/doc/click.5
index cb80f7d554b36dadc33d494f8eb0b47fe3a742f7..b1eab74a8f7ca7820a2aa5f61394b1ce0d1afee0 100644
--- a/doc/click.5
+++ b/doc/click.5
@@ -321,7 +321,7 @@ a -> X -> b;
 c -> Y -> d;
 .Re
 .PP
-The `input' and `output' pseudo-elements have no existence in a running
+The `input' and `output' pseudoelements have no existence in a running
 router; they serve as placeholders for connections from outside. (In fact,
 they are connection tunnel endpoints. See below for more on connection
 tunnels.)
@@ -362,6 +362,11 @@ like
 Nothing prevents a user from declaring an element named like a compound
 element component. We suggest that users generally avoid using the `/'
 character in their element names.
+.PP
+It is an error to use the `input' pseudoelement's input ports or the
+`output' pseudoelement's output ports. It is also an error to leave an
+intermediate port unused\*Efor example, to use `input [0]' and `input [2]'
+but not `input [1]'.
 '
 .SS "The `elementclass' statement"
 '
@@ -449,7 +454,58 @@ expands to this:
 .Rs
 \&... -> A(1, 100, 3) -> ...
 .Re
-You can avoid this substitution by quoting a dollar sign with a backslash.
+You can avoid this substitution by putting the dollar sign inside single
+quotes.
+'
+.SS "Overloading"
+'
+A single compound element may contain multiple overloaded definitions
+separated from one another by two vertical bars "\f(CW||\fR". Different
+definitions may have different numbers of input ports, output ports, or
+configuration arguments. For example, this extended MyQueue compound
+element takes an optional capacity argument, just like Queue itself:
+.Rs
+elementclass MyQueue {
+.br
+\%  input -> Queue -> Shaper(1000) -> output;
+.br
+\%||
+.br
+\%  $cap | input -> Queue($cap)
+.br
+\%               -> Shaper(1000) -> output;
+.br
+}
+.Re
+For each use of an overloaded compound element, Click will choose one
+definition that matches the numbers of input ports, output ports, and
+configuration arguments provided. It is an error if no definition matches
+these properties exactly.
+.PP
+It is also possible to extend an existing element class with new overloaded
+definitions with "\f(CW...\fR". For example, this definition introduces a
+two-argument version of Queue:
+.Rs
+elementclass Queue { ... ||
+.br
+\%  $cap, $rate | input -> Queue($cap)
+.br
+\%                -> Shaper($rate) -> output;
+.br
+}
+.Re
+(The ellipsis in this example must be typed verbatim.) The overloadings
+visible at a given declaration are those that lexically precede that
+declaration. For example, the following example is an error since the
+two-argument version of Test is not visible at the declaration where it is
+required:
+.Rs
+elementclass Test { $a | /* nothing */ }
+.br
+test :: Test(1, 2);
+.br
+elementclass Test { ... || $a, $b | /* nothing */ }
+.Re
 '
 .SH "CONNECTION TUNNELS"
 '
@@ -562,11 +618,12 @@ The keywords `connectiontunnel', `elementclass' and `require' may not be
 used as identifiers. The normal identifiers `input' and `output' have
 special meaning inside compound element definitions.
 .PP
-The following characters and two-character sequences are single Click
+The following characters and multi-character sequences are single Click
 tokens:
 .TS
 l l l l l l l l l l l l l.
 	->	::	;	,	(	)	[	]	{	}	|
+	||	...
 .\" ^
 .TE
 .PP
@@ -646,9 +703,13 @@ kernel module will not accept archives; use
 .IR element-name " ::= identifier
 .\" | ""^"" identifier
 .br
-.IR class " ::= identifier | ""{"" " stmts " ""}"""
+.IR class " ::= identifier | ""{"" " compounds " ""}"""
+.br
+.RI "    | ""{"" ""..."" ""||"" " compounds " ""}"""
+.br
+.IR compounds " ::= " compound " | " compounds " ""||"" " compound
 .br
-.RI "    | ""{"" " formals " ""|"" " stmts " ""}"""
+.IR compound " ::= " stmts " | " formals " ""|"" " stmts
 .br
 .IR formals " ::= parameter | " formals " "","" parameter"
 .br
diff --git a/elements/standard/nulls.hh b/elements/standard/nulls.hh
index 0738f9f0187cf96b3fdbf88c62258cd12f5741e3..83cacad812b16555fb363814ba48a698654dcac2 100644
--- a/elements/standard/nulls.hh
+++ b/elements/standard/nulls.hh
@@ -3,21 +3,28 @@
 #include <click/element.hh>
 
 /*
- * =c
- * Null1()
- * Null2()
- * Null3()
- * Null4()
- * Null5()
- * Null6()
- * Null7()
- * Null8()
- * =s
- * copies of Null
- * =d
- * Copies of Null that do not share code.
- * =a Null
- */
+=c
+
+Null1()
+Null2()
+Null3()
+Null4()
+Null5()
+Null6()
+Null7()
+Null8()
+
+=s
+
+copy of Null
+
+=d
+
+Each of these elements is a reimplementation of Null. However, each has
+independent code, so the i-cache cost of using all eight elements (Null1
+through Null8) is eight times the cost of eight Null elements.
+
+=a Null */
 
 class Null1 : public Element {
   
diff --git a/include/click/lexer.hh b/include/click/lexer.hh
index 1656c3c4ddc7ced7290dad17140796b32aaaaebf..fb22a40f507687eb77299b0bf93c687dbd9a175b 100644
--- a/include/click/lexer.hh
+++ b/include/click/lexer.hh
@@ -4,6 +4,7 @@
 #include <click/router.hh>
 #include <click/glue.hh>
 class LexerExtra;
+class VariableEnvironment;
 
 enum Lexemes {
   lexEOF = 0,
@@ -11,6 +12,8 @@ enum Lexemes {
   lexVariable,
   lexArrow,
   lex2Colon,
+  lex2Bar,
+  lex3Dot,
   lexTunnel,
   lexElementclass,
   lexRequire,
@@ -38,6 +41,7 @@ class Lexer {
   
   class TunnelEnd;
   class Compound;
+  class Synonym;
   typedef Router::Hookup Hookup;
   
   // lexer
@@ -70,21 +74,16 @@ class Lexer {
   Vector<Element *> _element_types;
   Vector<String> _element_type_names;
   Vector<int> _element_type_next;
-  Element *_tunnel_element_type;
   int _last_element_type;
   int _free_element_type;
 
-  // configuration arguments for compounds
-  HashMap<String, int> _variable_map;
-  Vector<String> _variable_values;
-  
   // elements
   HashMap<String, int> _element_map;
-  Vector<Element *> _elements;
+  Vector<int> _elements;
   Vector<String> _element_names;
   Vector<String> _element_configurations;
   Vector<String> _element_landmarks;
-  
+
   Vector<Hookup> _hookup_from;
   Vector<Hookup> _hookup_to;
   
@@ -92,8 +91,8 @@ class Lexer {
   TunnelEnd *_defoutputs;
   
   // compound elements
-  String _element_prefix;
   int _anonymous_offset;
+  int _compound_depth;
 
   // requirements
   Vector<String> _requirements;
@@ -107,10 +106,11 @@ class Lexer {
   int get_element(String, int, const String & = String(), const String & = String());
   int lexical_scoping_in() const;
   void lexical_scoping_out(int);
-  void lexical_scoping_back(int, Vector<int> &);
-  void lexical_scoping_forward(const Vector<int> &);
-  int make_compound_element(String, int, const String &);
+  int make_compound_element(int);
+  void expand_compound_element(int, Vector<int> &, Vector<VariableEnvironment *> &);
   void add_router_connections(int, const Vector<int> &, Router *);
+
+  friend class Compound;
   
  public:
 
@@ -128,7 +128,6 @@ class Lexer {
   const Lexeme &lex();
   void unlex(const Lexeme &);
   String lex_config();
-  String lex_compound_body();
   String landmark() const;
   
   bool expect(int, bool report_error = true);
@@ -142,20 +141,20 @@ class Lexer {
   
   void remove_element_type(int);
 
-  void connect(int f1, int p1, int f2, int p2);
+  void connect(int element1, int port1, int element2, int port2);
   String element_name(int) const;
   String element_landmark(int) const;
   
   void add_tunnel(String, String);
   
   bool yport(int &port);
-  bool yelement_upref(int &element);
   bool yelement(int &element, bool comma_ok);
-  void ydeclaration(const String &first_element = "");
+  void ydeclaration(const String &first_element = String());
   bool yconnection();
   void yelementclass();
   void ytunnel();
-  int ylocal(String = "");
+  void ycompound_arguments(Compound *);
+  int ycompound(String name = String());
   void yrequire();
   bool ystatement(bool nested = false);
   
diff --git a/lib/confparse.cc b/lib/confparse.cc
index a4549ab078704a39028e343112d7b33ece4d09f0..6697dbb37d1b04ed3e305c7cf0049392ac0ff16a 100644
--- a/lib/confparse.cc
+++ b/lib/confparse.cc
@@ -2025,7 +2025,7 @@ cp_unparse_ulonglong(unsigned long long q, int base, bool uppercase)
 void
 cp_va_static_initialize()
 {
-  cp_add_argtype(cpArgument, "??", 0, default_parsefunc, default_storefunc);
+  cp_add_argtype(cpArgument, "arg", 0, default_parsefunc, default_storefunc);
   cp_add_argtype(cpString, "string", 0, default_parsefunc, default_storefunc);
   cp_add_argtype(cpWord, "word", 0, default_parsefunc, default_storefunc);
   cp_add_argtype(cpBool, "bool", 0, default_parsefunc, default_storefunc);
diff --git a/lib/lexer.cc b/lib/lexer.cc
index a28aa02930295ef2063f64c8181fdda0bf092ce9..49a9db9758927816a08b986739d7a0606b2fe46a 100644
--- a/lib/lexer.cc
+++ b/lib/lexer.cc
@@ -26,6 +26,8 @@
 #include <click/error.hh>
 #include <click/confparse.hh>
 #include <click/glue.hh>
+#include <click/straccum.hh>
+#include <click/variableenv.hh>
 #include "elements/standard/errorelement.hh"
 
 //
@@ -56,6 +58,33 @@ class Lexer::TunnelEnd {
   
 };
 
+//
+// CLASS LEXER::SYNONYM
+//
+
+class Lexer::Synonym : public Element {
+  
+  Element *_element;
+  
+ public:
+  
+  Synonym(Element *e)			: _element(e) { }
+
+  const char *class_name() const	{ return _element->class_name(); }
+  void *cast(const char *);
+  Element *clone() const		{ return _element->clone(); }
+  
+};
+
+void *
+Lexer::Synonym::cast(const char *s)
+{
+  if (strcmp(s, "Lexer::Synonym") == 0)
+    return this;
+  else
+    return _element->cast(s);
+}
+
 //
 // CLASS LEXER::COMPOUND
 //
@@ -63,56 +92,235 @@ class Lexer::TunnelEnd {
 class Lexer::Compound : public Element {
   
   mutable String _name;
-  String _body;
-  String _filename;
-  unsigned _lineno;
-  Vector<String> _arguments;
-  int _scope;
+  String _landmark;
+  int _depth;
+  Element *_next;
+
+  Vector<String> _formals;
+  int _ninputs;
+  int _noutputs;
+  
+  Vector<int> _elements;
+  Vector<String> _element_names;
+  Vector<String> _element_configurations;
+  Vector<String> _element_landmarks;
+  
+  Vector<Hookup> _hookup_from;
+  Vector<Hookup> _hookup_to;
   
  public:
   
-  Compound(const String &, const String &, const String &, unsigned,
-	   const Vector<String> &);
-  Compound(const Compound &);
+  Compound(const String &, const String &, int, Element *);
+
+  const String &name() const		{ return _name; }
+  const String &landmark() const	{ return _landmark; }
+  int nformals() const			{ return _formals.size(); }
+  const Vector<String> &formals() const	{ return _formals; }
+  void add_formal(const String &f)	{ _formals.push_back(f); }
+  int depth() const			{ return _depth; }
+
+  void swap_router(Lexer *);
+  void finish(ErrorHandler *);
+  void check_duplicates_until(Element *, ErrorHandler *);
+
+  Element *find_relevant_class(int, int, const Vector<String> &);
+  void report_signatures(ErrorHandler *);
+  void expand_into(Lexer *, int, const VariableEnvironment &);
   
   const char *class_name() const	{ return _name.cc(); }
   void *cast(const char *);
   Compound *clone() const		{ return 0; }
 
-  void set_scope(int s)			{ _scope = s; }
-  
-  const String &body() const		{ return _body; }
-  const String &filename() const	{ return _filename; }
-  unsigned lineno() const		{ return _lineno; }
-  int narguments() const		{ return _arguments.size(); }
-  const String &argument(int i) const	{ return _arguments[i]; }
-  int scope() const			{ return _scope; }
+  String signature() const;
+  static String signature(const String &, int, int, int);
   
 };
 
-Lexer::Compound::Compound(const String &name, const String &body,
-			  const String &filename, unsigned lineno,
-			  const Vector<String> &args)
-  : _name(name), _body(body), _filename(filename), _lineno(lineno),
-    _arguments(args), _scope(-1)
-{
-}
-
-Lexer::Compound::Compound(const Compound &o)
-  : Element(), _name(o._name), _body(o._body), _filename(o._filename),
-    _lineno(o._lineno), _arguments(o._arguments), _scope(o._scope)
+Lexer::Compound::Compound(const String &name, const String &lm, int depth, Element *next)
+  : _name(name), _landmark(lm), _depth(depth), _next(next),
+    _ninputs(0), _noutputs(0)
 {
 }
 
 void *
 Lexer::Compound::cast(const char *s)
 {
-  if (String("Lexer::Compound") == s || _name == s)
+  if (strcmp(s, "Lexer::Compound") == 0 || _name == s)
     return this;
   else
     return 0;
 }
 
+void
+Lexer::Compound::swap_router(Lexer *lexer)
+{
+  lexer->_elements.swap(_elements);
+  lexer->_element_names.swap(_element_names);
+  lexer->_element_configurations.swap(_element_configurations);
+  lexer->_element_landmarks.swap(_element_landmarks);
+
+  lexer->_hookup_from.swap(_hookup_from);
+  lexer->_hookup_to.swap(_hookup_to);
+}
+
+void
+Lexer::Compound::finish(ErrorHandler *errh)
+{
+  assert(_element_names[0] == "input" && _element_names[1] == "output");
+
+  // count numbers of inputs and outputs
+  Vector<int> from_in, to_out;
+  bool to_in = false, from_out = false;
+  for (int i = 0; i < _hookup_from.size(); i++) {
+    const Hookup &hf = _hookup_from[i], &ht = _hookup_to[i];
+    
+    if (hf.idx == 0) {
+      if (from_in.size() <= hf.port)
+	from_in.resize(hf.port + 1, 0);
+      from_in[hf.port] = 1;
+    } else if (hf.idx == 1)
+      from_out = true;
+    
+    if (ht.idx == 1) {
+      if (to_out.size() <= ht.port)
+	to_out.resize(ht.port + 1, 0);
+      to_out[ht.port] = 1;
+    } else if (ht.idx == 0)
+      to_in = true;
+  }
+  
+  // store information
+  _ninputs = from_in.size();
+  if (to_in)
+    errh->lerror(_landmark, "`%s' pseudoelement `input' may only be used as output", _name.cc());
+  for (int i = 0; i < from_in.size(); i++)
+    if (!from_in[i])
+      errh->lerror(_landmark, "compound element `%s' input %d unused", _name.cc(), i);
+  
+  _noutputs = to_out.size();
+  if (from_out)
+    errh->lerror(_landmark, "`%s' pseudoelement `output' may only be used as input", _name.cc());
+  for (int i = 0; i < to_out.size(); i++)
+    if (!to_out[i])
+      errh->lerror(_landmark, "compound element `%s' output %d unused", _name.cc(), i);
+}
+
+void
+Lexer::Compound::check_duplicates_until(Element *last, ErrorHandler *errh)
+{
+  if (this == last || !_next)
+    return;
+  
+  Element *n = _next;
+  while (n && n != last) {
+    Compound *nc = (Compound *)n->cast("Lexer::Compound");
+    if (!nc) break;
+    if (nc->_ninputs == _ninputs && nc->_noutputs == _noutputs && nc->_formals.size() == _formals.size()) {
+      errh->lerror(_landmark, "redeclaration of `%s'", signature().cc());
+      errh->lerror(nc->_landmark, "`%s' previously declared here", signature().cc());
+      break;
+    }
+    n = nc->_next;
+  }
+
+  if (Compound *nc = (Compound *)_next->cast("Lexer::Compound"))
+    nc->check_duplicates_until(last, errh);
+}
+
+Element *
+Lexer::Compound::find_relevant_class(int ninputs, int noutputs, const Vector<String> &args)
+{
+  Compound *ct = this;
+  
+  while (1) {
+    if (ct->_ninputs == ninputs && ct->_noutputs == noutputs && ct->_formals.size() == args.size())
+      return ct;
+    Element *e = ct->_next;
+    if (!e)
+      return 0;
+    else if (Compound *next = (Compound *)e->cast("Lexer::Compound"))
+      ct = next;
+    else
+      return e;
+  }
+}
+
+String
+Lexer::Compound::signature(const String &name, int ninputs, int noutputs, int nargs)
+{
+  StringAccum sa;
+  const char *pl_args = (nargs == 1 ? " argument, " : " arguments, ");
+  const char *pl_ins = (ninputs == 1 ? " input, " : " inputs,");
+  const char *pl_outs = (noutputs == 1 ? " output" : " outputs");
+  sa << name << '[' << nargs << pl_args << ninputs << pl_ins << noutputs << pl_outs << ']';
+  return sa.take_string();
+}
+
+String
+Lexer::Compound::signature() const
+{
+  return signature(_name, _ninputs, _noutputs, _formals.size());
+}
+
+void
+Lexer::Compound::report_signatures(ErrorHandler *errh)
+{
+  if (_next) {
+    if (Compound *n = (Compound *)_next->cast("Lexer::Compound"))
+      n->report_signatures(errh);
+    else
+      errh->lmessage(_landmark, "`%s[...]'", _name.cc());
+  }
+  errh->lmessage(_landmark, "`%s'", signature().cc());
+}
+
+void
+Lexer::Compound::expand_into(Lexer *lexer, int which, const VariableEnvironment &ve)
+{
+  ErrorHandler *errh = lexer->_errh;
+  
+  // `name_slash' is `name' constrained to end with a slash
+  String ename = lexer->_element_names[which];
+  String ename_slash;
+  if (ename[ename.length() - 1] == '/')
+    ename_slash = ename;
+  else
+    ename_slash = ename + "/";
+
+  assert(_element_names[0] == "input" && _element_names[1] == "output");
+
+  lexer->_elements[which] = TUNNEL_TYPE;
+  lexer->add_tunnel(ename, ename_slash + "input");
+  lexer->add_tunnel(ename_slash + "output", ename);
+
+  Vector<int> eidx_map;
+  eidx_map.push_back(lexer->_element_map[ename_slash + "input"]);
+  eidx_map.push_back(lexer->_element_map[ename_slash + "output"]);
+
+  for (int i = 2; i < _elements.size(); i++) {
+    String cname = ename_slash + _element_names[i];
+    int eidx = lexer->_element_map[cname];
+    if (eidx >= 0) {
+      errh->lerror(lexer->element_landmark(which), "redeclaration of element `%s'", cname.cc());
+      errh->lerror(lexer->element_landmark(eidx), "`%s' previously declared here", cname.cc());
+      eidx_map.push_back(-1);
+    } else {
+      if (lexer->_element_type_map[cname] >= 0)
+	errh->lerror(lexer->element_landmark(which), "`%s' is an element class", cname.cc());
+      eidx = lexer->get_element(cname, _elements[i], ve.interpolate(_element_configurations[i]), _element_landmarks[i]);
+      eidx_map.push_back(eidx);
+    }
+  }
+
+  // now copy hookups
+  int nh = _hookup_from.size();
+  for (int i = 0; i < nh; i++) {
+    const Hookup &hf = _hookup_from[i], &ht = _hookup_to[i];
+    if (eidx_map[hf.idx] >= 0 && eidx_map[ht.idx] >= 0)
+      lexer->connect(eidx_map[hf.idx], hf.port, eidx_map[ht.idx], ht.port);
+  }
+}
+
 //
 // LEXER
 //
@@ -121,26 +329,22 @@ Lexer::Lexer(ErrorHandler *errh)
   : _data(0), _len(0), _pos(0), _lineno(1), _lextra(0),
     _tpos(0), _tfull(0),
     _element_type_map(-1),
-    _tunnel_element_type(new ErrorElement),
     _last_element_type(-1),
     _free_element_type(-1),
-    _variable_map(-1),
     _element_map(-1),
     _definputs(0), _defoutputs(0),
     _errh(errh)
 {
   if (!_errh) _errh = ErrorHandler::default_handler();
   end_parse(-1);		// clear private state
-  add_element_type("<tunnel>", _tunnel_element_type);
+  add_element_type("<tunnel>", new ErrorElement);
   add_element_type(new ErrorElement);
-  assert(element_type("<tunnel>") == TUNNEL_TYPE
-	 && element_type("Error") == ERROR_TYPE);
+  assert(element_type("<tunnel>") == TUNNEL_TYPE && element_type("Error") == ERROR_TYPE);
 }
 
 Lexer::~Lexer()
 {
   end_parse(-1);
-  // _default_element_type and _tunnel_element_type removed by end_parse()
 }
 
 int
@@ -161,17 +365,13 @@ Lexer::begin_parse(const String &data, const String &filename,
   _lineno = 1;
 
   _lextra = lextra;
-  return _last_element_type;
+  return lexical_scoping_in();
 }
 
 void
 Lexer::end_parse(int cookie)
 {
   lexical_scoping_out(cookie);
-  for (int i = 0; i < _elements.size(); i++)
-    // don't delete _tunnel_element_type!
-    if (_elements[i] != _tunnel_element_type)
-      delete _elements[i];
   
   delete _definputs;
   _definputs = 0;
@@ -198,8 +398,8 @@ Lexer::end_parse(int cookie)
   _tpos = 0;
   _tfull = 0;
   
-  _element_prefix = "";
   _anonymous_offset = 0;
+  _compound_depth = 0;
 }
 
 
@@ -373,8 +573,15 @@ Lexer::next_lexeme()
     } else if (_data[pos] == ':' && _data[pos+1] == ':') {
       _pos = pos + 2;
       return Lexeme(lex2Colon, _big_string.substring(word_pos, 2));
+    } else if (_data[pos] == '|' && _data[pos+1] == '|') {
+      _pos = pos + 2;
+      return Lexeme(lex2Bar, _big_string.substring(word_pos, 2));
     }
   }
+  if (pos < _len - 2 && _data[pos] == '.' && _data[pos+1] == '.' && _data[pos+2] == '.') {
+    _pos = pos + 3;
+    return Lexeme(lex3Dot, _big_string.substring(word_pos, 3));
+  }
   
   _pos = pos + 1;
   return Lexeme(_data[word_pos], _big_string.substring(word_pos, 1));
@@ -386,7 +593,6 @@ Lexer::lex_config()
   unsigned config_pos = _pos;
   unsigned pos = _pos;
   unsigned paren_depth = 1;
-  bool have_arguments = _variable_values.size() != 0;
   int quote = 0;
   String output;
   
@@ -413,23 +619,6 @@ Lexer::lex_config()
     else if (_data[pos] == '\\' && pos < _len - 1 && quote == '\"') {
       if (_data[pos+1] == '\"' || _data[pos+1] == '$')
 	pos++;
-    } else if (_data[pos] == '$' && have_arguments && quote != '\'') {
-      unsigned word_pos = pos;
-      for (pos++; isalnum(_data[pos]) || _data[pos] == '_'; pos++)
-	/* nada */;
-      String name = _big_string.substring(word_pos, pos - word_pos);
-      int variable = _variable_map[name];
-      if (variable >= 0) {
-	output += _big_string.substring(config_pos, word_pos - config_pos);
-	String value = _variable_values[variable];
-	if (quote == '\"') {	// interpolate inside the quotes
-	  value = cp_quote(cp_unquote(value));
-	  if (value[0] == '\"') value = value.substring(1, value.length() - 2);
-	}
-	output += value;
-	config_pos = pos;
-      }
-      pos--;
     }
   
   _pos = pos;
@@ -439,40 +628,6 @@ Lexer::lex_config()
     return output + _big_string.substring(config_pos, pos - config_pos);
 }
 
-String
-Lexer::lex_compound_body()
-{
-  unsigned config_pos = _pos;
-  unsigned pos = _pos;
-  unsigned paren_depth = 0;
-  unsigned brace_depth = 1;
-  for (; pos < _len; pos++)
-    if (_data[pos] == '(')
-      paren_depth++;
-    else if (_data[pos] == ')' && paren_depth)
-      paren_depth--;
-    else if (_data[pos] == '{' && !paren_depth)
-      brace_depth++;
-    else if (_data[pos] == '}' && !paren_depth) {
-      brace_depth--;
-      if (!brace_depth) break;
-    } else if (_data[pos] == '/' && pos < _len - 1) {
-      if (_data[pos+1] == '/')
-	pos = skip_line(pos + 2) - 1;
-      else if (_data[pos+1] == '*')
-	pos = skip_slash_star(pos + 2) - 1;
-    } else if (_data[pos] == '\n')
-      _lineno++;
-    else if (_data[pos] == '\r') {
-      if (pos < _len - 1 && _data[pos+1] == '\n') pos++;
-      _lineno++;
-    } else if ((_data[pos] == '\'' || _data[pos] == '\"') && paren_depth)
-      pos = skip_quote(pos + 1, _data[pos]) - 1;
-  
-  _pos = pos;
-  return _big_string.substring(config_pos, pos - config_pos);
-}
-
 String
 Lexer::lexeme_string(int kind)
 {
@@ -485,10 +640,16 @@ Lexer::lexeme_string(int kind)
     return "`->'";
   else if (kind == lex2Colon)
     return "`::'";
+  else if (kind == lex2Bar)
+    return "`||'";
+  else if (kind == lex3Dot)
+    return "`...'";
   else if (kind == lexTunnel)
     return "`connectiontunnel'";
   else if (kind == lexElementclass)
     return "`elementclass'";
+  else if (kind == lexRequire)
+    return "`require'";
   else if (kind >= 32 && kind < 127) {
     sprintf(buf, "`%c'", kind);
     return buf;
@@ -567,6 +728,7 @@ Lexer::add_element_type(Element *e)
 int
 Lexer::add_element_type(String name, Element *e)
 {
+  assert(e);
   // Lexer now owns `e'
   int tid;
   if (!name)
@@ -673,18 +835,18 @@ Lexer::element_type_names(Vector<String> &v) const
 void
 Lexer::add_tunnel(String namein, String nameout)
 {
-  Hookup hin(get_element(_element_prefix + namein, TUNNEL_TYPE), 0);
-  Hookup hout(get_element(_element_prefix + nameout, TUNNEL_TYPE), 0);
+  Hookup hin(get_element(namein, TUNNEL_TYPE), 0);
+  Hookup hout(get_element(nameout, TUNNEL_TYPE), 0);
   
   bool ok = true;
-  if (_elements[hin.idx] != _tunnel_element_type) {
+  if (_elements[hin.idx] != TUNNEL_TYPE) {
     lerror("redeclaration of element `%s'", namein.cc());
-    _errh->lerror(_elements[hin.idx]->landmark(), "`%s' previously declared here", namein.cc());
+    _errh->lerror(_element_landmarks[hin.idx], "`%s' previously declared here", namein.cc());
     ok = 0;
   }
-  if (_elements[hout.idx] != _tunnel_element_type) {
+  if (_elements[hout.idx] != TUNNEL_TYPE) {
     lerror("redeclaration of element `%s'", nameout.cc());
-    _errh->lerror(_elements[hout.idx]->landmark(), "`%s' previously declared here", nameout.cc());
+    _errh->lerror(_element_landmarks[hout.idx], "`%s' previously declared here", nameout.cc());
     ok = 0;
   }
   if (_definputs && _definputs->find(hin))
@@ -710,33 +872,19 @@ Lexer::get_element(String name, int etype, const String &conf,
   if (_element_map[name] >= 0)
     return _element_map[name];
 
-  // check type
-  Element *e;
-  Element *et = _element_types[etype];
-  if (etype == TUNNEL_TYPE)
-    e = et;
-  else if ((e = et->clone()))
-    /* do nothing */;
-  else if (et->cast("Lexer::Compound"))
-    return make_compound_element(name, etype, conf);
-  else {
-    lerror("can't clone `%s'", et->declaration().cc());
-    e = 0;
-  }
-
   int eid = _elements.size();
   _element_map.insert(name, eid);
   _element_names.push_back(name);
   _element_configurations.push_back(conf);
   _element_landmarks.push_back(lm ? lm : landmark());
-  _elements.push_back(e);
+  _elements.push_back(etype);
   return eid;
 }
 
 String
 Lexer::anon_element_name(const String &class_name) const
 {
-  String prefix = _element_prefix + class_name + "@";
+  String prefix = class_name + "@";
   int anonymizer = _elements.size() - _anonymous_offset + 1;
   String name = prefix + String(anonymizer);
   while (_element_map[name] >= 0) {
@@ -746,144 +894,6 @@ Lexer::anon_element_name(const String &class_name) const
   return name;
 }
 
-void
-Lexer::lexical_scoping_back(int first, Vector<int> &insertions)
-{
-  for (int t = _last_element_type; t >= 0; t = _element_type_next[t]) {
-    const String &name = _element_type_names[t];
-    if (_element_type_map[name] == t) {
-      // remove it from map
-      int prev = _element_type_next[first];
-      while (prev >= 0 && _element_type_names[prev] != name)
-	prev = _element_type_next[prev];
-      _element_type_map.insert(name, prev);
-      insertions.push_back(t);
-    }
-    if (t == first)
-      break;
-  }
-}
-
-void
-Lexer::lexical_scoping_forward(const Vector<int> &insertions)
-{
-  for (int i = 0; i < insertions.size(); i++) {
-    int j = insertions[i];
-    _element_type_map.insert(_element_type_names[j], j);
-  }
-}
-
-int
-Lexer::make_compound_element(String name, int etype, const String &conf)
-{
-  Compound *compound = (Compound *)_element_types[etype];
-  if (!name)
-    name = anon_element_name(compound->class_name());
-  // change `name' to not contain the current prefix
-  name = name.substring(_element_prefix.length());
-
-  // handle configuration string
-  Vector<String> args;
-  cp_argvec(conf, args);
-  int nargs = compound->narguments();
-  if (args.size() != nargs) {
-    const char *whoops = (args.size() < nargs ? "few" : "many");
-    String signature;
-    for (int i = 0; i < nargs; i++) {
-      if (i) signature += ", ";
-      signature += compound->argument(i);
-    }
-    lerror("too %s arguments to compound element `%s(%s)'", whoops,
-	   compound->class_name(), signature.cc());
-    for (int i = args.size(); i < nargs; i++)
-      args.push_back("");
-  }
-  
-  // store arguments
-  Vector<int> prev_variable_value;
-  for (int i = 0; i < nargs; i++) {
-    const String &name = compound->argument(i);
-    int prev = _variable_map[name];
-    _variable_values.push_back(args[i]);
-    prev_variable_value.push_back(prev);
-    _variable_map.insert(name, _variable_values.size() - 1);
-  }
-
-  // `name_slash' is `name' constrained to end with a slash
-  String name_slash;
-  if (name[name.length() - 1] == '/')
-    name_slash = name;
-  else
-    name_slash = name + "/";
-  
-  int fake_index = get_element(_element_prefix + name, TUNNEL_TYPE);
-  add_tunnel(name, name_slash + "input");
-  add_tunnel(name_slash + "output", name);
-  
-  // save parser state
-  String old_prefix = _element_prefix;
-  String old_big_string = _big_string;
-  unsigned old_len = _len;
-  unsigned old_pos = _pos;
-  String old_filename = _filename;
-  unsigned old_lineno = _lineno;
-  Lexeme old_tcircle[TCIRCLE_SIZE];
-  old_tcircle = _tcircle;
-  int old_tpos = _tpos;
-  int old_tfull = _tfull;
-  int old_anonymous_offset = _anonymous_offset;
-
-  // reparse the saved compound element's body!
-  _element_prefix += name_slash;
-  _big_string = compound->body();
-  _data = _big_string.data();
-  _len = _big_string.length();
-  _pos = 0;
-  _filename = compound->filename();
-  _lineno = compound->lineno();
-  _tpos = _tfull;
-  // manipulate the anonymous offset so anon elements in 2 instances of the
-  // compound have identical suffixes
-  int old_elements_size = _elements.size();
-  _anonymous_offset = _elements.size();
-  
-  // lexical scoping of types
-  int cookie = lexical_scoping_in();
-  Vector<int> lexical_scoping_help;
-  lexical_scoping_back(compound->scope(), lexical_scoping_help);
-  
-  while (ystatement())
-    /* do nothing */;
-
-  // restore parser state
-  _element_prefix = old_prefix;
-  _big_string = old_big_string;
-  _data = _big_string.data();
-  _len = old_len;
-  _pos = old_pos;
-  _filename = old_filename;
-  _lineno = old_lineno;
-  _tcircle = old_tcircle;
-  _tpos = old_tpos;
-  _tfull = old_tfull;
-  // manipulate the anonymous offset to get consistent results with tools
-  _anonymous_offset = old_anonymous_offset + _elements.size()
-    - old_elements_size + 2;
-
-  // lexical scoping fixup
-  lexical_scoping_out(cookie);
-  lexical_scoping_forward(lexical_scoping_help);
-  
-  // lexical scoping of arguments
-  for (int i = nargs - 1; i >= 0; i--) {
-    const String &name = compound->argument(i);
-    _variable_map.insert(name, prev_variable_value[i]);
-  }
-  _variable_values.resize(_variable_values.size() - nargs);
-
-  return fake_index;
-}
-
 void
 Lexer::connect(int element1, int port1, int element2, int port2)
 {
@@ -903,12 +913,13 @@ Lexer::element_name(int eid) const
   else {
     char buf[100];
     sprintf(buf, "@%d", eid);
-    if (_elements[eid] == _tunnel_element_type)
-      return "<tunnel " + String(buf) + ">";
-    else if (!_elements[eid])
-      return "<null " + String(buf) + ">";
+    int t = _elements[eid];
+    if (t == TUNNEL_TYPE)
+      return "<tunnel" + String(buf) + ">";
+    else if (!_element_types[t])
+      return "<null" + String(buf) + ">";
     else
-      return _elements[eid]->class_name() + String(buf);
+      return _element_types[t]->class_name() + String(buf);
   }
 }
 
@@ -954,35 +965,6 @@ Lexer::yport(int &port)
   }
 }
 
-bool
-Lexer::yelement_upref(int &element)
-{
-  Lexeme t = lex();
-  if (!t.is(lexIdent)) {
-    lerror("syntax error: expected element name");
-    unlex(t);
-    return false;
-  }
-
-  String prefix = _element_prefix;
-  while (1) {
-    int pos = prefix.find_right('/', prefix.length() - 2);
-    prefix = (pos >= 0 ? prefix.substring(0, pos + 1) : String());
-    
-    String name = prefix + t.string();
-    int en = _element_map[name];
-    if (en >= 0) {
-      element = en;
-      return true;
-    }
-
-    if (!prefix) {
-      lerror("`%s' not found in enclosing scopes", t.string().cc());
-      return false;
-    }
-  }
-}
-
 bool
 Lexer::yelement(int &element, bool comma_ok)
 {
@@ -990,13 +972,11 @@ Lexer::yelement(int &element, bool comma_ok)
   String name;
   int etype;
 
-  if (t.is('^')) {
-    return yelement_upref(element);
-  } else if (t.is(lexIdent)) {
+  if (t.is(lexIdent)) {
     name = t.string();
     etype = element_type(name);
   } else if (t.is('{')) {
-    etype = ylocal();
+    etype = ycompound();
     name = _element_types[etype]->class_name();
   } else {
     unlex(t);
@@ -1016,8 +996,7 @@ Lexer::yelement(int &element, bool comma_ok)
   if (etype >= 0)
     element = get_element(anon_element_name(name), etype, configuration, lm);
   else {
-    String lookup_name = _element_prefix + name;
-    element = _element_map[lookup_name];
+    element = _element_map[name];
     if (element < 0) {
       const Lexeme &t2colon = lex();
       unlex(t2colon);
@@ -1025,9 +1004,9 @@ Lexer::yelement(int &element, bool comma_ok)
 	ydeclaration(name);
       else {
 	lerror("undeclared element `%s' (first use this block)", name.cc());
-	get_element(lookup_name, ERROR_TYPE);
+	get_element(name, ERROR_TYPE);
       }
-      element = _element_map[lookup_name];
+      element = _element_map[name];
     }
   }
   
@@ -1071,7 +1050,7 @@ Lexer::ydeclaration(const String &first_element)
   if (t.is(lexIdent))
     etype = force_element_type(t.string());
   else if (t.is('{'))
-    etype = ylocal();
+    etype = ycompound();
   else {
     lerror("missing element type in declaration");
     return;
@@ -1087,18 +1066,15 @@ Lexer::ydeclaration(const String &first_element)
 
   for (int i = 0; i < decls.size(); i++) {
     String name = decls[i];
-    String lookup_name = _element_prefix + name;
-    if (_element_map[lookup_name] >= 0) {
-      int e = _element_map[lookup_name];
+    if (_element_map[name] >= 0) {
+      int e = _element_map[name];
       lerror("redeclaration of element `%s'", name.cc());
-      if (_elements[e] != _tunnel_element_type)
-	_errh->lerror(_elements[e]->landmark(), "element `%s' previously declared here", name.cc());
+      if (_elements[e] != TUNNEL_TYPE)
+	_errh->lerror(_element_landmarks[e], "element `%s' previously declared here", name.cc());
     } else if (_element_type_map[name] >= 0)
       lerror("`%s' is an element class", name.cc());
-    else if (_element_type_map[lookup_name] >= 0)
-      lerror("`%s' is an element class", lookup_name.cc());
     else
-      get_element(lookup_name, etype, configuration, lm);
+      get_element(name, etype, configuration, lm);
   }
 }
 
@@ -1146,9 +1122,12 @@ Lexer::yconnection()
       goto relex;
       
      case lexIdent:
-     case '^':
      case '{':
      case '}':
+     case lex2Bar:
+     case lexTunnel:
+     case lexElementclass:
+     case lexRequire:
       unlex(t);
       // FALLTHRU
      case ';':
@@ -1184,24 +1163,13 @@ Lexer::yelementclass()
 
   Lexeme tnext = lex();
   if (tnext.is('{'))
-    ylocal(name);
+    ycompound(name);
     
   else if (tnext.is(lexIdent)) {
-    // Go through some rigamarole because other code always assumes that
-    // _element_type_map[x] == _element_type_map[y] <==>
+    // define synonym type
     int t = force_element_type(tnext.string());
     Element *et = _element_types[t];
-    Element *e = et->clone();
-    if (!e) {
-      Compound *comp = static_cast<Compound *>(et->cast("Lexer::Compound"));
-      if (comp)
-	e = new Compound(*comp);
-    }
-    if (!e) {
-      lerror("can't clone `%s'", et->declaration().cc());
-      add_element_type(name, new ErrorElement);
-    } else
-      add_element_type(name, e);
+    add_element_type(name, new Synonym(et));
 
   } else {
     lerror("syntax error near `%s'", String(tnext.string()).cc());
@@ -1238,42 +1206,87 @@ Lexer::ytunnel()
   }
 }
 
+void
+Lexer::ycompound_arguments(Compound *comptype)
+{
+  while (1) {
+    const Lexeme &tvar = lex();
+    if (!tvar.is(lexVariable)) {
+      unlex(tvar);
+      return;
+    }
+    comptype->add_formal(tvar.string());
+    const Lexeme &tsep = lex();
+    if (tsep.is('|'))
+      return;
+    else if (!tsep.is(',')) {
+      lerror("expected `,' or `|'");
+      unlex(tsep);
+      return;
+    }
+  }
+}
+
 int
-Lexer::ylocal(String name)
+Lexer::ycompound(String name)
 {
-  // OK because every used ylocal() corresponds to at least one element
+  // OK because every used ycompound() corresponds to at least one element
   if (!name)
     name = "@Class" + String(_elements.size() - _anonymous_offset + 1);
 
-  // check for arguments
-  Vector<String> arguments;
-  unsigned old_pos = _pos;
+  HashMap<String, int> old_element_map(-1);
+  old_element_map.swap(_element_map);
+  HashMap<String, int> old_type_map(_element_type_map);
+  int old_offset = _anonymous_offset;
+  Element *created = 0;
+
+  // check for '...'
+  const Lexeme &t = lex();
+  if (t.is(lex3Dot)) {
+    if (_element_type_map[name] < 0) {
+      lerror("extending unknwon element class `%s'", name.cc());
+      add_element_type(name, new ErrorElement);
+    }
+    created = _element_types[ _element_type_map[name] ];
+    expect(lex2Bar);
+  } else
+    unlex(t);
+
+  Element *first = created;
+
   while (1) {
-    Lexeme t = lex();
-    if (!t.is(lexVariable))
-      break;
-    arguments.push_back(t.string());
-    old_pos = _pos;
-    const Lexeme &sep = lex();
-    if (sep.is('|')) {
-      old_pos = _pos;
-      break;
-    } else if (!sep.is(','))
+    // prepare
+    _element_map.clear();
+    Compound *ct = new Compound(name, landmark(), _compound_depth, created);
+    ct->swap_router(this);
+    get_element("input", TUNNEL_TYPE);
+    get_element("output", TUNNEL_TYPE);
+    _anonymous_offset = 2;
+    _compound_depth++;
+
+    ycompound_arguments(ct);
+    while (ystatement(true))
+      /* nada */;
+
+    _compound_depth--;
+    _anonymous_offset = old_offset;
+    old_type_map.swap(_element_type_map);
+    ct->swap_router(this);
+
+    ct->finish(_errh);
+    created = ct;
+
+    // check for '||' or '}'
+    const Lexeme &t = lex();
+    if (!t.is(lex2Bar))
       break;
   }
-  _pos = old_pos;
   
-  // opening brace was already read
-  String body_filename = _filename;
-  unsigned body_lineno = _lineno;
-  String body = lex_compound_body();
-  expect('}');
-
-  Compound *c =
-    new Compound(name, body, body_filename, body_lineno, arguments);
-  int t = add_element_type(name, c);
-  c->set_scope(t);
-  return t;
+  // on the way out
+  ((Compound *)created)->check_duplicates_until(first, _errh);
+  old_element_map.swap(_element_map);
+  
+  return add_element_type(name, created);
 }
 
 void
@@ -1304,7 +1317,6 @@ Lexer::ystatement(bool nested)
   switch (t.kind()) {
     
    case lexIdent:
-   case '^':
    case '[':
    case '{':
     unlex(t);
@@ -1327,8 +1339,10 @@ Lexer::ystatement(bool nested)
     return true;
     
    case '}':
+   case lex2Bar:
     if (!nested)
       goto syntax_error;
+    unlex(t);
     return false;
     
    case lexEOF:
@@ -1366,6 +1380,68 @@ Lexer::add_router_connections(int c, const Vector<int> &router_id,
   }
 }
 
+void
+Lexer::expand_compound_element(int which, Vector<int> &environment_map, Vector<VariableEnvironment *> &environments)
+{
+  String name = _element_names[which];
+  int etype = _elements[which];
+  assert(name);
+  int old_nelements = _elements.size();
+    
+  // find right version
+  Vector<String> args;
+  cp_argvec(_element_configurations[which], args);
+  int inputs_used = 0, outputs_used = 0;
+  for (int i = 0; i < _hookup_from.size(); i++) {
+    const Hookup &hf = _hookup_from[i], &ht = _hookup_to[i];
+    if (ht.idx == which && ht.port >= inputs_used)
+      inputs_used = ht.port + 1;
+    if (hf.idx == which && hf.port >= outputs_used)
+      outputs_used = hf.port + 1;
+  }
+  Compound *c = (Compound *)_element_types[etype];
+  Element *found_c = c->find_relevant_class(inputs_used, outputs_used, args);
+  if (!found_c) {
+    _errh->lerror(c->landmark(), "no match for `%s'", Compound::signature(c->name(), inputs_used, outputs_used, args.size()).cc());
+    ContextErrorHandler cerrh(_errh, "possibilities are:", "  ");
+    c->report_signatures(&cerrh);
+    _elements[which] = ERROR_TYPE;
+    return;
+  }
+
+  // have putatively correct version
+  Compound *found_comp = (Compound *)found_c->cast("Lexer::Compound");
+  if (!found_comp) {
+    for (int i = 0; i < _element_types.size(); i++)
+      if (_element_types[i] == found_c) {
+	_elements[which] = i;
+	return;
+      }
+    assert(0);
+    _elements[which] = ERROR_TYPE;
+    return;
+  }
+  
+  int vei = environment_map[which];
+  if (args.size() == 0 && found_comp->depth() == 0)
+    vei = 0;
+  else if (args.size() || environments[vei]->depth() >= found_comp->depth()) {
+    VariableEnvironment *new_ve = new VariableEnvironment;
+    if (vei > 0)
+      new_ve->enter(*environments[vei]);
+    new_ve->limit_depth(found_comp->depth());
+    new_ve->enter(found_comp->formals(), args, found_comp->depth());
+    environments.push_back(new_ve);
+    vei = environments.size() - 1;
+  }
+
+  found_comp->expand_into(this, which, *environments[vei]);
+
+  // mark new environments
+  for (int i = old_nelements; i < _elements.size(); i++)
+    environment_map.push_back(vei);
+}
+
 Router *
 Lexer::create_router()
 {
@@ -1373,19 +1449,28 @@ Lexer::create_router()
   if (!router)
     return 0;
   
-  // check for elements w/o types
-  for (int i = 0; i < _elements.size(); i++)
-    if (!_elements[i])
-      _errh->error("undeclared element `%s'", element_name(i).cc());
+  // expand compounds
+  Vector<int> environment_map(_elements.size(), 0);
+  Vector<VariableEnvironment *> environments;
+  environments.push_back(new VariableEnvironment);
+  
+  for (int i = 0; i < _elements.size(); i++) {
+    int t = _elements[i];
+    if (t != TUNNEL_TYPE && _element_types[t]->cast("Lexer::Compound"))
+      expand_compound_element(i, environment_map, environments);
+  }
+
+  for (int i = 0; i < environments.size(); i++)
+    delete environments[i];
   
   // add elements to router
   Vector<int> router_id;
   for (int i = 0; i < _elements.size(); i++)
-    if (_elements[i] && _elements[i] != _tunnel_element_type) {
-      int fi = router->add_element
-	(_elements[i], _element_names[i], _element_configurations[i],
-	 _element_landmarks[i]);
-      router_id.push_back(fi);
+    if (_elements[i] != TUNNEL_TYPE) {
+      Element *tc = _element_types[_elements[i]];
+      int ei = router->add_element
+	(tc->clone(), _element_names[i], _element_configurations[i], _element_landmarks[i]);
+      router_id.push_back(ei);
     } else
       router_id.push_back(-1);
   
@@ -1404,9 +1489,6 @@ Lexer::create_router()
   for (int i = 0; i < _requirements.size(); i++)
     router->add_requirement(_requirements[i]);
 
-  // clear _elements, as all elements are owned by router
-  _elements.assign(_elements.size(), (Element *)0);
-  
   return router;
 }
 
@@ -1518,7 +1600,7 @@ void
 Lexer::expand_connection(const Hookup &this_end, bool is_out,
 			 Vector<Hookup> &into) const
 {
-  if (_elements[this_end.idx] != _tunnel_element_type)
+  if (_elements[this_end.idx] != TUNNEL_TYPE)
     into.push_back(this_end);
   else {
     TunnelEnd *dp = (is_out ? _defoutputs : _definputs);
diff --git a/linuxmodule/Makefile.in b/linuxmodule/Makefile.in
index 43e99a1d552bae42f6b939654bbb1339029bbe21..cb9f18e98fe305c75fe58fa5d34012d8341d1984 100644
--- a/linuxmodule/Makefile.in
+++ b/linuxmodule/Makefile.in
@@ -45,7 +45,7 @@ GENERIC_OBJS = string.o straccum.o \
 	packet.o \
 	error.o glue.o timer.o gaprate.o \
 	elemlink.o element.o \
-	confparse.o lexer.o elemfilter.o router.o \
+	confparse.o variableenv.o lexer.o elemfilter.o router.o \
 	crc32.o iptable.o iptable2.o ip6table.o radix.o
 
 LINUXMODULE_OBJS = kernelerror.o kernelversion.o module.o sched.o \
diff --git a/tools/click-align/click-align.cc b/tools/click-align/click-align.cc
index 905d9ea0414e530fad87e78681ad7f1ffa23c49b..a59997d7a9c300d0c4bbb2bbd5642336af8a40aa 100644
--- a/tools/click-align/click-align.cc
+++ b/tools/click-align/click-align.cc
@@ -323,9 +323,10 @@ particular purpose.\n");
   
  done:
   RouterT *router = read_router_file(router_file, prepared_router(), errh);
+  if (router)
+    router->flatten(errh);
   if (!router || errh->nerrors() > 0)
     exit(1);
-  router->flatten(errh);
   int align_tindex = router->type_index("Align");
 
   int original_nelements = router->nelements();
diff --git a/tools/click-check/click-check.cc b/tools/click-check/click-check.cc
index 0a2feff92eb551b62791556463ca856db31f6788..6a790ab81fead225a98f536aef9d5d64759f1b38 100644
--- a/tools/click-check/click-check.cc
+++ b/tools/click-check/click-check.cc
@@ -212,11 +212,12 @@ particular purpose.\n");
   
  done:
   RouterT *r = read_router_file(router_file, errh);
+  if (r)
+    r->flatten(errh);
   if (!r || errh->nerrors() > 0)
     exit(1);
   if (!router_file || strcmp(router_file, "-") == 0)
     router_file = "<stdin>";
-  r->flatten(errh);
 
   // open output file
   FILE *outf = stdout;
diff --git a/tools/click-combine/click-combine.cc b/tools/click-combine/click-combine.cc
index e89ce88df9d29ac9ee6b6b33d2c54e0e66072688..34fe2f63e6a40e047771307ebdfa821841a5c92c 100644
--- a/tools/click-combine/click-combine.cc
+++ b/tools/click-combine/click-combine.cc
@@ -27,6 +27,7 @@
 #include "toolutils.hh"
 #include <click/confparse.hh>
 #include <click/straccum.hh>
+#include <click/variableenv.hh>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -446,11 +447,8 @@ particular purpose.\n");
 
   // combine routers
   RouterT *combined = new RouterT;
-  for (int i = 0; i < routers.size(); i++) {
-    int ei = combined->get_eindex(router_names[i], RouterT::TUNNEL_TYPE,
-				  String(), String());
-    routers[i]->expand_into(combined, ei, combined, RouterScope(), errh);
-  }
+  for (int i = 0; i < routers.size(); i++)
+    routers[i]->expand_into(combined, VariableEnvironment(router_names[i]), errh);
 
   // nested combinations: change config strings of included RouterLinks
   int link_type = combined->type_index("RouterLink");
diff --git a/tools/click-combine/click-uncombine.cc b/tools/click-combine/click-uncombine.cc
index 5b4f3f003b3a225543f712669976bdbd816abd32..a046d420af730cc32fb1df97329434f8f9e17607 100644
--- a/tools/click-combine/click-uncombine.cc
+++ b/tools/click-combine/click-uncombine.cc
@@ -365,13 +365,13 @@ particular purpose.\n");
   }
   
  done:
-  RouterT *r = 0;
-  r = read_router_file(router_file, errh);
+  RouterT *r = read_router_file(router_file, errh);
+  if (r)
+    r->flatten(errh);
   if (!r || errh->nerrors() > 0)
     exit(1);
   if (!router_file || strcmp(router_file, "-") == 0)
     router_file = "<stdin>";
-  r->flatten(errh);
 
   // find component names
   if (r->archive_index("componentmap") < 0)
diff --git a/tools/click-devirtualize/click-devirtualize.cc b/tools/click-devirtualize/click-devirtualize.cc
index 4904c91f1556e4b2d8e6aeb64545108145211dfa..46c9daf6a7e99bd7c6b76f25e2a5dba4b234d2da 100644
--- a/tools/click-devirtualize/click-devirtualize.cc
+++ b/tools/click-devirtualize/click-devirtualize.cc
@@ -322,9 +322,10 @@ particular purpose.\n");
 
   // read router
   RouterT *router = read_router_file(router_file, errh);
+  if (router)
+    router->flatten(errh);
   if (!router || errh->nerrors() > 0)
     exit(1);
-  router->flatten(errh);
 
   // open output file
   FILE *outf = stdout;
diff --git a/tools/click-fastclassifier/click-fastclassifier.cc b/tools/click-fastclassifier/click-fastclassifier.cc
index afe41261720946ebb7b55f766ea0285e79467816..613c82a7ee4dc1be54c44edaf63a3a408c2e8b5b 100644
--- a/tools/click-fastclassifier/click-fastclassifier.cc
+++ b/tools/click-fastclassifier/click-fastclassifier.cc
@@ -916,9 +916,10 @@ particular purpose.\n");
   
  done:
   RouterT *r = read_router_file(router_file, errh);
+  if (r)
+    r->flatten(errh);
   if (!r || errh->nerrors() > 0)
     exit(1);
-  r->flatten(errh);
   if (source_only || config_only)
     compile_user = compile_kernel = false;
 
diff --git a/tools/click-install/click-install.cc b/tools/click-install/click-install.cc
index 3538e662b54e4db4f19ed13c75e7f06103922266..09b1f9b582ae1222aa6cf97361811f81e3303ece 100644
--- a/tools/click-install/click-install.cc
+++ b/tools/click-install/click-install.cc
@@ -348,8 +348,8 @@ main(int argc, char **argv)
 {
   String::static_initialize();
   ErrorHandler::static_initialize(new FileErrorHandler(stderr));
-  ErrorHandler *errh = new PrefixErrorHandler
-    (ErrorHandler::default_handler(), "click-install: ");
+  ErrorHandler *nop_errh = ErrorHandler::default_handler();
+  ErrorHandler *errh = new PrefixErrorHandler(nop_errh, "click-install: ");
 
   // read command line arguments
   Clp_Parser *clp =
@@ -424,10 +424,11 @@ particular purpose.\n");
   if (hotswap && uninstall)
     errh->warning("`--hotswap' and `--uninstall' are mutually exclusive");
   
-  RouterT *r = read_router_file(router_file, errh);
+  RouterT *r = read_router_file(router_file, nop_errh);
+  if (r)
+    r->flatten(nop_errh);
   if (!r || errh->nerrors() > 0)
     exit(1);
-  r->flatten(errh);
 
   // uninstall Click if requested
   if (uninstall && access("/proc/click/version", F_OK) >= 0) {
diff --git a/tools/click-undead/click-undead.cc b/tools/click-undead/click-undead.cc
index 1113d51908140b809d7e52b5e106a13a02182515..6e6a611d8bb4b339f0839ba8187cc86337e5e397 100644
--- a/tools/click-undead/click-undead.cc
+++ b/tools/click-undead/click-undead.cc
@@ -647,9 +647,10 @@ particular purpose.\n");
   
  done:
   RouterT *r = read_router_file(router_file, default_errh);
+  if (r)
+    r->flatten(default_errh);
   if (!r || default_errh->nerrors() > 0)
     exit(1);
-  r->flatten(default_errh);
 
   // open output file
   FILE *outf = stdout;
diff --git a/tools/click-xform/click-xform.cc b/tools/click-xform/click-xform.cc
index 475b060363de823ab3cf1eb3e1dd23f8deb8dbb8..bfa89300f42a2d8a6a1683203d39a0d8ada416c0 100644
--- a/tools/click-xform/click-xform.cc
+++ b/tools/click-xform/click-xform.cc
@@ -24,6 +24,7 @@
 #include "lexert.hh"
 #include <click/error.hh>
 #include <click/confparse.hh>
+#include <click/variableenv.hh>
 #include <click/clp.h>
 #include "toolutils.hh"
 #include "adjacency.hh"
@@ -77,8 +78,6 @@ Matcher::Matcher(RouterT *pat, AdjacencyMatrix *pat_m,
   : _pat(pat), _pat_m(pat_m), _body(body), _body_m(body_m), _patid(patid),
     _pat_input_idx(-1), _pat_output_idx(-1)
 {
-  _pat->use();
-  _body->use();
   // check tunnel situation
   for (int i = 0; i < _pat->nelements(); i++) {
     ElementT &fac = _pat->element(i);
@@ -97,8 +96,6 @@ Matcher::Matcher(RouterT *pat, AdjacencyMatrix *pat_m,
 
 Matcher::~Matcher()
 {
-  _pat->unuse();
-  _body->unuse();
 }
 
 bool
@@ -324,8 +321,14 @@ Matcher::replace(RouterT *replacement, const String &try_prefix,
   // add replacement
   // collect new element indices in `changed_elements'
   _body->set_new_eindex_collector(&changed_elements);
+
+  // make element named `prefix'
   int new_eindex = _body->get_eindex(prefix, RouterT::TUNNEL_TYPE, String(), landmark);
-  replacement->expand_into(_body, new_eindex, _body, RouterScope(), errh);
+
+  // expand 'replacement' into '_body'; need crap compound element
+  Vector<String> crap_args;
+  CompoundElementClassT comp(replacement);
+  comp.complex_expand_element(_body, new_eindex, String(), crap_args, _body, VariableEnvironment(), errh);
 
   // mark replacement
   for (int i = 0; i < changed_elements.size(); i++) {
@@ -340,11 +343,9 @@ Matcher::replace(RouterT *replacement, const String &try_prefix,
   for (int i = 0; i < _match.size(); i++)
     if (_match[i] >= 0) {
       String n = _pat->ename(i);
-      if (replacement->eindex(n) >= 0) {
-	int new_index = _body->eindex(prefix + "/" + n);
-	assert(new_index >= 0);
+      int new_index = _body->eindex(prefix + "/" + n);
+      if (new_index >= 0) 
 	_body->change_ename(new_index, old_names[i]);
-      }
     }
 
   // find input and output, add connections to tunnels
@@ -577,9 +578,10 @@ particular purpose.\n");
   
  done:
   RouterT *r = read_router_file(router_file, errh);
+  if (r)
+    r->flatten(errh);
   if (!r || errh->nerrors() > 0)
     exit(1);
-  r->flatten(errh);
 
   if (!patterns_attempted)
     errh->warning("no patterns read");
@@ -593,8 +595,8 @@ particular purpose.\n");
 	new_patterns.push_back(replacements[i]);
 	new_replacements.push_back(patterns[i]);
       }
-      patterns = new_patterns;
-      replacements = new_replacements;
+      patterns.swap(new_patterns);
+      replacements.swap(new_replacements);
     }
 
     // flatten patterns
@@ -602,6 +604,8 @@ particular purpose.\n");
       patterns[i]->flatten(errh);
 
     // unify pattern types
+    // (warning: creates circular element class references, which are
+    // removed below by remove_unused_element_types())
     for (int i = 0; i < patterns.size(); i++) {
       r->get_types_from(patterns[i]);
       r->get_types_from(replacements[i]);
@@ -641,6 +645,7 @@ particular purpose.\n");
   // write result
   if (nreplace)
     r->remove_dead_elements(0);
+  r->remove_unused_element_types(); // remove circular compound elements
   if (write_router_file(r, output_file, errh) < 0)
     exit(1);
   return 0;
diff --git a/tools/lib/Makefile.in b/tools/lib/Makefile.in
index f0ba512f5ddf706248c78b9f3dbdeec3c97c9fe0..1e82796b4ef4293953e6d209bd55aa5dd028dc60 100644
--- a/tools/lib/Makefile.in
+++ b/tools/lib/Makefile.in
@@ -39,7 +39,8 @@ mkinstalldirs = @top_srcdir@/mkinstalldirs
 
 
 OBJS = vectori.o vectorv.o bitvector.o hashmapi.o straccum.o string.o error.o \
-	elementt.o routert.o lexert.o confparse.o archive.o processingt.o \
+	elementt.o routert.o variableenv.o lexert.o confparse.o \
+	archive.o processingt.o \
 	ipaddress.o ipaddressset.o ip6address.o etheraddress.o \
 	userutils.o toolutils.o clp.o @LIBOBJS@
 
diff --git a/tools/lib/elementt.cc b/tools/lib/elementt.cc
index e7dd298e44803095ee8d1dbed89eb2ec61478dff..a268daec4ed87551e6632b997121e65eeff8e791 100644
--- a/tools/lib/elementt.cc
+++ b/tools/lib/elementt.cc
@@ -24,6 +24,8 @@
 #include "elementt.hh"
 #include "routert.hh"
 #include <click/straccum.hh>
+#include <click/confparse.hh>
+#include <click/variableenv.hh>
 #include <stdlib.h>
 
 ElementT::ElementT()
@@ -82,77 +84,125 @@ ElementClassT::ElementClassT()
 {
 }
 
-static int
-resolve_upref(const String &upref, String prefix, RouterT *r)
+ElementClassT *
+ElementClassT::find_relevant_class(int, int, const Vector<String> &)
 {
-  while (prefix) {
-    int pos = prefix.find_right('/', prefix.length() - 2);
-    prefix = (pos >= 0 ? prefix.substring(0, pos + 1) : String());
-    
-    String try_name = prefix + upref;
-    int en = r->eindex(try_name);
-    if (en >= 0) return en;
-  }
-  return -1;
+  return this;
 }
 
-int
-ElementClassT::simple_expand_into(RouterT *fromr, int which,
-				  RouterT *tor,
-				  const RouterScope &scope, ErrorHandler *errh)
+void
+ElementClassT::report_signatures(const String &lm, String name, ErrorHandler *errh)
 {
-  if (fromr == tor)
-    return which;
+  errh->lmessage(lm, "`%s[...]'", name.cc());
+}
 
-  const ElementT &e = fromr->element(which);
+int
+ElementClassT::direct_expand_element(RouterT *fromr, int which, ElementClassT *thus, RouterT *tor,
+				     const VariableEnvironment &env, ErrorHandler *errh)
+{
+  ElementT &e = fromr->element(which);
+  String new_name = env.prefix() + e.name;
+  String new_configuration = env.interpolate(e.configuration);
   
-  if (e.type == RouterT::UPREF_TYPE) {
-    // try and resolve the upref within the current prefix. if not, then
-    // pass the upref on up, without tacking on the prefix.
-    int new_index = resolve_upref(e.name, scope.prefix(), tor);
-    if (new_index < 0)
-      new_index = tor->get_anon_eindex(e.name, RouterT::UPREF_TYPE,
-				       e.configuration, e.landmark);
-    return new_index;
+  switch (e.type) {
+
+   case RouterT::TUNNEL_TYPE: {
+     // common case -- expanding router into itself
+     if (fromr == tor && !env.prefix())
+       return which;
+     // make the tunnel or tunnel pair
+     if (e.tunnel_output >= 0 && e.tunnel_output < fromr->nelements()) {
+       tor->add_tunnel(new_name,
+		       env.prefix() + fromr->ename(e.tunnel_output),
+		       e.landmark, errh);
+       return tor->eindex(new_name);
+     } else
+       return tor->get_eindex
+	 (new_name, RouterT::TUNNEL_TYPE, e.configuration, e.landmark);
+   }
+
+   default: {
+     // get element type index. add new "anonymous" element type if needed
+     String type_name = fromr->type_name(e.type);
+     int new_type = tor->get_type_index(type_name, thus);
+     ElementClassT *new_type_class = tor->type_class(new_type);
+     if (thus != new_type_class && new_type_class)
+       new_type = tor->get_anon_type_index(type_name, thus);
+     
+     // check for common case -- expanding router into itself
+     if (fromr == tor && !env.prefix()) {
+       e.configuration = new_configuration;
+       e.type = new_type;
+       return which;
+     }
+
+     // check for old element
+     int new_eidx = tor->eindex(new_name);
+     if (new_eidx >= 0) {
+       errh->lerror(e.landmark, "redeclaration of element `%s'", new_name.cc());
+       errh->lerror(tor->elandmark(new_eidx), "`%s' previously declared here", tor->edeclaration(new_eidx).cc());
+     }
+     
+     // add element
+     return tor->get_eindex(new_name, new_type, new_configuration, e.landmark);
+   }
+
   }
+}
 
-  if (e.type == RouterT::TUNNEL_TYPE) {
-    // make the tunnel or tunnel pair
-    String new_name = scope.prefix() + e.name;
-    if (e.tunnel_output >= 0 && e.tunnel_output < fromr->nelements()) {
-      tor->add_tunnel(new_name,
-		      scope.prefix() + fromr->ename(e.tunnel_output),
-		      e.landmark, errh);
-      return tor->eindex(new_name);
-    } else
-      return tor->get_eindex
-	(new_name, RouterT::TUNNEL_TYPE, e.configuration, e.landmark);
+int
+ElementClassT::expand_element(RouterT *fromr, int which, RouterT *tor,
+			      const VariableEnvironment &env, ErrorHandler *errh)
+{
+  ElementClassT *c = fromr->etype_class(which);
+
+  if (!c || c->direct_expansion())
+    return direct_expand_element(fromr, which, c, tor, env, errh);
+
+  // if not direct expansion, do some more work
+  int inputs_used = fromr->ninputs(which);
+  int outputs_used = fromr->noutputs(which);
+
+  Vector<String> args;
+  String new_configuration = env.interpolate(fromr->econfiguration(which));
+  cp_argvec(new_configuration, args);
+
+  ElementClassT *found_c = c->find_relevant_class(inputs_used, outputs_used, args);
+  if (!found_c) {
+    String lm = fromr->elandmark(which), name = fromr->etype_name(which);
+    errh->lerror(lm, "no match for `%s'", name.cc(), signature(name, inputs_used, outputs_used, args.size()).cc());
+    ContextErrorHandler cerrh(errh, "possibilities are:", "  ");
+    c->report_signatures(lm, name, &cerrh);
+    if (fromr == tor)
+      tor->kill_element(which);
+    return -1;
   }
-  
-  // get element type index. add new "anonymous" element type if needed
-  String type_name = fromr->type_name(e.type);
-  ElementClassT *type_class = fromr->type_class(e.type);
-  int new_type = tor->get_type_index(type_name, type_class);
-  ElementClassT *new_type_class = tor->type_class(new_type);
-  if (type_class != new_type_class && new_type_class)
-    new_type = tor->get_anon_type_index(type_name, type_class);
-  
-  // add element
-  return tor->get_eindex(scope.prefix() + e.name, new_type,
-			 scope.interpolate(e.configuration), e.landmark);
+
+  return found_c->complex_expand_element(fromr, which, new_configuration, args,
+					 tor, env, errh);
 }
 
 int
-ElementClassT::expand_into(RouterT *fromr, int which,
-			   RouterT *tor, const RouterScope &scope,
-			   ErrorHandler *errh)
+ElementClassT::complex_expand_element(RouterT *fromr, int which, const String &, Vector<String> &,
+				      RouterT *tor, const VariableEnvironment &env, ErrorHandler *errh)
 {
-  return simple_expand_into(fromr, which, tor, scope, errh);
+  return direct_expand_element(fromr, which, this, tor, env, errh);
 }
 
 void
-ElementClassT::compound_declaration_string(StringAccum &, const String &, const String &)
+ElementClassT::declaration_string(StringAccum &, const String &, const String &)
+{
+}
+
+String
+ElementClassT::signature(const String &name, int ninputs, int noutputs, int nargs)
 {
+  const char *pl_args = (nargs == 1 ? " argument, " : " arguments, ");
+  const char *pl_ins = (ninputs == 1 ? " input, " : " inputs, ");
+  const char *pl_outs = (noutputs == 1 ? " output" : " outputs");
+  StringAccum sa;
+  sa << name << '[' << nargs << pl_args << ninputs << pl_ins << noutputs << pl_outs << ']';
+  return sa.take_string();
 }
 
 
@@ -162,9 +212,9 @@ SynonymElementClassT::SynonymElementClassT(const String &name, ElementClassT *ec
 }
 
 int
-SynonymElementClassT::expand_into(RouterT *fromr, int which,
-				  RouterT *tor, const RouterScope &scope,
-				  ErrorHandler *)
+SynonymElementClassT::complex_expand_element(
+	RouterT *fromr, int which, const String &new_config, Vector<String> &,
+	RouterT *tor, const VariableEnvironment &env, ErrorHandler *errh)
 {
   // get element type index. add new "anonymous" element type if needed
   int new_type = tor->get_type_index(_name, _eclass);
@@ -173,16 +223,212 @@ SynonymElementClassT::expand_into(RouterT *fromr, int which,
     new_type = tor->get_anon_type_index(_name, _eclass);
 
   ElementT &e = fromr->element(which);
-  if (fromr == tor) {
+  if (fromr == tor && !env.prefix()) {
+    e.configuration = new_config; // interpolated
     e.type = new_type;
     return which;
-  } else
-    return tor->get_eindex(scope.prefix() + e.name, new_type,
-			   scope.interpolate(e.configuration), e.landmark);
+  } else {
+    String new_name = env.prefix() + e.name;
+    int new_eidx = tor->eindex(new_name);
+    if (new_eidx >= 0) {
+      errh->lerror(e.landmark, "redeclaration of element `%s'", new_name.cc());
+      errh->lerror(tor->elandmark(new_eidx), "`%s' previously declared here", tor->edeclaration(new_eidx).cc());
+      return -1;
+    } else
+      return tor->get_eindex(new_name, new_type, new_config, e.landmark);
+  }
 }
 
 void
-SynonymElementClassT::compound_declaration_string(StringAccum &sa, const String &name, const String &indent)
+SynonymElementClassT::declaration_string(StringAccum &sa, const String &name, const String &indent)
 {
   sa << indent << "elementclass " << name << " " << _name << ";\n";
 }
+
+
+CompoundElementClassT::CompoundElementClassT(RouterT *r)
+  : _router(r), _depth(0), _ninputs(0), _noutputs(0), _next(0),
+    _circularity_flag(false)
+{
+  _router->use();
+}
+
+CompoundElementClassT::CompoundElementClassT(const String &name, const String &landmark, RouterT *r, ElementClassT *next, int depth)
+  : _name(name), _landmark(landmark), _router(r), _depth(depth),
+    _ninputs(0), _noutputs(0), _next(next),
+    _circularity_flag(false)
+{
+  _router->use();
+  if (_next)
+    _next->use();
+}
+
+CompoundElementClassT::~CompoundElementClassT()
+{
+  _router->unuse();
+  if (_next)
+    _next->unuse();
+}
+
+void
+CompoundElementClassT::finish(ErrorHandler *errh)
+{
+  if (!errh)
+    errh = ErrorHandler::silent_handler();
+
+  int einput = _router->eindex("input");
+  if (einput >= 0) {
+    _ninputs = _router->noutputs(einput);
+    
+    if (_router->ninputs(einput))
+      errh->lerror(_landmark, "`%s' pseudoelement `input' may only be used as output", _name.cc());
+
+    if (_ninputs) {
+      Vector<int> used;
+      _router->find_connection_vector_from(einput, used);
+      assert(used.size() == _ninputs);
+      for (int i = 0; i < _ninputs; i++)
+	if (used[i] == -1)
+	  errh->lerror(_landmark, "compound element `%s' input %d unused", _name.cc(), i);
+    }
+  } else
+    _ninputs = 0;
+
+  int eoutput = _router->eindex("output");
+  if (eoutput >= 0) {
+    _noutputs = _router->ninputs(eoutput);
+    if (_router->noutputs(eoutput))
+      errh->lerror(_landmark, "`%s' pseudoelement `output' may only be used as input", _name.cc());
+
+    if (_noutputs) {
+      Vector<int> used;
+      _router->find_connection_vector_to(eoutput, used);
+      assert(used.size() == _noutputs);
+      for (int i = 0; i < _noutputs; i++)
+	if (used[i] == -1)
+	  errh->lerror(_landmark, "compound element `%s' output %d unused", _name.cc(), i);
+    }
+  } else
+    _noutputs = 0;
+}
+
+void
+CompoundElementClassT::check_duplicates_until(ElementClassT *last, ErrorHandler *errh)
+{
+  if (this == last || !_next)
+    return;
+  
+  ElementClassT *n = _next;
+  while (n && n != last) {
+    CompoundElementClassT *nc = n->cast_compound();
+    if (!nc) break;
+    if (nc->_ninputs == _ninputs && nc->_noutputs == _noutputs && nc->_formals.size() == _formals.size()) {
+      errh->lerror(_landmark, "redeclaration of `%s'", signature().cc());
+      errh->lerror(nc->_landmark, "`%s' previously declared here", signature().cc());
+      break;
+    }
+    n = nc->_next;
+  }
+
+  if (CompoundElementClassT *nc = _next->cast_compound())
+    nc->check_duplicates_until(last, errh);
+}
+
+ElementClassT *
+CompoundElementClassT::find_relevant_class(int ninputs, int noutputs, const Vector<String> &args)
+{
+  if (ninputs == _ninputs && noutputs == _noutputs && args.size() == _formals.size())
+    return this;
+  else if (_next)
+    return _next->find_relevant_class(ninputs, noutputs, args);
+  else
+    return 0;
+}
+
+String
+CompoundElementClassT::signature() const
+{
+  return ElementClassT::signature(_name, _ninputs, _noutputs, _formals.size());
+}
+
+void
+CompoundElementClassT::report_signatures(const String &lm, String name, ErrorHandler *errh)
+{
+  if (_next)
+    _next->report_signatures(lm, name, errh);
+  errh->lmessage(lm, "`%s'", signature().cc());
+}
+
+int
+CompoundElementClassT::complex_expand_element(
+	RouterT *fromr, int which, const String &, Vector<String> &args,
+	RouterT *tor, const VariableEnvironment &env, ErrorHandler *errh)
+{
+  assert(fromr != _router && tor != _router);
+  assert(!_circularity_flag);
+  _circularity_flag = true;
+  
+  // must make a copy of `compound' because we might be growing the _elements
+  // vector, in which case our reference would die
+  ElementT compound = fromr->element(which);
+  
+  // parse configuration string
+  int nargs = _formals.size();
+  if (args.size() != nargs) {
+    const char *whoops = (args.size() < nargs ? "few" : "many");
+    String signature;
+    for (int i = 0; i < nargs; i++) {
+      if (i) signature += ", ";
+      signature += _formals[i];
+    }
+    if (errh)
+      errh->lerror(compound.landmark,
+		   "too %s arguments to compound element `%s(%s)'", whoops,
+		   compound.name.cc() /* XXX should be class_name */, signature.cc());
+    for (int i = args.size(); i < nargs; i++)
+      args.push_back("");
+  }
+
+  // create prefix
+  assert(compound.name);
+  VariableEnvironment new_env(env, compound.name);
+  String prefix = env.prefix();
+  String new_prefix = new_env.prefix(); // includes previous prefix
+  new_env.limit_depth(_depth);
+  new_env.enter(_formals, args, _depth);
+
+  // create input/output tunnels
+  if (fromr == tor)
+    tor->element(which).type = RouterT::TUNNEL_TYPE;
+  tor->add_tunnel(prefix + compound.name, new_prefix + "input", compound.landmark, errh);
+  tor->add_tunnel(new_prefix + "output", prefix + compound.name, compound.landmark, errh);
+  int new_eindex = tor->eindex(prefix + compound.name);
+
+  // dump compound router into `tor'
+  _router->expand_into(tor, new_env, errh);
+  
+  // yes, we expanded it
+  _circularity_flag = false;
+  return new_eindex;
+}
+
+void
+CompoundElementClassT::declaration_string(StringAccum &sa, const String &name, const String &indent)
+{
+  assert(!_circularity_flag);
+  _circularity_flag = true;
+
+  sa << indent << "elementclass " << name << " {";
+  
+  // print formals
+  for (int i = 0; i < _formals.size(); i++)
+    sa << (i ? ", " : " ") << _formals[i];
+  if (_formals.size())
+    sa << " |";
+  sa << "\n";
+
+  _router->configuration_string(sa, indent + "  ");
+  
+  sa << indent << "}\n";
+  _circularity_flag = false;
+}
diff --git a/tools/lib/elementt.hh b/tools/lib/elementt.hh
index 3c98107b61fdeafd2ef02e30c44d47fbf6d50b86..9757bf1819b56c7d7d7727257beb64cdf3896131 100644
--- a/tools/lib/elementt.hh
+++ b/tools/lib/elementt.hh
@@ -4,9 +4,10 @@
 #include <stddef.h>
 #include <click/vector.hh>
 class RouterT;
-class RouterScope;
+class VariableEnvironment;
 class ErrorHandler;
 class StringAccum;
+class CompoundElementClassT;
 
 struct ElementT {
   
@@ -47,11 +48,7 @@ struct Hookup {
   
 };
 
-class ElementClassT {
-
-  int _use_count;
-  
- public:
+class ElementClassT { public:
 
   ElementClassT();
   virtual ~ElementClassT()		{ }
@@ -59,14 +56,25 @@ class ElementClassT {
   void use()				{ _use_count++; }
   void unuse()				{ if (--_use_count <= 0) delete this; }
 
-  static int simple_expand_into(RouterT *, int, RouterT *,
-				const RouterScope &, ErrorHandler *);
-  virtual int expand_into(RouterT *, int, RouterT *,
-			  const RouterScope &, ErrorHandler *);
-  virtual void compound_declaration_string(StringAccum &, const String &, const String &);
+  static int expand_element(RouterT *, int, RouterT *, const VariableEnvironment &, ErrorHandler *);
+  
+  virtual ElementClassT *find_relevant_class(int ninputs, int noutputs, const Vector<String> &);
+  virtual void report_signatures(const String &, String, ErrorHandler *);
+  virtual int complex_expand_element(RouterT *, int, const String &, Vector<String> &, RouterT *, const VariableEnvironment &, ErrorHandler *);
+  
+  virtual void declaration_string(StringAccum &, const String &, const String &);
 
-  virtual bool expands_away() const	{ return false; }
+  virtual bool direct_expansion() const	{ return true; }
+  virtual CompoundElementClassT *cast_compound() { return 0; }
   virtual RouterT *cast_router()	{ return 0; }
+
+  static String signature(const String &, int, int, int);
+  
+ private:
+  
+  int _use_count;
+  
+  static int direct_expand_element(RouterT *, int, ElementClassT *, RouterT *, const VariableEnvironment &, ErrorHandler *);
   
 };
 
@@ -79,12 +87,51 @@ class SynonymElementClassT : public ElementClassT {
 
   SynonymElementClassT(const String &, ElementClassT *);
 
-  int expand_into(RouterT *, int, RouterT *,
-		  const RouterScope &, ErrorHandler *);
-  void compound_declaration_string(StringAccum &, const String &,
-				   const String &);
+  int complex_expand_element(RouterT *, int, const String &, Vector<String> &, RouterT *, const VariableEnvironment &, ErrorHandler *);
+  
+  void declaration_string(StringAccum &, const String &, const String &);
+
+  bool direct_expansion() const		{ return false; }
+  
+};
+
+class CompoundElementClassT : public ElementClassT {
+
+  String _name;
+  String _landmark;
+  RouterT *_router;
+  int _depth;
+  Vector<String> _formals;
+  int _ninputs;
+  int _noutputs;
+  
+  ElementClassT *_next;
+  
+  bool _circularity_flag;
+  
+  int actual_expand(RouterT *, int, RouterT *, const VariableEnvironment &, ErrorHandler *);
+  
+ public:
+
+  CompoundElementClassT(RouterT *);
+  CompoundElementClassT(const String &name, const String &landmark, RouterT *, ElementClassT *, int depth);
+  ~CompoundElementClassT();
+
+  void add_formal(const String &n)	{ _formals.push_back(n); }
+  void finish(ErrorHandler *);
+  void check_duplicates_until(ElementClassT *, ErrorHandler *);
+  
+  ElementClassT *find_relevant_class(int ninputs, int noutputs, const Vector<String> &);
+  void report_signatures(const String &, String, ErrorHandler *);
+  int complex_expand_element(RouterT *, int, const String &, Vector<String> &, RouterT *, const VariableEnvironment &, ErrorHandler *);
+  
+  void declaration_string(StringAccum &, const String &, const String &);
+
+  bool direct_expansion() const		{ return false; }
+  CompoundElementClassT *cast_compound() { return this; }
+  RouterT *cast_router()		{ return _router; }
 
-  bool expands_away() const		{ return true; }
+  String signature() const;
   
 };
 
diff --git a/tools/lib/lexert.cc b/tools/lib/lexert.cc
index 6958c0036e2e6b095045d214345e13ca27f78e46..e63fae6c6f9b3d8ff0f57634adb60880135625f2 100644
--- a/tools/lib/lexert.cc
+++ b/tools/lib/lexert.cc
@@ -66,7 +66,7 @@ void
 LexerT::clear()
 {
   if (_router)
-    _router->unuse();
+    delete _router;
   _router = new RouterT;
   
   _big_string = "";
@@ -79,15 +79,15 @@ LexerT::clear()
   _tpos = 0;
   _tfull = 0;
   
-  _element_prefix = "";
   _anonymous_offset = 0;
+  _compound_depth = 0;
 }
 
 void
 LexerT::set_router(RouterT *r)
 {
   if (_router)
-    _router->unuse();
+    delete _router;
   _router = r;
 }
 
@@ -263,8 +263,15 @@ LexerT::next_lexeme()
     } else if (_data[pos] == ':' && _data[pos+1] == ':') {
       _pos = pos + 2;
       return Lexeme(lex2Colon, _big_string.substring(word_pos, 2));
+    } else if (_data[pos] == '|' && _data[pos+1] == '|') {
+      _pos = pos + 2;
+      return Lexeme(lex2Bar, _big_string.substring(word_pos, 2));
     }
   }
+  if (pos < _len - 2 && _data[pos] == '.' && _data[pos+1] == '.' && _data[pos+2] == '.') {
+    _pos = pos + 3;
+    return Lexeme(lex3Dot, _big_string.substring(word_pos, 3));
+  }
   
   _pos = pos + 1;
   return Lexeme(_data[word_pos], _big_string.substring(word_pos, 1));
@@ -312,10 +319,16 @@ LexerT::lexeme_string(int kind)
     return "`->'";
   else if (kind == lex2Colon)
     return "`::'";
+  else if (kind == lex2Bar)
+    return "`||'";
+  else if (kind == lex3Dot)
+    return "`...'";
   else if (kind == lexTunnel)
     return "`connectiontunnel'";
   else if (kind == lexElementclass)
     return "`elementclass'";
+  else if (kind == lexRequire)
+    return "`require'";
   else if (kind >= 32 && kind < 127) {
     sprintf(buf, "`%c'", kind);
     return buf;
@@ -404,7 +417,7 @@ LexerT::force_element_type(String s)
 String
 LexerT::anon_element_name(const String &class_name) const
 {
-  String prefix = _element_prefix + class_name + "@";
+  String prefix = class_name + "@";
   int anonymizer = _router->nelements() - _anonymous_offset + 1;
   String name = prefix + String(anonymizer);
   while (_router->eindex(name) >= 0) {
@@ -472,21 +485,6 @@ LexerT::yport(int &port)
   }
 }
 
-bool
-LexerT::yelement_upref(int &element)
-{
-  Lexeme t = lex();
-  if (!t.is(lexIdent)) {
-    lerror("syntax error: expected element name");
-    unlex(t);
-    return false;
-  }
-
-  element = _router->get_anon_eindex
-    (_element_prefix + t.string(), RouterT::UPREF_TYPE, String(), landmark());
-  return true;
-}
-
 bool
 LexerT::yelement(int &element, bool comma_ok)
 {
@@ -494,13 +492,11 @@ LexerT::yelement(int &element, bool comma_ok)
   String name;
   int ftype;
 
-  if (t.is('^')) {
-    return yelement_upref(element);
-  } else if (t.is(lexIdent)) {
+  if (t.is(lexIdent)) {
     name = t.string();
     ftype = element_type(name);
   } else if (t.is('{')) {
-    ftype = ylocal();
+    ftype = ycompound(String());
     name = _router->type_name(ftype);
   } else {
     unlex(t);
@@ -520,15 +516,14 @@ LexerT::yelement(int &element, bool comma_ok)
   if (ftype >= 0)
     element = make_anon_element(name, ftype, configuration, lm);
   else {
-    String lookup_name = _element_prefix + name;
-    element = _router->eindex(lookup_name);
+    element = _router->eindex(name);
     
     const Lexeme &t2colon = lex();
     unlex(t2colon);
 
     if (t2colon.is(lex2Colon) || (t2colon.is(',') && comma_ok)) {
       ydeclaration(name);
-      element = _router->eindex(lookup_name);
+      element = _router->eindex(name);
     } else if (element < 0) {
       // assume it's an element type
       ftype = force_element_type(name);
@@ -576,7 +571,7 @@ LexerT::ydeclaration(const String &first_element)
   if (t.is(lexIdent))
     ftype = force_element_type(t.string());
   else if (t.is('{'))
-    ftype = ylocal();
+    ftype = ycompound(String());
   else {
     lerror("missing element type in declaration");
     return;
@@ -592,17 +587,14 @@ LexerT::ydeclaration(const String &first_element)
   
   for (int i = 0; i < decls.size(); i++) {
     String name = decls[i];
-    String lookup_name = _element_prefix + name;
-    int old_eidx = _router->eindex(lookup_name);
+    int old_eidx = _router->eindex(name);
     if (old_eidx >= 0) {
       lerror("redeclaration of element `%s'", name.cc());
       _errh->lerror(_router->elandmark(old_eidx), "`%s' previously declared here", _router->edeclaration(old_eidx).cc());
     } else if (_router->type_index(name) >= 0)
       lerror("`%s' is an element class", name.cc());
-    else if (_router->type_index(lookup_name) >= 0)
-      lerror("`%s' is an element class", lookup_name.cc());
     else
-      make_element(lookup_name, ftype, configuration, lm);
+      make_element(name, ftype, configuration, lm);
   }
 }
 
@@ -650,9 +642,12 @@ LexerT::yconnection()
       goto relex;
       
      case lexIdent:
-     case '^':
      case '{':
      case '}':
+     case lex2Bar:
+     case lexTunnel:
+     case lexElementclass:
+     case lexRequire:
       unlex(t);
       // FALLTHRU
      case ';':
@@ -674,27 +669,6 @@ LexerT::yconnection()
   }
 }
 
-void
-LexerT::ycompound_arguments()
-{
-  while (1) {
-    const Lexeme &tvar = lex();
-    if (!tvar.is(lexVariable)) {
-      unlex(tvar);
-      return;
-    }
-    _router->add_formal(tvar.string());
-    const Lexeme &tsep = lex();
-    if (tsep.is('|'))
-      return;
-    else if (!tsep.is(',')) {
-      lerror("expected `,' or `|'");
-      unlex(tsep);
-      return;
-    }
-  }
-}
-
 void
 LexerT::yelementclass()
 {
@@ -712,29 +686,11 @@ LexerT::yelementclass()
   }
 
   Lexeme tnext = lex();
-  if (tnext.is('{')) {
-    RouterT *old_router = _router;
-    int old_offset = _anonymous_offset;
-    _router = new RouterT(old_router);
-    _router->get_eindex("input", RouterT::TUNNEL_TYPE, String(), landmark());
-    _router->get_eindex("output", RouterT::TUNNEL_TYPE, String(), landmark());
-    _anonymous_offset = 2;
-    
-    ycompound_arguments();
-    while (ystatement(true))
-      /* nada */;
-    
-    // '}' was consumed
-    
-    if (facclass_name)
-      old_router->add_type_index(facclass_name, _router);
-    
-    _router->unuse();
-    _router = old_router;
-    _anonymous_offset = old_offset;
+  if (tnext.is('{'))
+    (void) ycompound(facclass_name);
     
-  } else if (tnext.is(lexIdent)) {
-    ElementClassT *tclass = _router->find_type_class(tnext.string());
+  else if (tnext.is(lexIdent)) {
+    ElementClassT *tclass = _router->type_class(tnext.string());
     if (facclass_name)
       _router->add_type_index(facclass_name, new SynonymElementClassT(tnext.string(), tclass));
 
@@ -771,31 +727,83 @@ LexerT::ytunnel()
   }
 }
 
+void
+LexerT::ycompound_arguments(CompoundElementClassT *comptype)
+{
+  while (1) {
+    const Lexeme &tvar = lex();
+    if (!tvar.is(lexVariable)) {
+      unlex(tvar);
+      return;
+    }
+    comptype->add_formal(tvar.string());
+    const Lexeme &tsep = lex();
+    if (tsep.is('|'))
+      return;
+    else if (!tsep.is(',')) {
+      lerror("expected `,' or `|'");
+      unlex(tsep);
+      return;
+    }
+  }
+}
+
 int
-LexerT::ylocal()
+LexerT::ycompound(String name)
 {
-  // OK because every used ylocal() corresponds to at least one element
-  String name = "@Class" + String(_router->nelements() - _anonymous_offset + 1);
+  bool anonymous = (name.length() == 0);
+  if (anonymous)
+    // OK because every used ylocal() corresponds to at least one element
+    name = "@Class" + String(_router->nelements() - _anonymous_offset + 1);
   
   // '{' was already read
   RouterT *old_router = _router;
   int old_offset = _anonymous_offset;
-  _router = new RouterT(old_router);
-  _router->get_eindex("input", RouterT::TUNNEL_TYPE, String(), landmark());
-  _router->get_eindex("output", RouterT::TUNNEL_TYPE, String(), landmark());
-  _anonymous_offset = 2;
+  ElementClassT *created = 0;
 
-  ycompound_arguments();
-  while (ystatement(true))
-    /* nada */;
+  // check for '...'
+  const Lexeme &t = lex();
+  if (t.is(lex3Dot)) {
+    if (_router->type_index(name) < 0)
+      _router->add_type_index(name, new ElementClassT);
+    created = _router->type_class(name);
+    expect(lex2Bar);
+  } else
+    unlex(t);
+
+  ElementClassT *first = created;
   
-  // '}' was consumed
+  while (1) {
+    _router = new RouterT(old_router);
+    _router->get_eindex("input", RouterT::TUNNEL_TYPE, String(), landmark());
+    _router->get_eindex("output", RouterT::TUNNEL_TYPE, String(), landmark());
+    CompoundElementClassT *ct = new CompoundElementClassT(name, landmark(), _router, created, _compound_depth);
+    _anonymous_offset = 2;
+    _compound_depth++;
 
-  int tindex = old_router->get_anon_type_index(name, _router);
-  _router->unuse();
-  _router = old_router;
-  _anonymous_offset = old_offset;
-  return tindex;
+    ycompound_arguments(ct);
+    while (ystatement(true))
+      /* nada */;
+    
+    _compound_depth--;
+    _anonymous_offset = old_offset;
+    _router = old_router;
+    
+    ct->finish(_errh);
+    created = ct;
+
+    // check for '||' or '}'
+    const Lexeme &t = lex();
+    if (!t.is(lex2Bar))
+      break;
+  }
+
+  created->cast_compound()->check_duplicates_until(first, _errh);
+  
+  if (anonymous)
+    return old_router->get_anon_type_index(name, created);
+  else
+    return old_router->add_type_index(name, created);
 }
 
 void
@@ -823,7 +831,6 @@ LexerT::ystatement(bool nested)
   switch (t.kind()) {
     
    case lexIdent:
-   case '^':
    case '[':
    case '{':
     unlex(t);
@@ -846,8 +853,10 @@ LexerT::ystatement(bool nested)
     return true;
     
    case '}':
+   case lex2Bar:
     if (!nested)
       goto syntax_error;
+    unlex(t);
     return false;
     
    case lexEOF:
diff --git a/tools/lib/lexert.hh b/tools/lib/lexert.hh
index 087880986fb46033d919d34d8455068400311f35..5a593a63b1b1147a81b2f7e5de13e40526c94bec 100644
--- a/tools/lib/lexert.hh
+++ b/tools/lib/lexert.hh
@@ -3,6 +3,7 @@
 #include <click/error.hh>
 #include <stdio.h>
 class RouterT;
+class CompoundElementClassT;
 
 enum {
   lexEOF = 0,
@@ -10,6 +11,8 @@ enum {
   lexVariable,
   lexArrow,
   lex2Colon,
+  lex2Bar,
+  lex3Dot,
   lexTunnel,
   lexElementclass,
   lexRequire,
@@ -64,8 +67,8 @@ class LexerT { protected:
   // router
   RouterT *_router;
 
-  String _element_prefix;
   int _anonymous_offset;
+  int _compound_depth;
   
   // errors
   ErrorHandler *_errh;
@@ -99,13 +102,12 @@ class LexerT { protected:
   
   bool yport(int &port);
   bool yelement(int &element, bool comma_ok);
-  bool yelement_upref(int &element);
   void ydeclaration(const String &first_element = "");
   bool yconnection();
-  void ycompound_arguments();
+  void ycompound_arguments(CompoundElementClassT *);
   void yelementclass();
   void ytunnel();
-  int ylocal();
+  int ycompound(String);
   void yrequire();
   bool ystatement(bool nested = false);
 
diff --git a/tools/lib/processingt.cc b/tools/lib/processingt.cc
index 17959576b3812bbe23f245e32d6627bbc5158d6d..770854d5f08847553ecb2efb72791b5fd4810649 100644
--- a/tools/lib/processingt.cc
+++ b/tools/lib/processingt.cc
@@ -130,7 +130,7 @@ ProcessingT::initial_processing_for(int ei, const ElementMap &em, ErrorHandler *
 {
   // don't handle uprefs or tunnels
   int etype = _router->etype(ei);
-  if (etype < 0 || etype == RouterT::TUNNEL_TYPE || etype == RouterT::UPREF_TYPE)
+  if (etype < 0 || etype == RouterT::TUNNEL_TYPE)
     return;
   
   String etype_name = _router->type_name(etype);
diff --git a/tools/lib/routert.cc b/tools/lib/routert.cc
index 0bfa3ba9c647cc7bf5179271843ce8eb4cdae262..393cf0b5bef702be546e47de4dfad3d432ffec2e 100644
--- a/tools/lib/routert.cc
+++ b/tools/lib/routert.cc
@@ -25,28 +25,28 @@
 #include <click/bitvector.hh>
 #include <click/confparse.hh>
 #include <click/straccum.hh>
+#include <click/variableenv.hh>
 #include <stdio.h>
 
 RouterT::RouterT(RouterT *enclosing)
-  : _enclosing_scope(enclosing),
-    _element_type_map(-1), _element_name_map(-1),
+  : _use_count(0), _element_type_map(-1), _element_name_map(-1),
     _free_element(-1), _real_ecount(0), _new_eindex_collector(0),
     _free_hookup(-1), _archive_map(-1)
 {
-  if (_enclosing_scope)
-    _enclosing_scope->use();
-  // add space for tunnel and upref types
+  // add space for tunnel type
   _element_type_names.push_back("<tunnel>");
   _element_type_map.insert("<tunnel>", TUNNEL_TYPE);
   _element_classes.push_back(0);
-  _element_type_names.push_back("<upref>");
-  _element_type_map.insert("<upref>", UPREF_TYPE);
-  _element_classes.push_back(0);
+  // borrow definitions from `enclosing'
+  if (enclosing) {
+    for (int i = 0; i < enclosing->_element_classes.size(); i++)
+      if (ElementClassT *ec = enclosing->_element_classes[i])
+	add_type_index(enclosing->_element_type_names[i], ec);
+  }
 }
 
 RouterT::RouterT(const RouterT &o)
-  : ElementClassT(),
-    _enclosing_scope(o._enclosing_scope),
+  : _use_count(0),
     _element_type_map(o._element_type_map),
     _element_type_names(o._element_type_names),
     _element_classes(o._element_classes),
@@ -65,8 +65,6 @@ RouterT::RouterT(const RouterT &o)
     _archive_map(o._archive_map),
     _archive(o._archive)
 {
-  if (_enclosing_scope)
-    _enclosing_scope->use();
   for (int i = 0; i < _element_classes.size(); i++)
     if (_element_classes[i])
       _element_classes[i]->use();
@@ -74,8 +72,6 @@ RouterT::RouterT(const RouterT &o)
 
 RouterT::~RouterT()
 {
-  if (_enclosing_scope)
-    _enclosing_scope->unuse();
   for (int i = 0; i < _element_classes.size(); i++)
     if (_element_classes[i])
       _element_classes[i]->unuse();
@@ -162,30 +158,8 @@ RouterT::is_flat() const
 }
 
 
-ElementClassT *
-RouterT::find_type_class(const String &s) const
-{
-  const RouterT *r = this;
-  while (r) {
-    int i = r->_element_type_map[s];
-    if (i >= 0) return r->_element_classes[i];
-    r = r->_enclosing_scope;
-  }
-  return 0;
-}
-
-int
-RouterT::get_type_index(const String &s)
-{
-  int i = _element_type_map[s];
-  if (i >= 0)
-    return i;
-  else
-    return get_type_index(s, find_type_class(s));
-}
-
 int
-RouterT::get_type_index(const String &name, ElementClassT *eclass)
+RouterT::get_type_index(const String &name, ElementClassT *eclass = 0)
 {
   int i = _element_type_map[name];
   if (i < 0) {
@@ -815,8 +789,7 @@ RouterT::finish_remove_elements(Vector<int> &new_eindex, ErrorHandler *errh)
   for (int i = 0; i < nelements; i++) {
     j = new_eindex[i];
     if (j != i) {
-      if (_elements[i].type != UPREF_TYPE)
-	_element_name_map.insert(_elements[i].name, j);
+      _element_name_map.insert(_elements[i].name, j);
       if (j >= 0) {
 	_elements[j] = _elements[i];
 	_hookup_first[j] = _hookup_first[i];
@@ -969,8 +942,8 @@ RouterT::finish_remove_element_types(Vector<int> &new_tindex)
   int nelements = _elements.size();
 
   // find new ftypeindexes
-  // save TUNNEL_TYPE and UPREF_TYPE
-  new_tindex[TUNNEL_TYPE] = new_tindex[UPREF_TYPE] = 0;
+  // save TUNNEL_TYPE
+  new_tindex[TUNNEL_TYPE] = 0;
   int j = 0;
   for (int i = 0; i < ntype; i++)
     if (new_tindex[i] >= 0)
@@ -979,7 +952,6 @@ RouterT::finish_remove_element_types(Vector<int> &new_tindex)
 
   // return if nothing has changed
   assert(new_tindex[TUNNEL_TYPE] == TUNNEL_TYPE);
-  assert(new_tindex[UPREF_TYPE] == UPREF_TYPE);
   if (new_ntype == ntype)
     return;
 
@@ -1015,6 +987,32 @@ RouterT::remove_unused_element_types()
 }
 
 
+void
+RouterT::expand_into(RouterT *tor, const VariableEnvironment &env, ErrorHandler *errh)
+{
+  assert(tor != this);
+  
+  int nelements = _elements.size();
+  Vector<int> new_fidx(nelements, -1);
+  
+  // add tunnel pairs and expand below
+  for (int i = 0; i < nelements; i++)
+    new_fidx[i] = ElementClassT::expand_element(this, i, tor, env, errh);
+  
+  // add hookup
+  int nh = _hookup_from.size();
+  for (int i = 0; i < nh; i++) {
+    const Hookup &hf = _hookup_from[i], &ht = _hookup_to[i];
+    tor->add_connection(Hookup(new_fidx[hf.idx], hf.port),
+			Hookup(new_fidx[ht.idx], ht.port),
+			_hookup_landmark[i]);
+  }
+
+  // add requirements
+  for (int i = 0; i < _requirements.size(); i++)
+    tor->add_requirement(_requirements[i]);
+}
+
 void
 RouterT::expand_tunnel(Vector<Hookup> *pp_expansions,
 		       bool is_input, int magice, int which,
@@ -1140,205 +1138,22 @@ RouterT::remove_tunnels()
 }
 
 
-RouterScope::RouterScope(const RouterScope &o, const String &suffix)
-  : _prefix(o._prefix + suffix), _formals(o._formals), _values(o._values)
-{
-}
-
-void
-RouterScope::combine(const Vector<String> &formals, const Vector<String> &values)
-{
-  for (int i = 0; i < formals.size(); i++) {
-    for (int j = 0; j < _formals.size(); j++)
-      if (_formals[j] == formals[i]) {
-	_values[j] = values[i];
-	goto done;
-      }
-    _formals.push_back(formals[i]);
-    _values.push_back(values[i]);
-   done: ;
-  }
-}
-
-String
-RouterScope::interpolate(const String &config) const
-{
-  if (_formals.size() == 0)
-    return config;
-  
-  const char *data = config.data();
-  int config_pos = 0;
-  int pos = 0;
-  int len = config.length();
-  int quote = 0;
-  String output;
-  
-  for (; pos < len; pos++)
-    if (data[pos] == '\\' && pos < len - 1 && quote == '\"')
-      pos++;
-    else if (data[pos] == '\'' && quote == 0)
-      quote = '\'';
-    else if (data[pos] == '\"' && quote == 0)
-      quote = '\"';
-    else if (data[pos] == quote)
-      quote = 0;
-    else if (data[pos] == '/' && pos < len - 1) {
-      if (data[pos+1] == '/') {
-	for (pos += 2; pos < len && data[pos] != '\n' && data[pos] != '\r'; )
-	  pos++;
-      } else if (data[pos+1] == '*') {
-	for (pos += 2; pos < len; pos++)
-	  if (data[pos] == '*' && pos < len - 1 && data[pos+1] == '/') {
-	    pos++;
-	    break;
-	  }
-      }
-    } else if (data[pos] == '$' && quote != '\'') {
-      unsigned word_pos = pos;
-      for (pos++; isalnum(data[pos]) || data[pos] == '_'; pos++)
-	/* nada */;
-      String name = config.substring(word_pos, pos - word_pos);
-      for (int variable = 0; variable < _formals.size(); variable++)
-	if (name == _formals[variable]) {
-	  output += config.substring(config_pos, word_pos - config_pos);
-	  String value = _values[variable];
-	  if (quote == '\"') {	// interpolate inside the quotes
-	    value = cp_quote(cp_unquote(value));
-	    if (value[0] == '\"')
-	      value = value.substring(1, value.length() - 2);
-	  }
-	  output += value;
-	  config_pos = pos;
-	}
-      pos--;
-    }
-
-  if (!output)
-    return config;
-  else
-    return output + config.substring(config_pos, pos - config_pos);
-}
-
-int
-RouterT::expand_into(RouterT *fromr, int which, RouterT *tor,
-		     const RouterScope &scope, ErrorHandler *errh)
-{
-  assert(fromr != this && tor != this);
-  // must make a copy of `compound' because we might be growing the _elements
-  // vector, in which case our reference would die
-  ElementT compound = fromr->element(which);
-  
-  // parse configuration string
-  Vector<String> args;
-  int nargs = _formals.size();
-  cp_argvec(scope.interpolate(compound.configuration), args);
-  if (args.size() != nargs) {
-    const char *whoops = (args.size() < nargs ? "few" : "many");
-    String signature;
-    for (int i = 0; i < nargs; i++) {
-      if (i) signature += ", ";
-      signature += _formals[i];
-    }
-    if (errh)
-      errh->lerror(compound.landmark,
-		   "too %s arguments to compound element `%s(%s)'", whoops,
-		   compound.name.cc() /* XXX should be class_name */, signature.cc());
-    for (int i = args.size(); i < nargs; i++)
-      args.push_back("");
-  }
-
-  // create prefix
-  String suffix;
-  assert(compound.name);
-  if (compound.name[compound.name.length() - 1] == '/')
-    suffix = compound.name;
-  else
-    suffix = compound.name + "/";
-  
-  RouterScope new_scope(scope, suffix);
-  String prefix = scope.prefix();
-  String new_prefix = new_scope.prefix(); // includes previous prefix
-  new_scope.combine(_formals, args);
-
-  // create input/output tunnels
-  if (fromr == tor)
-    tor->element(which).type = TUNNEL_TYPE;
-  tor->add_tunnel(prefix + compound.name, new_prefix + "input", compound.landmark, errh);
-  tor->add_tunnel(new_prefix + "output", prefix + compound.name, compound.landmark, errh);
-  int new_eindex = tor->eindex(prefix + compound.name);
-
-  int nelements = _elements.size();
-  Vector<int> new_fidx(nelements, -1);
-  
-  // add tunnel pairs and resolve uprefs
-  for (int i = 0; i < nelements; i++) {
-    ElementClassT *ect = _element_classes[_elements[i].type];
-    if (ect)
-      new_fidx[i] = ect->expand_into(this, i, tor, new_scope, errh);
-    else
-      new_fidx[i] = ElementClassT::simple_expand_into(this, i, tor, new_scope, errh);
-  }
-  
-  // add hookup
-  for (int i = 0; i < _hookup_from.size(); i++) {
-    Hookup &hfrom = _hookup_from[i], &hto = _hookup_to[i];
-    tor->add_connection(Hookup(new_fidx[hfrom.idx], hfrom.port),
-			Hookup(new_fidx[hto.idx], hto.port),
-			_hookup_landmark[i]);
-  }
-
-  // add requirements
-  for (int i = 0; i < _requirements.size(); i++)
-    tor->add_requirement(_requirements[i]);
-  
-  // yes, we expanded it
-  return new_eindex;
-}
-
 void
 RouterT::remove_compound_elements(ErrorHandler *errh)
 {
   int nelements = _elements.size();
-  RouterScope scope;
+  VariableEnvironment env;
   for (int i = 0; i < nelements; i++)
-    if (_elements[i].live()) {	// allow for deleted elements
-      ElementClassT *ect = _element_classes[_elements[i].type];
-      if (ect)
-	ect->expand_into(this, i, this, scope, errh);
-      else
-	ElementClassT::simple_expand_into(this, i, this, scope, errh);
-    }
+    if (_elements[i].live())	// allow for deleted elements
+      ElementClassT::expand_element(this, i, this, env, errh);
   
-  // remove all compound classes
+  /* // remove all compound classes
   int neclass = _element_classes.size();
   Vector<int> removed_eclass(neclass, 0);
   for (int i = 0; i < neclass; i++)
     if (_element_classes[i] && _element_classes[i]->expands_away())
       removed_eclass[i] = -1;
-  finish_remove_element_types(removed_eclass);
-}
-
-void
-RouterT::remove_unresolved_uprefs(ErrorHandler *errh)
-{
-  if (!errh) errh = ErrorHandler::silent_handler();
-  
-  int nelements = _elements.size();
-  Vector<int> new_eindex(nelements, 0);
-  bool any = false;
-  
-  // find uprefs
-  for (int i = 0; i < nelements; i++) {
-    ElementT &e = _elements[i];
-    if (e.type == UPREF_TYPE) {
-      errh->lerror(e.landmark, "unresolved upref `%s'", e.name.cc());
-      new_eindex[i] = -1;
-      any = true;
-    }
-  }
-
-  if (any)
-    finish_free_elements(new_eindex);
+      finish_remove_element_types(removed_eclass);*/ 
 }
 
 void
@@ -1346,45 +1161,15 @@ RouterT::flatten(ErrorHandler *errh)
 {
   remove_compound_elements(errh);
   remove_tunnels();
-  remove_unresolved_uprefs(errh);
   remove_dead_elements();
   compact_connections();
+  remove_unused_element_types();
   check();
 }
 
 
 // PRINTING
 
-void
-RouterT::compound_declaration_string(StringAccum &sa, const String &name,
-				     const String &indent)
-{
-  sa << indent << "elementclass " << name << " {";
-  
-  // print formals
-  for (int i = 0; i < _formals.size(); i++)
-    sa << (i ? ", " : " ") << _formals[i];
-  if (_formals.size())
-    sa << " |";
-  sa << "\n";
-
-  configuration_string(sa, indent + "  ");
-  
-  sa << indent << "}\n";
-}
-
-String
-RouterT::ename_upref(int idx) const
-{
-  if (idx >= 0 && idx < _elements.size()) {
-    if (_elements[idx].type == UPREF_TYPE)
-      return "^" + _elements[idx].name;
-    else
-      return _elements[idx].name;
-  } else
-    return String("/*BAD_") + String(idx) + String("*/");
-}
-
 static void
 add_line_directive(StringAccum &sa, const String &landmark)
 {
@@ -1417,7 +1202,7 @@ RouterT::configuration_string(StringAccum &sa, const String &indent) const
   int old_sa_len = sa.length();
   for (int i = 0; i < nelemtype; i++)
     if (_element_classes[i])
-      _element_classes[i]->compound_declaration_string
+      _element_classes[i]->declaration_string
 	(sa, _element_type_names[i], indent);
   if (sa.length() != old_sa_len)
     sa << "\n";
@@ -1439,8 +1224,8 @@ RouterT::configuration_string(StringAccum &sa, const String &indent) const
   int nprinted_elements = 0;
   for (int i = 0; i < nelements; i++) {
     const ElementT &e = _elements[i];
-    if (e.dead() || e.type == TUNNEL_TYPE || e.type == UPREF_TYPE)
-      continue; // skip tunnels and uprefs
+    if (e.dead() || e.type == TUNNEL_TYPE)
+      continue; // skip tunnels
     add_line_directive(sa, e.landmark);
     sa << indent << e.name << " :: ";
     if (e.type < nelemtype)
@@ -1501,7 +1286,7 @@ RouterT::configuration_string(StringAccum &sa, const String &indent) const
       if (used[c] || !startchain[c])
 	continue;
       
-      sa << indent << ename_upref(hf.idx);
+      sa << indent << ename(hf.idx);
       if (hf.port)
 	sa << " [" << hf.port << "]";
       
@@ -1512,7 +1297,7 @@ RouterT::configuration_string(StringAccum &sa, const String &indent) const
 	const Hookup &ht = _hookup_to[d];
 	if (ht.port)
 	  sa << "[" << ht.port << "] ";
-	sa << ename_upref(ht.idx);
+	sa << ename(ht.idx);
 	used[d] = true;
 	d = next[d];
       }
diff --git a/tools/lib/routert.hh b/tools/lib/routert.hh
index cdea0e9a4dae134b77ba8637ffb7d61c57fd5dee..60980db65d1a82a6f373eac0c9c1a3bde8d56d78 100644
--- a/tools/lib/routert.hh
+++ b/tools/lib/routert.hh
@@ -6,7 +6,7 @@
 #include <click/archive.hh>
 typedef HashMap<String, int> StringMap;
 
-class RouterT : public ElementClassT {
+class RouterT {
 
   struct Pair {
     int from;
@@ -15,8 +15,7 @@ class RouterT : public ElementClassT {
     Pair(int f, int t) : from(f), to(t) { }
   };
 
-  RouterT *_enclosing_scope;
-  Vector<String> _formals;
+  int _use_count;
   
   StringMap _element_type_map;
   Vector<String> _element_type_names;
@@ -52,24 +51,24 @@ class RouterT : public ElementClassT {
 
  public:
 
-  enum { TUNNEL_TYPE = 0, UPREF_TYPE = 1 };
+  enum { TUNNEL_TYPE = 0 };
   
   RouterT(RouterT * = 0);
   RouterT(const RouterT &);
   virtual ~RouterT();
 
+  void use()				{ _use_count++; }
+  void unuse()				{ if (--_use_count <= 0) delete this; }
+  
   void check() const;
   bool is_flat() const;
   
-  void add_formal(const String &n)	{ _formals.push_back(n); }
-  
   int ntypes() const			{ return _element_classes.size(); }
   const String &type_name(int i) const	{ return _element_type_names[i]; }
   ElementClassT *type_class(int i) const { return _element_classes[i]; }
+  ElementClassT *type_class(const String &) const;
   int type_index(const String &s) const { return _element_type_map[s]; }
-  ElementClassT *find_type_class(const String &) const;
-  int get_type_index(const String &);
-  int get_type_index(const String &, ElementClassT *);
+  int get_type_index(const String &, ElementClassT * = 0);
   int add_type_index(const String &, ElementClassT *);
   int get_anon_type_index(const String &, ElementClassT *);
   void get_types_from(const RouterT *);
@@ -83,7 +82,6 @@ class RouterT : public ElementClassT {
   bool elive(int ei) const		{ return _elements[ei].live(); }
   bool edead(int ei) const		{ return _elements[ei].dead(); }
   String ename(int) const;
-  String ename_upref(int) const;
   int etype(int) const;
   String etype_name(int) const;
   String edeclaration(int) const;
@@ -109,6 +107,7 @@ class RouterT : public ElementClassT {
   const Hookup &hookup_from(int i) const	{ return _hookup_from[i]; }
   const Vector<Hookup> &hookup_to() const	{ return _hookup_to; }
   const Hookup &hookup_to(int i) const		{ return _hookup_to[i]; }
+  const Vector<String> &hookup_landmark() const	{ return _hookup_landmark; }
   const String &hookup_landmark(int i) const	{ return _hookup_landmark[i]; }
   bool hookup_live(int i) const		{ return _hookup_from[i].live(); }
  
@@ -153,20 +152,16 @@ class RouterT : public ElementClassT {
   
   void add_components_to(RouterT *, const String &prefix = String()) const;
 
-  int expand_into(RouterT *, int, RouterT *, const RouterScope &, ErrorHandler *);
-  bool expands_away() const			{ return true; }
-  
   void remove_unused_element_types();
   void remove_duplicate_connections();
   void remove_dead_elements(ErrorHandler * = 0);
   
   void remove_compound_elements(ErrorHandler *);
   void remove_tunnels();
-  void remove_unresolved_uprefs(ErrorHandler *);
 
+  void expand_into(RouterT *, const VariableEnvironment &, ErrorHandler *);
   void flatten(ErrorHandler *);
 
-  void compound_declaration_string(StringAccum &, const String &, const String &);
   void configuration_string(StringAccum &, const String & = String()) const;
   String configuration_string() const;
 
@@ -174,26 +169,13 @@ class RouterT : public ElementClassT {
 
 };
 
-class RouterScope {
-
-  String _prefix;
-  Vector<String> _formals;
-  Vector<String> _values;
-
- public:
-  
-  RouterScope()				{ }
-  RouterScope(const RouterScope &, const String &suffix);
-
-  operator bool() const			{ return _formals.size() != 0; }
-  const String &prefix() const		{ return _prefix; }
-  
-  void combine(const Vector<String> &, const Vector<String> &);
-  
-  String interpolate(const String &) const;
-  
-};
 
+inline ElementClassT *
+RouterT::type_class(const String &n) const
+{
+  int i = type_index(n);
+  return (i < 0 ? 0 : _element_classes[i]);
+}
 
 inline String
 RouterT::ename(int idx) const
diff --git a/userlevel/Makefile.in b/userlevel/Makefile.in
index df55a1398dbacd45b95ce9372a433768a5b41cc5..41977089de7dc506fd715209f5a5595b4461db7a 100644
--- a/userlevel/Makefile.in
+++ b/userlevel/Makefile.in
@@ -43,7 +43,7 @@ GENERIC_OBJS = string.o straccum.o \
 	packet.o \
 	error.o glue.o timer.o gaprate.o \
 	elemlink.o element.o \
-	confparse.o lexer.o archive.o elemfilter.o router.o \
+	confparse.o variableenv.o lexer.o archive.o elemfilter.o router.o \
 	crc32.o in_cksum.o iptable.o iptable2.o ip6table.o radix.o \
 	userutils.o