Browse Source

fcl-pasrc: fixed multilinestrings # combos, double backticks become one, fixed apostroph, renamed MultilineStringsTrimLeft, MultilineStringsEOLStyle

mattias 3 years ago
parent
commit
7c68ead57a

+ 10 - 2
packages/fcl-passrc/src/pparser.pp

@@ -817,7 +817,7 @@ begin
         inc(Src);
       end;
     end;
-  '''','#':  // string constant
+  '''','#','`':  // string constant
     while true do
       case Src^ of
       #0: break;
@@ -830,7 +830,15 @@ begin
       '''':
         begin
         inc(Src);
-        while not (Src^ in ['''',#0]) do
+        while not (Src^ in ['''',#0,#10,#13]) do
+          inc(Src);
+        if Src^='''' then
+          inc(Src);
+        end;
+      '`':
+        begin
+        inc(Src);
+        while not (Src^ in ['`',#0]) do
           inc(Src);
         if Src^='''' then
           inc(Src);

+ 221 - 127
packages/fcl-passrc/src/pscanner.pp

@@ -300,8 +300,8 @@ type
     msPrefixedAttributes,  { Allow attributes, disable proc modifier [] }
     msOmitRTTI,            { treat class section 'published' as 'public' and typeinfo does not work on symbols declared with this switch }
     msMultiHelpers,        { off=only one helper per type, on=all }
-    msImplicitFunctionSpec, { implicit function specialization }
-    msMultiLineStrings      { Multiline strings }
+    msImplicitFunctionSpec,{ implicit function specialization }
+    msMultiLineStrings     { Multiline strings }
     );
   TModeSwitches = Set of TModeSwitch;
 
@@ -749,8 +749,8 @@ type
     FModuleRow: Integer;
     FMacros: TStrings; // Objects are TMacroDef
     FDefines: TStrings;
-    FMultilineLineFeedStyle: TEOLStyle;
-    FMultilineLineTrimLeft: Integer;
+    FMultilineStringsEOLStyle: TEOLStyle;
+    FMultilineStringsTrimLeft: Integer;
     FNonTokens: TTokens;
     FOnComment: TPScannerCommentEvent;
     FOnDirective: TPScannerDirectiveEvent;
@@ -856,8 +856,8 @@ type
     procedure HandleWarnIdentifier(Identifier, Value: String); virtual;
     procedure PushStackItem; virtual;
     procedure PopStackItem; virtual;
-    function DoFetchTextToken: TToken;
-    function DoFetchMultilineTextToken: TToken;
+    function DoFetchTextToken: TToken; // including quotes
+    function DoFetchMultilineTextToken: TToken; // back ticks are converted to apostrophs, unindented
     function DoFetchToken: TToken;
     procedure ClearFiles;
     Procedure ClearMacros;
@@ -931,8 +931,8 @@ type
     property SkipGlobalSwitches: Boolean read FSkipGlobalSwitches write FSkipGlobalSwitches;
     property MaxIncludeStackDepth: integer read FMaxIncludeStackDepth write FMaxIncludeStackDepth default DefaultMaxIncludeStackDepth;
     property ForceCaret : Boolean read GetForceCaret;
-    Property MultilineLineFeedStyle : TEOLStyle Read FMultilineLineFeedStyle Write FMultilineLineFeedStyle;
-    Property MultilineLineTrimLeft : Integer Read FMultilineLineTrimLeft Write FMultilineLineTrimLeft;
+    Property MultilineStringsEOLStyle : TEOLStyle Read FMultilineStringsEOLStyle Write FMultilineStringsEOLStyle;
+    Property MultilineStringsTrimLeft : Integer Read FMultilineStringsTrimLeft Write FMultilineStringsTrimLeft; // All=-2, Auto=-1, None=0, >1 fixed amount
     Property OnDirectiveForConditionals : Boolean Read FOnDirectiveForConditionals Write FOnDirectiveForConditionals;
     property LogEvents : TPScannerLogEvents read FLogEvents write FLogEvents;
     property OnLog : TPScannerLogHandler read FOnLog write FOnLog;
@@ -2294,6 +2294,7 @@ var
   {$else}
   Src: string;
   p, l, StartP: Integer;
+  c: char;
   {$endif}
 begin
   Result:='';
@@ -2307,7 +2308,7 @@ begin
       StartP:=p;
       repeat
         case p^ of
-        #0: Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0']);
+        #0,#10,#13: Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0']);
         '''': break;
         else inc(p);
         end;
@@ -2316,6 +2317,21 @@ begin
         Result:=Result+copy(Expression,StartP-PChar(Expression)+1,p-StartP);
       inc(p);
       end;
+    '`':
+      begin
+      inc(p);
+      StartP:=p;
+      repeat
+        case p^ of
+        #0: Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0']);
+        '`': break;
+        else inc(p);
+        end;
+      until false;
+      if p>StartP then
+        Result:=Result+copy(Expression,StartP-PChar(Expression)+1,p-StartP);
+      inc(p);
+      end;
     else
       Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0']);
     end;
@@ -2324,17 +2340,20 @@ begin
   Src:=Expression;
   l:=length(Src);
   repeat
-    if (p>l) or (Src[p]<>'''') then
+    if (p>l) or not (Src[p] in ['''','`']) then
       Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0'])
     else
       begin
+      c:=Src[p];
       inc(p);
       StartP:=p;
       repeat
-        if p>l then
+        if (p>l) then
           Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#0'])
-        else if Src[p]='''' then
+        else if Src[p]=c then
           break
+        else (c='''') and (Src[p] in [#10,#13]) then
+          Log(mtError,nErrInvalidCharacter,SErrInvalidCharacter,['#'+IntToStr(ord(Src[p]))])
         else
           inc(p);
       until false;
@@ -3369,7 +3388,7 @@ Var
 
 
 begin
-  aStyle:=MultilineLineFeedStyle;
+  aStyle:=MultilineStringsEOLStyle;
   if aStyle=elSource then
     aStyle:=aReader.LastEOLStyle;
   case aStyle of
@@ -3383,69 +3402,23 @@ begin
   Result:=aLF;
 end;
 
-function TPascalScanner.DoFetchMultilineTextToken:TToken;
-
+function TPascalScanner.DoFetchTextToken:TToken;
 var
-  StartPos,OldLength     : Integer;
-  TokenStart    : {$ifdef UsePChar}PChar{$else}integer{$endif};
+  TokenStart, StartP : {$ifdef UsePChar}PChar{$else}integer{$endif};
+  SectionLength : Integer;
   {$ifndef UsePChar}
   s: String;
   l: integer;
   {$endif}
-
-
-  Procedure AddToCurString(addLF : Boolean);
-  var
-    SectionLength,i : Integer;
-    aLF : String;
-
-  begin
-    i:=MultilineLineTrimLeft;
-    if I=-1 then
-      I:=StartPos+1;
-    if I>0 then
-      begin
-      While ({$ifdef UsePChar} TokenStart^{$ELSE}FCurLine[TokenStart]{$ENDIF} in [' ',#9]) and (TokenStart<=FTokenPos) and (I>0) do
-        begin
-        Inc(TokenStart);
-        Dec(I);
-        end;
-      end
-    else if I=-2 then
-      begin
-      While ({$ifdef UsePChar} TokenStart^{$ELSE}FCurLine[TokenStart]{$ENDIF} in [' ',#9]) and (TokenStart<=FTokenPos) do
-        Inc(TokenStart);
-      end;
-
-    SectionLength := FTokenPos - TokenStart+Ord(AddLF);
-    {$ifdef UsePChar}
-    SetLength(FCurTokenString, OldLength + SectionLength);
-    if SectionLength > 0 then
-      Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
-    {$else}
-    FCurTokenString:=FCurTokenString+copy(FCurLine,TokenStart,SectionLength);
-    {$endif}
-    if AddLF then
-      begin
-      alf:=GetMultiLineStringLineEnd(FCurSourceFile);
-      FCurTokenString:=FCurTokenString+aLF;
-      Inc(OldLength,Length(aLF));
-      end;
-    Inc(OldLength, SectionLength);
-  end;
-
 begin
   Result:=tkEOF;
-  OldLength:=0;
   FCurTokenString := '';
   {$ifndef UsePChar}
   s:=FCurLine;
   l:=length(s);
-  StartPos:=FTokenPos;
-  {$ELSE}
-  StartPos:=FTokenPos-PChar(FCurLine);
   {$endif}
 
+  StartP := FTokenPos;
   repeat
     {$ifndef UsePChar}
     if FTokenPos>l then break;
@@ -3453,15 +3426,16 @@ begin
     case {$ifdef UsePChar}FTokenPos[0]{$else}s[FTokenPos]{$endif} of
       '^' :
         begin
-        TokenStart := FTokenPos;
         Inc(FTokenPos);
         if {$ifdef UsePChar}FTokenPos[0] in Letters{$else}(FTokenPos<l) and (s[FTokenPos] in Letters){$endif} then
           Inc(FTokenPos);
-        if Result=tkEOF then Result := tkChar else Result:=tkString;
+        if Result=tkEOF then
+          Result := tkChar
+        else
+          Result := tkString;
         end;
       '#':
         begin
-        TokenStart := FTokenPos;
         Inc(FTokenPos);
         if {$ifdef UsePChar}FTokenPos[0]='$'{$else}(FTokenPos<l) and (s[FTokenPos]='$'){$endif} then
         begin
@@ -3473,64 +3447,132 @@ begin
           repeat
             Inc(FTokenPos);
           until {$ifdef UsePChar}not (FTokenPos[0] in Digits){$else}(FTokenPos>l) or not (s[FTokenPos] in Digits){$endif};
-        if Result=tkEOF then Result := tkChar else Result:=tkString;
+        if Result=tkEOF then
+          Result := tkChar
+        else
+          Result := tkString;
         end;
-      '`':
+      '''':
         begin
           TokenStart := FTokenPos;
           Inc(FTokenPos);
 
           while true do
           begin
-            if {$ifdef UsePChar}FTokenPos[0] = '`'{$else}(FTokenPos<=l) and (s[FTokenPos]=''''){$endif} then
-              if {$ifdef UsePChar}FTokenPos[1] = '`'{$else}(FTokenPos<l) and (s[FTokenPos+1]=''''){$endif} then
-                Inc(FTokenPos)
+            if {$ifdef UsePChar}FTokenPos[0] = ''''{$else}(FTokenPos<=l) and (s[FTokenPos]=''''){$endif} then
+              if {$ifdef UsePChar}FTokenPos[1] = ''''{$else}(FTokenPos<l) and (s[FTokenPos+1]=''''){$endif} then
+                begin
+                Inc(FTokenPos);
+                if Result=tkEOF then
+                  Result:=tkChar
+                else
+                  Result:=tkString;
+                end
               else
                 break;
 
             if {$ifdef UsePChar}FTokenPos[0] = #0{$else}FTokenPos>l{$endif} then
-              begin
-              FTokenPos:=FTokenPos-1;
-              AddToCurString(true);
-              // Writeln('Curtokenstring : >>',FCurTOkenString,'<<');
-              if not Self.FetchLine then
-                Error(nErrOpenString,SErrOpenString);
-              // Writeln('Current line is now : ',FCurLine);
-              {$ifndef UsePChar}
-              s:=FCurLine;
-              l:=length(s);
-              {$ELSE}
-              FTokenPos:=PChar(FCurLine);
-              {$endif}
-              TokenStart:=FTokenPos;
-              end
-            else
-              Inc(FTokenPos);
+              Error(nErrOpenString,SErrOpenString);
+
+            Inc(FTokenPos);
           end;
           Inc(FTokenPos);
-          Result := tkString;
+          if (Result=tkEOF) and ((FTokenPos - TokenStart)=3) then // 'z'
+            Result := tkChar
+          else
+            Result := tkString;
         end;
     else
       Break;
     end;
-    AddToCurString(false);
   until false;
-  if length(FCurTokenString)>1 then
-    begin
-    FCurTokenString[1]:='''';
-    FCurTokenString[Length(FCurTokenString)]:='''';
-    end;
+  SectionLength := FTokenPos - StartP;
+  {$ifdef UsePChar}
+  SetLength(FCurTokenString, SectionLength);
+  if SectionLength > 0 then
+    Move(StartP^, FCurTokenString[1], SectionLength);
+  {$else}
+  FCurTokenString:=FCurTokenString+copy(FCurLine,StartP,SectionLength);
+  {$endif}
 end;
 
-function TPascalScanner.DoFetchTextToken:TToken;
+function TPascalScanner.DoFetchMultilineTextToken:TToken;
+// works similar to DoFetchTextToken, except changes indentation
+
 var
-  OldLength     : Integer;
+  StartPos,OldLength     : Integer;
   TokenStart    : {$ifdef UsePChar}PChar{$else}integer{$endif};
-  SectionLength : Integer;
   {$ifndef UsePChar}
   s: String;
   l: integer;
   {$endif}
+  Apostroph, CurLF : String;
+
+  {$IFDEF UsePChar}
+  procedure Add(StartP: PChar; Cnt: integer);
+  begin
+    if Cnt=0 then exit;
+    if OldLength+Cnt>length(FCurTokenString) then
+      SetLength(FCurTokenString,length(FCurTokenString)*2+128);
+    Move(StartP^,FCurTokenString[OldLength+1],Cnt);
+    inc(OldLength,Cnt);
+  end;
+  {$ELSE}
+  procedure Add(const S: string);
+  begin
+    FCurTokenString:=FCurTokenString+S;
+  end;
+  {$ENDIF}
+
+  Procedure AddToCurString(addLF : Boolean);
+  var
+    i : Integer;
+
+  begin
+    i:=MultilineStringsTrimLeft;
+    if I=-1 then
+      // auto unindent -> use line indent of first line
+      I:=StartPos+1;
+    if I>0 then
+      begin
+      // fixed unindent -> remove up to I leading spaces
+      While ({$ifdef UsePChar} TokenStart^{$ELSE}FCurLine[TokenStart]{$ENDIF} in [' ',#9]) and (TokenStart<=FTokenPos) and (I>0) do
+        begin
+        Inc(TokenStart);
+        Dec(I);
+        end;
+      end
+    else if I=-2 then
+      begin
+      // no indent -> remove all leading spaces
+      While ({$ifdef UsePChar} TokenStart^{$ELSE}FCurLine[TokenStart]{$ENDIF} in [' ',#9]) and (TokenStart<=FTokenPos) do
+        Inc(TokenStart);
+      end;
+
+    {$ifdef UsePChar}
+    Add(TokenStart,FTokenPos - TokenStart);
+    {$else}
+    Add(copy(FCurLine,TokenStart,FTokenPos - TokenStart));
+    {$ENDIF}
+    if addLF then
+      begin
+      {$IFDEF UsePChar}
+      Add(@CurLF[1],length(CurLF));
+      {$ELSE}
+      Add(CurLF);
+      {$endif}
+      end;
+  end;
+
+  procedure AddApostroph;
+  begin
+    {$IFDEF UsePChar}
+    Add(@Apostroph[1],length(Apostroph));
+    {$ELSE}
+    Add(Apostroph);
+    {$ENDIF}
+  end;
+
 begin
   Result:=tkEOF;
   OldLength:=0;
@@ -3538,7 +3580,12 @@ begin
   {$ifndef UsePChar}
   s:=FCurLine;
   l:=length(s);
+  StartPos:=FTokenPos;
+  {$ELSE}
+  StartPos:=FTokenPos-PChar(FCurLine);
   {$endif}
+  Apostroph:='''';
+  CurLF:=GetMultiLineStringLineEnd(FCurSourceFile);
 
   repeat
     {$ifndef UsePChar}
@@ -3551,7 +3598,15 @@ begin
         Inc(FTokenPos);
         if {$ifdef UsePChar}FTokenPos[0] in Letters{$else}(FTokenPos<l) and (s[FTokenPos] in Letters){$endif} then
           Inc(FTokenPos);
-        if Result=tkEOF then Result := tkChar else Result:=tkString;
+        {$IFDEF UsePChar}
+        Add(TokenStart,FTokenPos-TokenStart);
+        {$ELSE}
+        Add(copy(FCurLine,TokenStart,FTokenPos-TokenStart));
+        {$ENDIF}
+        if Result=tkEOF then
+          Result := tkChar
+        else
+          Result := tkString;
         end;
       '#':
         begin
@@ -3567,44 +3622,84 @@ begin
           repeat
             Inc(FTokenPos);
           until {$ifdef UsePChar}not (FTokenPos[0] in Digits){$else}(FTokenPos>l) or not (s[FTokenPos] in Digits){$endif};
-        if Result=tkEOF then Result := tkChar else Result:=tkString;
+        {$IFDEF UsePChar}
+        Add(TokenStart,FTokenPos-TokenStart);
+        {$ELSE}
+        Add(copy(FCurLine,TokenStart,FTokenPos-TokenStart));
+        {$ENDIF}
+        if Result=tkEOF then
+          Result := tkChar
+        else
+          Result := tkString;
         end;
-      '''':
+      '`':
         begin
-          TokenStart := FTokenPos;
+          AddApostroph;
           Inc(FTokenPos);
+          TokenStart := FTokenPos;
 
           while true do
           begin
-            if {$ifdef UsePChar}FTokenPos[0] = ''''{$else}(FTokenPos<=l) and (s[FTokenPos]=''''){$endif} then
-              if {$ifdef UsePChar}FTokenPos[1] = ''''{$else}(FTokenPos<l) and (s[FTokenPos+1]=''''){$endif} then
-                Inc(FTokenPos)
-              else
-                break;
-
             if {$ifdef UsePChar}FTokenPos[0] = #0{$else}FTokenPos>l{$endif} then
-              Error(nErrOpenString,SErrOpenString);
-
-            Inc(FTokenPos);
+              begin
+              AddToCurString(true);
+              // Writeln('Curtokenstring : >>',FCurTokenString,'<<');
+              if not Self.FetchLine then
+                begin
+                {$IFDEF UsePChar}
+                SetLength(FCurTokenString,OldLength);
+                {$ENDIF}
+                Error(nErrOpenString,SErrOpenString);
+                end;
+              // Writeln('Current line is now : ',FCurLine);
+              {$ifndef UsePChar}
+              s:=FCurLine;
+              l:=length(s);
+              {$ELSE}
+              FTokenPos:=PChar(FCurLine);
+              {$endif}
+              TokenStart:=FTokenPos;
+              end
+            else
+              begin
+              case {$ifdef UsePChar}FTokenPos^{$else}s[FTokenPos]{$endif} of
+              '`':
+                if {$ifdef UsePChar}FTokenPos[1] = '`'{$else}(FTokenPos<l) and (s[FTokenPos+1]='`'){$endif} then
+                  begin
+                  // treat two backticks as one
+                  Inc(FTokenPos);
+                  AddToCurString(false);
+                  Inc(FTokenPos);
+                  TokenStart := FTokenPos;
+                  continue;
+                  end
+                else
+                  begin
+                  AddToCurString(false);
+                  AddApostroph;
+                  break;
+                  end;
+              '''':
+                begin
+                // convert apostroph to two apostrophs
+                Inc(FTokenPos);
+                AddToCurString(false);
+                AddApostroph;
+                TokenStart := FTokenPos;
+                end;
+              end;
+              Inc(FTokenPos);
+              end;
           end;
           Inc(FTokenPos);
-          if ((FTokenPos - TokenStart)=3) then // 'z'
-            Result := tkChar
-          else
-            Result := tkString;
+          Result := tkString;
         end;
     else
+      {$IFDEF UsePChar}
+      SetLength(FCurTokenString,OldLength);
+      {$ENDIF}
       Break;
     end;
-    SectionLength := FTokenPos - TokenStart;
-    {$ifdef UsePChar}
-    SetLength(FCurTokenString, OldLength + SectionLength);
-    if SectionLength > 0 then
-      Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
-    {$else}
-    FCurTokenString:=FCurTokenString+copy(FCurLine,TokenStart,SectionLength);
-    {$endif}
-    Inc(OldLength, SectionLength);
   until false;
 end;
 
@@ -4659,8 +4754,7 @@ begin
     If not TryStrToInt(S,I) then
       I:=0;
   end;
-  MultilineLineTrimLeft:=I;
-
+  MultilineStringsTrimLeft:=I;
 end;
 
 procedure TPascalScanner.HandleMultilineStringLineEnding(const AParam: string);
@@ -4678,7 +4772,7 @@ begin
   else
     Error(nErrInvalidMultiLineLineEnding,sErrInvalidMultiLineLineEnding);
   end;
-  MultilineLineFeedStyle:=S;
+  MultilineStringsEOLStyle:=S;
 end;
 
 function TPascalScanner.DoFetchToken: TToken;

+ 49 - 36
packages/fcl-passrc/tests/tcscanner.pas

@@ -119,6 +119,7 @@ type
     Procedure TestMultilineStringCR;
     Procedure TestMultilineStringCRLF;
     Procedure TestMultilineStringPlatform;
+    Procedure TestMultilineStringDoubleBackticks;
     Procedure TestMultilineLineEndingDirective;
     Procedure TestMultilineTrimLeftDirective;
     procedure TestMultilineStringTrimAll;
@@ -732,7 +733,7 @@ const
 
 begin
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elSource;
+  Scanner.MultilineStringsEOLStyle:=elSource;
   DoTestToken(pscanner.tkString,'`AB'#13#10'CD`');
   AssertEquals('Correct lineending',S,TestTokenString);
 end;
@@ -744,7 +745,7 @@ const
 
 begin
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elLF;
+  Scanner.MultilineStringsEOLStyle:=elLF;
   DoTestToken(pscanner.tkString,'`AB'#13#10'CD`');
   AssertEquals('Correct lineending',S,TestTokenString);
 end;
@@ -755,7 +756,7 @@ const
 
 begin
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elCR;
+  Scanner.MultilineStringsEOLStyle:=elCR;
   DoTestToken(pscanner.tkString,'`AB'#10'CD`');
   AssertEquals('Correct lineending',S,TestTokenString);
 end;
@@ -766,7 +767,7 @@ const
 
 begin
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elCRLF;
+  Scanner.MultilineStringsEOLStyle:=elCRLF;
   DoTestToken(pscanner.tkString,'`AB'#10'CD`');
   AssertEquals('Correct lineending',S,TestTokenString);
 end;
@@ -778,38 +779,50 @@ const
 
 begin
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elPlatform;
+  Scanner.MultilineStringsEOLStyle:=elPlatform;
   DoTestToken(pscanner.tkString,'`AB'#13#10'CD`');
   AssertEquals('Correct lineending',S,TestTokenString);
 end;
 
+procedure TTestScanner.TestMultilineStringDoubleBackticks;
+
+const
+  S = '''AB`CD''';
+
+begin
+  Scanner.CurrentModeSwitches:=[msMultiLineStrings];
+  Scanner.MultilineStringsEOLStyle:=elSource;
+  DoTestToken(pscanner.tkString,'`AB``CD`');
+  AssertEquals('Correct lineending',S,TestTokenString);
+end;
+
 procedure TTestScanner.TestMultilineLineEndingDirective;
 begin
-  AssertTrue('Default platform', FSCanner.MultilineLineFeedStyle=elPlatform);
+  AssertTrue('Default platform', FScanner.MultilineStringsEOLStyle=elPlatform);
   TestTokens([tkComment],'{$MULTILINESTRINGLINEENDING CR}');
-  AssertTrue('CR', FSCanner.MultilineLineFeedStyle=elCR);
+  AssertTrue('CR', FScanner.MultilineStringsEOLStyle=elCR);
   TestTokens([tkComment],'{$MULTILINESTRINGLINEENDING LF}');
-  AssertTrue('LF', FSCanner.MultilineLineFeedStyle=elLF);
+  AssertTrue('LF', FScanner.MultilineStringsEOLStyle=elLF);
   TestTokens([tkComment],'{$MULTILINESTRINGLINEENDING CRLF}');
-  AssertTrue('CRLF', FSCanner.MultilineLineFeedStyle=elCRLF);
+  AssertTrue('CRLF', FScanner.MultilineStringsEOLStyle=elCRLF);
   TestTokens([tkComment],'{$MULTILINESTRINGLINEENDING SOURCE}');
-  AssertTrue('SOURCE', FSCanner.MultilineLineFeedStyle=elSOURCE);
+  AssertTrue('SOURCE', FScanner.MultilineStringsEOLStyle=elSOURCE);
   TestTokens([tkComment],'{$MULTILINESTRINGLINEENDING PLATFORM}');
-  AssertTrue('Platform', FSCanner.MultilineLineFeedStyle=elPlatform);
+  AssertTrue('Platform', FScanner.MultilineStringsEOLStyle=elPlatform);
 
 end;
 
 procedure TTestScanner.TestMultilineTrimLeftDirective;
 begin
-  AssertTrue('Default', FSCanner.MultilineLineTrimLeft=0);
+  AssertTrue('Default', FScanner.MultilineStringsTrimLeft=0);
   TestTokens([tkComment],'{$MULTILINESTRINGTRIMLEFT 1}');
-  AssertTrue('1', FSCanner.MultilineLineTrimLeft=1);
+  AssertTrue('1', FScanner.MultilineStringsTrimLeft=1);
   TestTokens([tkComment],'{$MULTILINESTRINGTRIMLEFT 2}');
-  AssertTrue('2', FSCanner.MultilineLineTrimLeft=2);
+  AssertTrue('2', FScanner.MultilineStringsTrimLeft=2);
   TestTokens([tkComment],'{$MULTILINESTRINGTRIMLEFT ALL}');
-  AssertTrue('ALL', FSCanner.MultilineLineTrimLeft=-2);
+  AssertTrue('ALL', FScanner.MultilineStringsTrimLeft=-2);
   TestTokens([tkComment],'{$MULTILINESTRINGTRIMLEFT AUTO}');
-  AssertTrue('AUTO', FSCanner.MultilineLineTrimLeft=-1);
+  AssertTrue('AUTO', FScanner.MultilineStringsTrimLeft=-1);
 end;
 
 procedure TTestScanner.TestMultilineStringTrimAll;
@@ -818,9 +831,9 @@ const
    S = '''AB'#10'CD''';
 
 begin
-  SCanner.MultilineLineTrimLeft:=-2;
+  SCanner.MultilineStringsTrimLeft:=-2;
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elLF;
+  Scanner.MultilineStringsEOLStyle:=elLF;
   DoTestToken(pscanner.tkString,'`AB'#13#10'    CD`');
   AssertEquals('Correct trim',S,TestTokenString);
 
@@ -831,9 +844,9 @@ const
    S = '''AB'#10' CD''';
 
 begin
-  SCanner.MultilineLineTrimLeft:=-1;
+  SCanner.MultilineStringsTrimLeft:=-1;
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elLF;
+  Scanner.MultilineStringsEOLStyle:=elLF;
   Scanner.SkipWhiteSpace:=True;
   DoTestToken(pscanner.tkString,' `AB'#13#10'   CD`');
   AssertEquals('Correct trim',S,TestTokenString);
@@ -846,9 +859,9 @@ const
   S2 = '''AB'#10'CD''';
 
 begin
-  SCanner.MultilineLineTrimLeft:=2;
+  SCanner.MultilineStringsTrimLeft:=2;
   Scanner.CurrentModeSwitches:=[msMultiLineStrings];
-  Scanner.MultilineLineFeedStyle:=elLF;
+  Scanner.MultilineStringsEOLStyle:=elLF;
   Scanner.SkipWhiteSpace:=True;
   DoTestToken(pscanner.tkString,' `AB'#13#10'   CD`');
   AssertEquals('Correct trim',S,TestTokenString);
@@ -1685,25 +1698,25 @@ end;
 procedure TTestScanner.TestDefine0;
 begin
   TestTokens([tkComment],'{$DEFINE NEVER}');
-  AssertTrue('Define not defined', FSCanner.Defines.IndexOf('NEVER')<>-1);
+  AssertTrue('Define not defined', FScanner.Defines.IndexOf('NEVER')<>-1);
 end;
 
 procedure TTestScanner.TestDefine0Spaces;
 begin
   TestTokens([tkComment],'{$DEFINE  NEVER}');
-  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
+  AssertTrue('Define not defined',FScanner.Defines.IndexOf('NEVER')<>-1);
 end;
 
 procedure TTestScanner.TestDefine0Spaces2;
 begin
   TestTokens([tkComment],'{$DEFINE NEVER }');
-  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
+  AssertTrue('Define not defined',FScanner.Defines.IndexOf('NEVER')<>-1);
 end;
 
 procedure TTestScanner.TestDefine01;
 begin
   TestTokens([tkComment],'(*$DEFINE NEVER*)');
-  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
+  AssertTrue('Define not defined',FScanner.Defines.IndexOf('NEVER')<>-1);
 end;
 
 procedure TTestScanner.TestDefine1;
@@ -1714,26 +1727,26 @@ end;
 procedure TTestScanner.TestDefine2;
 
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'{$IFDEF ALWAYS comment} of {$ENDIF}');
 end;
 
 procedure TTestScanner.TestDefine21;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'(*$IFDEF ALWAYS*) of (*$ENDIF*)');
 end;
 
 procedure TTestScanner.TestDefine22;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   // No whitespace. Test border of *)
   TestTokens([tkComment,tkOf,tkWhitespace,tkcomment],'(*$IFDEF ALWAYS*)of (*$ENDIF*)');
 end;
 
 procedure TTestScanner.TestDefine3;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
 end;
 
@@ -1751,14 +1764,14 @@ end;
 procedure TTestScanner.TestDefine6;
 
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   FScanner.SkipComments:=True;
   TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ENDIF}');
 end;
 
 procedure TTestScanner.TestDefine7;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   FScanner.SkipComments:=True;
   TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
 end;
@@ -1778,14 +1791,14 @@ end;
 procedure TTestScanner.TestDefine10;
 
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   FScanner.SkipComments:=True;
   TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ENDIF}');
 end;
 
 procedure TTestScanner.TestDefine11;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   FScanner.SkipComments:=True;
   FScanner.SkipWhiteSpace:=True;
   TestTokens([tkOf],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
@@ -1874,14 +1887,14 @@ begin
   FResolver.AddStream('myinclude.inc',TStringStream.Create('if true then'#10'else'));
   FScanner.SkipWhiteSpace:=True;
   FScanner.SkipComments:=True;
-  FScanner.MultilineLineFeedStyle:=elCRLF;
+  FScanner.MultilineStringsEOLStyle:=elCRLF;
   TestTokens([tkString],'{$INCLUDESTRING myinclude.inc}',False,False);
   AssertEquals('Correct string','''if true then'#13#10'else''',TestTokenString)
 end;
 
 procedure TTestScanner.TestUnDefine1;
 begin
-  FSCanner.Defines.Add('ALWAYS');
+  FScanner.Defines.Add('ALWAYS');
   TestTokens([tkComment],'{$UNDEF ALWAYS}');
   AssertEquals('No more define',-1,FScanner.Defines.INdexOf('ALWAYS'));
 end;

+ 31 - 0
packages/pastojs/tests/tcmodules.pas

@@ -323,6 +323,7 @@ type
     Procedure TestStringConst;
     Procedure TestStringConst_InvalidUTF16;
     Procedure TestStringConstSurrogate;
+    Procedure TestStringConst_Multiline;
     Procedure TestString_Length;
     Procedure TestString_Compare;
     Procedure TestString_SetLength;
@@ -8730,6 +8731,36 @@ begin
     ]));
 end;
 
+procedure TTestModule.TestStringConst_Multiline;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch multilinestrings}',
+  'const',
+  '  a = ``;',
+  '  b = `',
+  'line`;',
+  '  c = `Single`;',
+  '  d = ````;',
+  '  e = `abc``xyz`;',
+  '  f = `first''line',
+  '       second''line`#10;',
+  'begin',
+  '']);
+  ConvertProgram;
+  CheckSource('TestStringConst_Multiline',
+    LinesToStr([
+    'this.a = "";',
+    'this.b = "\nline";',
+    'this.c = "Single";',
+    'this.d = "`";',
+    'this.e = "abc`xyz";',
+    'this.f = "first''line\n       second''line\n";',
+    '']),
+    LinesToStr([
+    ]));
+end;
+
 procedure TTestModule.TestString_Length;
 begin
   StartProgram(false);