4

I tried to find a clever and robust way to use non-letter symbols like %, _, ], [ etc.inside the definition of an environment. I tried for example to change [ and ] to letters for using them locally. The following code worked well

\begingroup
\catcode`\[=11
\catcode`\]=11
\def\[a{some unicode character}
\def\]m{something else}

\[a\]m
\endgroup

but when I tried to roll up the above code into an special enviroment like

\newenvironment{newaccents}{\begingroup%
\catcode`\[=11%
\catcode`\]=11%
\def\[a{some unicode character}
\def\]m{something else}
}{%
\catcode`\[=12%
\catcode`\]=12%
 \endgroup}

the TeX comlains with

Undefined control sequence.
 \begin{newaccents}\[a
                           \end{newaccents}

So my questions is: How one could change in a local special way the codes of non-letters characters so that they can to be used inside the definition of local-defined commands?

kornaros
  • 1,095
  • If \[a is a command, I don't understand why you want to change the catcodes of \[ etc. \def\[a{foo} works, but I don't recommend it (again). The \begingroup...\endgroup pair isn't necessary –  Nov 01 '15 at 09:49
  • 1
    not related to the problem in the question, but you have % at the ends of exactly the wrong lines. You need a space after 11 and 12 to terminate the number so should not have % there, space is ignored after a command token so you don't need % after \begingroup but conversely you do want % to hide the space tokens after character} and else} – David Carlisle Nov 01 '15 at 10:05
  • @David Carlisle. You are right! But I dont' understand why such definitions like [a above does not work locally.... I know that they a special meaning inside math mode so I don't dare to define them globally – kornaros Nov 01 '15 at 10:14
  • @Danid Carlisle I tried to remove the % as you suggested and I think it WORKED! Thank you again – kornaros Nov 01 '15 at 10:21
  • 3
    I hope you will never have to use a command or environment with optional argument in your environment. One get quite interesting errors e.g. with \parbox[t]{5cm}{abc}. – Ulrike Fischer Nov 01 '15 at 11:03

4 Answers4

10

Assuming you really want to do it, you need a two-step approach: first define the commands you want under the right category code setup

\begingroup
\catcode`[=11 \catcode`]=11
\gdef\[a{foo}
\gdef\]m{baz}
\endgroup

Then you can define the environment so that it changes the category codes:

\newenvironment{newaccents}
  {\catcode`[=11 \catcode`]=11 }
  {}

The reason why your attempt fails is that when you do

\newenvironment{newaccents}{\begingroup%
\catcode`\[=11%
\catcode`\]=11%
\def\[a{some unicode character}
\def\]m{something else}
}{%
\catcode`\[=12%
\catcode`\]=12%
 \endgroup}

TeX has already absorbed the tokens in the three arguments to \newenvironment under a regime where [ and ] have category code 12, so they cannot be changed any more.

The global definitions I give in the first code block have no influence on usage outside the newaccents environment, where the category code of [ and ] is 12. You can still use \[ and \] with their original meaning within the newaccents environment, provided you ensure they're followed by a space.

In any case, you should recall that an environment where category code changes are performed cannot be used in the argument to a command.


There is a different strategy for obtaining a similar result without category code changes.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\DeclareTextCommand{\NAlbrack}{T1}{?} % default is not really defined
\DeclareTextCompositeCommand{\NAlbrack}{T1}{a}{foo}
% other composites of the form \[x

\DeclareTextCommand{\NArbrack}{T1}{?} % default is not really defined
\DeclareTextCompositeCommand{\NArbrack}{T1}{m}{baz}
% other composites of the form \]x

\newenvironment{newaccents}
  {\let\[\NAlbrack \let\]\NArbrack}
  {}

\begin{document}

\[a=b\]

