TypeCobol icon indicating copy to clipboard operation
TypeCobol copied to clipboard

Replace inside PictureCharacterString must match the whole token

Open smedilol opened this issue 1 year ago • 0 comments

Describe the bug We currently consider a token to be the target of a replacement even if it only partially matches. This happens for PartialCobolWord and PictureCharacterString.

This issue only focus on PictureCharacterString.

To Reproduce (Type)Cobol code that cause the bug : (if any)

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DVZF0OSM.
       DATA DIVISION.
       REPLACE
          ==C-N==                  BY ==xxx==
          ==C-NbX==                BY ==3==
          .

       WORKING-STORAGE SECTION.
       01 Var1  PIC X(C-NbX).

       end program DVZF0OSM.

The parser currently match C-N because we use IndexOf instead of an Equals between C-NbX inside the picture and C-N. The second replace C-NbX is then ignored.

Expected behavior Apply the replace inside a PictureCharacterString only if token fully match what's inside parenthesis.

Technical Short but incomplete solution: In TypeCobol.Compiler.Scanner.Token.CompareForReplace, when TokenType == TokenType.PictureCharacterString, the comparisonToken.NormalizedText must be Equals to what's inside the parenthesis.

Source code sample:

if(TokenType == TokenType.PictureCharacterString && comparisonToken.NormalizedText.Length > 0)
            {
                //Fast test to avoid creating a new string
                if (NormalizedText.IndexOf(comparisonToken.NormalizedText, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    return NormalizedText.IndexOf((comparisonToken.NormalizedText[0] == '(' ? "": "(") 
                                                  + comparisonToken.NormalizedText 
                                                  + (comparisonToken.NormalizedText[comparisonToken.NormalizedText.Length-1] == ')' ? "" : ")"),
                        StringComparison.OrdinalIgnoreCase) >= 0;
                }
            }

To optimize a little access to comparisonToken.NormalizedText, we could cache the value in Token: internal string NormalizedText => _normalizedText ?? (_normalizedText = Regex.Replace(Text, @"\s*", string.Empty));

Long and complete solution: See #2077.

How to test automatically Standard unit test.

smedilol avatar Mar 20 '23 08:03 smedilol