Comments without increasing macro size

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Comments without increasing macro size

#1 Post by jeb » 13 Feb 2014 03:22

Hi,

I saw a post from Liviu ReturnVar macro revisitedwhere he used two versions of a macro, one commented and one uncommented.

Here I try to show a technic how to write macros with comments without increasing the macro size.

This uses some sort of the comment style invented by Ed.

Percent comments are a better choice here, as they are removed completly when the macro will be created.

But when you add line with only one comment, you get a problem with the multiline caracter at the line end.

Sample, this works and outputs "Word1Word2"

Code: Select all

echo Word1^
Word2


But with a comment it fails

Code: Select all

echo Word1^
%= REM this is a comment line%^
Word2

It fails as the caret in the comment line is the first character in the line, after the parser has removed the percent comment.
But then the multiline caret from the first line will not only append the comment line, it will also escape the first character!

To avoid this you can simply use the obvious :wink: dummy redirect trick.

Code: Select all

echo Word1<nul ^
%= REM this is a comment line%^
Word2


And here is a sample for a real macro, in this case the NUL redirection is executed only for the set-macro command and so it will not be a part of the macro itself.

Code: Select all

set LF=^


::Above 2 blank lines are required - do not remove
@set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
rem ***

::: Timediff pTime1 pTime2 pResult
set $timediff=for /L %%n in (1 1 2) do if %%n==2 (%\n%
%=    Read the parameters =%^
      for /F "tokens=1,2,3 delims=, " %%1 in ("!argv!") do (%\n%
      set "time1=!%%~1: =0!" %\n%
      set "time2=!%%~2: =0!" %\n%
%=      Convert the time format to milliseconds =%^
      set /a "t1=((1!time1:~0,2!*60+1!time1:~3,2!)*60+1!time1:~6,2!-366100)*1000+1!time1:~-2!*10-1000" %\n%
      set /a "t2=((1!time2:~0,2!*60+1!time2:~3,2!)*60+1!time2:~6,2!-366100)*1000+1!time2:~-2!*10-1000" %\n%
      set /a "diff=t2-t1" %\n%
      for /f "delims=" %%r in ("!diff!") do endlocal^& set "%%~3=%%~r" %\n%
      ) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,


jeb

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Comments without increasing macro size

#2 Post by dbenham » 13 Feb 2014 09:10

Nice trick with the <nul. I had seen you document that before, but this is a good practical application. 8)
Although my macro comments tend to be indented, so <nul is not needed. The indents add a bit to the macro size, but I find it a bit more readable.

It should be noted that the comment cannot contain % or : characters.


Dave Benham

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Comments without increasing macro size

#3 Post by jeb » 13 Feb 2014 09:39

dbenham wrote:It should be noted that the comment cannot contain % or : characters.
You are right, but I suppose that the most macro writers know this :)

dbenham wrote:Although my macro comments tend to be indented, so <nul is not needed. The indents add a bit to the macro size, but I find it a bit more readable.

Normally I also indent my code and also my comments, but once I struggled when I tried to not increase the macro size.
And I need some time to see the difference between this two fragments.

Code: Select all

echo Hello ^
   %my idented comment%^
world


Code: Select all

echo Hello ^
%   my not idented comment%^
world


jeb

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Comments without increasing macro size

#4 Post by Liviu » 14 Feb 2014 23:59

jeb wrote:To avoid this you can simply use the obvious :wink: dummy redirect trick.
Ah, so obvious, of course ;-) Nice trick, thanks for pointing.

dbenham wrote:It should be noted that the comment cannot contain % or : characters.
Which also excludes comments like %=C:% or %=%homedrive%% among others. Good reminder of why the '=' is more than esthetic in '%='.

Liviu

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Comments without increasing macro size

#5 Post by Ed Dyreen » 16 Feb 2014 08:07

thanks jeb, may I suggest

Code: Select all

@set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"
instead of

Code: Select all

@set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
then it works always

Code: Select all

@echo off &setlocal enableDelayedExpansion
ver &echo.
set $lf=^


:: $n1c newLine + continuation, 1 expansions, 0 sets, 1 echo
set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^^<nul ^^"

%=   =%set ^"icho_=for /L %%n in (1 1 2) do if %%n==2 (^
!==^^^!^
%=      =%echo.hello%$n1c%
%=      =%echo.^
!==^^^!^
%=   =%)else echo."

set icho_
( %icho_% myFunc^( okido ^) )
echo.


%=   =%set ^"icho_=^
!==^^^!^
%=      =%echo.hello%$n1c%
%=      =%echo.%$n1c%
%=      =%echo.^
!==^^^!^
%=   =%"