\begin{newaccents}
Does \[a print `foo'?

Does \]m print `baz'?
\end{newaccents}

\end{document}

enter image description here

This of course means \[ and \] cannot be used for displays, but I don't think it's a problem for your application.

I used T1 as the font encoding, but you can use \encodingdefault, provided the code is after loading the package for font encodings, be it fontenc or fontspec.

The trick is to define a command \NAlbrack that can look forward for a declared combination, in this case an a. If it finds it, it prints whatever you defined the combination to be, otherwise it prints a ? (just an arbitrary choice, you can define it to do something else in case of no predefined combinations).

In the body of newaccents, I simply change \[ to be the same as \NAlbrack.

egreg
  • 1,121,712
  • I tried your code and I face the following problem :Command \NAlbrack unavailable in encoding EU1 – kornaros Nov 01 '15 at 13:57
  • 1
    @kornaros How can I guess you're using XeLaTeX? Change T1 into EU1 (and, of course, load neither fontenc nor inputenc). Better yet, change T1 into \encodingdefault, so the file will be compatible also with LuaLaTeX. Note that the code must go after loading fontspec. – egreg Nov 01 '15 at 14:01
  • I use \documentclass{article} \usepackage[EU1]{fontenc} %\usepackage[no-math]{fontspec} \usepackage{xunicode} \usepackage{polyglossia} \setmainlanguage[variant=polytonic]{greek} \setmainfont{GFS Neohellenic} inmy preample. Also I see a strange ? in your \DeclareTextCommand{\NAlbrack}{T1}{?} Maybe my browser does not print this character correctly! Is really a ? or some other symbol? Also could you please explain your code? Thanks a lot! – kornaros Nov 01 '15 at 14:17
  • 1
    @kornaros What I gave is just a proof of concept, given that your question was very generic. But changing T1 into \encodingdefault as suggested is what you need. – egreg Nov 01 '15 at 14:29
2

Change the catcodes temporarily, for the duration of your definition; then they go back to default, until your environment re-activates them the way you do now.

\catcode`[=11
\catcode`]=11
\newenvironment{newaccents}{
      \catcode`[=11
      \catcode`]=11
\def\[a{(Accent)}
}{}
\catcode`[=12
\catcode`]=12

Note that \begin will begin a group for you; and since catcodes are local, there's no need to set them back by hand.

Edit: Of course \global\newenvironment doesn't work. Fixed now, sorry for the distraction.

alexis
  • 7,961
  • 1
    \global\newenvironment? Oh, not going to work; please, try it. – egreg Nov 01 '15 at 10:01
  • Thank you for your naswer! I tried to define commands staring with [ or ] locally because they have a special meaning in math mode eniroment. I dont understand why latex complains with such enviroments. Also the code \newenvironment{newaccents}{\begingroup% \def[a{some unicode character} \def]m{something else}}{ \endgroup} have the same problem!! – kornaros Nov 01 '15 at 10:04
  • Rats. I had my doubts about using \global with \newenvironment, I should have checked before going offline... but I was busy IRL. I wouldn't quite say it's "completely wrong", though: The main point (in intent at least) is that the catcodes need to be active for both definition and use. – alexis Nov 01 '15 at 20:17
2

I don't see the point in having cat code changes here: \[a is a 'valid' command name, if really needed.

Since all is defined in an environment, \begingroup and \endgroup aren't needed here at all.

\documentclass{article}

\usepackage[utf8]{inputenc}


\newenvironment{newaccents}{%\begingroup%
%\catcode`\[=11%
%\catcode`\]=11%
\def\[a{some unicode character}%
\def\]m{something else}%
}{%
%\catcode`\[=12%
%\catcode`\]=12%
% \endgroup
}

\begin{document}
\begin{newaccents}
\[a äöü \]m
\end{newaccents}
\end{document}
David Carlisle
  • 757,742
  • Of course you don't want to have displayed equations using \[...\] in the newaccents environment. Note also that you can't have both \[a and \[e – egreg Nov 01 '15 at 10:15
  • 1
    You will see the point of the catcode change if you add \def\[b{some other unicode}. – Ulrike Fischer Nov 01 '15 at 10:53
  • 1
    @egreg: I never want to such stuff at all because I don't want to shoot myself into the foot ;-) It's useless code golfing –  Nov 01 '15 at 12:46
2

The following answer doesn't add much to @egreg's answer, but shows an alternative, slightly more compact route to the same result which might possibly be illuminating.

As noted, the crucial thing to know here is that TeX assigns a (catcode, charactercode) pair to every character once, when it is first read. That means that your \def\[a has to happen in a context where [ will be read with catcode 11.

The second thing to know is that an environment \begin{foo} ... \end{foo} expands into (essentially) \begingroup\foo ... \endfoo\endgroup (plus some bookkeeping); that is, you can (mostly) define an environment {foo} simply by defining the pair of macros \foo and \endfoo.

Thus:

\begingroup
\catcode`\[=11 % treat [ as a letter within this group
\gdef\newaccents{% Now define the {newaccents} environment
  \def\[a{foo}% a macro with a two-letter name
  \catcode`\[=11 % make [ a letter within the body of this environment
}
\global\let\endnewaccents=\relax
\endgroup

Notice that \def\[a happened within the group, so it's interpreted as two letters. The \gdef means that the definition of this and of \foo ‘escape’ the group.

After that...

\begin{document}
Here is text.
\[
E=mc^2.
\]

\begin{newaccents}
  Here is some \[a text, with displayed maths:
  \[ F=ma. \]
\end{newaccents}

\end{document}

Doing it this way means that you don't have to wish that \global\newenvironment worked. Also, a difference between this solution and @egreg's (too slight to be much of an advantage) is that \[a isn't defined at all, even inaccessibly, outside the {newaccent} environment.

Norman Gray
  • 7,497
  • 2
  • 25
  • 35