flagging starting and ending parens in batch files



  • In the following code the use of command continuation character ^ causes the paren highlighting feature to break. The do ( maps to the incorrect closing paren and the else ( that include or follow the ^ character do not map at all. Replacing (delete and re-enter) the paren in either else ( results in a temporary incorrect mapping to the next closing paren regardless of nesting. Can anyone point out where in the code this pairing occurs? I would like to try to code and submit a fix.

    DO %%F IN (z y x) DO (
    IF %%F==x (
    ECHO ‘x’
    ) ELSE (
    IF %%F==y (
    ECHO ^
    ‘y’
    ) ELSE (
    IF %%F==z (
    ECHO ‘z’
    )
    )
    )
    )



  • Batch syntax can be so crazy that I for one give Notepad++ (or any lexer) a total pass on it! :-)



  • You may be correct. However if anyone can identify where this pairing is done (I could not find a ‘(’ or ‘\(’ literal in LexBatch.cxx in scintilla) so I am guessing not there. If the source code that contains this function can be identified I am willing to attempt to provide a fix (patch) for consideration, assuming I can fix it.



  • I attempted to create a user defined batch file editor.

        <UserLang name="bat" ext="bat cmd nt" udlVersion="2.1">
            <Settings>
                <Global caseIgnored="yes" allowFoldOfComments="no" foldCompact="no" forcePureLC="1" decimalSeparator="0" />
                <Prefix Keywords1="no" Keywords2="no" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
            </Settings>
            <KeywordLists>
                <Keywords name="Comments">00rem 01 02 03 04</Keywords>
                <Keywords name="Numbers, prefix1">- + ~</Keywords>
                <Keywords name="Numbers, prefix2"></Keywords>
                <Keywords name="Numbers, extras1"></Keywords>
                <Keywords name="Numbers, extras2"></Keywords>
                <Keywords name="Numbers, suffix1"></Keywords>
                <Keywords name="Numbers, suffix2"></Keywords>
                <Keywords name="Numbers, range"></Keywords>
                <Keywords name="Operators1">! = : \ @ . , / -</Keywords>
                <Keywords name="Operators2">^</Keywords>
                <Keywords name="Folders in code1, open">(</Keywords>
                <Keywords name="Folders in code1, middle"></Keywords>
                <Keywords name="Folders in code1, close">)</Keywords>
                <Keywords name="Folders in code2, open"></Keywords>
                <Keywords name="Folders in code2, middle"></Keywords>
                <Keywords name="Folders in code2, close"></Keywords>
                <Keywords name="Folders in comment, open"></Keywords>
                <Keywords name="Folders in comment, middle"></Keywords>
                <Keywords name="Folders in comment, close"></Keywords>
                <Keywords name="Keywords1">assoc aux break call cd chcp chdir choice cls cmdextversion color con copy country ctty date defined del dir dpath echo endlocal erase errorlevel exit ftype goto loadfix loadhigh md mkdir mklink move path pause popd prn prompt pushd rd ren rename rmdir set setlocal shift start time title type ver verify vol</Keywords>
                <Keywords name="Keywords2">com com1 com2 com3 com4 do else exist for if in lpt lpt1 lpt2 lpt3 lpt4 not nul on</Keywords>
                <Keywords name="Keywords3"></Keywords>
                <Keywords name="Keywords4"></Keywords>
                <Keywords name="Keywords5"></Keywords>
                <Keywords name="Keywords6">*</Keywords>
                <Keywords name="Keywords7">%%a %%b %%c %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n %%o %%p %%q %%r %%s %%t %%u %%v %%w %%x %%y %%z</Keywords>
                <Keywords name="Keywords8">%1 %2 %3 %4 %5 %6 %7 %8 %9 %~1 %~2 %~3 %~4 %~5 %~6 %~7 %/~8 %~9</Keywords>
                <Keywords name="Delimiters">00&quot; 01 02&quot; 03&apos; 04 05&apos; 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
    <!--
                <Keywords name="Delimiters">00&quot; 01 02&quot; 03&apos; 04 05&apos; 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21% 22 23%</Keywords>
    -->
            </KeywordLists>
            <Styles>
                <WordsStyle name="DEFAULT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="COMMENTS" fgColor="008000" bgColor="80FF80" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="LINE COMMENTS" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="NUMBERS" fgColor="FF80FF" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="KEYWORDS1" fgColor="0000FF" bgColor="A0FFFF" fontName="" fontStyle="7" nesting="0" />
                <WordsStyle name="KEYWORDS2" fgColor="008000" bgColor="80FF80" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="KEYWORDS3" fgColor="0000FF" bgColor="A0FFFF" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="KEYWORDS4" fgColor="0000FF" bgColor="A0FFFF" fontName="" fontStyle="2" nesting="0" />
                <WordsStyle name="KEYWORDS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="KEYWORDS6" fgColor="008000" bgColor="80FF80" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="KEYWORDS7" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="KEYWORDS8" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="3" nesting="0" />
                <WordsStyle name="OPERATORS" fgColor="0000FF" bgColor="FFC060" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="FOLDER IN CODE1" fgColor="000040" bgColor="FF8080" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="FOLDER IN CODE2" fgColor="0000FF" bgColor="A0FFFF" fontName="" fontStyle="3" nesting="0" />
                <WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="DELIMITERS1" fgColor="008000" bgColor="FFFF00" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="DELIMITERS2" fgColor="008000" bgColor="FFFF80" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="DELIMITERS3" fgColor="008000" bgColor="FFFF80" fontName="" fontStyle="1" nesting="0" />
                <WordsStyle name="DELIMITERS4" fgColor="E020E0" bgColor="E0C0E0" fontName="" fontStyle="0" nesting="67108864" />
                ```
    <WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="256" />
                <WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
                <WordsStyle name="DELIMITERS8" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="83886080" />
            </Styles>
        </UserLang>
    

    In the above keyword1 is a command list (incomplete)
    keyword2 is an operand list (probably incomplete)
    keyword6 is an operand the * in dir .
    (there may be a better way to do this)
    keyword7 is for FOR loop iteration variables
    keyword8 is for command line arguments
    The rest are fairly common

    Next consider the following bat file

    @ECHO   ON
    
    REM     LINKdir.bat
    REM
    REM     %1    = A99LINK
    REM     %2    = A99TARGET
    REM     %3    = A99FILTER
    REM
    SET             A99LINK=%~1
    SET             A99TARGET=%~2
    SET             A99FILTER=%~3
    REM
    REM     make directories and link files
    REM
    PUSHD           %A99TARGET:~0,-1%
    REM
    REM     make directories
    FOR   %%F   IN (DIR /ad /b) DO (
      SET           %A99FILE%=%%F
      IF            %A99FILE:~0,1%!='.' (
        MKDIR       %A99LINK%%A99FILE%
        %A01TESTbatPATH%LINKdir.bat ^
                    %A99LINK%%A99FILE% ^
                    %A99TARGET%%A99FILE%
      )
    )
    REM
    REM     link files
    REM
    FOR   %%T   IN (%A99FILTER%)       DO (
      FOR %%F   IN (DIR /a-d /b *.%%T)  DO (
        SET         %A99FILE%=%%F
        IF          %A99FILE:~0,1%!='.' (
          MKLINK /h %A99LINK%%A99FILE% ^
                    %A99TARGET%%A99FILE%
        )
      )
    )
    POPD
    

    Quirk 1 the POPD command requires a space or CR/LF to color as a keyword – adding the CRLF is an easy workaround

    Desire 1 – to be able to use bold, italic and underline instead of background colors for most keywords. (This does not seem to work in the current portable version)

    Desire 2 a program change that would permit %…% to be added to the delimiters as in the commented code. This requires a program change (maybe easier said than done) to eliminate the keywords (list 7 and 8) starting with %% or % from being considered as a %…% delimited named variable resulting from a set command or an environment variable. Allowing numbers and operands (not space delimited) to be nested in this delimited value would support the substring construct of :~nn,snn inside a delimited named variable. (%{named variable:~nn,snn%) As a result the length value can be determined to be a number which does not work now. This would also allow named variables to be styled like keywords 7 and 8 which is also what I would do.

    Is there another way to do Desire 2 in UDL?

    Thanks for considering this.



  • @dshuman52

    Desire 1 works when the stupid user (me) does not set the global overrides for BIU.



  • Hello @dshuman52, @alan-kilborn and All,

    @dshuman52, Here is a method which could help you ! It works whatever the current language as it’s a recursive regular expression looking for well-balanced ( .... ) blocks, containing possible juxtaposed / nested other ( .... ) blocks !

    SEARCH \((?:[^()]++|(?0))*\) ( Select the Regular expression search mode )

    Notes :

    • From current location of the caret, this regex selects, automatically, the next longest well balanced ( .... ) block

    • If you prefer to get the ending parenthesis, only, simply change this regex as below :

    SEARCH \((?:[^()]++|(?0))*\K\)


    To explain this regex, the best is to use the free-spacing mode, which allows comments in regexes :

    (?x)             #  FREE-SPACING mode
    \(               #  STARTING parenthesis
    (?:              #  Start of the NON-CAPTURING group
        [^()]++      #      Any NON-NULL ATOMIC range of ALLOWED characters
        |            #      OR
        (?0)         #      A RECURSION, using the WHOLE regex pattern ( group #0 )
    )*               #  End of the NON-CAPTURING group, repeated 0 or MORE times
    # \K             #  DELETE the initial '#' character, of that line, to match the ENDING parenthesis, ONLY
    \)               #  ENDING parenthesis
    

    Try this regex against your text, below :

    @ECHO   ON
    
    REM     LINKdir.bat
    REM
    REM     %1    = A99LINK
    REM     %2    = A99TARGET
    REM     %3    = A99FILTER
    REM
    SET             A99LINK=%~1
    SET             A99TARGET=%~2
    SET             A99FILTER=%~3
    REM
    REM     make directories and link files
    REM
    PUSHD           %A99TARGET:~0,-1%
    REM
    REM     make directories
    FOR   %%F   IN (DIR /ad /b) DO (
      SET           %A99FILE%=%%F
      IF            %A99FILE:~0,1%!='.' (
        MKDIR       %A99LINK%%A99FILE%
        %A01TESTbatPATH%LINKdir.bat ^
                    %A99LINK%%A99FILE% ^
                    %A99TARGET%%A99FILE%
      )
    )
    REM
    REM     link files
    REM
    FOR   %%T   IN (%A99FILTER%)       DO (
      FOR %%F   IN (DIR /a-d /b *.%%T)  DO (
        SET         %A99FILE%=%%F
        IF          %A99FILE:~0,1%!='.' (
          MKLINK /h %A99LINK%%A99FILE% ^
                    %A99TARGET%%A99FILE%
        )
      )
    )
    POPD
    

    Notes :

    • Place the caret at different locations, of this batch file

    • Then do the search hitting, several times, the F3 shortcut

    Important :

    • Each time that a well-balanced ( .... ) block is selected :

      • An hit on the Left Arrow key moves the caret right before the ( starting parenthesis

      • An hit on the Right Arrow key moves the caret right after the ) ending parenthesis

    • The nice thing is that, when you hit the shift + F3 shortcut, it gets the inner ( ... ) block, closed to the end of the outer ( ... ) block !

    Try also, with your first sample text :

    DO %%F IN (z y x) DO (
    IF %%F==x (
    ECHO ‘x’
    ) ELSE (
    IF %%F==y (
    ECHO ^
    ‘y’
    ) ELSE (
    IF %%F==z (
    ECHO ‘z’
    )
    )
    )
    )
    

    Best Regards,

    guy038

    P.S. :

    In batch files , the & ampersand, the | vertical line and the ( and ) parentheses are special chars. So, they must be preceded with the escape character ^ or embedded inside double quotes.

    Thus, here’s a more elaborate version, where we suppose that the ^ symbol is the default escape character and any character, preceded with a ^ symbol, is just considered as a literal chars, including the syntaxes ^( and ^) !

    (?x)             #  FREE-SPACING mode
    (?<!\\)          #  NEXT character is NOT PRECEDED with a '\' symbol ( LOOK-BEHIND condition )
    \(               #  STARTING parenthesis
    (?:              #  Start of the 1st NON-CAPTURING group
        (?:          #      Start of the 2nd NON-CAPTURING group
            \^ [()]  #          STRINGS '^(' or '^)' are supposed to be ALLOWED characters     
            |        #          OR
            [^()]    #          Any ALLOWED character, even EOL ones, DIFFERENT of the PARENTHESES '(' and ')'
        )++          #      End of the 2nd NON-CAPTURING group ( NON-NULL ATOMIC range of ALLOWED characters )
        |            #      OR
        (?0)         #      A RECURSION, using the WHOLE regex pattern ( group #0 )
    )*               #  End of the 1st NON-CAPTURING group, repeated 0 or MORE times
    (?<!\\)          #  NEXT character is NOT PREDECED with a '\' symbol ( LOOK-BEHIND condition )
    \)               #  ENDING parenthesis
    

    Here is its shortest syntax :

    SEARCH (?x)(?<!\\)\((?:(?:\^[()]|[^()])|(?0))*(?<!\\)\)

    Test it against this sample text :

    DO %%F IN (z y x) DO (
    IF %%F==x (
    ECHO ‘x’  ^(
    ) ELSE (
    IF %%F==y (
    ECHO ^
    ‘y’
    ) ELSE (
    IF %%F==z (
    ECHO ‘z’
    )
    )
    ^)
    )
    )
    

Log in to reply