set icho_
( %icho_% myFunc^( okido ^) )
echo.


:: $n1c newLine + continuation, 1 expansions, 0 sets, 1 echo
set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^<nul ^^"

%=   =%set ^"icho_=for /L %%n in (1 1 2) do if %%n==2 (^
!==^^^!^
%=      =%echo.hello%$n1c%
%=      =%echo.^
!==^^^!^
%=   =%)else echo."

set icho_
( %icho_% myFunc^( okido ^) )
echo.


%=   =%set ^"icho_=^
!==^^^!^
%=      =%echo.hello%$n1c%
%=      =%echo.%$n1c%
%=      =%echo.^
!==^^^!^
%=   =%"

set icho_
( %icho_% myFunc^( okido ^) )
echo.


pause
exit

Code: Select all


Microsoft Windows XP [versie 5.1.2600]

icho_=for /L %n in (1 1 2) do if %n==2 (echo.hello
 echo.)else echo.
 myFunc( okido )
hello


icho_=echo.hello
 echo.
 echo.
hello

 myFunc( okido )

icho_=for /L %n in (1 1 2) do if %n==2 (echo.hello
)else echo.
 myFunc( okido )
hello

icho_=echo.hello
echo.

hello

myFunc( wordt niet herkend als een interne
of externe opdracht, programma of batchbestand.

Druk op een toets om door te gaan. . .
This '<nul ^^' injects an unneeded space in the final definition :?
jeb wrote:
dbenham wrote:Although my macro comments tend to be indented, so <nul is not needed. The indents add a bit to the macro size, but I find it a bit more readable.
Normally I also indent my code and also my comments, but once I struggled when I tried to not increase the macro size.
And I need some time to see the difference between this two fragments.
but the <LF><SPACE> can be replaced with <LF> logically after definition.

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Comments without increasing macro size

#6 Post by Liviu » 19 Feb 2014 01:10

jeb wrote:And here is a sample for a real macro, in this case the NUL redirection is executed only for the set-macro command and so it will not be a part of the macro itself.

Code: Select all

...
set $timediff=for /L %%n in (1 1 2) do if %%n==2 (%\n%
%=    Read the parameters =%^
      for /F "tokens=1,2,3 delims=, " %%1 in ("!argv!") do (%\n%
...
Consecutive comment lines fail when ended with =%^, but (appear to?) work with double carets =%^^...

Code: Select all

%=    Read the parameters =%^^
%=    more comment here =%^^
...though I can't help that nagging feeling that I must be missing something ;-)

Ed Dyreen wrote:thanks jeb, may I suggest

Code: Select all

@set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"
instead of

Code: Select all

@set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
then it works always
Sorry, you lost me here. What's the failure case your ^^<nul is solving? I know it's supposed to be self-evident from the example you gave, but it's getting late in my timezone and a little pointer could help ;-)

Liviu

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Comments without increasing macro size

#7 Post by jeb » 19 Feb 2014 06:40

Ed Dyreen wrote:thanks jeb, may I suggest
Code:
@set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"


No, this will not work, as it results to

Code: Select all

set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"
set \n
-----
Output:
\n=^

^ ^

So there is no "<nul" anymore in the "\n" variable.

Liviu wrote:
jeb wrote:And here is a sample for a real macro, in this case the NUL redirection is executed only for the set-macro command and so it will not be a part of the macro itself.

Code: Select all

...
set $timediff=for /L %%n in (1 1 2) do if %%n==2 (%\n%
%=    Read the parameters =%^
      for /F "tokens=1,2,3 delims=, " %%1 in ("!argv!") do (%\n%
...
Consecutive comment lines fail when ended with =%^, but (appear to?) work with double carets =%^^...

Code: Select all

%=    Read the parameters =%^^
%=    more comment here =%^^
...though I can't help that nagging feeling that I must be missing something ;-)
Liviu

Yes, you missed the point, that a multiline caret normally escapes the first character of the next line.
With you double carets you get a working multiline appending but you also include carets into the macro.

But my sample was badly choosen, it was contrary to the rest I described before :oops:

For comments you should use the changed %\n% variable, or better a new %\C% variable to avoid even the injection of the unnessesary newline.

Code: Select all

set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
set "\C=<nul ^"

set myMacro=echo Line1%\n%
%= My first comment%%\C%
%= My second comment%%\C%
echo Line2%\n%
echo End

Now the comment lines will be removed without any left spaces or newlines

jeb

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Comments without increasing macro size

#8 Post by Liviu » 19 Feb 2014 11:00

jeb wrote:For comments you should use the changed %\n% variable, or better a new %\C% variable to avoid even the injection of the unnessesary newline.

Code: Select all

set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
set "\C=<nul ^"

That's a neat solution. Maybe I'd call it "\\" instead of "\C" to keep with the "\n" C syntax analogies - where "\\" expands to "\" which is the line continuation character in macros. But that's completely immaterial.

jeb wrote:With you double carets you get a working multiline appending but you also include carets into the macro.

I may still be missing something, but I don't see extra carets or newlines here when using ^^.

Code: Select all

set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"

set myMacro=(echo Line1%\n%
%= My first comment%^^
%= My second comment%^^
echo Line2%\n%
echo End)

set myMacro

setlocal enableDelayedExpansion
echo(
echo !myMacro!
endlocal

echo(
%myMacro%
Output:

Code: Select all

myMacro=(echo Line1
echo Line2
echo End)

(echo Line1
echo Line2
echo End)

Line1
Line2
End

Liviu

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Comments without increasing macro size

#9 Post by Ed Dyreen » 19 Feb 2014 11:22

jeb wrote:
Ed Dyreen wrote:thanks jeb, may I suggest
Code:
@set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"


No, this will not work, as it results to

Code: Select all

set ^"\n=^^^%LF%%LF%^%LF%%LF%^^<nul ^^"
set \n
-----
Output:
\n=^

^ ^

So there is no "<nul" anymore in the "\n" variable.
Lol, that is why it worked :mrgreen:

Code: Select all

@echo off &setlocal enableDelayedExpansion

%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^^"

%=   =%set ^"$macro=!forQ_! (1,2) do if %%?==2 (^
%=      =%(^
echo.test0%$n1c%
%=      =%)^
%=   =%)else setlocal enableDelayedExpansion^&set º="

echo.
set $macro
( %$macro% )


%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^<nul ^^"

%=   =%set ^"$macro=!forQ_! (1,2) do if %%?==2 (^
%=      =%(^
echo.test1%$n1c%
%=      =%)^
%=   =%)else setlocal enableDelayedExpansion^&set º="

echo.
set $macro
( %$macro% )

pause
exit
it alters behavior

Code: Select all

$macro=for %? in (1,2) do if %?==2 ((echo.test0
))else setlocal enableDelayedExpansion&set ║=
test0

$macro=for %? in (1,2) do if %?==2 ((echo.test1
)else setlocal enableDelayedExpansion&set ║=
else niet verwacht op dit moment.

>
and fails where it used to work :(

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Comments without increasing macro size

#10 Post by jeb » 19 Feb 2014 12:30

Liviu wrote:I may still be missing something, but I don't see extra carets or newlines here when using ^^.

Sorry, I missed here the obvious point :oops:

I forgot the general knowlegde about the <nul caret construct.

First a sample

Code: Select all

echo Hello <nul the^
only^
world

Output wrote:Hello world


In the first line the word "the" is removed, as the word/token directly preceding the caret is removed (you can build also long tokens with quotes).
Normally that's all, the next line is normally appended as expected.

But in this case the next line consists also by only one token directly connected to a caret, and this is also completly removed.

Why :?: I have absolutly no idea :roll:
I can't imagine any reasonable design with such an effect.

jeb

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Comments without increasing macro size

#11 Post by Liviu » 19 Feb 2014 14:28

jeb wrote:In the first line the word "the" is removed, as the word/token directly preceding the caret is removed (you can build also long tokens with quotes).
Normally that's all, the next line is normally appended as expected.
Calling that "general knowledge" is a bit of a stretch ;-) It's not obvious to me how/why that happens, and I don't see it following from the parsing rules - which seem to imply that carets are processed before tokenizing.

But the really fascinating issue here is that execution depends on where <nul is, and I don't remember offhand another case where the placement of redirections mattered. Case in point, <nul before the echo works as expected, but <nul in the middle of the line produces odd effects.

Code: Select all

C:\tmp>echo a <nul b^
More? c^
More? d
a d

C:\tmp><nul echo a b^
More? c^
More? d
a bcd

Also, as long as the continuation line is not a single token, it doesn't seem to happen any longer.

Code: Select all

C:\tmp>echo a <nul b^
More? c d^
More? e f
a c de f


Ed Dyreen wrote:it alters behavior [...] and fails where it used to work :(
This looks to be a side-effect of what Jeb describes above. But, just curious, why does your code mix ^ and %$n1c% all over the place? If you used %$n1c% consistently, there seems to be no such problem. At least, this works correctly.

Code: Select all

%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^<nul ^^"

%=   =%set ^"$macro=for %%? in (1,2) do if %%?==2 (%$n1c%
%=      =%(%$n1c%
echo.test1%$n1c%
%=      =%)%$n1c%
%=   =%)else setlocal enableDelayedExpansion^&set º="

Code: Select all

$macro=for %? in (1,2) do if %?==2 ((echo.test1))else setlocal enableDelayedExpansion&set ║=
test1

Liviu

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Comments without increasing macro size

#12 Post by Ed Dyreen » 19 Feb 2014 16:21

Code: Select all

@echo off &set $lf=^


echo.&echo.expected result:
%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^^"

%=   =%set ^"$macro=for %%? in (1,2) do if %%?==2 (^
%=      =%(^
%=         =%echo.test%$n1c%
%=         =%echo.test^
%=      =%)^
!==^^^!^
%=   =%)else setlocal enableDelayedExpansion^&set º="

set "$macro"


echo.&echo.jeb result:
%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^<nul ^^"

%=   =%set ^"$macro=for %%? in (1,2) do if %%?==2 (^
%=      =%(^
%=         =%echo.test%$n1c%
%=         =%echo.test^
%=      =%)^
!==^^^!^
%=   =%)else setlocal enableDelayedExpansion^&set º="

set "$macro"

echo.&echo.liviu result:

%=   =%set ^"$n1c=^^^%$lf%%$lf%^%$lf%%$lf%^<nul ^^"

%=   =%set ^"$macro=for %%? in (1,2) do if %%?==2 (%$n1c%
%=      =%(%$n1c%
%=      =%echo.test%$n1c%
%=      =%echo.test%$n1c%
%=      =%)%$n1c%
%=   =%)else setlocal enableDelayedExpansion^&set º="

set "$macro"

pause
exit

Code: Select all


expected result:
$macro=for %? in (1,2) do if %?==2 ((echo.test
echo.test)!==^!)else setlocal enableDelayedExpansion&set ║=

jeb result:
$macro=for %? in (1,2) do if %?==2 ((echo.test
!==^!)else setlocal enableDelayedExpansion&set ║=

liviu result:
$macro=for %? in (1,2) do if %?==2 (
(
echo.test
echo.test
)
)else setlocal enableDelayedExpansion&set ║=
Druk op een toets om door te gaan. . .
Liviu wrote:This looks to be a side-effect of what Jeb describes above. But, just curious, why does your code mix ^ and %$n1c% all over the place? If you used %$n1c% consistently, there seems to be no such problem. At least, this works correctly.
True, but I am jewish with linefeeds

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Comments without increasing macro size

#13 Post by Liviu » 21 Feb 2014 01:16

To paraphrase your own classic viewtopic.php?p=8388#p8388 "why does everyone seems to be interested in speed byte-count lately, we used to be happy if we got it working :D".

Back on topic, I don't think we have a final resolution on newlines and single-token continuations at this point.

Liviu

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Comments without increasing macro size

#14 Post by jeb » 24 Feb 2014 06:46

Liviu wrote:Back on topic, I don't think we have a final resolution on newlines and single-token continuations at this point.


I suppose it's a 90% solution.

This works fine

Code: Select all

@echo off
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^<nul ^^"
set "\\=<nul ^"

set macro=(echo Line1%\n%
%=comment line1%%\\%
%=comment line2%%\\%
cd%\n%
echo Line2%\n%
)


But when the last line consists only by one token and the previous line was a comment line ended with %\\% it fails :!:

Code: Select all

@echo on

set macro=(echo Line1%\n%
%=comment line1%%\\%
%=comment line2%%\\%
echo.oneToken)


Output wrote:C:\temp>set macro=(echo Line1
echo.oneToken) 0<<nul


Currently I don't understand how it creates the "<<".

jeb

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Comments without increasing macro size

#15 Post by penpen » 24 Feb 2014 17:54

I'm not sure if the creation of "<<" may be a kind of bug, or an inbetween result of the batch parser:
(Edit: The next is completely wrong... sry for that)
It seems, that the parser not only parses the source line by line,
it also parses all virtual lines line by line.
So this:

Code: Select all

:: ...
set macro=(echo Line1%\n%
%=comment line1%%\\%
%=comment line2%%\\%
echo.oneToken)
:: ...
is parsed to that virtual lines:

Code: Select all

set macro=^

<nul ^<nul ^<nul echo.oneToken)
which results in:

Code: Select all

set macro=(echo Line1
echo.oneToken) 0<<nul

Nevertheless the %\n% may induce an unwanted "<nul" at the end of the macro which may be suboptimal for such macro usage (here macro $readLine):

Code: Select all

(
   %$readLine% PARAMS
) < file

penpen
Last edited by penpen on 25 Feb 2014 07:54, edited 1 time in total.

Post Reply