unit tcbaseparser; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fpcunit, pastree, pscanner, pparser, testregistry; const DefaultMainFilename = 'afile.pp'; Type { TTestEngine } TTestEngine = Class(TPasTreeContainer) Private FList : TFPList; public Destructor Destroy; override; function CreateElement(AClass: TPTreeElement; const AName: String; AParent: TPasElement; AVisibility: TPasMemberVisibility; const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override; function FindElement(const AName: String): TPasElement; override; end; TTestPasParser = Class(TPasParser); { TTestParser } TTestParser = class(TTestCase) Private FDeclarations: TPasDeclarations; FDefinition: TPasElement; FEngine : TPasTreeContainer; FMainFilename: string; FModule: TPasModule; FParseResult: TPasElement; FScanner : TPascalScanner; FResolver : TStreamResolver; FParser : TTestPasParser; FSource: TStrings; FFileName : string; FIsUnit : Boolean; FImplementation : Boolean; FEndSource: Boolean; FUseImplementation: Boolean; function GetPL: TPasLibrary; function GetPP: TPasProgram; procedure CleanupParser; procedure SetupParser; protected procedure SetUp; override; procedure TearDown; override; procedure CreateEngine(var TheEngine: TPasTreeContainer); virtual; Procedure StartUnit(AUnitName : String); Procedure StartProgram(AFileName : String; AIn : String = ''; AOut : String = ''); Procedure StartLibrary(AFileName : String); Procedure UsesClause(Units : Array of string); Procedure StartImplementation; Procedure EndSource; Procedure Add(Const ALine : String); Procedure Add(Const Lines : array of String); Procedure StartParsing; Procedure ParseDeclarations; Procedure ParseModule; virtual; procedure ResetParser; Procedure CheckHint(AHint : TPasMemberHint); Function AssertExpression(Const Msg: String; AExpr : TPasExpr; aKind : TPasExprKind; AClass : TClass) : TPasExpr; Function AssertExpression(Const Msg: String; AExpr : TPasExpr; aKind : TPasExprKind; AValue : String) : TPrimitiveExpr; Function AssertExpression(Const Msg: String; AExpr : TPasExpr; OpCode : TExprOpCode) : TBinaryExpr; Procedure AssertExportSymbol(Const Msg: String; AIndex : Integer; AName,AExportName : String; AExportIndex : Integer = -1); Procedure AssertEquals(Const Msg : String; AExpected, AActual: TPasExprKind); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TLoopType); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TPasObjKind); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TExprOpCode); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TPasMemberHint); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TCallingConvention); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TArgumentAccess); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TVariableModifier); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TVariableModifiers); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TPasMemberVisibility); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureModifier); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureModifiers); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcTypeModifiers); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TAssignKind); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureMessageType); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TOperatorType); overload; Procedure AssertSame(Const Msg : String; AExpected, AActual: TPasElement); overload; Procedure HaveHint(AHint : TPasMemberHint; AHints : TPasMemberHints); Property Resolver : TStreamResolver Read FResolver; Property Scanner : TPascalScanner Read FScanner; Property Engine : TPasTreeContainer read FEngine; Property Parser : TTestPasParser read FParser ; Property Source : TStrings Read FSource; Property Module : TPasModule Read FModule; Property PasProgram : TPasProgram Read GetPP; Property PasLibrary : TPasLibrary Read GetPL; Property Declarations : TPasDeclarations read FDeclarations Write FDeclarations; Property Definition : TPasElement Read FDefinition Write FDefinition; // If set, Will be freed in teardown Property ParseResult : TPasElement Read FParseResult Write FParseResult; Property UseImplementation : Boolean Read FUseImplementation Write FUseImplementation; Property MainFilename: string read FMainFilename write FMainFilename; end; function ExtractFileUnitName(aFilename: string): string; function GetPasElementDesc(El: TPasElement): string; procedure ReadNextPascalToken(var Position: PChar; out TokenStart: PChar; NestedComments: boolean; SkipDirectives: boolean); implementation uses typinfo; function ExtractFileUnitName(aFilename: string): string; var p: Integer; begin Result:=ExtractFileName(aFilename); if Result='' then exit; for p:=length(Result) downto 1 do case Result[p] of '/','\': exit; '.': begin Delete(Result,p,length(Result)); exit; end; end; end; function GetPasElementDesc(El: TPasElement): string; begin if El=nil then exit('nil'); Result:=El.Name+':'+El.ClassName+'['+El.SourceFilename+','+IntToStr(El.SourceLinenumber)+']'; end; procedure ReadNextPascalToken(var Position: PChar; out TokenStart: PChar; NestedComments: boolean; SkipDirectives: boolean); const IdentChars = ['a'..'z','A'..'Z','_','0'..'9']; HexNumberChars = ['0'..'9','a'..'f','A'..'F']; var c1:char; CommentLvl: Integer; Src: PChar; begin Src:=Position; // read till next atom while true do begin case Src^ of #0: break; #1..#32: // spaces and special characters inc(Src); #$EF: if (Src[1]=#$BB) and (Src[2]=#$BF) then begin // skip UTF BOM inc(Src,3); end else break; '{': // comment start or compiler directive if (Src[1]='$') and (not SkipDirectives) then // compiler directive break else begin // Pascal comment => skip CommentLvl:=1; while true do begin inc(Src); case Src^ of #0: break; '{': if NestedComments then inc(CommentLvl); '}': begin dec(CommentLvl); if CommentLvl=0 then begin inc(Src); break; end; end; end; end; end; '/': // comment or real division if (Src[1]='/') then begin // comment start -> read til line end inc(Src); while not (Src^ in [#0,#10,#13]) do inc(Src); end else break; '(': // comment, bracket or compiler directive if (Src[1]='*') then begin if (Src[2]='$') and (not SkipDirectives) then // compiler directive break else begin // comment start -> read til comment end inc(Src,2); CommentLvl:=1; while true do begin case Src^ of #0: break; '(': if NestedComments and (Src[1]='*') then inc(CommentLvl); '*': if (Src[1]=')') then begin dec(CommentLvl); if CommentLvl=0 then begin inc(Src,2); break; end; inc(Position); end; end; inc(Src); end; end; end else // round bracket open break; else break; end; end; // read token TokenStart:=Src; c1:=Src^; case c1 of #0: ; 'A'..'Z','a'..'z','_': begin // identifier inc(Src); while Src^ in IdentChars do inc(Src); end; '0'..'9': // number begin inc(Src); // read numbers while (Src^ in ['0'..'9']) do inc(Src); if (Src^='.') and (Src[1]<>'.') then begin // real type number inc(Src); while (Src^ in ['0'..'9']) do inc(Src); end; if (Src^ in ['e','E']) then begin // read exponent inc(Src); if (Src^='-') then inc(Src); while (Src^ in ['0'..'9']) do inc(Src); end; end; '''','#': // string constant while true do case Src^ of #0: break; '#': begin inc(Src); while Src^ in ['0'..'9'] do inc(Src); end; '''': begin inc(Src); while not (Src^ in ['''',#0]) do inc(Src); if Src^='''' then inc(Src); end; else break; end; '$': // hex constant begin inc(Src); while Src^ in HexNumberChars do inc(Src); end; '&': // octal constant or keyword as identifier (e.g. &label) begin inc(Src); if Src^ in ['0'..'7'] then while Src^ in ['0'..'7'] do inc(Src) else while Src^ in IdentChars do inc(Src); end; '{': // compiler directive (it can't be a comment, because see above) begin CommentLvl:=1; while true do begin inc(Src); case Src^ of #0: break; '{': if NestedComments then inc(CommentLvl); '}': begin dec(CommentLvl); if CommentLvl=0 then begin inc(Src); break; end; end; end; end; end; '(': // bracket or compiler directive if (Src[1]='*') then begin // compiler directive -> read til comment end inc(Src,2); while (Src^<>#0) and ((Src^<>'*') or (Src[1]<>')')) do inc(Src); inc(Src,2); end else // round bracket open inc(Src); #192..#255: begin // read UTF8 character inc(Src); if ((ord(c1) and %11100000) = %11000000) then begin // could be 2 byte character if (ord(Src[0]) and %11000000) = %10000000 then inc(Src); end else if ((ord(c1) and %11110000) = %11100000) then begin // could be 3 byte character if ((ord(Src[0]) and %11000000) = %10000000) and ((ord(Src[1]) and %11000000) = %10000000) then inc(Src,2); end else if ((ord(c1) and %11111000) = %11110000) then begin // could be 4 byte character if ((ord(Src[0]) and %11000000) = %10000000) and ((ord(Src[1]) and %11000000) = %10000000) and ((ord(Src[2]) and %11000000) = %10000000) then inc(Src,3); end; end; else inc(Src); case c1 of '<': if Src^ in ['>','='] then inc(Src); '.': if Src^='.' then inc(Src); '@': if Src^='@' then begin // @@ label repeat inc(Src); until not (Src^ in IdentChars); end else if (Src^='=') and (c1 in [':','+','-','/','*','<','>']) then inc(Src); end; end; Position:=Src; end; { TTestEngine } destructor TTestEngine.Destroy; begin FreeAndNil(FList); inherited Destroy; end; function TTestEngine.CreateElement(AClass: TPTreeElement; const AName: String; AParent: TPasElement; AVisibility: TPasMemberVisibility; const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; begin //writeln('TTestEngine.CreateElement ',AName,' ',AClass.ClassName); Result := AClass.Create(AName, AParent); {$IFDEF CheckPasTreeRefCount}Result.RefIds.Add('CreateElement');{$ENDIF} Result.Visibility := AVisibility; Result.SourceFilename := ASourceFilename; Result.SourceLinenumber := ASourceLinenumber; if NeedComments and Assigned(CurrentParser) then begin // Writeln('Saving comment : ',CurrentParser.SavedComments); Result.DocComment:=CurrentParser.SavedComments; end; if AName<>'' then begin If not Assigned(FList) then FList:=TFPList.Create; FList.Add(Result); end; end; function TTestEngine.FindElement(const AName: String): TPasElement; Var I : Integer; begin Result:=Nil; if Assigned(FList) then begin I:=FList.Count-1; While (Result=Nil) and (I>=0) do begin if CompareText(TPasElement(FList[I]).Name,AName)=0 then Result:=TPasElement(FList[i]); Dec(i); end; end; end; function TTestParser.GetPP: TPasProgram; begin Result:=Module as TPasProgram; end; function TTestParser.GetPL: TPasLibrary; begin Result:=Module as TPasLibrary; end; procedure TTestParser.SetupParser; begin FResolver:=TStreamResolver.Create; FResolver.OwnsStreams:=True; FScanner:=TPascalScanner.Create(FResolver); FScanner.CurrentBoolSwitches:=FScanner.CurrentBoolSwitches+[bsHints,bsNotes,bsWarnings]; CreateEngine(FEngine); FParser:=TTestPasParser.Create(FScanner,FResolver,FEngine); FSource:=TStringList.Create; FModule:=Nil; FDeclarations:=Nil; FEndSource:=False; FImplementation:=False; FIsUnit:=False; end; procedure TTestParser.CleanupParser; begin {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser START'); {$ENDIF} if Not Assigned(FModule) then FreeAndNil(FDeclarations) else FDeclarations:=Nil; FImplementation:=False; FEndSource:=False; FIsUnit:=False; {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FModule'); {$ENDIF} ReleaseAndNil(TPasElement(FModule){$IFDEF CheckPasTreeRefCount},'CreateElement'{$ENDIF}); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FSource'); {$ENDIF} FreeAndNil(FSource); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FParseResult'); {$ENDIF} FreeAndNil(FParseResult); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FParser'); {$ENDIF} FreeAndNil(FParser); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FEngine'); {$ENDIF} FreeAndNil(FEngine); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FScanner'); {$ENDIF} FreeAndNil(FScanner); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser FResolver'); {$ENDIF} FreeAndNil(FResolver); {$IFDEF VerbosePasResolverMem} writeln('TTestParser.CleanupParser END'); {$ENDIF} end; procedure TTestParser.ResetParser; begin CleanupParser; SetupParser; end; procedure TTestParser.SetUp; begin FMainFilename:=DefaultMainFilename; Inherited; SetupParser; end; procedure TTestParser.TearDown; begin {$IFDEF VerbosePasResolverMem} writeln('TTestParser.TearDown START CleanupParser'); {$ENDIF} CleanupParser; {$IFDEF VerbosePasResolverMem} writeln('TTestParser.TearDown inherited'); {$ENDIF} Inherited; {$IFDEF VerbosePasResolverMem} writeln('TTestParser.TearDown END'); {$ENDIF} end; procedure TTestParser.CreateEngine(var TheEngine: TPasTreeContainer); begin TheEngine:=TTestEngine.Create; end; procedure TTestParser.StartUnit(AUnitName: String); begin FIsUnit:=True; If (AUnitName='') then AUnitName:=ExtractFileUnitName(MainFilename); Add('unit '+aUnitName+';'); Add(''); Add('interface'); Add(''); FFileName:=AUnitName+'.pp'; end; procedure TTestParser.StartProgram(AFileName : String; AIn : String = ''; AOut : String = ''); begin FIsUnit:=False; If (AFileName='') then AFileName:='proga'; FFileName:=AFileName+'.pp'; If (AIn<>'') then begin AFileName:=AFileName+'('+AIn; if (AOut<>'') then AFileName:=AFileName+','+AOut; AFileName:=AFileName+')'; end; Add('program '+AFileName+';'); FImplementation:=True; end; procedure TTestParser.StartLibrary(AFileName: String); begin FIsUnit:=False; If (AFileName='') then AFileName:='liba'; FFileName:=AFileName+'.pp'; Add('library '+AFileName+';'); FImplementation:=True; end; procedure TTestParser.UsesClause(Units: array of string); Var S : String; I : integer; begin S:=''; For I:=Low(units) to High(units) do begin If (S<>'') then S:=S+', '; S:=S+Units[i]; end; Add('uses '+S+';'); Add(''); end; procedure TTestParser.StartImplementation; begin if Not FImplementation then begin if UseImplementation then begin FSource.Insert(0,''); FSource.Insert(0,'Implementation'); FSource.Insert(0,''); end else begin Add(''); Add('Implementation'); Add(''); end; FImplementation:=True; end; end; procedure TTestParser.EndSource; begin if Not FEndSource then begin Add('end.'); FEndSource:=True; end; end; procedure TTestParser.Add(const ALine: String); begin FSource.Add(ALine); end; procedure TTestParser.Add(const Lines: array of String); var i: Integer; begin for i:=Low(Lines) to High(Lines) do Add(Lines[i]); end; procedure TTestParser.StartParsing; var i: Integer; begin If FIsUnit then StartImplementation; EndSource; If (FFileName='') then FFileName:=MainFilename; FResolver.AddStream(FFileName,TStringStream.Create(FSource.Text)); FScanner.OpenFile(FFileName); Writeln('// Test : ',Self.TestName); for i:=0 to FSource.Count-1 do Writeln(Format('%:4d: ',[i+1]),FSource[i]); end; procedure TTestParser.ParseDeclarations; begin if UseImplementation then StartImplementation; FSource.Insert(0,''); FSource.Insert(0,'interface'); FSource.Insert(0,''); FSource.Insert(0,'unit afile;'); if Not UseImplementation then StartImplementation; EndSource; ParseModule; if UseImplementation then FDeclarations:=Module.ImplementationSection else FDeclarations:=Module.InterfaceSection; end; procedure TTestParser.ParseModule; begin StartParsing; FParser.ParseMain(FModule); AssertNotNull('Module resulted in Module',FModule); AssertEquals('modulename',ChangeFileExt(FFileName,''),Module.Name); end; procedure TTestParser.CheckHint(AHint: TPasMemberHint); begin HaveHint(AHint,Definition.Hints); end; function TTestParser.AssertExpression(const Msg: String; AExpr: TPasExpr; aKind: TPasExprKind; AClass: TClass): TPasExpr; begin AssertNotNull(AExpr); AssertEquals(Msg+': Correct expression kind',aKind,AExpr.Kind); AssertEquals(Msg+': Correct expression class',AClass,AExpr.ClassType); Result:=AExpr; end; function TTestParser.AssertExpression(const Msg: String; AExpr: TPasExpr; aKind: TPasExprKind; AValue: String): TPrimitiveExpr; begin Result:=AssertExpression(Msg,AExpr,aKind,TPrimitiveExpr) as TPrimitiveExpr; AssertEquals(Msg+': Primitive expression value',AValue,TPrimitiveExpr(AExpr).Value); end; function TTestParser.AssertExpression(const Msg: String; AExpr: TPasExpr; OpCode: TExprOpCode): TBinaryExpr; begin Result:=AssertExpression(Msg,AExpr,pekBinary,TBinaryExpr) as TBinaryExpr; AssertEquals(Msg+': Binary opcode',OpCode,TBinaryExpr(AExpr).OpCode); end; procedure TTestParser.AssertExportSymbol(const Msg: String; AIndex: Integer; AName, AExportName: String; AExportIndex: Integer); Var E: TPasExportSymbol; begin AssertNotNull(Msg+'Have export symbols list',PasLibrary.LibrarySection.ExportSymbols); if AIndex>=PasLibrary.LibrarySection.ExportSymbols.Count then Fail(Format(Msg+'%d not a valid export list symbol',[AIndex])); AssertNotNull(Msg+'Have export symbol',PasLibrary.LibrarySection.ExportSymbols[Aindex]); AssertEquals(Msg+'Correct export symbol class',TPasExportSymbol,TObject(PasLibrary.LibrarySection.ExportSymbols[Aindex]).ClassType); E:=TPasExportSymbol(PasLibrary.LibrarySection.ExportSymbols[Aindex]); AssertEquals(Msg+'Correct export symbol name',AName,E.Name); if (AExportName='') then AssertNull(Msg+'No export name',E.ExportName) else begin AssertNotNull(Msg+'Export name symbol',E.ExportName); AssertEquals(Msg+'TPrimitiveExpr',TPrimitiveExpr,E.ExportName.CLassType); AssertEquals(Msg+'Correct export symbol export name ',''''+AExportName+'''',TPrimitiveExpr(E.ExportName).Value); end; If AExportIndex=-1 then AssertNull(Msg+'No export name',E.ExportIndex) else begin AssertNotNull(Msg+'Export name symbol',E.ExportIndex); AssertEquals(Msg+'TPrimitiveExpr',TPrimitiveExpr,E.ExportIndex.CLassType); AssertEquals(Msg+'Correct export symbol export index',IntToStr(AExportindex),TPrimitiveExpr(E.ExportIndex).Value); end; end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TPasExprKind); begin AssertEquals(Msg,GetEnumName(TypeInfo(TPasExprKind),Ord(AExpected)), GetEnumName(TypeInfo(TPasExprKind),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TLoopType); begin AssertEquals(Msg,GetEnumName(TypeInfo(TLoopType),Ord(AExpected)), GetEnumName(TypeInfo(TLoopType),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TPasObjKind); begin AssertEquals(Msg,GetEnumName(TypeInfo(TPasObjKind),Ord(AExpected)), GetEnumName(TypeInfo(TPasObjKind),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TExprOpCode); begin AssertEquals(Msg,GetEnumName(TypeInfo(TexprOpcode),Ord(AExpected)), GetEnumName(TypeInfo(TexprOpcode),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TPasMemberHint); begin AssertEquals(Msg,GetEnumName(TypeInfo(TPasMemberHint),Ord(AExpected)), GetEnumName(TypeInfo(TPasMemberHint),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TCallingConvention); begin AssertEquals(Msg,GetEnumName(TypeInfo(TCallingConvention),Ord(AExpected)), GetEnumName(TypeInfo(TCallingConvention),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TArgumentAccess); begin AssertEquals(Msg,GetEnumName(TypeInfo(TArgumentAccess),Ord(AExpected)), GetEnumName(TypeInfo(TArgumentAccess),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TVariableModifier); begin AssertEquals(Msg,GetEnumName(TypeInfo(TVariableModifier),Ord(AExpected)), GetEnumName(TypeInfo(TVariableModifier),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TVariableModifiers); Function sn (S : TVariableModifiers) : string; Var M : TVariableModifier; begin Result:=''; For M:=Low(TVariableModifier) to High(TVariableModifier) do if M in S then begin if (Result<>'') then Result:=Result+','; end; Result:='['+Result+']'; end; begin AssertEquals(Msg,Sn(AExpected),Sn(AActual)); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TPasMemberVisibility); begin AssertEquals(Msg,GetEnumName(TypeInfo(TPasMemberVisibility),Ord(AExpected)), GetEnumName(TypeInfo(TPasMemberVisibility),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TProcedureModifier); begin AssertEquals(Msg,GetEnumName(TypeInfo(TProcedureModifier),Ord(AExpected)), GetEnumName(TypeInfo(TProcedureModifier),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TProcedureModifiers); Function Sn (S : TProcedureModifiers) : String; Var m : TProcedureModifier; begin Result:=''; For M:=Low(TProcedureModifier) to High(TProcedureModifier) do If (m in S) then begin If (Result<>'') then Result:=Result+','; Result:=Result+GetEnumName(TypeInfo(TProcedureModifier),Ord(m)) end; end; begin AssertEquals(Msg,Sn(AExpected),SN(AActual)); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TProcTypeModifiers); Function Sn (S : TProcTypeModifiers) : String; Var m : TProcTypeModifier; begin Result:=''; For M:=Low(TProcTypeModifier) to High(TProcTypeModifier) do If (m in S) then begin If (Result<>'') then Result:=Result+','; Result:=Result+GetEnumName(TypeInfo(TProcTypeModifier),Ord(m)) end; end; begin AssertEquals(Msg,Sn(AExpected),SN(AActual)); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TAssignKind); begin AssertEquals(Msg,GetEnumName(TypeInfo(TAssignKind),Ord(AExpected)), GetEnumName(TypeInfo(TAssignKind),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TProcedureMessageType); begin AssertEquals(Msg,GetEnumName(TypeInfo(TProcedureMessageType),Ord(AExpected)), GetEnumName(TypeInfo(TProcedureMessageType),Ord(AActual))); end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TOperatorType); begin AssertEquals(Msg,GetEnumName(TypeInfo(TOperatorType),Ord(AExpected)), GetEnumName(TypeInfo(TOperatorType),Ord(AActual))); end; procedure TTestParser.AssertSame(const Msg: String; AExpected, AActual: TPasElement); begin if AExpected=AActual then exit; AssertEquals(Msg,GetPasElementDesc(AExpected),GetPasElementDesc(AActual)); end; procedure TTestParser.HaveHint(AHint: TPasMemberHint; AHints: TPasMemberHints); begin If not (AHint in AHints) then Fail(GetEnumName(TypeInfo(TPasMemberHint),Ord(AHint))+'hint expected.'); end; end.