2

I have a big file of macros, they are all in this form :

\def\eclaire{\mathbb}
\def\R{\ensuremath{\eclaire R}}

I want to have them in \newcommand form, for those macro it's quite easy, i just have to replace \def by \newcommand like this :

\newcommand \eclaire{\mathbb}
\newcommand \R{\ensuremath{\eclaire R}}

But for some others macros like this one :

\def\rond#1{\build#1_{}^{\>\circ}}

I can't because the solution is :

\newcommand\rond[1]{\build#1_{}^{\>\circ}}

So my question is, is there a script that can transform my \def command into \newcommand automatically ?

Thank you in advance for your help.

  • Your \newcommand for the \rond is wrong, I just corrected it. Also, \def and \newcommand, not /def and /newcommand. – user156344 Mar 23 at 8:27
  • 1
    I'm afraid there are no scripts which can convert \def to \newcommand generally. For example \def\hello person #1 and person #2 {Hello you #1 and #2}, it is very difficult to convert this to \newcommand. However, with \def#1#2#3#4 for example, I think there are (though I don't know of). – user156344 Mar 23 at 8:31
  • 1
    The regexp s/\\def(\\[^{#]*)(#[0-9])*/\\newcommand{$1}[0$2]/g might work for \defs of the form \def\<cmd>#1#2...#9 (at least it seemed to when tested against regexr.com). The result is not the prettiest for parameterless macros, because it produces \newcommand{\foo}[0]{...} instead of \newcommand{\foo}{...}, but that should be acceptable. – moewe Mar 23 at 8:33
  • I forgot to ignore the # in the second group and you may want to catch things like \def\foo##1 as well. So \\def(\\[^{#]*)(?:#+([0-9]))* would probably be better. – moewe Mar 23 at 8:49
  • 1
    Possibly relevant for the motivation: tex.stackexchange.com/q/475311/35864 – moewe Mar 23 at 11:19
0

\def and \newcommand are not fully equivalent, since \newcommand will throw an error if it is asked to redefine an existing command. \newcommand is therefore preferred if you want to define new commands, but it can not be used to redefine existing commands. This is not something a script can easily detect, so I will ignore it for now, even if it is relevant to your intended use case, where you have, for example

\newenvironment{Cases}{%
  \left\lbrace
  \def\arraystretch{1.2}%
  \array{@{}l@{\quad}l@{}}
}{%
  \endarray\right.%
}

Furthermore, \def and \newcommand don't support exactly the same syntax definitions. \newcommand can only define macros with up to nine (mandatory) arguments, where the first argument may be optional (and wrapped in square brackets instead). \def can also support other delimited macros. I will assume that your \defs are only of the form \def\<cmd>{...} or \def\<cmd>#1...#n. This was already mentioned by JouleV in the comments. This is an issue for

\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}} %CLENET

\newcommand also does not allow for prefixes like \long, \outer, \protected. So if you use those you can't easily switch to \newcommand, either. (Except maybe for \long/not \long which is mapped to \newcommand/\newcommand*.) I will assume you use none of those prefixes and I will use \newcommand, which makes its arguments \long, but you could of course use \newcommand* to get "un\long" arguments.

If we allow for these simplifications

s/\\def(\\[^{#]*)(?:#+([0-9]))*{/\\newcommand{$1}[0$2]{/g

yields acceptable results.

The following (crude approximation of a) Lua script does something quite similar. I didn't manage to get the matching for the argument structure #1, #1#2, ... done in a single group, so I opted for a loop doing that instead.

local io = require('io')
local string = string

local filename = arg[1] or nil

function newcommandify(content)
  content = string.gsub(content, "\\def(\\[^{#]*){",
                        "\\newcommand{%1}{")
  local iargs = ''
  for i=1,9 do
    iargs = iargs .. "#+" .. i
    content = string.gsub(content, "\\def(\\[^{#]*)" .. iargs .. "{",
                          "\\newcommand{%1}[".. i .. "]{")
  end
  return content
end

if filename then
  local file_handle = assert(io.open(filename, 'r'))
  local content = file_handle:read('*all')
  io.close(file_handle)

  content = newcommandify(content)

  file_handle = assert(io.open(filename .. "-newcommand", 'w'))
  file_handle:write(content)
  io.close(file_handle)
end

Save it as def-to-newcommand.lua and run

texlua def-to-newcommand.lua <yourfilename>

to obtain <yourfilename>-newcommand with \defs converted to \newcommand.

  • the LUA script do not work for this ` \def{\Sp}{\mathop{\rm Sp}\nolimits} \def{\spec}{\mathop{\rm sp}\nolimits}` Do you know why ? – Evan Barberousse Mar 23 at 12:02
  • @EvanBarberousse It doesn't catch those. You can catch them by additionally searching for "\\def{(\\[^{#]*)}{" and "\\def{(\\[^{#]*)}" .. iargs .. "{". – moewe Mar 23 at 12:13
  • \def{\Sp} is bad syntax for \def. – wipet Mar 24 at 14:17
0

Why do you wish to transform assignments in terms of \def into assignments in terms of \newcommand?

If it is only about ensuring that macros that are already defined won't get overridden, you can do another approach:

Basically \newcommand behaves as follows:

It checks for a star and optional argument and applies the kernel-macro \@ifdefinable for finding out whether the control sequence token in question is already defined within the current scope.

If so, \@ifdefinable will throw an error-message and not (re)define the control sequence token in question.

If not so, the control-sequence-token in question will be defined in terms of \def (, probably preceded by \long). In case of processing optional arguments, routines for detecting the presence of an optional argument will be included into the definitions performed by \newcommand, also.

Caveats with \newcommand:

  • \newcommand itself is a macro/is a "mechanism" which is based on macros. If the arguments of macros contain tokens that are defined in terms of \outer, (La)TeX will deliver an error-message. Thus: If you try to define a command via \newcommand which is already defined in terms of \outer, you will not get the error-message about the control-sequence already being defined but you will get an error-message ! Forbidden control sequence found while scanning use of \new@command.

  • \newcommand will check for the command being already defined only within the current scope. If not, it will define that command only within the current scope. Therefore the check of \newcommand is not completely safe when intending to define a macro globally, e.g., via \newcommand\foo{bar}\global\let\foo=\foo.

  • Actually the \newcommand-"mechanism" does not check for the command being already defined, but it checks whether the command is already defined to something other than the meaning of the \relax primitive. It does so by applying \csname..\endcsname to the name of the command (which is formed by applying \string and removing the leading backslash) and then via \ifx checking whether the meaning of the command equals the meaning of the \relax-primitive. It does so because \csname..\endcsname itself does (regardless the value of the \globaldefs-parameter) (only) within the current scope assign the meaning of the \relax-primitive to the tokens it forms in case they are undefined at the time of forming them. Therefore \newcommand silently redefines control-sequence-tokens that are already defined and whose meaning equals the \relax-primitive. E.g., you can do

    \let\MyCommand=\relax
    % now \MyCommand is defined and its meaning equals the meaning of the \relax-primitive
    \newcommand\MyCommand{This is the redefinition of MyCommand which gets carried out without warnings/error-messages.}
    

    You can also do this within a local scope/within a group for causing \newcommand to take \MyCommand for undefined within that scope.

    A caveat is: If you do something like

    \begingroup
    \let\MyCommand=\relax
    % now \MyCommand is defined and its meaning equals the meaning of the \relax-primitive
    \newcommand\MyCommand{This is the redefinition of MyCommand which gets carried out without warnings/error-messages.}
    \global\let\MyCommand=\MyCommand
    \endgroup
    

    , \MyCommand might already have been defined outside the group and it will be overridden outside the group/within all scopes without whatsoever notification.

My suggested approach is:

Assignments in terms of \def are always of pattern

⟨prefix⟩\def⟨control sequence token⟩⟨parameter text⟩{⟨replacement text⟩}

, with ⟨prefix⟩ =emptiness or a combination of \global and/or \long and/or \outer.

(With \gdef or \edef or \xdef the pattern looks accordingly.)

You can implement a macro \definedchecker which via catching a left-brace-delimited argument (→see the TeXbook for #1#{-notation) gathers the ⟨prefix⟩, the \def, the ⟨control sequence token⟩ and the ⟨parameter text⟩ and then checks whether this contains \def or \gdef or \edef or \xdef and if so uses that information for splitting the ⟨prefix⟩, the \def, the ⟨control sequence token⟩ and the ⟨parameter text⟩ apart before applying \@ifdefinable—but be aware that the \@ifdefinable-check is not completely save when it is about defining a control sequence token not only within the current scope but globally.

In other words: You can implement a macro \definedchecker which is to be prepended to your \def/\gdef/\edef/\xdef-sequences and which checks whether the control sequence token in question is already defined within the current scope and if so delivers an error-message and if not so performs the assignment.
!!!Be aware that checking for a control sequence token being defined within the current scope is not completely safe when it comes to defining it globally!!!

% Compile this example by calling LaTeX from a shell/command-prompt/console
% where you can also see the messages of the program!
%
\documentclass{article}
\makeatletter
%%<-------------------------------------------------------------------->
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%<-------------------------------------------------------------------->
%% Code for definedchecker
%%......................................................................     
\@ifdefinable\UD@gobbleto@gef{\long\def\UD@gobbleto@def#1\def{}}%
\@ifdefinable\UD@gobbleto@gdef{\long\def\UD@gobbleto@gdef#1\gdef{}}%
\@ifdefinable\UD@gobbleto@edef{\long\def\UD@gobbleto@edef#1\edef{}}%
\@ifdefinable\UD@gobbleto@xdef{\long\def\UD@gobbleto@xdef#1\xdef{}}%
\newcommand\UD@CheckWhetherNoDef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@def#1\def}}%
\newcommand\UD@CheckWhetherNoGdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@gdef#1\gdef}}%
\newcommand\UD@CheckWhetherNoEdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@edef#1\edef}}%
\newcommand\UD@CheckWhetherNoXdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@xdef#1\xdef}}%
\@ifdefinable\UD@catchto@gef{\long\def\UD@catch@def#1\def#2#3#{\innerdefinedchecker{#1}{\def}{#2}{#3}}}%
\@ifdefinable\UD@catchto@gdef{\long\def\UD@catch@gdef#1\gdef#2#3#{\innerdefinedchecker{#1}{\gdef}{#2}{#3}}}%
\@ifdefinable\UD@catchto@edef{\long\def\UD@catch@edef#1\edef#2#3#{\innerdefinedchecker{#1}{\edef}{#2}{#3}}}%
\@ifdefinable\UD@catchto@xdef{\long\def\UD@catch@xdef#1\xdef#2#3#{\innerdefinedchecker{#1}{\xdef}{#2}{#3}}}%
\newcommand\innerdefinedchecker[5]{\@ifdefinable{#3}{#1#2#3#4{#5}}}%
\@ifdefinable\definedchecker{%
  \long\def\definedchecker#1#{%
     \UD@CheckWhetherNoDef{#1}{%
       \UD@CheckWhetherNoGdef{#1}{%
         \UD@CheckWhetherNoEdef{#1}{%
           \UD@CheckWhetherNoXdef{#1}{%
             #1%
           }{\UD@catch@xdef#1}%
         }{\UD@catch@edef#1}%
       }{\UD@catch@gdef#1}%
     }{\UD@catch@def#1}%
  }%
}%
\makeatother

\definedchecker\xdef\eclaire{\noexpand\mathbb}
\show\eclaire

\definedchecker\def\R{\ensuremath{\eclaire R}}
\show\R

\definedchecker\outer\global\long\def\myWeirdCommand#1Delimier#2delimiter#{This is my weird command}
\show\myWeirdCommand

% These throw "command already defined"-errors and leave the previous meanings untouched:

\definedchecker\edef\eclaire{Weird Redefinition of eclaire}
\show\eclaire

\definedchecker\gdef\R{Weird Redefinition of R}
\show\R

% Don't do this as \myWeirdCommand is outer.
% \newcommand\myWeirdCommand{Blah}

\begin{document}
\end{document}

Here are the messages that I get into test.log when saving this as test.tex and compiling with pdflatex:

> \eclaire=macro:
->\mathbb .
l.111     \show\eclaire

? 
> \R=macro:
->\ensuremath {\eclaire R}.
l.114     \show\R

? 
> \myWeirdCommand=\long\outer macro:
#1Delimier#2delimiter{->This is my weird command{.
l.117     \show\myWeirdCommand

? 

! LaTeX Error: Command \eclaire already defined.
               Or name \end... illegal, see p.192 of the manual.

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

l.121 ...ef\eclaire{Weird Redefinition of eclaire}

? 
> \eclaire=macro:
->\mathbb .
l.122     \show\eclaire

? 

! LaTeX Error: Command \R already defined.
               Or name \end... illegal, see p.192 of the manual.

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

l.124 ...edchecker\gdef\R{Weird Redefinition of R}

? 
> \R=macro:
->\ensuremath {\eclaire R}.
l.125     \show\R

From these messages you can see:

In case the command in question within the current scope is already defined to have a meaning other the meaning of the \relax-primitive, \definedchecker delivers an error and does not carry out the assignment.
Otherwise the assignment is carried out.

Be aware that, regarding avoiding overriding definitions of macros that are already defined, this strategy is not completely safe when it comes to defining macros globally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.