Jedi Code Format: Support generic types with const. Issue #38088, patch from Domingo Galmés.

git-svn-id: trunk@64149 -
This commit is contained in:
juha 2020-11-19 22:12:49 +00:00
parent 2f371ac685
commit b6078f53b5
9 changed files with 167 additions and 26 deletions

View File

@ -65,6 +65,7 @@ type
fiTokenCount: integer; fiTokenCount: integer;
procedure RecogniseTypeHelper; procedure RecogniseTypeHelper;
procedure SplitGreaterThanOrEqual; procedure SplitGreaterThanOrEqual;
procedure SplitShr_gg;
procedure RecogniseGoal; procedure RecogniseGoal;
procedure RecogniseUnit; procedure RecogniseUnit;
@ -1260,6 +1261,9 @@ begin
PopNode; PopNode;
end; end;
const
CONST_GENERIC_TOKENS = [ttAt, ttOpenBracket, ttOpenSquareBracket, ttIdentifier, ttPlus, ttMinus,
ttNot, ttNumber, ttQuotedLiteralString, ttNil, ttTrue, ttFalse];
function TBuildParseTree.GenericAhead: boolean; function TBuildParseTree.GenericAhead: boolean;
var var
@ -1287,7 +1291,7 @@ begin
if liTokenIndex mod 2 = 0 then if liTokenIndex mod 2 = 0 then
begin begin
// should be id // should be id
if (lcToken.WordType <> wtBuiltInType) and (not IsIdentifierToken(lcToken, idAny)) then if (lcToken.WordType <> wtBuiltInType) and (not IsIdentifierToken(lcToken, idAny)) and (not (lcToken.TokenType in CONST_GENERIC_TOKENS)) then
begin begin
break; break;
end; end;
@ -1320,26 +1324,85 @@ end;
const const
ConstraintTokens = [ttClass, ttRecord, ttConstructor]; ConstraintTokens = [ttClass, ttRecord, ttConstructor, ttInterface, ttObject];
procedure TBuildParseTree.RecogniseGenericType; procedure TBuildParseTree.RecogniseGenericType;
var
lbHasConst: boolean;
lbIsGenericType:boolean;
procedure RecogniseP;
var
liNestLevel: integer;
begin
if fcTokenList.FirstSolidTokenType = ttConst then
begin
Recognise(ttConst);
lbHasConst := True;
end;
if lbHasConst = False then //can be a expresion like h: specialize TNames<[Blaise,Pascal]>;
begin
lbisGenericType:=(fcTokenList.FirstSolidTokenType=ttIdentifier) and (fcTokenList.SolidTokenType(2)=ttLessThan); //is generic type
if fcTokenList.FirstSolidTokenType=ttSpecialize then
lbIsGenericType:=true;
if (fcTokenList.FirstSolidTokenType in CONST_GENERIC_TOKENS) and (not lbIsGenericType) then
begin //hack. recognise tokens until ; , or >
liNestLevel := 0;
while (liNestLevel > 0) or (not (fcTokenList.FirstTokenType in [ttComma, ttGreaterThan, ttSemiColon, ttGreaterThanOrEqual, ttShr_gg])) do
begin
if fcTokenList.FirstTokenType in [ttOpenSquareBracket, ttOpenBracket] then
Inc(liNestLevel);
if fcTokenList.FirstTokenType in [ttCloseSquareBracket, ttCloseBracket] then
Dec(liNestLevel);
Recognise(fcTokenList.FirstTokenType);
if fcTokenList.EOF then
begin
raise TEParseError.Create('Unexpected EOF. ',nil);
end;
end;
end
else
RecogniseType;
end
else
RecogniseType;
if lbHasConst then
begin
if fcTokenList.FirstSolidTokenType = ttColon then
RecogniseGenericConstraints;
end;
end;
begin begin
PushNode(nGeneric); PushNode(nGeneric);
// angle brackets // angle brackets
Recognise(ttLessThan); Recognise(ttLessThan);
RecogniseType;
while True do
begin
lbHasConst := False;
PushNode(nType);
RecogniseP;
// more types after commas
while fcTokenList.FirstSolidTokenType = ttComma do
begin
Recognise(ttComma);
RecogniseP;
end;
if fcTokenList.FirstSolidTokenType = ttColon then if fcTokenList.FirstSolidTokenType = ttColon then
begin begin
RecogniseGenericConstraints; RecogniseGenericConstraints;
end; end;
if fcTokenList.FirstSolidTokenType <> ttSemiColon then
// more types after commas
while fcTokenList.FirstSolidTokenType = ttComma do
begin begin
Recognise(ttComma); PopNode;
RecogniseType; break;
end;
Recognise(ttSemiColon);
PopNode;
end; end;
if fcTokenList.FirstSolidTokenType = ttGreaterThanOrEqual then if fcTokenList.FirstSolidTokenType = ttGreaterThanOrEqual then
@ -1347,10 +1410,17 @@ begin
// the tokenizer got it wrong - e.g "TTestNullable<T:Record>=Class" // the tokenizer got it wrong - e.g "TTestNullable<T:Record>=Class"
// this is the same as TTestNullable<T:Record> =Class // this is the same as TTestNullable<T:Record> =Class
RecogniseWhiteSpace; RecogniseWhiteSpace;
SplitGreaterThanOrEqual; SplitGreaterThanOrEqual;
end; end;
if fcTokenList.FirstSolidTokenType = ttShr_gg then
begin
// >> operator
RecogniseWhiteSpace;
SplitShr_gg;
end;
Recognise(ttGreaterThan); Recognise(ttGreaterThan);
PopNode; PopNode;
@ -1421,6 +1491,35 @@ begin
end; end;
end; end;
procedure TBuildParseTree.SplitShr_gg;
var
liIndex: integer;
lcNewToken: TSourceToken;
fsFileName: string;
begin
if fcTokenList.FirstTokenType = ttShr_gg then
begin
liIndex := fcTokenList.CurrentTokenIndex;
fsFileName := fcTokenList.SourceTokens[liIndex].FileName;
fcTokenList.Delete(liIndex);
lcNewToken := TSourceToken.Create();
lcNewToken.FileName := fsFileName;
lcNewToken.SourceCode := '>';
lcNewToken.TokenType := ttGreaterThan;
fcTokenList.Insert(liIndex, lcNewToken);
lcNewToken := TSourceToken.Create();
lcNewToken.FileName := fsFileName;
lcNewToken.SourceCode := '>';
lcNewToken.TokenType := ttGreaterThan;
fcTokenList.Insert(liIndex + 1 , lcNewToken);
end;
end;
{ helper proc for RecogniseTypedConstant { helper proc for RecogniseTypedConstant
need to distinguish need to distinguish

View File

@ -63,6 +63,7 @@ type
function ForwardChar(const piOffset: integer): Char; function ForwardChar(const piOffset: integer): Char;
function ForwardChars(const piOffset, piCount: integer): String; function ForwardChars(const piOffset, piCount: integer): String;
procedure Consume(const piCount: integer = 1); procedure Consume(const piCount: integer = 1);
procedure UndoConsume(const piCount: integer = 1);
function EndOfFile: boolean; function EndOfFile: boolean;
function EndOfFileAfter(const piChars: integer): boolean; function EndOfFileAfter(const piChars: integer): boolean;
@ -820,10 +821,15 @@ function TBuildTokenList.TryPunctuation(const pcToken: TSourceToken): boolean;
// "<<" is the start of two nested generics, // "<<" is the start of two nested generics,
// likewise '>>' is not an operator, it is two "end-of-generic" signs in sucession // likewise '>>' is not an operator, it is two "end-of-generic" signs in sucession
if (chLast = '<') and (ch = '<') then if (chLast = '<') and (ch = '<') then
begin
Result := True; // <<
exit; exit;
end;
if (chLast = '>') and (ch = '>') then if (chLast = '>') and (ch = '>') then
begin
Result := True; // >>
exit; exit;
end;
Result := CharIsPuncChar(ch); Result := CharIsPuncChar(ch);
end; end;
@ -851,6 +857,12 @@ begin
Consume; Consume;
end; end;
if length(pcToken.SourceCode) > 2 then // nested generic specialize TC1<TC2<TC3<integer>>>=record end;
begin
// only consume the first >
UndoConsume(Length(pcToken.SourceCode) - 1);
pcToken.SourceCode := pcToken.SourceCode[1];
end;
{ try to recognise the punctuation as an operator } { try to recognise the punctuation as an operator }
TypeOfToken(pcToken.SourceCode, leWordType, leTokenType); TypeOfToken(pcToken.SourceCode, leWordType, leTokenType);
if leTokenType <> ttUnknown then if leTokenType <> ttUnknown then
@ -932,6 +944,11 @@ begin
inc(fiCurrentIndex, piCount); inc(fiCurrentIndex, piCount);
end; end;
procedure TBuildTokenList.UndoConsume(const piCount: integer);
begin
dec(fiCurrentIndex, piCount);
end;
function TBuildTokenList.EndOfFile: boolean; function TBuildTokenList.EndOfFile: boolean;
begin begin
Result := fiCurrentIndex > Length(fsSourceCode); Result := fiCurrentIndex > Length(fsSourceCode);

View File

@ -640,9 +640,12 @@ begin
end; end;
function IsDfmIncludeDirective(const pt: TSourceToken): boolean; function IsDfmIncludeDirective(const pt: TSourceToken): boolean;
var
lsToken:string;
begin begin
// form dfm comment // form dfm comment
Result := (pt.TokenType = ttComment) and AnsiSameText(pt.SourceCode, '{$R *.dfm}') and lsToken:=UpperCase(pt.SourceCode);
Result := (pt.TokenType = ttComment) and ((lsToken = '{$R *.DFM}') or (lsToken = '[$R *.LFM}')) and
pt.HasParentNode(nImplementationSection, 4); pt.HasParentNode(nImplementationSection, 4);
end; end;

View File

@ -295,7 +295,9 @@ type
ttPlusAssign, // += ttPlusAssign, // +=
ttMinusAssign, // -= ttMinusAssign, // -=
ttTimesAssign, // *= ttTimesAssign, // *=
ttFloatDivAssign // /= ttFloatDivAssign, // /=
ttShl_ll, // <<
ttShr_gg // >>
); );
TTokenTypeSet = set of TTokenType; TTokenTypeSet = set of TTokenType;
@ -419,7 +421,7 @@ const
AddOperators: TTokenTypeSet = [ttPlus, ttMinus, ttOr, ttXor]; AddOperators: TTokenTypeSet = [ttPlus, ttMinus, ttOr, ttXor];
MulOperators: TTokenTypeSet = [ttTimes, ttFloatDiv, ttDiv, ttMod, ttAnd, ttShl, ttShr, ttExponent]; MulOperators: TTokenTypeSet = [ttTimes, ttFloatDiv, ttDiv, ttMod, ttAnd, ttShl, ttShr, ttExponent, ttShl_ll,ttShr_gg];
SingleSpaceOperators = [ SingleSpaceOperators = [
// some unary operators // some unary operators
@ -427,7 +429,7 @@ const
// all operators that are always binary // all operators that are always binary
ttAnd, ttAs, ttDiv, ttIn, ttIs, ttMod, ttOr, ttShl, ttShr, ttXor, ttAnd, ttAs, ttDiv, ttIn, ttIs, ttMod, ttOr, ttShl, ttShr, ttXor,
ttTimes, ttFloatDiv, ttExponent, ttEquals, ttGreaterThan, ttLessThan, ttTimes, ttFloatDiv, ttExponent, ttEquals, ttGreaterThan, ttLessThan,
ttGreaterThanOrEqual, ttLessThanOrEqual, ttNotEqual, ttSetSymDif]; ttGreaterThanOrEqual, ttLessThanOrEqual, ttNotEqual, ttSetSymDif, ttShl_ll, ttShr_gg];
StringWords: TTokenTypeSet = [ttString, ttAnsiString, ttWideString]; StringWords: TTokenTypeSet = [ttString, ttAnsiString, ttWideString];
@ -789,6 +791,8 @@ begin
AddKeyword('<=', wtOperator, ttLessThanOrEqual); AddKeyword('<=', wtOperator, ttLessThanOrEqual);
AddKeyword('<>', wtOperator, ttNotEqual); AddKeyword('<>', wtOperator, ttNotEqual);
AddKeyword('><', wtOperator, ttSetSymDif); AddKeyword('><', wtOperator, ttSetSymDif);
AddKeyword('<<', wtOperator, ttShl_ll); // in FreePascal
AddKeyword('>>', wtOperator, ttShr_gg); // in FreePascal
// these must come after the above as they are shorter // these must come after the above as they are shorter
AddKeyword('>', wtOperator, ttGreaterThan); AddKeyword('>', wtOperator, ttGreaterThan);
AddKeyword('<', wtOperator, ttLessThan); AddKeyword('<', wtOperator, ttLessThan);

