Jedi Code Format: Prevent choking on some new and rare language syntax. Issue #39945, patch by Bruno K.

This commit is contained in:
Juha 2022-10-04 19:29:00 +03:00
parent f5dd7766de
commit 27dfc5b4cd
5 changed files with 160 additions and 85 deletions

View File

@ -61,6 +61,8 @@ type
fbMadeTree: boolean; fbMadeTree: boolean;
fiTokenIndex: integer; fiTokenIndex: integer;
fcIsIncFile: boolean;
fcRoot: TParseTreeNode; fcRoot: TParseTreeNode;
fcStack: TStack; fcStack: TStack;
fcTokenList: TSourceTokenList; fcTokenList: TSourceTokenList;
@ -76,6 +78,7 @@ type
procedure RecogniseProgram; procedure RecogniseProgram;
procedure RecognisePackage; procedure RecognisePackage;
procedure RecogniseLibrary; procedure RecogniseLibrary;
procedure RecogniseInclude;
procedure RecogniseFileEnd; procedure RecogniseFileEnd;
@ -164,6 +167,7 @@ type
procedure RecogniseObjectType; procedure RecogniseObjectType;
procedure RecogniseVariantSection; procedure RecogniseVariantSection;
procedure RecogniseVarDecl(aInClassBody:boolean=false); procedure RecogniseVarDecl(aInClassBody:boolean=false);
procedure RecogniseVarExpPubDir;
procedure RecogniseAddOp; procedure RecogniseAddOp;
procedure RecogniseDesignator; procedure RecogniseDesignator;
procedure RecogniseDesignatorTail; procedure RecogniseDesignatorTail;
@ -205,6 +209,7 @@ type
procedure RecogniseFunctionDecl(const pbAnon: boolean); procedure RecogniseFunctionDecl(const pbAnon: boolean);
procedure RecogniseProcedureDecl(const pbAnon: boolean); procedure RecogniseProcedureDecl(const pbAnon: boolean);
procedure RecogniseSquareBracketDir;
procedure RecogniseConstructorDecl; procedure RecogniseConstructorDecl;
procedure RecogniseDestructorDecl; procedure RecogniseDestructorDecl;
@ -287,6 +292,7 @@ type
procedure BuildParseTree; procedure BuildParseTree;
procedure Clear; procedure Clear;
property IsIncFile : boolean Read fcIsIncFile Write fcIsIncFile;
property Root: TParseTreeNode Read fcRoot; property Root: TParseTreeNode Read fcRoot;
property TokenList: TSourceTokenList Read fcTokenList Write fcTokenList; property TokenList: TSourceTokenList Read fcTokenList Write fcTokenList;
end; end;
@ -305,6 +311,7 @@ begin
fcStack := TStack.Create; fcStack := TStack.Create;
fcRoot := nil; fcRoot := nil;
fiTokenCount := 0; fiTokenCount := 0;
AllProcDirectives := ProcedureDirectives + [ttOpenSquareBracket];
end; end;
destructor TBuildParseTree.Destroy; destructor TBuildParseTree.Destroy;
@ -417,32 +424,30 @@ procedure TBuildParseTree.Recognise(const peTokenTypes: TTokenTypeSet;
end; end;
var var
lcCurrentToken: TSourceToken; lcToken: TSourceToken;
begin begin
// must accept something // must accept something
Assert(peTokenTypes <> []); Assert(peTokenTypes <> []);
{ read tokens up to and including the specified one. { read tokens up to and including the specified one.
Add them to the parse tree at the current growing point } Add them to the parse tree at the current growing point }
while not fcTokenList.EOF do while not fcTokenList.EOF do begin
begin lcToken := fcTokenList.Extract;
lcCurrentToken := fcTokenList.Extract; Assert(lcToken <> nil);
CheckNilInstance(lcCurrentToken, fcRoot.LastLeaf);
TopNode.AddChild(lcCurrentToken); TopNode.AddChild(lcToken);
// the the match must be the first solid token // the the match must be the first solid token
if lcCurrentToken.TokenType in peTokenTypes then if lcToken.TokenType in peTokenTypes then
begin begin
// found it // found it
Break; Break;
end end
// accept any white space until we find it // accept any white space until we find it
else if not (lcCurrentToken.TokenType in NotSolidTokens) then else if not (lcToken.TokenType in NotSolidTokens) then
RaiseParseError('Unexpected token, expected ' + raise TEParseError.Create('Unexpected token "'+lcToken.SourceCode+ '" , expected ' +
DescribeTarget, lcCurrentToken); DescribeTarget, lcToken);
end; end;
Inc(fiTokenCount); Inc(fiTokenCount);
{$IFnDEF LCLNOGUI} {$IFnDEF LCLNOGUI}
if (fiTokenCount mod UPDATE_INTERVAL) = 0 then if (fiTokenCount mod UPDATE_INTERVAL) = 0 then
@ -465,6 +470,9 @@ end;
function TBuildParseTree.PushNode(const peNodeType: TParseTreeNodeType): TParseTreeNode; function TBuildParseTree.PushNode(const peNodeType: TParseTreeNodeType): TParseTreeNode;
begin begin
if peNodeType = nProcedureDirectives then
Result := nil;
Result := TParseTreeNode.Create; Result := TParseTreeNode.Create;
Result.NodeType := peNodeType; Result.NodeType := peNodeType;
@ -529,10 +537,15 @@ begin
RecogniseLibrary; RecogniseLibrary;
ttUnit: ttUnit:
RecogniseUnit; RecogniseUnit;
else begin
if Self.IsIncFile then
RecogniseInclude
else else
RaiseParseError('Expected program, package, library, unit, got "' + s + '" ', RaiseParseError('Expected program, package, library, unit, ''.inc'' got "' +
s + '" ',
fcTokenList.FirstSolidToken); fcTokenList.FirstSolidToken);
end end;
end;
end; end;
procedure TBuildParseTree.RecogniseProgram; procedure TBuildParseTree.RecogniseProgram;
@ -650,6 +663,16 @@ begin
PopNode; PopNode;
end; end;
procedure TBuildParseTree.RecogniseInclude;
begin
PushNode(nInclude);
RecogniseDeclSections;
RecogniseFileEnd;
PopNode;
end;
procedure TBuildParseTree.RecogniseFileEnd; procedure TBuildParseTree.RecogniseFileEnd;
var var
lcCurrentToken: TSourceToken; lcCurrentToken: TSourceToken;
@ -2623,6 +2646,7 @@ const
VariableModifiers: TTokenTypeSet = [ttExternal, ttExport, ttPublic]; VariableModifiers: TTokenTypeSet = [ttExternal, ttExport, ttPublic];
var var
lc: TSourceToken; lc: TSourceToken;
lct: TTokenType;
begin begin
// (* attempted EBNF definition of a variable definition *) // (* attempted EBNF definition of a variable definition *)
// named : 'name' var_name // named : 'name' var_name
@ -2688,16 +2712,42 @@ begin
Recognise(ttEquals); Recognise(ttEquals);
{ not just an expr - can be an array, record or the like { not just an expr - can be an array, record or the like
reuse the code from typed constant declaration as it works the same reuse the code from typed constant declaration as it works the same }
}
RecogniseTypedConstant; RecogniseTypedConstant;
PopNode; PopNode;
end; end;
end; end;
{ yes, they can occur here too } { This loop will attempt to recognize HintDirectives and special directives }
RecogniseHintDirectives; repeat
lct := fcTokenList.FirstSolidTokenType;
if lct = ttSemicolon then // need to look ahead
lct := fcTokenList.SolidTokenType(2);
if lct in HintDirectives then
RecogniseHintDirectives
else if lct in [ttExport, ttPublic] then
RecogniseVarExpPubDir
else
break;
until False;
PopNode;
end;
procedure TBuildParseTree.RecogniseVarExpPubDir;
var
lTokenType : TTokenType;
begin
PushNode(nVarExpPubl);
Recognise(ttSemicolon); // close previous term
{ Skip checking anything until ';' }
while not fcTokenList.EOF do begin
lTokenType := fcTokenList.FirstTokenType;
if lTokenType = ttSemicolon then
break;
Recognise(lTokenType);
end;
PopNode; PopNode;
end; end;
@ -4014,18 +4064,18 @@ end;
function IsForwardExtern(pt: TParseTreeNode): boolean; function IsForwardExtern(pt: TParseTreeNode): boolean;
var var
lcDirectives: TParseTreeNode; lpt: TParseTreeNode;
begin begin
Assert(pt <> nil); Assert(pt <> nil);
{ Path to directives : <pt>/nProcedureHeading/nProcedureDirectives }
if pt.NodeType in ProcedureNodes then if pt.NodeType in ProcedureNodes then
pt := pt.GetImmediateChild(ProcedureHeadings); lpt:= pt.GetImmediateChild(nProcedureHeading);
Assert(pt <> nil); if Assigned(lpt) then
result := lpt.HasChildNode([ttForward,ttExternal]) // This searches all sub nodes !
lcDirectives := pt.GetImmediateChild(nProcedureDirectives); else
result := False;
Result := (lcDirectives <> nil) and lcDirectives.HasChildNode([ttExternal, ttForward])
end; end;
procedure TBuildParseTree.RecogniseProcedureDecl(const pbAnon: boolean); procedure TBuildParseTree.RecogniseProcedureDecl(const pbAnon: boolean);
@ -4047,6 +4097,10 @@ begin
RecogniseNotSolidTokens; RecogniseNotSolidTokens;
//opt
if fcTokenList.FirstSolidTokenType in AllProcDirectives then
RecogniseProcedureDirectives;
{ if the proc declaration has the directive external or forward, { if the proc declaration has the directive external or forward,
it will not have a body it will not have a body
note that though 'forward' is a spectacularly unfortunate variable name, note that though 'forward' is a spectacularly unfortunate variable name,
@ -4089,7 +4143,6 @@ begin
if not IsForwardExtern(lcTop) then if not IsForwardExtern(lcTop) then
begin begin
RecogniseBlock; RecogniseBlock;
if (not pbAnon) and (fcTokenList.FirstSolidTokenType = ttSemiColon) then if (not pbAnon) and (fcTokenList.FirstSolidTokenType = ttSemiColon) then
begin begin
Recognise(ttSemicolon); Recognise(ttSemicolon);
@ -4099,6 +4152,26 @@ begin
PopNode; PopNode;
end; end;
procedure TBuildParseTree.RecogniseSquareBracketDir;
var
lTokenType : TTokenType;
{ lNextToken : TSourceToken; }
begin
{ just add all tokens until the ']' is reached ] }
PushNode(nProcedureDirBracket);
{ Skip checking anything until ']' }
while not fcTokenList.EOF do begin
lTokenType := fcTokenList.FirstSolidTokenType;
Recognise(lTokenType);
if lTokenType = ttCloseSquareBracket then
break;
end;
PopNode;
end;
procedure TBuildParseTree.RecogniseConstructorDecl; procedure TBuildParseTree.RecogniseConstructorDecl;
begin begin
// ProcedureDecl -> ProcedureHeading ';' [Directive] Block ';' // ProcedureDecl -> ProcedureHeading ';' [Directive] Block ';'
@ -4324,7 +4397,7 @@ end;
procedure TBuildParseTree.RecogniseProcedureDirectives; procedure TBuildParseTree.RecogniseProcedureDirectives;
var var
lbFirstPass: boolean; lTokenType, lNextType: TTokenType;
begin begin
{ these are semi-colon separated { these are semi-colon separated
@ -4333,65 +4406,48 @@ begin
external is more complex external is more complex
} }
CheckEnumeratorToken(fcTokenList.FirstSolidTokenType = ttSemicolon);
if (fcTokenList.FirstSolidTokenType in ProcedureDirectives) or lTokenType := fcTokenList.FirstSolidTokenType;
((fcTokenList.FirstSolidTokenType = ttSemicolon) and lNextType := fcTokenList.SolidTokenType(2);
(fcTokenList.SolidTokenType(2) in ProcedureDirectives)) then if (lTokenType in AllProcDirectives) or (lNextType in AllProcDirectives) then begin
begin
PushNode(nProcedureDirectives); PushNode(nProcedureDirectives);
while (lTokenType in AllProcDirectives) or (lNextType in AllProcDirectives) do begin
if fcTokenList.FirstSolidTokenType = ttSemiColon then if (lTokenType = ttSemiColon) then
Recognise(ttSemiColon); Recognise(ttSemiColon)
lbFirstPass := True; else begin
case lTokenType of
CheckEnumeratorToken(fcTokenList.FirstSolidTokenType = ttSemicolon); ttOpenSquareBracket:
while (fcTokenList.FirstSolidTokenType in ProcedureDirectives) or RecogniseSquareBracketDir;
((fcTokenList.FirstSolidTokenType = ttSemicolon) and ttExternal:
(fcTokenList.SolidTokenType(2) in ProcedureDirectives)) do RecogniseExternalProcDirective;
begin ttPublic:
if ( not lbFirstPass) and (fcTokenList.FirstSolidTokenType = ttSemiColon) then { Break the loop if we have found a class visibility "public" }
Recognise(ttSemiColon); if not RecognisePublicProcDirective then
break;
case fcTokenList.FirstSolidTokenType of ttDispId: begin
ttExternal: Recognise(ttDispId);
begin
RecogniseExternalProcDirective;
end;
ttPublic:
begin
{ Break the loop if we have found a class visibility "public" }
if not RecognisePublicProcDirective then
break;
end;
ttDispId:
begin
Recognise(ttDispId);
RecogniseConstantExpression;
end;
ttMessage:
begin
Recognise(ttMessage);
RecogniseConstantExpression;
end;
ttEnumerator:
begin
Recognise(ttEnumerator);
RecogniseIdentifier(False, idStrict);
end;
ttDeprecated:
begin
Recognise(ttDeprecated);
if fcTokenList.FirstSolidTokenType <> ttSemicolon then
RecogniseConstantExpression; RecogniseConstantExpression;
end end;
else ttMessage: begin
Recognise(ProcedureDirectives); Recognise(ttMessage);
RecogniseConstantExpression;
end;
ttEnumerator: begin
Recognise(ttEnumerator);
RecogniseIdentifier(False, idStrict);
end;
ttDeprecated: begin
Recognise(ttDeprecated);
if fcTokenList.FirstSolidTokenType <> ttSemicolon then
RecogniseConstantExpression;
end
else
Recognise(ProcedureDirectives);
end;
end; end;
lTokenType := fcTokenList.FirstSolidTokenType;
lbFirstPass := False; lNextType := fcTokenList.SolidTokenType(2);
CheckEnumeratorToken();
end; end;
PopNode; PopNode;
end; end;
end; end;

View File

@ -45,6 +45,7 @@ type
nUnitName, nUnitName,
nPackage, nPackage,
nLibrary, nLibrary,
nInclude,
nUses, nUses,
nUsesItem, nUsesItem,
nRequires, nRequires,
@ -79,6 +80,8 @@ type
nVarDecl, nVarDecl,
nVarAbsolute, nVarAbsolute,
nVariableInit, nVariableInit,
nVarExpPubl, // ( 'export' | 'public' ) [ named ] ';'
nVarExternal, // ( 'external' [[ lib_name ] [named]] ';'
nDesignator, nDesignator,
nExpression, nExpression,
nTerm, nTerm,
@ -122,6 +125,7 @@ type
nFormalParam, nFormalParam,
nFunctionReturnType, nFunctionReturnType,
nProcedureDirectives, nProcedureDirectives,
nProcedureDirBracket, // proc dir backet
nExternalDirective, nExternalDirective,
nObjectType, nObjectType,
nInitSection, nInitSection,
@ -198,13 +202,15 @@ uses SysUtils;
const const
TreeNodeTypeNames: array[TParseTreeNodeType] of string = ( TreeNodeTypeNames: array[TParseTreeNodeType] of string = (
'UnkNown', 'Leaf', 'Program', 'Unit', 'Unit header', 'Unit name', 'Package', 'Library', 'Uses', 'UnkNown', 'Leaf', 'Program', 'Unit', 'Unit header', 'Unit name', 'Package', 'Library',
'Include', 'Uses',
'Uses Item', 'Requires', 'Contains', 'ident list', 'Identifier', 'Interface section', 'Uses Item', 'Requires', 'Contains', 'ident list', 'Identifier', 'Interface section',
'Implementation section', 'Block', 'Statement list', 'Decl section', 'Label decl section', 'Implementation section', 'Block', 'Statement list', 'Decl section', 'Label decl section',
'const section', 'Const decl', 'type section', 'Type Decl', 'Array constant', 'Record Constant', 'const section', 'Const decl', 'type section', 'Type Decl', 'Array constant', 'Record Constant',
'Field constant', 'Type', 'Restricted type', 'Subrange type', 'Enumerated type', 'Array type', 'Field constant', 'Type', 'Restricted type', 'Subrange type', 'Enumerated type', 'Array type',
'record type', 'Field declarations', 'Record variant section', 'Record variant', 'Set type', 'record type', 'Field declarations', 'Record variant section', 'Record variant', 'Set type',
'procedure type', 'Var section', 'Var decl', 'Absolute var', 'Variable init', 'Designator', 'procedure type', 'Var section', 'Var decl', 'Absolute var', 'Variable init',
'Var Export | Public', 'Var External/Lib', 'Designator',
'Expression', 'Term', 'Unary op', 'Actual params', 'Statement', 'Assignment', 'Inline', 'Expression', 'Term', 'Unary op', 'Actual params', 'Statement', 'Assignment', 'Inline',
'Inline item', 'Statement label', 'Compound statement', 'If Condition', 'If Block', 'Else block', 'Inline item', 'Statement label', 'Compound statement', 'If Condition', 'If Block', 'Else block',
'Case statement', 'Case selector', 'Case labels', 'Case label', 'else case', 'Repeat statement', 'Case statement', 'Case selector', 'Case labels', 'Case label', 'else case', 'Repeat statement',
@ -213,6 +219,7 @@ const
'On exception handler', 'Procedure decl', 'Function Decl', 'Constructor decl', 'Destructor decl', 'On exception handler', 'Procedure decl', 'Function Decl', 'Constructor decl', 'Destructor decl',
'Function heading', 'Procedure Heading', 'Constructor Heading', 'Destructor heading', 'Function heading', 'Procedure Heading', 'Constructor Heading', 'Destructor heading',
'Formal params', 'formal param', 'Function Return type', 'Procedure directives', 'Formal params', 'formal param', 'Function Return type', 'Procedure directives',
'Procedure [directives]',
'external directive', 'object type', 'init section', 'class type', 'class heritage', 'external directive', 'object type', 'init section', 'class type', 'class heritage',
'class body', 'class visiblity', 'class declarations', 'property', 'property param list', 'class body', 'class visiblity', 'class declarations', 'property', 'property param list',
'property specifier', 'interface type', 'interface heritage', 'interface type guid', 'property specifier', 'interface type', 'interface heritage', 'interface type guid',

View File

@ -164,6 +164,8 @@ type
ttStrict, ttStrict,
ttStdcall, ttStdcall,
ttAssembler, ttAssembler,
ttCompilerproc,
ttrtlproc,
ttForward, ttForward,
ttProtected, ttProtected,
ttStored, ttStored,
@ -225,6 +227,7 @@ type
ttExperimental, ttExperimental,
ttUnimplemented, ttUnimplemented,
ttInterrupt, ttInterrupt,
ttAlias,
{ built-in constants } { built-in constants }
ttNil, ttNil,
@ -389,7 +392,7 @@ const
[ttPrivate, ttProtected, ttPublic, ttPublished, ttAutomated]; [ttPrivate, ttProtected, ttPublic, ttPublished, ttAutomated];
ProcedureDirectives: TTokenTypeSet = [ttExternal, ttPascal, ttSafecall, ttAbstract, ProcedureDirectives: TTokenTypeSet = [ttExternal, ttPascal, ttSafecall, ttAbstract,
ttFar, ttStdcall, ttAssembler, ttInline, ttForward, ttFar, ttStdcall, ttAssembler, ttInline, ttCompilerproc, ttrtlproc, ttForward,
ttVirtual, ttCdecl, ttMessage, ttName, ttRegister, ttDispId, ttVirtual, ttCdecl, ttMessage, ttName, ttRegister, ttDispId,
ttNear, ttDynamic, ttExport, ttOverride, ttResident, ttLocal, ttNear, ttDynamic, ttExport, ttOverride, ttResident, ttLocal,
ttOverload, ttReintroduce, ttOverload, ttReintroduce,
@ -402,6 +405,8 @@ const
HintDirectives: TTokenTypeSet = [ttDeprecated, ttLibrary, ttPlatform, ttCVar, HintDirectives: TTokenTypeSet = [ttDeprecated, ttLibrary, ttPlatform, ttCVar,
ttExperimental, ttUnimplemented, ttStatic]; ttExperimental, ttUnimplemented, ttStatic];
AllProcDirectives: TTokenTypeSet = [];
AllDirectives: TTokenTypeSet = AllDirectives: TTokenTypeSet =
[ttAbsolute, ttExternal, ttPascal, ttSafecall, [ttAbsolute, ttExternal, ttPascal, ttSafecall,
ttAbstract, ttFar, ttPrivate, ttStdcall, ttAssembler, ttForward, ttAbstract, ttFar, ttPrivate, ttStdcall, ttAssembler, ttForward,
@ -695,6 +700,8 @@ begin
AddKeyword('public', wtReservedWordDirective, ttPublic); AddKeyword('public', wtReservedWordDirective, ttPublic);
AddKeyword('virtual', wtReservedWordDirective, ttVirtual); AddKeyword('virtual', wtReservedWordDirective, ttVirtual);
AddKeyword('cdecl', wtReservedWordDirective, ttCdecl); AddKeyword('cdecl', wtReservedWordDirective, ttCdecl);
AddKeyword('compilerproc', wtReservedWordDirective, ttCompilerproc);
AddKeyword('rtlproc', wtReservedWordDirective, ttrtlproc);
AddKeyword('ms_abi_default', wtReservedWordDirective, ttCdecl); AddKeyword('ms_abi_default', wtReservedWordDirective, ttCdecl);
AddKeyword('ms_abi_cdecl', wtReservedWordDirective, ttCdecl); AddKeyword('ms_abi_cdecl', wtReservedWordDirective, ttCdecl);
AddKeyword('sysv_abi_default', wtReservedWordDirective, ttCdecl); AddKeyword('sysv_abi_default', wtReservedWordDirective, ttCdecl);
@ -755,6 +762,7 @@ begin
AddKeyword('experimental', wtReservedWordDirective, ttExperimental); AddKeyword('experimental', wtReservedWordDirective, ttExperimental);
AddKeyword('unimplemented', wtReservedWordDirective, ttUnimplemented); AddKeyword('unimplemented', wtReservedWordDirective, ttUnimplemented);
AddKeyword('interrupt', wtReservedWordDirective, ttInterrupt); AddKeyword('interrupt', wtReservedWordDirective, ttInterrupt);
AddKeyword('alias', wtReservedWordDirective, ttAlias);
{ operators that are words not symbols } { operators that are words not symbols }
AddKeyword('and', wtOperator, ttAnd); AddKeyword('and', wtOperator, ttAnd);

View File

@ -38,6 +38,8 @@ interface
uses uses
SysUtils, strutils, SysUtils, strutils,
// LazUtils
LazFileUtils,
// LCL // LCL
Controls, Forms, Controls, Forms,
// local // local
@ -178,6 +180,7 @@ begin
// make a parse tree from it // make a parse tree from it
fcBuildParseTree.TokenList := lcTokenList; fcBuildParseTree.TokenList := lcTokenList;
fcBuildParseTree.IsIncFile := FilenameExtIs(FileName, 'inc');
fcBuildParseTree.BuildParseTree; fcBuildParseTree.BuildParseTree;
if fbShowParseTree then if fbShowParseTree then
ShowParseTree; ShowParseTree;

View File

@ -120,6 +120,7 @@ begin
fcConverter.InputCode := ReadFromIDE(pciUnit); fcConverter.InputCode := ReadFromIDE(pciUnit);
// now convert // now convert
fcConverter.FileName := fsCurrentUnitName;
fcConverter.Convert; fcConverter.Convert;
fsCurrentUnitName := ''; fsCurrentUnitName := '';
if not ConvertError then if not ConvertError then