Traversing Ground Expressions

Suppose we want to define a function NMB replacing all symbols appearing in a ground expression with their ordinal numbers. For example:
    <NMB A (B A) C A>   =>   1 (2 3) 4 5

The main difficulty is that, having encountered a pair of parentheses, the function cannot know in advance the number of symbols enclosed in the parentheses. But this information will be necessary for the function to resume the processing of the top level of the expression after the contents of the parentheses will be done away with. Therefore, the symbol numbering function must have two arguments: the expression to be processed and the number to be assigned to the first symbol in the expression (if any). This function must return two results: the expression processed and the first "unused" number. Thus we come to the following definition of the function NMB (making use of two auxiliary functions NMBExp and NMBTerm).

$func NMB     e.Exp    = e.Exp;
$func NMBExp  e.Exp sN = e.Exp sN;
$func NMBTerm t.Exp sN = t.Exp sN;

NMB e.Exp =
 <NMBExp e.Exp 1> :: e.Exp s,
   e.Exp;

NMBExp e.Exp sN =
  e.Exp :
  {
  = sN;
  tX e.Rest =
    <NMBTerm tX sN> :: tX sN,
    <NMBExp e.Rest sN> :: e.Rest sN,
       tX e.Rest sN;
  };

NMBTerm tX sN =
  tX :
  {
  s =
    sN <Add sN 1>;
  (eE) =
    <NMBExp eE sN> :: eE sN,
      (eE) sN;
  };