View File

@ -128,6 +128,13 @@ begin
Result := True; Result := True;
exit; exit;
end; end;
{ in generic definition}
if pt.HasParentNode(nGeneric,2) then
begin
Result := False;
exit;
end;
end; end;

View File

@ -71,6 +71,12 @@ begin
if ptNext.TokenType = ttComment then if ptNext.TokenType = ttComment then
exit; exit;
if (pt.TokenType in [ttLessThan,ttGreaterThan]) and pt.HasParentNode(nGeneric,1) then
begin
Result := True;
Exit;
end;
if pt.TokenType in NoSpaceAnywhere then if pt.TokenType in NoSpaceAnywhere then
begin begin
Result := True; Result := True;

View File

@ -78,6 +78,11 @@ begin
exit; exit;
end; end;
if (pt.TokenType in [ttLessThan,ttGreaterThan]) and pt.HasParentNode(nGeneric,1) then
begin
Result := True;
Exit;
end;
// '@@' in asm, e.g. "JE @@initTls" needs the space // '@@' in asm, e.g. "JE @@initTls" needs the space
if pt.HasParentNode(nAsm) then if pt.HasParentNode(nAsm) then

View File

@ -80,9 +80,9 @@ begin
if pt.HasParentNode(nAsm) then if pt.HasParentNode(nAsm) then
exit; exit;
if pt.HasParentNode(nGeneric, 1) then if pt.HasParentNode(nGeneric, 2) then
begin begin
if pt.TokenType in [ttComma, ttColon] then if pt.TokenType in [ttComma, ttColon, ttSemiColon] then
begin begin
Result := true; Result := true;
end; end;

View File

@ -1150,7 +1150,7 @@ object Form1: TForm1
Left = 18 Left = 18
Height = 15 Height = 15
Top = 465 Top = 465
Width = 80 Width = 152
Anchors = [akLeft, akBottom] Anchors = [akLeft, akBottom]
AutoSize = False AutoSize = False
ParentColor = False ParentColor = False