7

Mostly for fun, I'm trying to make % comments manipulatable using the \lowercase trick. For some reason, I'm getting a strange (to me) error:

ERROR: LaTeX Error: Missing \begin{document}.

--- TeX said ---

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.22 \VisibleComments

Generally when I get this kind of error, I have unbalanced brace groups somewhere. As far as I can tell, this is not the case here.

MWE

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn

\cs_new_protected:Nn \vc_make_comment:n
  { \textbf{Comment: `#1'} }
\cs_new:Nn \vc_visible_comments:
  {
    \group_begin:
    \char_set_lccode:nn {`\~} {`\%}
    \tl_to_lowercase:n
      { \group_end: \cs_set_eq:NN ~ } \vc_make_comment:n
    \char_set_catcode_active:N \%
  }
\NewDocumentCommand \VisibleComments { }
  { \vc_visible_comments: }

\ExplSyntaxOff

\VisibleComments

\begin{document}

Hello, this is a % comment

\end{document}
Expecting: Hello, this is a \textbf{Comment: `comment'}
Sean Allred
  • 27,421

2 Answers2

9

The '\lowercase trick' relies on you having a char that is already active. The usual one in a document is ~ as it is active in plain, LaTeX and ConTeXt for creating a non-breaking space. However, inside an expl3 code block it's a space, so you get the wrong outcome (you've effectively go \cs_set_eq:NN}...). You can fix this by making it active:

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn

\cs_new_protected:Nn \vc_make_comment:n
  { \textbf{Comment: `#1'} }
\group_begin:
  \char_set_catcode_active:N \~
  \cs_new_protected:Nn \vc_visible_comments:
    {
      \group_begin:
        \char_set_lccode:nn {`\~} {`\%}
        \tl_to_lowercase:n
        { \group_end: \cs_set_eq:NN ~ } \vc_make_comment:n
      \char_set_catcode_active:N \%
    }
\group_end:
\NewDocumentCommand \VisibleComments { }
  { \vc_visible_comments: }

\ExplSyntaxOff

\VisibleComments

\begin{document}

Hello, this is a % comment

\end{document}

However, the team are working to provide better interfaces that the above (what's case changing got to do with making something active, really). The current (still experimental) interface is

\cs_new_protected:Nn \vc_visible_comments:
  {
    \char_set_active_eq:NN \% \vc_make_comment:n
    \char_set_catcode_active:N \%
  }

There's been a (very) recent change in the definition of \char_set_active_eq:NN: with the current release, it means 'set the behaviour of #1 to be equal to #2 if #1 is active'. As such, actually making the token active is a second step, as shown above.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • I don't think you quite realize how happy \char_set_active_eq:NN makes me. :) – Sean Allred Sep 07 '15 at 18:49
  • As a beginner I didn't appreciate the implication of "having a char that is already active" in this excellent answer. @PhelypeOleinik explained it to me: you (usually) can't execute commands in the preamble (before \begin{document}) that try to typeset something in the document. This is because the document that is produced hasn't started yet. This is basic LaTeX knowledge but all too easily overlooked when you're distracted by trying to learn LaTeX programming. – Doc Octal Aug 20 '21 at 08:27
0

Your question, however doesn't “grab until the end of line”. Here's a suggestion coming from an old question I asked.

\documentclass{scrartcl}

\usepackage{xparse}
\ExplSyntaxOn

\group_begin:
  \char_set_catcode_other:N \^^M %
  \cs_new_protected:Npn \cs_new_delimited_by_newline:Nn #1 #2 %
    { %
      \cs_new_protected:Npn #1 %
        { %
          \group_begin: %
            \char_set_catcode_other:N \^^M %
            \use:c { __ \cs_to_str:N #1 } %
        } %
      \cs_new_protected:cpn { __ \cs_to_str:N #1 } ##1 ^^M %
        { %
            #2 %
          \group_end: %
        } %
    } %
\group_end:
\cs_new_delimited_by_newline:Nn \vc_make_comment:w
  { \vc_make_comment:n { \tl_trim_spaces:n {#1} } }
\cs_new_protected:Npn \vc_make_comment:n #1
  { \textbf{Comment: ~ `#1'} }

\cs_new_protected:Nn \vc_visible_comments:
  {
    \char_set_active_eq:NN \% \vc_make_comment:w
    \char_set_catcode_active:N \%
  }
\NewDocumentCommand \VisibleComments { }
  { \vc_visible_comments: }

\ExplSyntaxOff

\VisibleComments

\begin{document}

Hello, this is a % comment

\end{document}

enter image description here

Manuel
  • 27,118
  • Should've taken a look at the comments on the question :) (on second thought, it should rather be an edit to the question or a comment on Joseph's answer) though \tl_trim_spaces:n is probably more appropriate than my \ignorespaces. – Sean Allred Sep 07 '15 at 18:37
  • Okey, that's exactly what you did on reddit. Well, this is a semi automated way of your solution :) – Manuel Sep 07 '15 at 18:39
  • Ah, by the way, this would work only for “outer” comments, if a comment is inside an argument of a macro it wouldn't be shown correctly, in that case it's probably better the sed script you proposed. – Manuel Sep 07 '15 at 18:41