How do you write a macro that takes arguments containing paragraphs?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












2















I'm trying to write a macro that takes arguments containing paragraphs. If you write a normal macro and one of its arguments contains a paragraph, it will break:



documentclassarticle
usepackage[utf8]inputenc

titletest

begindocument

maketitle

sectionIntroduction

defmymacro#1#1

mymacroThis

contains a paragraph

enddocument


This produces an error:




Runaway argument?
 
show 9 more comments








11












11








11







At the time TeX was written, one page of a document would take several minutes to be processed, and syntax highlighting was not a thing, so it was a good thing to have some mechanism to detect if you forgot a . A def, by default, doesn't allow a par token unless you explicitly say it's a longdef:



defmymacro#1#1


LaTeX, on the other hand, uses that by default, so if you use proper LaTeX commands (def shouldn't be used in LaTeX documents), newcommand makes a longdef by default. If you want a “short” def then you use newcommand*.



xparse returns the short argument default, but lets you define a long macro using the + argument modifier:



NewDocumentCommandmymacro m#1% def
NewDocumentCommandmymacro+m#1% longdef



Your second attempt is clever, and it could have worked except for two things.



First is that you are using gdefoldparpar and then gdefparoldpar. Once you expand par you get oldpar which, when expanded, yields par which, when expanded, yields oldpar which, when expanded, yields par which, when expanded, yields oldpar... Running forever :/



You need to use let (or globallet to have global effect) in this case: letoldparpar. This creates an exact copy of par named oldpar which does not depend on what is par.



Second, the runaway argument checking is implemented in a lower level, independent of the definition of par, so this would fail with the same error:



letparrelax
defmymacro#1#1
mymacrofoo

bar


because when TeX sees two endlinechar tokens (which is a space by default) TeX inserts an implicit par token, which raises the Runaway argument error. Knowing that, then:



newcountoldELchar
oldELchar=endlinechar
defmymacroendlinechar=-1relaxmymacroi
defmymacroi#1#1endlinechar=oldELchar
mymacrofoo

bar


won't raise an error, but a new line won't be a space anymore.






share|improve this answer













At the time TeX was written, one page of a document would take several minutes to be processed, and syntax highlighting was not a thing, so it was a good thing to have some mechanism to detect if you forgot a }. A def, by default, doesn't allow a par token unless you explicitly say it's a longdef:



defmymacro#1#1


LaTeX, on the other hand, uses that by default, so if you use proper LaTeX commands (def shouldn't be used in LaTeX documents), newcommand makes a longdef by default. If you want a “short” def then you use newcommand*.



xparse returns the short argument default, but lets you define a long macro using the + argument modifier:



NewDocumentCommandmymacro m#1% def
NewDocumentCommandmymacro+m#1% longdef



Your second attempt is clever, and it could have worked except for two things.



First is that you are using gdefoldparpar and then gdefparoldpar. Once you expand par you get oldpar which, when expanded, yields par which, when expanded, yields oldpar which, when expanded, yields par which, when expanded, yields oldpar... Running forever :/



You need to use let (or globallet to have global effect) in this case: letoldparpar. This creates an exact copy of par named oldpar which does not depend on what is par.



Second, the runaway argument checking is implemented in a lower level, independent of the definition of par, so this would fail with the same error:



letparrelax
defmymacro#1#1
mymacrofoo

bar


because when TeX sees two endlinechar tokens (which is a space by default) TeX inserts an implicit par token, which raises the Runaway argument error. Knowing that, then:



newcountoldELchar
oldELchar=endlinechar
defmymacroendlinechar=-1relaxmymacroi
defmymacroi#1#1endlinechar=oldELchar
mymacrofoo

bar


won't raise an error, but a new line won't be a space anymore.







share|improve this answer












share|improve this answer



share|improve this answer










answered Mar 7 at 13:32









Phelype OleinikPhelype Oleinik

24.9k54690




24.9k54690












  • Haha, I was wondering where I went into infinite recursion...

    – sgf
    Mar 7 at 13:36











  • @sgf You can say, for instance, tracingall before the command you suspect is infinite-looping. You'll get a ton of garbage in the terminal and the log, but it gets easier to find out what is TeX doing.

    – Phelype Oleinik
    Mar 7 at 13:39











  • I would take a slight difference with the statement "(def shouldn't be used in LaTeX documents)". Certain service routines a user writes may require the special parsing provided by def syntax. I think it would be better to say "(def should be avoided, where possible)".

    – Steven B. Segletes
    Mar 7 at 13:44






  • 1





    the endlinechar version has to be used with care, for example it also removes the end of line after mymacro (even if there is text following mymacro) not just the one in the argument.

    – David Carlisle
    Mar 7 at 13:47






  • 1





    tex takes a line at a time and inserts the character specified by endlinechar at the point it grabs the next line at the end of the buffer before tokenizing., so its timing is well defined but er "delicate" :-)

    – David Carlisle
    Mar 7 at 13:59

















  • Haha, I was wondering where I went into infinite recursion...

    – sgf
    Mar 7 at 13:36











  • @sgf You can say, for instance, tracingall before the command you suspect is infinite-looping. You'll get a ton of garbage in the terminal and the log, but it gets easier to find out what is TeX doing.

    – Phelype Oleinik
    Mar 7 at 13:39











  • I would take a slight difference with the statement "(def shouldn't be used in LaTeX documents)". Certain service routines a user writes may require the special parsing provided by def syntax. I think it would be better to say "(def should be avoided, where possible)".

    – Steven B. Segletes
    Mar 7 at 13:44






  • 1





    the endlinechar version has to be used with care, for example it also removes the end of line after mymacro (even if there is text following mymacro) not just the one in the argument.

    – David Carlisle
    Mar 7 at 13:47






  • 1





    tex takes a line at a time and inserts the character specified by endlinechar at the point it grabs the next line at the end of the buffer before tokenizing., so its timing is well defined but er "delicate" :-)

    – David Carlisle
    Mar 7 at 13:59
















