Function containing iff doesn't work in build 48

#1
Why doesn't MakeDir1 in the Test.btm file below work? It works in build 41.

C:\Junk>ver /r

TCC 11.00.48 Windows Vista [Version 6.0.6002]
TCC Build 48 Windows Vista Build 6002 Service Pack 2
Registered to davidmarcus - 1 System License

C:\Junk>type Test.btm
*setlocal
function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done1`
function MakeDir2=`echo start2 %+ iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done2`
echo MakeDir1
%@MakeDir1[C:\Doc]
echo MakeDir2
%@MakeDir2[C:\Doc]

C:\Junk>Test.btm
MakeDir1
MakeDir2
start2
done2

C:\Junk>

Here it is with "echo on" in the btm:

C:\Junk>Test.btm
function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done1`
function MakeDir2=`echo start2 %+ iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done2`
echo MakeDir1
MakeDir1
echo MakeDir2
MakeDir2
echo start2
start2
iff not isdir "C:\Doc" then
echo done2
done2

C:\Junk>
 
#2
| Why doesn't MakeDir1 in the Test.btm file below work? It works in
| build 41.
...
| function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff
%+ echo done1`
...

Your MakeDir1 and MakeDir2 seem to define aliases, not variable
functions. Using the value of a variable function as a command, as
your TEST.BTM does, seems to stretch even the DWIM parser! What
surprises me is your claim it worked in an earlier build. If it did,
my guess is that it did by chance, not design. Variable functions are
intended to return a string or numeric value, to be used in the same
way as the value of an internal or environment variable, i.e., as a
command parameter. Your function MakeDir1 returns the string done1 to
be used as a command. MakeDir2 returns two lines as a single command.

Your function definitions should not surround %1 by quotation
marks (though irrelevant in your example of C:\DOC) because they must
be included in the CALL to the function when the desired path
contains whitespace, else the part following the whitespace would not
be part of %1. If the path is already quoted, the quotation marks in
the function definition would make it look like:

iff not isdir ""C:\Progarm Files\ABC"" then ...

which would yield syntax error.

BTW, remember that SETLOCAL does NOT work on functions. If you use a
global list for functions, all changes to the function table are
global, regardless of the use of SETLOCAL.
--
Steve
 
#3
Your MakeDir1 and MakeDir2 seem to define aliases, not variable functions. Using the value of a variable function as a command, as your TEST.BTM does, seems to stretch even the DWIM parser!
OK, but the Help says, "User-defined functions are powerful alternatives to subroutines." So, that's where I got the idea that they worked like subroutines. In quite a few languages, functions and subroutines are both procedures that can be called.

Your function definitions should not surround %1 by quotation
marks (though irrelevant in your example of C:\DOC) because they must
be included in the CALL to the function when the desired path
contains whitespace, else the part following the whitespace would not
be part of %1.
I know. I had %$, but changed it when simplifying the example.
 
#4
| ---Quote (Originally by Steve Fbin)---
| Your MakeDir1 and MakeDir2 seem to define aliases, not variable
| functions. Using the value of a variable function as a command, as
| your TEST.BTM does, seems to stretch even the DWIM parser! ---End
| Quote---
| OK, but the Help says, "User-defined functions are powerful
| alternatives to subroutines." So, that's where I got the idea that
| they worked like subroutines. In quite a few languages, functions
| and subroutines are both procedures that can be called.

That statement was dropped from V11 HELP. It was present in the HELP of
earlier versions, but was using incorrect terminology, because in languages
such as Fortran which use the term SUBROUTINE the term usually refers to a
procedure that performs some action without returning any value to its
caller. These languages use the term FUNCTION when the procedure returns a
value, using the same terminology as mathematics. AFAIK only the C and C++
languages include procedures not returning a value (more technically
"function returning void") in their use of the term FUNCTION.

Regardless, your goal can be achieved more clearly by defining the very same
actions as aliases. I did not test, by have no doubt that making them
aliases will result in exactly what you want, with more readable code.
Furthermore, you can actually just use the alias

MakeDir=*mkdir/s/ne

which will silently ignore a request to create an existing directory, but
will report an error if the specified directory does not exist when the
command is completed.


| ---Quote---
| Your function definitions should not surround %1 by quotation
| marks (though irrelevant in your example of C:\DOC) because they must
| be included in the CALL to the function when the desired path
| contains whitespace, else the part following the whitespace would not
| be part of %1.
| ---End Quote---
| I know. I had %$, but changed it when simplifying the example.

I recommend frequent use of the @QUOTE[] function, esp. when names are
generated automatically.
--
Steve
 
#5
That statement was dropped from V11 HELP.
It is still in my copy of the V11 Help on the "FUNCTION" page.

AFAIK only the C and C++ languages include procedures not returning a value (more technically "function returning void") in their use of the term FUNCTION.
PHP is another.

Furthermore, you can actually just use the alias

MakeDir=*mkdir/s/ne

which will silently ignore a request to create an existing directory, but
will report an error if the specified directory does not exist when the
command is completed.
I assume I can use the /nt and /ne options together. A /e option would be more logical than overloading the /n option. One has to read the Help carefully to find all the options. Any idea in what version the /ne option appeared?
 
#6
| ---Quote (Originally by Steve Fbin)---
|| That statement was dropped from V11 HELP.
| ---End Quote---
| It is still in my copy of the V11 Help on the "FUNCTION" page.

You are right, it is still there. What confused me was that in the older
HELP versions using the "search" tab with the string "subroutine" the topic
FUNCTION is listed, but not in the V11 HELP.

|| Furthermore, you can actually just use the alias
||
|| MakeDir=*mkdir/s/ne
||
|| which will silently ignore a request to create an existing
|| directory, but
|| will report an error if the specified directory does not exist when
|| the
|| command is completed.

| I assume I can use the /nt and /ne options together. A /e option
| would be more logical than overloading the /n option. One has to
| read the Help carefully to find all the options. Any idea in what
| version the /ne option appeared?

Proper combined use of the suboptions is /net, and is valid. The e and t
suboptions of /N appeared in V7 for the MD/MKDIR command. Yes, a plain /e
might be simpler, but the option letter E is not available in every command
where it makes sense. This is the reason why the /N command with suboptions
was introduced in V7, to permit as closely identical syntax as possible of
different commands, one of the great benefits of using TCC instead of the
POSIX style of "a separate little program for each minor task (with its own
unique syntax)".

Documenting the suboptions in the quick help is difficult.
--
Steve

I agree that in some commands
 
#8
| ---Quote (Originally by Steve Fbin)---
|| Using the value of a variable function as a command, as your
|| TEST.BTM does, seems to stretch even the DWIM parser!
| ---End Quote---
| Seems to me that TCC should generate an error rather than accepting
| incorrect syntax.

I think your syntax was correct, but totally unexpected. While I have used
the value of a variable as a command, I never stretched that idea to using
the value of a variable function as a command, and the individual commands
executed during calculation of the function value as "subroutine steps".
Undoubtedly Rex will provide a better explanation.

Note that the value returned by either of your functions is an empty string,
which is a legal command. The embedded "echo" commands are executed without
a return value.
--
HTH, Steve
 

rconn

Administrator
Staff member
May 14, 2008
10,755
97
#9
> Why doesn't MakeDir1 in the Test.btm file below work? It works in build
> 41.
>
> C:\Junk>type Test.btm
> *setlocal
> function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+
> endiff %+ echo done1`
> function MakeDir2=`echo start2 %+ iff not isdir "%1" then %+ mkdir /n
> /s "%1" %+ endiff %+ echo done2`
> echo MakeDir1
> %@MakeDir1[C:\Doc]
> echo MakeDir2
> %@MakeDir2[C:\Doc]
Despite your puzzling usage of UDFs instead of aliases, using the obsolete
%+ construct, and using IFF when a plain IF would do, your batch file is
working fine here in build 48:


MakeDir1
done1
MakeDir2
start2
done2

Rex Conn
JP Software
 
#11
Despite your puzzling usage of UDFs instead of aliases,
The reason was to avoid (accidentally) interfering with commands. Using UDFs keeps the subroutines used by the batch file separate from the commands that are used from the command line. Essentially, a separate namespace.

using the obsolete %+ construct
I used to have different command separators on different computers. If I recall, the default command separator changed at some point in the past. Plus, the command separator is configurable. %+ always has the same meaning.

and using IFF when a plain IF would do
IFF is more readable since the command is separated from the condition.

your batch file is working fine here in build 48:
Still not working here on two different computers. Computer 1:

Microsoft Windows [Version 6.0.6002]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.

c:\>"\Program Files\JPSoft\TCMD11\tcc.exe" /i

TCC 11.00.48 Windows Vista [Version 6.0.6002]
Copyright 2010 Rex Conn & JP Software Inc. All Rights Reserved
Registered to davidmarcus - 1 System License

c:\>Junk\Test.btm
setlocal
echo off
MakeDir1
MakeDir2
start2
done2

c:\>type Junk\Test.btm
*setlocal
echo off
function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done1`
function MakeDir2=`echo start2 %+ iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done2`
echo MakeDir1
%@MakeDir1[C:\Doc]
echo MakeDir2
%@MakeDir2[C:\Doc]

c:\>

Computer 2:

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\>"\Program Files\JPSoft\TCMD11x64\tcc.exe" /i

TCC 11.00.48 x64 Windows 7 [Version 6.1.7600]
Copyright 2010 Rex Conn & JP Software Inc. All Rights Reserved
Registered to davidmarcus - 1 System License

C:\>Junk\Test.btm
setlocal
echo off
MakeDir1
MakeDir2
start2
done2

C:\>type Junk\Test.btm
*setlocal
echo off
function MakeDir1=`iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done1`
function MakeDir2=`echo start2 %+ iff not isdir "%1" then %+ mkdir /n /s "%1" %+ endiff %+ echo done2`
echo MakeDir1
%@MakeDir1[C:\Doc]
echo MakeDir2
%@MakeDir2[C:\Doc]

C:\>