* Parse inline variable declarations

This commit is contained in:
Michaël Van Canneyt 2023-12-06 19:15:14 +01:00
parent fc6fc67295
commit 5ba83e8d35
4 changed files with 296 additions and 40 deletions

View File

@ -1554,6 +1554,18 @@ type
Body: TPasImplElement;
end;
{ TPasInlineVarDeclStatement }
TPasInlineVarDeclStatement = class(TPasImplStatement)
public
Declarations: TFPList; // list of TPasVariable
Public
constructor Create(const aName : TPasTreeString; aParent: TPasElement); override;
procedure FreeChildren(Prepare: boolean); override;
destructor Destroy; override;
end;
TPasImplCaseStatement = class;
TPasImplCaseElse = class;
@ -1610,6 +1622,8 @@ type
StartExpr : TPasExpr;
EndExpr : TPasExpr; // if LoopType=ltIn this is nil
Variable: TPasVariable; // not used by TPasParser
VarType : TPasType; // For initialized variables
ImplicitTyped : Boolean;
Body: TPasImplElement;
Function Down: boolean; inline;// downto, backward compatibility
Function StartValue : TPasTreeString;
@ -3950,6 +3964,7 @@ begin
StartExpr:=TPasExpr(FreeChild(StartExpr,Prepare));
EndExpr:=TPasExpr(FreeChild(EndExpr,Prepare));
Variable:=TPasVariable(FreeChild(Variable,Prepare));
VarType:=TPasType(FreeChild(VarType,Prepare));
Body:=TPasImplElement(FreeChild(Body,Prepare));
inherited FreeChildren(Prepare);
end;
@ -5450,6 +5465,26 @@ begin
inherited ForEachCall(aMethodCall, Arg);
end;
{ TPasInlineVarDeclStatement }
constructor TPasInlineVarDeclStatement.Create(const aName: TPasTreeString; aParent: TPasElement);
begin
inherited Create('',aParent);
Declarations:=TFPList.Create;
end;
procedure TPasInlineVarDeclStatement.FreeChildren(Prepare: boolean);
begin
FreeChildList(Declarations,Prepare);
inherited FreeChildren(Prepare);
end;
destructor TPasInlineVarDeclStatement.Destroy;
begin
inherited Destroy;
FreeAndNil(Declarations)
end;
{ TPasImplTry }
procedure TPasImplTry.FreeChildren(Prepare: boolean);

View File

