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:
Bruno Le Floch <[log in to unmask]>
Reply To:
Mailing list for the LaTeX3 project <[log in to unmask]>
Date:
Thu, 2 Feb 2012 10:57:16 -0500
Content-Type:
text/plain
Parts/Attachments:
text/plain (162 lines)
Hello Marc,

> : Now here is the question. What I'd like to know if there's an easy way
> : for a substitution of the form
> :   a[expression] -> \csname myA[expression]\endcsname

\regex_replace_all:nnN
  { a \[ (.+?) \] } { \c{ myA \[ \1 \] } } \l_your_tl

or, if the names "a" and "myA" are actually stored in some variables
(in particular, if they contain characters that may be unsafe in
regular expressions), say, \l_array_name_tl and \l_rec_name_tl, you
can use the \u escape sequence (specific to l3regex, not present in
Perl or PCRE):

\regex_replace_all:nnN
  { \u{l_array_name_tl} \[ (.+?) \] }
  { \c{ \u{l_rec_name_tl} \[ \1 \] } } \l_your_tl

===================================

Feel free to ask details about the syntax: I'm not happy with how it's
explained in the doc. Here is a possible approach for parsing the
whole thing. I'm not entirely sure what you have in mind as a syntax,
so I may be a tad wrong in the following. I'm basing myself on your
example

    \NewRecDef{myA}{
        a[i]=1               | 0 <= i && i <= 1,
        a[i]=a[i-1]+a[i-2] | true
    }

So the input is a comma list of expressions of the form

    a[i]=<expression using "a" and "i"> | <boolean expression in "i">

and given a value of i, we compute a[i] by looping through the comma
list, testing the <boolean expression in "i">, stopping at the first
one that yields "true", and evaluating the <expression using "a" and
"i">, possibly calling a recursively.

If I understand correctly, you want to use regular expressions to
parse that. I think it is possible. A starting point is as follows.
I'm using \tl_replace_all whenever possible rather than the more
powerful regex features, because the former is roughly 100 times
faster.

    \RequirePackage{xparse,l3regex}
    \ExplSyntaxOn
    \cs_generate_variant:Nn \tl_replace_all:Nnn { Nx }
    \tl_new:N \l_nrd_name_tl
    \tl_new:N \l_nrd_input_tl
    \tl_new:N \l_nrd_tmp_tl
    \tl_new:N \l_nrd_array_name_tl
    \tl_new:N \l_nrd_index_name_tl
    \tl_new:N \l_nrd_expression_tl
    \tl_new:N \l_nrd_bool_expr_tl
    \int_new:N \l_nrd_arg_int
    \seq_new:N \l_nrd_tmp_seq
    \seq_new:N \l_nrd_parsed_seq
    \DeclareDocumentCommand{\NewRecDef}{mm}
      {
        \tl_set:Nn \l_nrd_name_tl {#1}
        \seq_clear:N \l_nrd_parsed_seq
        \clist_map_inline:nn {#2}
          {
            \regex_extract_once:nnNTF
              { ^(\w+)\[(\w+)\]\=(.+?)\|(.+) } {##1} \l_nrd_tmp_seq
              {
                 % at this stage, \l_nrd_tmp_seq has 5 items,
                 % 0th: a[i]=1 | 0 <= i && i <= 1
                 \seq_pop:NN \l_nrd_tmp_seq \l_nrd_tmp_tl

                 % 1st: a (array name)
                 \seq_pop:NN \l_nrd_tmp_seq \l_nrd_array_name_tl

                 % 2nd: i (index name)
                 \seq_pop:NN \l_nrd_tmp_seq \l_nrd_index_name_tl

                 % 3rd: 1 (or more general expression)
                 \seq_pop:NN \l_nrd_tmp_seq \l_nrd_expression_tl
                 \tl_replace_all:Nxn \l_nrd_bool_expr_tl
                   \l_nrd_index_name_tl { \l_nrd_arg_int }
                 \regex_replace_all:nnN
                   { \u{l_nrd_array_name_tl} \[ (.+?) \] }
                   { \c{nrd_array:n} \cB\{ \1 \cE\} }
                   \l_nrd_expression_tl

                 % \tl_show:N \l_nrd_expression_tl

                 % 4th: 0<=i&&i<=1
                 \seq_pop:NN \l_nrd_tmp_seq \l_nrd_bool_expr_tl
                 \tl_set:Nx \l_nrd_bool_expr_tl { ( \l_nrd_bool_expr_tl ) }
                 \tl_replace_all:Nxn \l_nrd_bool_expr_tl
                   \l_nrd_index_name_tl { \l_nrd_arg_int }
                 \tl_replace_all:Nnn \l_nrd_bool_expr_tl { ( } { (
\nrd_test:w [ }
                 \tl_replace_all:Nnn \l_nrd_bool_expr_tl { ) } { ] ) }
                 \tl_replace_all:Nxn \l_nrd_bool_expr_tl
                   { \tl_to_str:n {&&} } { ] && \nrd_test:w [ }
                 \tl_replace_all:Nxn \l_nrd_bool_expr_tl
                   { \tl_to_str:n {||} } { ] || \nrd_test:w [ }

                 % \tl_show:N \l_nrd_bool_expr_tl

                 \seq_put_right:Nx \l_nrd_parsed_seq
                   {
                      \exp_not:N \bool_if:nTF
                        { \exp_not:V \l_nrd_bool_expr_tl }
                        {
                           \message { test ~ " \tl_to_str:N
\l_nrd_bool_expr_tl " ~ succeeded ! }
                           % do something with \l_nrd_expression_tl
                           \exp_not:N \prg_map_break:
                        }
                   }
              }
              { \msg_error:nnx { nrd } { wrong-input } { \tl_to_str:n {##1} } }
          }
      }
    \cs_new:Npn \nrd_test:w [ #1 ]
      {
         \str_if_eq:xxTF {#1} {true} { \c_true_bool }
           {
             \str_if_eq:xxTF {#1} {false} { \c_false_bool }
                { \int_compare_p:n {#1} }
           }
      }
    \ExplSyntaxOff

So, roughly the idea is that each item of the clist has the form

    <name>[<index>] = <expression> | <bool expr>

and we extract the <name>, <index>, <expression> and <bool expr> using
a regex. Then in the <expression>, replace all occurrences of <index>
by \l_nrd_arg_int, and <name>[<whatever>] by \nrd_array:n
{<whatever>}, where we expect <whatever> to be a valid integer
expression now that we've replaced <index> by \l_nrd_arg_int. On the
other hand, the <bool expr> is converted to a boolean expression in
the sense of LaTeX3, which requires us to wrap each of the comparisons
in \int_compare_p:n. Since I don't know how complicated those
comparisons can be, I did the opposite, which is to wrap each
operator: (, ), &&, ||, with the relevant \nrd_test:w [ or ]. I'm not
using braces here because the output must be brace-balanced in all
cases. At the end of the day, \nrd_test:w [#1] tests whether its
argument is "true", "false", or an integer comparison (remember, "i"
got converted to \l_nrd_arg_int).

Presumably, using those expressions, you can reach your goal.

> I think I've solved the problem, so I probably won't need to know
> about the substitution. If the answer is easy, I'd still like to
> know. Otherwise, please forget about it.

I hope the first part of that email was easy enough; the second part
probably wasn't :). I assume that you are going to post the final
solution on TeX.sx?

Cheers,
Bruno

ATOM RSS1 RSS2