LATEX-L Archives

Mailing list for the LaTeX3 project

LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

Options: Use Forum View

Use Monospaced Font
Show Text Part by Default
Show All Mail Headers

Message: [<< First] [< Prev] [Next >] [Last >>]
Topic: [<< First] [< Prev] [Next >] [Last >>]
Author: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Subject:
From:
Lars Hellström <[log in to unmask]>
Reply To:
Mailing list for the LaTeX3 project <[log in to unmask]>
Date:
Wed, 12 Aug 2009 14:56:50 +0200
Content-Type:
text/plain
Parts/Attachments:
text/plain (254 lines)
Will Robertson skrev:
> Hi,
> 
> Going back to an earlier message, and taking a little step back. Sorry 
> for the long message.
> 
> On 11/08/2009, at 3:49 PM, Joseph Wright wrote:
> 
>> Will Robertson wrote:
>>>
>>>   \DeclareDocumentCommand \foo { ?{\my_sanitise:n} } {
>>>     % do whatever \foo is supposed to do with #1
>>>   }
>>>
>>> I neglected to include how \my_sanitise:n would actually be written. And
>>> I'm not too sure about that. If it ends up that \my_sanitise:n has to
>>> write to a scratch variable anyway, then I'm not sure we've saved too 
>>> much.
> 
> [...]
> 
>> In any case, I'd imagine any post-processor being a function of one
>> argument. Internally, they'd do something with a temporary variable,
>> then put the value of the variable back into the input of the underlying
>> function defined by <code> in \DeclareDocumentCommand.
> 
> So how would \my_sanitise:n above be written? If it's not expandable, 

I don't think expandability is always a realistic expectation here, 
even though I *like* doing things at expand-time.

> then the results of modifying #1 must be written to a standard scratch 
> variable (right?). At which stage I'm not convinced we've saved any 
> confusion over the original way to write this sort of thing.

The confusion saved is that one can make do with one scratch variable 
that is private to xparse, instead of having several that might have 
unclear sharing patterns.