@ -294,6 +294,7 @@ type
private
const FTokenRingSize = 32;
type
TDeclParseType = (dptBasic,dptFull,dptInline);
{ TTokenRec }
@ -336,6 +337,7 @@ type
procedure ParseRaise;
procedure ParseWhile;
procedure ParseWith;
procedure ParseVarStatement;
end;
//PParseStatementParams = ^TParseStatementParams;
private
@ -377,12 +379,12 @@ type
function GetVariableModifiers(Parent: TPasElement;
Out VarMods: TVariableModifiers; Out LibName, ExportName: TPasExpr;
const AllowedMods: TVariableModifiers): string;
function GetVariableValueAndLocation(Parent : TPasElement; Out Value: TPasExpr; Out AbsoluteExpr: TPasExpr; Out Location: String): Boolean;
function GetVariableValueAndLocation(Parent : TPasElement; IsUntypedInline : Boolean; Out Value: TPasExpr; Out AbsoluteExpr: TPasExpr; Out Location: String): Boolean;
procedure HandleProcedureModifier(Parent: TPasElement; pm : TProcedureModifier; IsBracketed : Boolean = false);
procedure HandleProcedureTypeModifier(ProcType: TPasProcedureType; ptm : TProcTypeModifier);
procedure ParseMembersLocalConsts(AType: TPasMembersType; AVisibility: TPasMemberVisibility);
procedure ParseMembersLocalTypes(AType: TPasMembersType; AVisibility: TPasMemberVisibility);
procedure ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; Full: Boolean);
procedure ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; varParseType : TDeclParseType);
procedure SetOptions(AValue: TPOptions);
procedure OnScannerModeChanged(Sender: TObject; NewMode: TModeSwitch;
Before: boolean; var Handled: boolean);
@ -509,6 +511,7 @@ type
function ParseTypeDecl(Parent: TPasElement): TPasType; overload;
function ParseTypeDecl(Parent: TPasElement; NamePos : TPasSourcePos): TPasType; overload;
function ParseGenericTypeDecl(Parent: TPasElement; AddToParent: boolean): TPasGenericType;
function ParseType(Parent: TPasElement; const NamePos: TPasSourcePos; const TypeName: String; DeclParseType: TDeclParseType): TPasType;
function ParseType(Parent: TPasElement; const NamePos: TPasSourcePos; const TypeName: String = ''; Full: Boolean = false): TPasType;
function ParseReferenceToProcedureType(Parent: TPasElement; Const NamePos: TPasSourcePos; Const TypeName: String): TPasProcedureType;
function ParseProcedureType(Parent: TPasElement; Const NamePos: TPasSourcePos; Const TypeName: String; const PT: TProcType): TPasProcedureType;
@ -2124,6 +2127,16 @@ function TPasParser.ParseType(Parent: TPasElement;
const NamePos: TPasSourcePos; const TypeName: String; Full: Boolean
): TPasType;
Const
TS : Array[boolean] of TDeclParseType = (dptBasic,dptFull);
begin
Result:=ParseType(Parent,NamePos,TypeName,TS[Full]);
end;
function TPasParser.ParseType(Parent: TPasElement; const NamePos: TPasSourcePos; const TypeName: String;
DeclParseType: TDeclParseType): TPasType;
Type
TLocalClassType = (lctClass,lctObjcClass,lctObjcCategory,lctHelper);
@ -2145,7 +2158,7 @@ begin
Result := nil;
// NextToken and check pack mode
Pm:=CheckPackMode;
if Full then
if DeclParseType=dptFull then
CH:=Not (CurToken in NoHintTokens)
else
begin
@ -2178,7 +2191,7 @@ begin
begin
lClassType:=lctClass;
NextToken;
if not (Full or (CurToken=tkOf)) then
if not ((DeclParseType=dptFull) or (CurToken=tkOf)) then
ParseExc(nParserTypeNotAllowedHere,SParserTypeNotAllowedHere,[CurtokenText]);
// Parser.CurrentModeswitches:=Parser.CurrentModeswitches+[msClass];
@ -2227,7 +2240,7 @@ begin
Result:=ParseReferencetoProcedureType(Parent,NamePos,TypeName)
end
else
Result:=ParseSimpleType(Parent,NamePos,TypeName,Full);
Result:=ParseSimpleType(Parent,NamePos,TypeName,declParseType=dptFull);
end;
tkCaret: Result:=ParsePointerType(Parent,NamePos,TypeName);
tkFile: Result:=ParseFileType(Parent,NamePos,TypeName);
@ -2257,7 +2270,7 @@ begin
tkNumber,tkMinus,tkChar:
begin
UngetToken;
Result:=ParseRangeType(Parent,NamePos,TypeName,Full);
Result:=ParseRangeType(Parent,NamePos,TypeName,declParseType=dptFull);
end;
else
ParseExcExpectedIdentifier;
@ -4729,19 +4742,27 @@ begin
end;
end;
function TPasParser.GetVariableValueAndLocation(Parent: TPasElement; out
Value: TPasExpr; out AbsoluteExpr: TPasExpr; out Location: String): Boolean;
function TPasParser.GetVariableValueAndLocation(Parent: TPasElement; IsUntypedInline: Boolean; out Value: TPasExpr; out
AbsoluteExpr: TPasExpr; out Location: String): Boolean;
begin
Value:=Nil;
AbsoluteExpr:=Nil;
Location:='';
NextToken;
Result:=CurToken=tkEqual;
if IsUntypedInline then
Result:=CurToken=tkAssign
else
Result:=CurToken=tkEqual;
if Result then
begin
NextToken;
Value := DoParseConstValueExpression(Parent);
if IsUntypedInline then
Value := DoParseExpression(Parent)
else
Value := DoParseConstValueExpression(Parent);
end;
if (CurToken=tkAbsolute) then
begin
@ -4834,8 +4855,8 @@ end;
// Full means that a full variable declaration is being parsed.
procedure TPasParser.ParseVarList(Parent: TPasElement; VarList: TFPList;
AVisibility: TPasMemberVisibility; Full : Boolean);
procedure TPasParser.ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility;
varParseType: TDeclParseType);
// on Exception the VarList is restored, no need to Release the new elements
var
@ -4847,6 +4868,7 @@ var
VarMods, AllowedVarMods: TVariableModifiers;
D,Mods,AbsoluteLocString: string;
OldForceCaret,ok,ExternalStruct: Boolean;
IsUntyped : Boolean;
begin
Value:=Nil;
@ -4856,6 +4878,7 @@ begin
AbsoluteLocString:='';
VarCnt:=0;
ok:=false;
IsUntyped:=False;
try
D:=SaveComments; // This means we support only one comment per 'list'.
VarEl:=nil;
@ -4878,29 +4901,39 @@ begin
case CurToken of
tkColon: break;
tkComma: ExpectIdentifier;
tkAssign :
begin
if varParseType<>dptInline then
ParseExc(nParserExpectedCommaColon,SParserExpectedCommaColon);
UnGetToken; // Value parsing starts with NextToken
IsUnTyped:=True;
break;
end;
else
ParseExc(nParserExpectedCommaColon,SParserExpectedCommaColon);
end;
Until (CurToken=tkColon);
OldForceCaret:=Scanner.SetForceCaret(True);
try
VarType := ParseVarType(VarEl); // Note: this can insert elements into VarList!
{$IFDEF CheckPasTreeRefCount}if VarType.RefIds.IndexOf('CreateElement')>=0 then VarType.ChangeRefId('CreateElement','TPasVariable.VarType'){$ENDIF};
finally
Scanner.SetForceCaret(OldForceCaret);
end;
// read type
for i := VarList.Count-VarCnt to VarList.Count - 1 do
if CurToken=tkColon then
begin
VarEl:=TPasVariable(VarList[i]);
// Writeln(VarEl.Name, AVisibility);
VarEl.VarType := VarType;
//VarType.Parent := VarEl; // this is wrong for references
OldForceCaret:=Scanner.SetForceCaret(True);
try
VarType := ParseVarType(VarEl); // Note: this can insert elements into VarList!
{$IFDEF CheckPasTreeRefCount}if VarType.RefIds.IndexOf('CreateElement')>=0 then VarType.ChangeRefId('CreateElement','TPasVariable.VarType'){$ENDIF};
finally
Scanner.SetForceCaret(OldForceCaret);
end;
// read type
for i := VarList.Count-VarCnt to VarList.Count - 1 do
begin
VarEl:=TPasVariable(VarList[i]);
// Writeln(VarEl.Name, AVisibility);
VarEl.VarType := VarType;
//VarType.Parent := VarEl; // this is wrong for references
end;
end;
H:=CheckHint(Nil,False);
If Full then
GetVariableValueAndLocation(VarEl,Value,AbsoluteExpr,AbsoluteLocString);
If varParseType in [dptFull,dptInline]then
GetVariableValueAndLocation(VarEl,IsUnTyped,Value,AbsoluteExpr,AbsoluteLocString);
if VarCnt>1 then
begin
// multiple variables
@ -4917,7 +4950,7 @@ begin
and (Parent is TPasMembersType);
H:=H+CheckHint(Nil,False);
if Full or ExternalStruct then
if (VarParseType=dptFull) or ExternalStruct then
begin
NextToken;
If Curtoken<>tkSemicolon then
@ -5058,7 +5091,7 @@ begin
if ClosingBrace then
Include(tt,tkBraceClose);
try
ParseVarList(Parent,List,AVisibility,False);
ParseVarList(Parent,List,AVisibility,dptBasic);
except
on E : Exception do
if not TryErrorRecovery(CreateRecovery(E,tt,False)) then
@ -5073,7 +5106,7 @@ procedure TPasParser.ParseVarDecl(Parent: TPasElement; List: TFPList);
begin
try
ParseVarList(Parent,List,visDefault,True);
ParseVarList(Parent,List,visDefault,dptFull);
except
on E : Exception do
if not TryErrorRecovery(CreateRecovery(E,[tkSemicolon],False)) then
@ -6387,6 +6420,16 @@ begin
break;
tkEOF:
CheckToken(tkend);
tkVar:
begin
if not (msInlineVars in CurrentModeswitches) then
ParseExcSyntaxError;
CheckStatementCanStart;
NextToken;
Params.ParseVarStatement;
Params.CloseStatement(true);
end;
tkAt,tkAtAt,
tkIdentifier,tkspecialize,
tkNumber,tkString,tkfalse,tktrue,tkChar,
@ -8114,14 +8157,27 @@ end;
procedure TPasParser.TParseStatementParams.ParseFor;
// for VarName := StartValue to EndValue do
// for var VarName := StartValue to EndValue do
// for var VarName : Integer := StartValue to EndValue do
// for VarName in Expression do
var
ForLoop: TPasImplForLoop;
Expr: TPasExpr;
lt: TLoopType;
SrcPos: TPasSourcePos;
isVarDef : Boolean;
begin
ForLoop:=TPasImplForLoop(CreateElement(TPasImplForLoop));
isVarDef:=False;
if (msInlineVars in Parser.CurrentModeswitches) then
begin
Parser.NextToken;
isVarDef:=Parser.CurToken=tkvar;
if not IsVarDef then
Parser.UngetToken;
end;
SrcPos:=Parser.CurTokenPos;
Parser.ExpectIdentifier;
Expr:=Parser.CreatePrimitiveExpr(ForLoop,pekIdent,Parser.CurTokenString);
ForLoop.VariableName:=Expr;
@ -8131,15 +8187,26 @@ begin
tkAssign:
begin
lt:=ltNormal;
ForLoop.ImplicitTyped:=IsVarDef and (ForLoop.VarType=Nil);
break;
end;
tkColon:
begin
if not IsVarDef then
Parser.ParseExc(nParserExpectedAssignIn,SParserExpectedAssignIn);
ForLoop.VarType:=Parser.ParseType(ForLoop,SrcPos);
// We should be on identifier
end;
tkin:
begin
lt:=ltIn;
ForLoop.ImplicitTyped:=IsVarDef and (ForLoop.VarType=Nil);
break;
end;
tkDot:
begin
if IsVarDef then
Parser.ParseExc(nParserExpectedAssignIn,SParserExpectedAssignIn);
SrcPos:=Parser.CurTokenPos;
Parser.ExpectIdentifier;
Parser.AddToBinaryExprChain(Expr,
@ -8276,6 +8343,41 @@ begin
until false;
end;
procedure TPasParser.TParseStatementParams.ParseVarStatement;
var
List : TFPList;
VarSt : TPasInlineVarDeclStatement;
SrcPos: TPasSourcePos;
I : Integer;
V : TPasVariable;
begin
// var a : Integer;
// var a : Integer = Expr;
// var a := Expr;
SrcPos:=Parser.CurTokenPos;
VarSt:=TPasInlineVarDeclStatement(CreateElement(TPasInlineVarDeclStatement,SrcPos));
NewImplElement:=VarSt;
CurBlock.AddElement(VarSt);
List := TFPList.Create;
try
Parser.ParseVarList(VarSt,List,visDefault,dptInline);
For I:=0 to List.Count-1 do
begin
V:=TPasVariable(List[i]);
List[i]:=Nil;
VarSt.Declarations.Add(V);
end;
finally
For I:=0 to List.count-1 do
if List[i]<>Nil then
TObject(List[I]).Free;
List.Free;
end;
end;
function TPasParser.TParseStatementParams.ParseOn: boolean;
// in try except:
// on E: Exception do

View File

@ -341,7 +341,8 @@ type
msExternalClass, { pas2js: Allow external class definitions }
msOmitRTTI, { pas2js: treat class section 'published' as 'public' and typeinfo does not work on symbols declared with this switch }
msMultiLineStrings, { pas2js: Multiline strings }
msDelphiMultiLineStrings { Delpi-compatible multiline strings }
msDelphiMultiLineStrings, { Delpi-compatible multiline strings }
msInlineVars { Allow inline var declarations }
);
TModeSwitches = Set of TModeSwitch;
@ -1184,7 +1185,8 @@ const
'EXTERNALCLASS',
'OMITRTTI',
'MULTILINESTRINGS',
'DELPHIMULTILINESTRINGS'
'DELPHIMULTILINESTRINGS',
'INLINEVARS'
);
LetterSwitchNames: array['A'..'Z'] of TPasScannerString=(
@ -1272,7 +1274,8 @@ const
msOut,msDefaultPara,msDuplicateNames,msHintDirective,
msProperty,msDefaultInline,msExcept,msAdvancedRecords,msTypeHelpers,
msPrefixedAttributes,msArrayOperators,msImplicitFunctionSpec,
msFunctionReferences,msAnonymousFunctions,msDelphiMultiLineStrings
msFunctionReferences,msAnonymousFunctions,msDelphiMultiLineStrings,
msInlineVars
];
DelphiUnicodeModeSwitches = delphimodeswitches + [msSystemCodePage,msDefaultUnicodestring];
@ -5359,6 +5362,7 @@ var
OldLength: integer;
Ch: AnsiChar;
LE: String[2];
I : Integer;
{$else}
TokenStart: Integer;
s: String;
@ -5393,14 +5397,14 @@ begin
begin
SectionLength:=FTokenPos - TokenStart;
{$ifdef UsePChar}
SetLength(FCurTokenString, OldLength + SectionLength + length(LineEnding)); // Corrected JC
SetLength(FCurTokenString, OldLength + SectionLength + length(LE)); // Corrected JC
if SectionLength > 0 then
Move(TokenStart^, FCurTokenString[OldLength + 1],SectionLength);
Inc(OldLength, SectionLength);
for Ch in LE do
for I:=1 to Length(LE) do
begin
Inc(OldLength);
FCurTokenString[OldLength] := Ch;
FCurTokenString[OldLength] := LE[i];
end;
{$else}
FCurTokenString:=FCurTokenString+copy(FCurLine,TokenStart,SectionLength)+LineEnding; // Corrected JC
@ -5455,6 +5459,7 @@ var
TokenStart: PAnsiChar;
OldLength: integer;
Ch: AnsiChar;
I : Integer;
LE: String[2];
{$else}
TokenStart: Integer;
@ -5487,16 +5492,16 @@ begin
begin
SectionLength := FTokenPos - TokenStart;
{$ifdef UsePChar}
SetLength(FCurTokenString, OldLength + SectionLength + length(LineEnding)); // Corrected JC
SetLength(FCurTokenString, OldLength + SectionLength + length(LE)); // Corrected JC
if SectionLength > 0 then
Move(TokenStart^, FCurTokenString[OldLength + 1],SectionLength);
// Corrected JC: Append the correct lineending
Inc(OldLength, SectionLength);
for Ch in LE do
for I:=1 to length(LE) do
begin
Inc(OldLength);
FCurTokenString[OldLength] := Ch;
FCurTokenString[OldLength] := LE[i];
end;
{$else}
FCurTokenString:=FCurTokenString+copy(FCurLine,TokenStart,SectionLength)+LineEnding; // Corrected JC

