```>> As I now understand, the key point in all of this is that we can
>> detect spaces and braces expandably using delimited arguments, and we
>> can then be sure to act on one token at a time.
>
> Ah, I see:
>
>     \long\gdef\UL_brace_check:nw#1#2#{%
>       \UL_if_empty:nTF{#2}%
>       {\UL_brace_yes:nn{#1}}%
>       {\UL_space_check:nw{#1}#2}%
>     }
>
> This is clever. Has anyone seen this done before? Are there any downsides?

One problem is that it only detects begin-group characters which are
actually braces (e.g. after |\catcode`\[=1|, it won't see "[").
Another problem is if there is no brace in a big argument, then #2
will be the whole argument (until the sentinel {}), and the test is
rather slow at every step.

In contexts where the replacement takes no argument (i.e. a simple
\tl_replace_all_in:nn), we could do much quicker by first putting a
\q_mark before each brace group, and then just checking for it (same
trick for spaces).

> If it's as good as it seems then it allows all sorts of expandable things,
> such as optional argument parsing and expandable nested mapping.

We had a short discussion with Joseph about optional argument parsing.
The obvious constraint is that the last argument must be mandatory, so
we can just look for its opening delimiter. Then it is in principle
possible, but argument specifications like {oom} become quite tricky,
since #{ has to be used when grabbing each of these arguments. I
haven't looked at the implementation of xparse in detail, though.

I've put the code for \fullyexpand online for those interested, as
well as a very primitive macro expander. (
http://users.aims.ac.za/~bruno/LaTeX/ULcase/ )

--
Regards,
Bruno
```