+/** Check whether the expression matches a given pattern. For every wildcard
+ * object in the pattern, an expression of the form "wildcard == matching_expression"
+ * is added to repl_lst. */
+bool basic::match(const ex & pattern, lst & repl_lst) const
+{
+/*
+ Sweet sweet shapes, sweet sweet shapes,
+ That's the key thing, right right.
+ Feed feed face, feed feed shapes,
+ But who is the king tonight?
+ Who is the king tonight?
+ Pattern is the thing, the key thing-a-ling,
+ But who is the king of Pattern?
+ But who is the king, the king thing-a-ling,
+ Who is the king of Pattern?
+ Bog is the king, the king thing-a-ling,
+ Bog is the king of Pattern.
+ Ba bu-bu-bu-bu bu-bu-bu-bu-bu-bu bu-bu
+ Bog is the king of Pattern.
+*/
+
+ if (is_ex_exactly_of_type(pattern, wildcard)) {
+
+ // Wildcard matches anything, but check whether we already have found
+ // a match for that wildcard first (if so, it the earlier match must
+ // be the same expression)
+ for (unsigned i=0; i<repl_lst.nops(); i++) {
+ if (repl_lst.op(i).op(0).is_equal(pattern))
+ return is_equal(ex_to<basic>(repl_lst.op(i).op(1)));
+ }
+ repl_lst.append(pattern == *this);
+ return true;
+
+ } else {
+
+ // Expression must be of the same type as the pattern
+ if (tinfo() != ex_to<basic>(pattern).tinfo())
+ return false;
+
+ // Number of subexpressions must match
+ if (nops() != pattern.nops())
+ return false;
+
+ // No subexpressions? Then just compare the objects (there can't be
+ // wildcards in the pattern)
+ if (nops() == 0)
+ return is_equal_same_type(ex_to<basic>(pattern));
+
+ // Check whether attributes that are not subexpressions match
+ if (!match_same_type(ex_to<basic>(pattern)))
+ return false;
+
+ // Otherwise the subexpressions must match one-to-one
+ for (unsigned i=0; i<nops(); i++)
+ if (!op(i).match(pattern.op(i), repl_lst))
+ return false;
+
+ // Looks similar enough, match found
+ return true;
+ }
+}
+
+/** Substitute a set of objects by arbitrary expressions. The ex returned