Interesting expansion experiment

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

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

Interesting expansion experiment

#1 Post by dbenham » 02 Aug 2011 08:04

I have no question here - just sharing the results of an interesting (but useless) expansion experiment demonstrating 4 levels of expansion within one "simple" statement, without using CALL:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set "myVar=999"
set "pointer=prefix.myVar"
set z=a
for %%a in (7) do set /a "result=!pointer:~%%%z%!"
set result
Output:

Code: Select all

result=999

The intersting bit: set /a "result=!pointer:~%%%z%!"

Expansion 1: %z% --> a

Expansion 2: %%a --> 7

Expansion 3: !pointer:~7! --> MyVar

Expansion 4: set /a "result=MyVar" --> set /a "result=999"

The above is a bit imprecise, but is easy to follow. By the time we get to Expansion 2, the parser actually sees %a, not %%a. During phase one of the parser, %% --> %, and %z% --> a


Dave Benham

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

Re: Interesting expansion experiment

#2 Post by jeb » 02 Aug 2011 09:07

Hi Dave,

I love interesting experiments. :)
dbenham wrote: By the time we get to Expansion 2, the parser actually sees %a, not %%a. During phase one of the parser, %% --> %, and %z% --> a

This is always the case, even for a simple construct like

Code: Select all

FOR %%a in (4) do echo %%a

%%->% and a=a, that is the cause why you only need one percent sign on the command line, as the cmd-line parser doesn't compress %% nor remove a single %.
I suppose the FOR-Loop-Variable detection is an own phase, perhaps only once after the special character phase.

Proof :wink:

Code: Select all

FOR %%a in (555) do echo %%^a

Resut: 555
This shows that the %a is scanned after the caret is removed.

But perhaps the complete content is parsed multiple times, once for each loop iteration.

But sometimes even the parser isn't sure what to do :!:

Code: Select all

@echo off
cls
setlocal
set "prompt=###PARSER ECHO### "
echo on
for %%a in (XXX YYY) do (
  for %%a in (1 2) do echo "%%a"
)
@echo off
exit /b
The parser wrote:###PARSER ECHO### for %a in (XXX YYY) do (for %a in (1 2) do echo "%a" )
###PARSER ECHO### (for %a in (1 2) do echo "XXX" )
###PARSER ECHO### echo "1"
"1"
###PARSER ECHO### echo "2"
"2"
###PARSER ECHO### (for %a in (1 2) do echo "YYY" )
###PARSER ECHO### echo "1"
"1"
###PARSER ECHO### echo "2"
"2"

Ok, it's hard, as I build two loops with the same variable name.
But in line 2 the parser decide to use the content "XXX" of the outer loop.
But in line 3 the parser revert his decision and takes the content of the inner loop.

So I suppose the parser has parsed the complete block and have internally marked the variable places for replacements.

jeb

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

Re: Interesting expansion experiment

#3 Post by Ed Dyreen » 02 Aug 2011 09:17

'
Hi, jeb long time no seeing you :D
But in line 2 the parser decide to use the content "XXX" of the outer loop.
But in line 3 the parser revert his decision and takes the content of the inner loop.
The 'XXX' should be replaced by '1' and '2' because the inner loop finally decides the value of %a.

Code: Select all

(for %a in (1 2) do echo "XXX" )
The parser just displays 'XXX' to con, but internally it's:

Code: Select all

(for %a in (1 2) do echo %a )
Weird, but logical.

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

Re: Interesting expansion experiment

#4 Post by dbenham » 02 Aug 2011 10:47

jeb wrote:But in line 2 the parser decide to use the content "XXX" of the outer loop.
But in line 3 the parser revert his decision and takes the content of the inner loop.
Yes, very interesting. I remember seeing you post something very similar before.

Another interesting quirk that I remember you (jeb) explained to me before - the FOR variable expansion is GREEDY :!: This can lead to unexpected behavior if we are not careful. Everything below is on the command line - no batch file involved.

Nothing special here:

Code: Select all

D:\utils>for /f %b in (""part1.part2"") do @echo %bPART3
"part1.part2"PART3

Now I want to eliminate the quotes from the display:

Code: Select all

D:\utils>for /f %b in (""part1.part2"") do @echo %~bPART3
part1.part2PART3
Still no problem

Now I want to do the same thing using a different variable:

Code: Select all

D:\utils>for /f %A in (""part1.part2"") do @echo %~APART3
\utils\RT3
:shock: :?: :?:
It is actually logical. The FOR variable expansion supports a number of modifiers, including A for the file attribute and P for the file path. The file doesn't actually exist, so it only displays the path that was searched.

When parsing FOR variables after encountering %~, the parser looks for either a modifier or a variable. If it finds a variable that is also a valid modifier, it checks to see if the next character is a modifier or variable. It continues until it finds neither.

If we create the file, then the output makes more sense:

Code: Select all

D:\utils>echo junk>part1.part2

D:\utils>for /f %A in (""part1.part2"") do @echo %~APART3
--a------ \utils\RT3
This illustrates something that is new to me: The order of the expanded text is fixed. It does not matter what order the modifiers appear, the output will always have the same order.

It also doesn't matter how many times the modifier appears, and the DPNX modifiers are redundant in the presense of the F modifier:

Code: Select all

D:\utils>for /f %A in (""part1.part2"") do @echo %~^XNPDTZASFXNPDTZASFAOK-ENOUGH ALREADY
--a------ 08/02/2011 12:14 PM 6 D:\utils\PART1~1.PAROK-ENOUGH ALREADY
All the characters up until the O are valid modifiers. The A before the O is the variable. Note how the S modifier converts the full path into the short 8.3 format.

THIS STATEMENT IN RED IS WRONG!One other new discovery for me - A character is only considered a modifier if it matches the case of the FOR variable.
The case of the modifier does not matter. I fooled myself with my testing. :oops:

These statements don't have any modifiers because there is not any valid FOR variable after the initial letter:

Code: Select all

D:\utils>for /f %A in (""part1.part2"") do @echo %~Apart3
part1.part2part3

D:\utils>for /f %a in (""part1.part2"") do @echo %~aPART3
part1.part2PART3

These statements DO have modifiers:

Code: Select all

D:\utils>for /f %A in (""part1.part2"") do @echo %~APART3
--a------ \utils\RT3

D:\utils>for /f %a in (""part1.part2"") do @echo %~apart3
--a------ \utils\rt3


Dave Benham
Last edited by dbenham on 11 Aug 2011 14:46, edited 2 times in total.

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

Re: Interesting expansion experiment

#5 Post by dbenham » 02 Aug 2011 12:36

Using FOR variable modifiers, we can really obfuscate my original experiment :twisted: :

Code: Select all

::@echo off
setlocal EnableDelayedExpansion
echo 12345>test.txt
set "myVar=999"
set "pointer=prefix.myVar"
set a=z
for %%z in (test.txt) do set /a "result=!pointer:~%%~%a%%a%!"
set result
Output

Code: Select all

result=999


Dave Benham

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

Re: Interesting expansion experiment

#6 Post by dbenham » 11 Aug 2011 14:51

Corrected a bone-headed conclusion in a prior post - FOR variable modifiers are actually NOT case dependent. Sorry everyone.

Dave Benham

Post Reply