View File

@ -82,7 +82,11 @@ Type
procedure TestRepeatBlockNosemicolon;
Procedure TestRepeatNested;
Procedure TestFor;
Procedure TestForVarDef;
Procedure TestForVarDefImplicit;
Procedure TestForIn;
Procedure TestForInDef;
Procedure TestForInDefImplicit;
Procedure TestForExpr;
Procedure TestForBlock;
procedure TestDowntoBlock;
@ -133,6 +137,9 @@ Type
Procedure TestPlatformIdentifier;
Procedure TestPlatformIdentifier2;
Procedure TestArgumentNameOn;
Procedure TestInlineVarDeclaration;
Procedure TestInlineVarDeclarationDotted;
Procedure TestInlineVarDeclarationNoType;
end;
@ -909,6 +916,44 @@ begin
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForVarDef;
Var
F : TPasImplForLoop;
begin
AddStatements([
'{$modeswitch inlinevars}',
'for var a : integer := 1 to 10 do',';'
]);
ParseModule;
F:=AssertStatement('For statement',TPasImplForLoop) as TPasImplForLoop;
AssertExpression('Loop variable name',F.VariableName,pekIdent,'a');
AssertEquals('Loop type',ltNormal,F.Looptype);
AssertEquals('Implicitly typed',False,F.ImplicitTyped);
AssertNotNull('Var type',F.VarType);
AssertExpression('Start value',F.StartExpr,pekNumber,'1');
AssertExpression('end value',F.EndExpr,pekNumber,'10');
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForVarDefImplicit;
Var
F : TPasImplForLoop;
begin
AddStatements([
'{$modeswitch inlinevars}',
'for var a := 1 to 10 do',';'
]);
ParseModule;
F:=AssertStatement('For statement',TPasImplForLoop) as TPasImplForLoop;
AssertExpression('Loop variable name',F.VariableName,pekIdent,'a');
AssertEquals('Loop type',ltNormal,F.Looptype);
AssertEquals('Implicitly typed',True,F.ImplicitTyped);
AssertNull('Var type',F.VarType);
AssertExpression('Start value',F.StartExpr,pekNumber,'1');
AssertExpression('end value',F.EndExpr,pekNumber,'10');
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForIn;
Var
@ -926,6 +971,45 @@ begin
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForInDef;
Var
F : TPasImplForLoop;
begin
TestStatement(['{$modeswitch inlinevars}',
'For var a : Integer in SomeSet Do',
';']);
F:=AssertStatement('For statement',TPasImplForLoop) as TPasImplForLoop;
AssertExpression('Loop variable name',F.VariableName,pekIdent,'a');
AssertEquals('Loop type',ltIn,F.Looptype);
AssertEquals('Implicitly typed',False,F.ImplicitTyped);
AssertNotNull('Var type',F.VarType);
AssertEquals('In loop',False,F.Down);
AssertExpression('Start value',F.StartExpr,pekIdent,'SomeSet');
AssertNull('Loop type',F.EndExpr);
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForInDefImplicit;
Var
F : TPasImplForLoop;
begin
TestStatement(['{$modeswitch inlinevars}',
'For var a in SomeSet Do',
';']);
F:=AssertStatement('For statement',TPasImplForLoop) as TPasImplForLoop;
AssertExpression('Loop variable name',F.VariableName,pekIdent,'a');
AssertEquals('Loop type',ltIn,F.Looptype);
AssertEquals('Implicitly typed',True,F.ImplicitTyped);
AssertNull('Var type',F.VarType);
AssertEquals('In loop',False,F.Down);
AssertExpression('Start value',F.StartExpr,pekIdent,'SomeSet');
AssertNull('Loop type',F.EndExpr);
AssertNull('Empty body',F.Body);
end;
procedure TTestStatementParser.TestForExpr;
Var
F : TPasImplForLoop;
@ -1984,6 +2068,36 @@ begin
ParseModule;
end;
procedure TTestStatementParser.TestInlineVarDeclaration;
begin
AddStatements([
'{$modeswitch inlinevars}',
'var a : integer;'
]);
ParseModule;
AssertStatement('Var declaration statement',TPasInlineVarDeclStatement);
end;
procedure TTestStatementParser.TestInlineVarDeclarationDotted;
begin
AddStatements([
'{$modeswitch inlinevars}',
'var a := c.d(x);'
]);
ParseModule;
AssertStatement('Var declaration statement',TPasInlineVarDeclStatement);
end;
procedure TTestStatementParser.TestInlineVarDeclarationNoType;
begin
AddStatements([
'{$modeswitch inlinevars}',
'var a := 1;'
]);
ParseModule;
AssertStatement('Var declaration statement',TPasInlineVarDeclStatement);
end;
procedure TTestStatementParser.TestGotoInIfThen;
begin