> For example:
> 
> \cs_set:Nn \my_sanitise:n {
>   \tl_set:Nn \l_scratch_tl {#1}
>   \tl_replace_in:Nnn \l_scratch_tl {&} {&amp;}
>   % but what to do now with \l_scratch_tl ??
> }

One could mandate that the result is to be left in \l_xparse_result_tl, 
or let xparse specify where to leave it (:Nn syntax).

> If it's easier to hook post-processing steps into the argument stage but 
> *harder* to write the functions that do so, have we gained much? Enough 
> to justify the initial increase in complexity?
> 
> Maybe to re-write my concern; how could we simplify this example from 
> xdoc2l3: (use as much hypothetical syntactic sugar as you like)
> 
> \gdef\XD@gp@intrange#1#2#3\XD@endgrab#4{%
>    \count@=#4\relax
>    \ifnum #1>\count@
>       \PackageError{xdoc2l3}%
>         {Too small! Changed to minimum value #1}\@ehc
>       \count@=#1\relax
>    \else\ifnum #2<\count@
>       \PackageError{xdoc2l3}%
>         {Too large! Changed to maximum value #2}\@ehc
>       \count@=#2\relax
>    \fi\fi
>    \expandafter\XD@gh@put \expandafter{\expandafter{\the\count@}}{#3}%
> }
> \gdef\XD@pgp@intrange#1#2{%
>    \XD@macro@prepend\@tempa{\XD@gp@intrange{#1}{#2}}%
>    \XD@parse@processor
> }

OK, rewrite in terms of ?{<code>} processor as outlined in previous 
mail. First the function actually doing anything:

\def\constrain_to_intrange:nnNn#1#2#3#4{%
    \count@=#4\relax
    \ifnum #1>\count@
       \PackageError{xdoc2l3}%
         {Too small! Changed to minimum value #1}\@ehc
       \count@=#1\relax
    \else\ifnum #2<\count@
       \PackageError{xdoc2l3}%
         {Too large! Changed to maximum value #2}\@ehc
       \count@=#2\relax
    \fi\fi
    \edef#3{\number\count@}%
}

That part is pretty obvious, I think. Now some examples from 
xdoc2l3test in that syntax:

\NewDocumentCommand{\testR}{
    @{ ?{\constrain_to_intrange:nnNn{0}{4}} } % Was @{.{intrange}{0}{4}}
}{\typeout{Got #1.}The value is #1.}

\NewDocumentCommand{\testT}{
   @{ ?{\constrain_to_intrange:nnNn{0}{4}} O{1} }
   @{ ?{\constrain_to_intrange:nnNn{0}{4}} o    }
}{The values are (#1,#2).}


I think an xdoc2l3 implementation of ? would be

\expandafter\def \csname XD@pgp@?\endcsname#1{%
    \XD@macro@prepend\@tempa{\XD@gp@callout{#1}}%
    \XD@parse@processor
}
\def\XD@gp@callout#1#2\XD@endgrab#3{%
    #1\XD@private@tl{#3}%
    \expandafter\XD@gh@put \expandafter{\expandafter{%
       \XD@private@tl}}{#2}%
}

but that's untested (modification in e-mail of definition of x). Also, 
I think "@" would be better than "?"; then @ would mean "drop out to 
more general mechanism for this" at both the argument specifier and 
processor level.


> In my opinion, based on this example, it will be easier for users of 
> xparse to write their own argument-parsing functions as nested 
> definitions inside \DeclareDocumentCommand than to write something like 
> the above. Notice the number of necessary but implicit macros that the 
> author must know about:
> 
>   \XD@gp@...
>   \XD@pgp@...
>   \XD@endgrab
>   \XD@gh@put
>   \XD@macro@prepend\@tempa
>   \XD@parse@processor

xdoc2l3 never evolved to the point of having much of an API for 
defining processors, it's more a demonstration that the basic 
mechanisms could be implemented. I suppose much of the same can be said 
about xparse-alt, as the following rather direct correspondencies exist:

   \XD@gp@...           to  \xparse_add_arg_type_...
   \XD@pgp@...          to  \xparse_arg_...:w
   \XD@endgrab          to  \l_xparse_grabbed_args_toks
   \XD@parse@processor  to  \xparse_prepare_signature:N

\XD@macro@prepend is merely the general utility command \tl_put_left:Nn 
  (I might have gotten the latter name wrong, haven't checked it).

The \XD@gh@put macro is however the core novelty of xdoc2l3 in 
comparison to xparse and xparse-alt, as it is what permits pipelining 
processors. What it concretely does is that it takes a piece of 
<balanced text> and puts it in front of the text still remaining to be 
parsed. Usually, that <balanced text> looks like a mandatory argument, 
so the next processor that comes along can be written with the 
expectation that it operates on a user-supplied mandatory argument. 
(The various l3 \exp_args... macros engage in a similar type of 
argument juggling.)

> To be clear, I'm not yet opposed to this style of argument processing 
> and I *do* think there are some simplifications that can be made to the 
> code above to make it more palatable for users to write.
> 
> But before we agree to include such complex ideas into what is now a 
> simple package (for end-users, I mean), I think it would be good to 
> verify that a distillation of xdoc2l3 will "fit" into the philosophy of 
> xparse.

Since it was a while ago I'm not so sure, but I think I arrived at the 
unified processor model only after I started coding. The seemingly less 
complex idea of a separate processing stage turned out to be more 
complex once you got down to do it.

> *  *  *
> 
> After I say that, I suppose it would be remiss of me to go away without 
> actually mentioning how I think the ideas in xdoc2l3 might work in 
> xparse(-alt).
> 
> First of all, I think we should drop the idea of using more letters for 
> shorthands. As we've discussed, each package will use different letters 
> and clashes will occur very quickly.

Agreed. When doing something not implemented in xparse (or more 
fundamental parts of l3), one should specify a call-out to a specific 
macro, and rely on that normal naming rules for control sequences will 
avoid clashes. (The functionality of the above 
\constrain_to_intrange:nnNn could be a candidate for inclusion into 
xparse, but \MakeHarmless probably isn't.)

> Secondly, for simplicity let's drop the idea of toks-grabbing as a proxy 
> for argument grabbing.

Not sure what you refer to here.

> Thirdly, use actual function names rather than character strings to 
> represent argument processors.
> 
>  From xdoc2l3, there seem to be two broad cases for argument processing:
>     \argumentprocess{#1}
> and
>     \argumentprocess#1<delimiter>
> 
> These can be referred to in the argument definitions as something like
>     >\CS
> and
>     @\CS{DELIMITER}
> where \CS is the processing function and DELIMITER is what's inserted 
> after the argument, in the second case.

Two points here:

1. One may well want the argument processor to receive extra data (as 
\constrain_to_intrange:nnNn does), so don't assume \CS is just one token.

2. The <delimiter> kind of construction usually arises when one wants 
to do some low-level TeX hackery; the \TD@convert@colons macro that 
occurred in some examples is the kind of thing that would be at the 
core of \tl_replace_in:Nnn or similar. It is not unreasonable that such 
a :w thing is hidden under a :n macro, so don't waste effort on 
supporting it directly.

> Finally, allow these to be chained together if necessary, as in 
> something like this: (\foo takes one argument)
> 
> \DeclareDocumentCommand \foo {
>   @\foo_split_colons:n{::\q_nil} >\foo_range_check:nnn m
> }{...}

I don't understand what you're saying here.

> Open questions:
> 
> 1.  Does this cover the functionality that you miss from xparse in xdoc2l3?
> 2.  Is it simple enough for xparse?
> 3.  Assuming that we must deal with both expandable and unexpandable 
> argument processing, is there a straightforward way we can write the 
> argument processors? (E.g., see how boolean expressions are written in 
> expl3; a consistent interface is used for both expandable and 
> unexpandable tests.)

I'd say an expandable argument processor will necessarily have to be 
more restricted than one that can make assignments. Is it even possible 
to distinguish between

   \foo[bar]{!}
and
   \foo{[}bar]!

at expand-time?


Lars Hellström

ATOM RSS1 RSS2