View Issue Details

IDProjectCategoryView StatusLast Update
0038067LazarusIDEpublic2020-11-13 23:06
ReporterYuri Serebrennikov Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version2.0.10 
Summary0038067: JCF does not support escaped identifiers (&identifier)
DescriptionFor JSON deserialization I using symbol &. (https://freepascal.org/docs-html/current/ref/refse4.html)

And the key combination Сtrl + D, stops working.
Steps To ReproduceCreate class

  TNameObject = class(TCollectionItem)
  private
    fType: string;
  published
    property &type: string read Ftype write Ftype;
  end;

Press Ctrl+D.
TagsNo tags attached.
Fixed in Revisionr64131, r64134
LazTarget-
WidgetsetWin32/Win64
Attached Files

Activities

Juha Manninen

2020-11-11 22:05

developer   ~0126840

Last edited: 2020-11-11 22:06

View 2 revisions

You mean the Jedi code formatting stops working? It can be added to the summary line.
Key combination Сtrl + D can be assigned to any command although it is the code format by default.

Domingo Galmés

2020-11-11 22:32

reporter   ~0126841

JCF doesn't support well identifiers names escaped with the ampersand char & so the &type causes the error.
This patch solves the problem and offers better support for nested types.
Now is posible format generics.collections.pas without errors.
NestedTypesAndReservedWordIdentifiers.patch (6,268 bytes)   
From 4b5976221fd0dd98b09a7c11d13b933cc62b0486 Mon Sep 17 00:00:00 2001
From: DomingoGP <dgalmesp@gmail.com>
Date: Wed, 11 Nov 2020 22:23:22 +0100
Subject: [PATCH] Better support for nested types and reserved word variable
 names.

---
 components/jcf2/Parse/BuildParseTree.pas | 86 ++++++++++++++----------
 components/jcf2/Parse/BuildTokenList.pas | 52 ++++++++++----
 2 files changed, 92 insertions(+), 46 deletions(-)

diff --git a/components/jcf2/Parse/BuildParseTree.pas b/components/jcf2/Parse/BuildParseTree.pas
index 51a5705faf..1a9f13f39b 100644
--- a/components/jcf2/Parse/BuildParseTree.pas
+++ b/components/jcf2/Parse/BuildParseTree.pas
@@ -1218,41 +1218,50 @@ begin
 
   Recognise(ttEquals);
 
-  //Recognise type helper (for fpc)
-  if (fcTokenList.FirstSolidTokenType in [ttType,ttRecord]) and
-    (fcTokenList.SolidToken(2).TokenType=ttHelper) then
-  begin
-     RecogniseTypeHelper;
-  end else
+  repeat
 
-  // type or restricted type
-  if (fcTokenList.FirstSolidTokenType in [ttObject, ttClass, ttInterface,
-    ttDispInterface]) then
-    RecogniseRestrictedType
-  else
-    RecogniseType;
+     //Recognise type helper (for fpc)
+    if (fcTokenList.FirstSolidTokenType in [ttType,ttRecord]) and
+      (fcTokenList.SolidToken(2).TokenType=ttHelper) then
+    begin
+       RecogniseTypeHelper;
+    end else
 
-  if fcTokenList.FirstSolidTokenType = ttLessThan then
-  begin
-    RecogniseGenericType;
-  end;
 
-  if fcTokenList.FirstSolidTokenType = ttIs then
-  begin
-    Recognise(ttIs);
-    Recognise(ttNested);
-  end;
+    // type or restricted type
+    if (fcTokenList.FirstSolidTokenType in [ttObject, ttClass, ttInterface,
+      ttDispInterface]) then
+      RecogniseRestrictedType
+    else
+      RecogniseType;
+
+    if fcTokenList.FirstSolidTokenType = ttLessThan then
+    begin
+      RecogniseGenericType;
+    end;
 
-  // the type can be deprecated
-  if fcTokenList.FirstSolidTokenType = ttDeprecated then
-    Recognise(ttDeprecated);
+    if fcTokenList.FirstSolidTokenType = ttIs then
+    begin
+      Recognise(ttIs);
+      Recognise(ttNested);
+    end;
 
+    // the type can be deprecated
+    if fcTokenList.FirstSolidTokenType = ttDeprecated then
+      Recognise(ttDeprecated);
+
+    if fcTokenList.FirstSolidTokenType <> ttDot then
+      break;
+    Recognise(ttDot);
+
+  until false;
 
   Recognise(ttSemicolon);
 
   PopNode;
 end;
 
+
 function TBuildParseTree.GenericAhead: boolean;
 var
   liTokenIndex: integer;
@@ -5000,22 +5009,26 @@ begin
   Recognise(IdentiferTokens);
 
   { tokens can be qualified by a unit name }
+  { can be nested types }
   if pbCanHaveUnitQualifier and (fcTokenList.FirstSolidTokenType = ttDot) then
   begin
-    Recognise(ttDot);
+    while fcTokenList.FirstSolidTokenType = ttDot do
+    begin
+      Recognise(ttDot);
 
-    { delphi.net can preface the identifier with an '&'
-      in order to do something obscure with it - make it a literal or something
+      { delphi.net can preface the identifier with an '&'
+        in order to do something obscure with it - make it a literal or something
 
-      e.g. "WebRequest.&Create" is not a constructor,
-      but a C# method called "Create", which is not a reserved word in C#
-    }
+        e.g. "WebRequest.&Create" is not a constructor,
+        but a C# method called "Create", which is not a reserved word in C#
+      }
 
-    RecognisePossiblyAmpdIdentifier;
+      RecognisePossiblyAmpdIdentifier;
+    end;
   end;
 
   PopNode;
-end;
+end; 
 
 { the name of a procedure/function/constructor can be
   a plain name or classname.methodname
@@ -5125,9 +5138,14 @@ begin
     // a use not a decl
     RecogniseGenericType;
   end;
-  
+  if fcTokenList.FirstSolidTokenType = ttDot then
+  begin
+    Recognise(ttDot);
+    RecogniseTypeId;
+  end;
 end;
 
+
 procedure TBuildParseTree.RecogniseAsmBlock;
 begin
   PushNode(nAsm);
@@ -5432,7 +5450,7 @@ begin
       begin
         lcLastChar := lcNext.SourceCode[Length(lcNext.SourceCode)];
 
-        if (lcLastChar = 'h') then
+        if ((lcLastChar = 'h') or (lcLastChar = 'H')) then
         begin
           Recognise(ttIdentifier);
         end;
diff --git a/components/jcf2/Parse/BuildTokenList.pas b/components/jcf2/Parse/BuildTokenList.pas
index 773c22430d..e2e0fbce9f 100644
--- a/components/jcf2/Parse/BuildTokenList.pas
+++ b/components/jcf2/Parse/BuildTokenList.pas
@@ -124,6 +124,13 @@ begin
     Result := IsMultiByte(pcChar);
 end;
 
+function CharIsOctDigit(const c: Char): Boolean;
+const
+  OctDigits: set of Char = [ '0', '1', '2', '3', '4', '5', '6', '7'];
+begin
+  Result := (c in OctDigits);
+end;
+
 { TBuildTokenList }
 
 constructor TBuildTokenList.Create;
@@ -468,14 +475,32 @@ end;
 
 
 function TBuildTokenList.TryWord(const pcToken: TSourceToken): boolean;
+
 begin
   Result := False;
 
-  if not CharIsWordChar(Current) then
-    exit;
-
-  pcToken.SourceCode := Current;
-  Consume;
+  // support reserved words as identifiers
+  // example.
+  // var &type:integer;
+  if Current='&' then
+  begin
+    if CharIsOctDigit(ForwardChar(1)) then
+      Exit;
+    pcToken.SourceCode := Current;
+    Consume;
+    if not CharIsWordChar(Current) then
+    begin
+      pcToken.TokenType := ttAmpersand;
+      exit;
+    end;
+  end
+  else
+  begin
+    if not CharIsWordChar(Current) then
+      exit;
+    pcToken.SourceCode := Current;
+    Consume;
+  end;
 
   { concat any subsequent word chars }
   while CharIsWordChar(Current) or CharIsDigit(Current) do
@@ -691,13 +716,6 @@ end;
 
 { ~pktb 2017.05.19 - Oct numbers are prefixed with & }
 function TBuildTokenList.TryOctNumber(const pcToken: TSourceToken): boolean;
-function CharIsOctDigit(const c: Char): Boolean;
-const
-  OctDigits: set of AnsiChar = [
-    '0', '1', '2', '3', '4', '5', '6', '7'];
-begin
-  Result := (c in OctDigits);
-end;
 begin
   Result := False;
 
@@ -705,6 +723,16 @@ begin
   if Current <> '&' then
     exit;
 
+  //ISN'T A Octal Number.
+  if not CharIsOctDigit(ForwardChar(1)) then
+  begin
+    pcToken.TokenType  := ttAmpersand;
+    pcToken.SourceCode := Current;
+    Consume;
+    result:=true;
+    exit;
+  end;
+
   pcToken.TokenType  := ttNumber;
   pcToken.SourceCode := Current;
   Consume;
-- 
2.29.1.windows.1

Yuri Serebrennikov

2020-11-11 23:20

reporter   ~0126843

Yes. Jedi code formatting stops working.

Yuri Serebrennikov

2020-11-11 23:24

reporter   ~0126844

And if in code exist this construction, then Jedi formatting stop working too.

for TCollectionItem(item) in ItemList do
begin
 // do something.....
end.

Juha Manninen

2020-11-13 10:40

developer   ~0126872

I applied the patch for &ident in r64131. Thanks.

The syntax "for TCollectionItem(item) in ItemList do" is unusual but it compiles.
I keep this report open for a while in case somebody wants to fix it. Otherwise a new report can be opened for it.

Domingo Galmés

2020-11-13 14:40

reporter   ~0126877

This patch solves the issue of casting in for .. in .

Supports more than one cast.

example.

for MyCast2(TCollectionItem(item)) in itemList do
begin
  // do something ...
end;
JCF_For_In_Typecasts.patch (1,321 bytes)   
From 379448915f0a888897c4d105939016562b40a8ac Mon Sep 17 00:00:00 2001
From: DomingoGP <dgalmesp@gmail.com>
Date: Fri, 13 Nov 2020 14:30:58 +0100
Subject: [PATCH] Typecasts in   for Typecast1(item) in itemList do

---
 components/jcf2/Parse/BuildParseTree.pas | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/components/jcf2/Parse/BuildParseTree.pas b/components/jcf2/Parse/BuildParseTree.pas
index 1a9f13f39b..1c248c839c 100644
--- a/components/jcf2/Parse/BuildParseTree.pas
+++ b/components/jcf2/Parse/BuildParseTree.pas
@@ -3473,6 +3473,7 @@ end;
 procedure TBuildParseTree.RecogniseForStmnt;
 var
   lc: TSourceToken;
+  lCountBrackets:integer;
 begin
   { ForStmt -> FOR QualId ':=' Expression (TO | DOWNTO) Expression DO Statement
 
@@ -3485,6 +3486,22 @@ begin
   Recognise(ttFor);
   RecogniseQualId;
 
+
+  //type cast    for TCollectionItem(item) in ItemList do 
+  // TypeCast1(TypeCast2(TypeCast3(item)))
+  lCountBrackets:=0;
+  while fcTokenList.FirstSolidTokenType=ttOpenBracket do
+  begin
+    Recognise(ttOpenBracket);
+    RecogniseQualId;
+    Inc(lCountBrackets);
+  end;
+  while lCountBrackets>0 do
+  begin
+    Recognise(ttCloseBracket);
+    Dec(lCountBrackets);
+  end;
+
   lc := fcTokenList.FirstSolidToken;
   if lc.TokenType = ttIn then
   begin
-- 
2.29.1.windows.1

JCF_For_In_Typecasts.patch (1,321 bytes)   

Juha Manninen

2020-11-13 23:06

developer   ~0126902

The typecast patch is now applied, too. Thanks.
Resolving.

Issue History

Date Modified Username Field Change
2020-11-11 21:30 Yuri Serebrennikov New Issue
2020-11-11 22:05 Juha Manninen Note Added: 0126840
2020-11-11 22:06 Juha Manninen Note Edited: 0126840 View Revisions
2020-11-11 22:32 Domingo Galmés Note Added: 0126841
2020-11-11 22:32 Domingo Galmés File Added: NestedTypesAndReservedWordIdentifiers.patch
2020-11-11 23:20 Yuri Serebrennikov Note Added: 0126843
2020-11-11 23:24 Yuri Serebrennikov Note Added: 0126844
2020-11-11 23:30 Maxim Ganetsky Summary Key combination Сtrl + D, stops working. => JCF does not support escaped identifiers (&identifier)
2020-11-11 23:30 Maxim Ganetsky LazTarget => -
2020-11-11 23:30 Maxim Ganetsky Widgetset Win32/Win64 => Win32/Win64
2020-11-13 10:36 Juha Manninen Assigned To => Juha Manninen
2020-11-13 10:36 Juha Manninen Status new => assigned
2020-11-13 10:40 Juha Manninen Note Added: 0126872
2020-11-13 14:40 Domingo Galmés Note Added: 0126877
2020-11-13 14:40 Domingo Galmés File Added: JCF_For_In_Typecasts.patch
2020-11-13 23:06 Juha Manninen Status assigned => resolved
2020-11-13 23:06 Juha Manninen Resolution open => fixed
2020-11-13 23:06 Juha Manninen Fixed in Revision => r64131, r64134
2020-11-13 23:06 Juha Manninen Widgetset Win32/Win64 => Win32/Win64
2020-11-13 23:06 Juha Manninen Note Added: 0126902