Haha, I was wondering where I went into infinite recursion...

– sgf
Mar 7 at 13:36





Haha, I was wondering where I went into infinite recursion...

– sgf
Mar 7 at 13:36













@sgf You can say, for instance, tracingall before the command you suspect is infinite-looping. You'll get a ton of garbage in the terminal and the log, but it gets easier to find out what is TeX doing.

– Phelype Oleinik
Mar 7 at 13:39





@sgf You can say, for instance, tracingall before the command you suspect is infinite-looping. You'll get a ton of garbage in the terminal and the log, but it gets easier to find out what is TeX doing.

– Phelype Oleinik
Mar 7 at 13:39













I would take a slight difference with the statement "(def shouldn't be used in LaTeX documents)". Certain service routines a user writes may require the special parsing provided by def syntax. I think it would be better to say "(def should be avoided, where possible)".

– Steven B. Segletes
Mar 7 at 13:44





I would take a slight difference with the statement "(def shouldn't be used in LaTeX documents)". Certain service routines a user writes may require the special parsing provided by def syntax. I think it would be better to say "(def should be avoided, where possible)".

– Steven B. Segletes
Mar 7 at 13:44




1




1





the endlinechar version has to be used with care, for example it also removes the end of line after mymacro (even if there is text following mymacro) not just the one in the argument.

– David Carlisle
Mar 7 at 13:47





the endlinechar version has to be used with care, for example it also removes the end of line after mymacro (even if there is text following mymacro) not just the one in the argument.

– David Carlisle
Mar 7 at 13:47




1




1





tex takes a line at a time and inserts the character specified by endlinechar at the point it grabs the next line at the end of the buffer before tokenizing., so its timing is well defined but er "delicate" :-)

– David Carlisle
Mar 7 at 13:59





tex takes a line at a time and inserts the character specified by endlinechar at the point it grabs the next line at the end of the buffer before tokenizing., so its timing is well defined but er "delicate" :-)

– David Carlisle
Mar 7 at 13:59











1














The argument of a macro defined with def does not allow par tokens. Neither it allows blank lines, because they're transformed to par during the phase in which TeX processes text input into tokens. Note that redefining par is useless in this respect, because it's precisely the token par that's disallowed, independently of its meaning.



Solution: make your macro long.



longdefmymacro#1#1


Better solution:



newcommandmymacro[1]#1


because newcommand uses longdef internally. The variant newcommand* instead uses def without the prefix.






share|improve this answer



























    1














    The argument of a macro defined with def does not allow par tokens. Neither it allows blank lines, because they're transformed to par during the phase in which TeX processes text input into tokens. Note that redefining par is useless in this respect, because it's precisely the token par that's disallowed, independently of its meaning.



    Solution: make your macro long.



    longdefmymacro#1#1


    Better solution:



    newcommandmymacro[1]#1


    because newcommand uses longdef internally. The variant newcommand* instead uses def without the prefix.






    share|improve this answer

























      1












      1








      1







      The argument of a macro defined with def does not allow par tokens. Neither it allows blank lines, because they're transformed to par during the phase in which TeX processes text input into tokens. Note that redefining par is useless in this respect, because it's precisely the token par that's disallowed, independently of its meaning.



      Solution: make your macro long.



      longdefmymacro#1#1


      Better solution:



      newcommandmymacro[1]#1


      because newcommand uses longdef internally. The variant newcommand* instead uses def without the prefix.






      share|improve this answer













      The argument of a macro defined with def does not allow par tokens. Neither it allows blank lines, because they're transformed to par during the phase in which TeX processes text input into tokens. Note that redefining par is useless in this respect, because it's precisely the token par that's disallowed, independently of its meaning.



      Solution: make your macro long.



      longdefmymacro#1#1


      Better solution:



      newcommandmymacro[1]#1


      because newcommand uses longdef internally. The variant newcommand* instead uses def without the prefix.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Mar 7 at 21:17









      egregegreg

      731k8919303252




      731k8919303252



























          draft saved

          draft discarded
















































          Thanks for contributing an answer to TeX - LaTeX Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f478189%2fhow-do-you-write-a-macro-that-takes-arguments-containing-paragraphs%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown






          Popular posts from this blog

          How to check contact read email or not when send email to Individual?

          Displaying single band from multi-band raster using QGIS

          How many registers does an x86_64 CPU actually have?